Чт авг 03, 2023 09:27:55
static void hw_dcmi_gpio_init ( void )
{
LL_GPIO_InitTypeDef GPIO_InitStruct = { 0 };
uint16_t pins[NUM_DCMI_PINS] = { DCMI_PIXCLK_PIN, DCMI_VSYNC_PIN, DCMI_HSYNC_PIN,
ADC_D0_PIN, ADC_D1_PIN, ADC_D2_PIN,
ADC_D3_PIN, ADC_D4_PIN, ADC_D5_PIN, ADC_D6_PIN,
ADC_D7_PIN, ADC_D8_PIN, ADC_D9_PIN, ADC_D10_PIN,
ADC_D11_PIN };
GPIO_TypeDef * ports[NUM_DCMI_PINS] = { DCMI_PIXCLK_PORT, DCMI_VSYNC_PORT, DCMI_HSYNC_PORT,
ADC_D0_PORT, ADC_D1_PORT, ADC_D2_PORT,
ADC_D3_PORT, ADC_D4_PORT, ADC_D5_PORT, ADC_D6_PORT,
ADC_D7_PORT, ADC_D8_PORT, ADC_D9_PORT, ADC_D10_PORT,
ADC_D11_PORT };
/* enable required clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = LL_GPIO_AF_13;
for ( uint8_t i = 0; i < NUM_DCMI_PINS; i++ )
{
GPIO_InitStruct.Pin = pins[i];
LL_GPIO_Init( ports[i], &GPIO_InitStruct );
}
/* not a DCMI pin, but shorted to HSYNC and VSYNC. Init and set high */
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pin = ADC_SYNC_PIN;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
LL_GPIO_Init(ADC_SYNC_PORT, &GPIO_InitStruct);
ADC_SYNC_PORT->BSRR |= (uint32_t)ADC_SYNC_PIN;
}
static void hw_adc_clksrc_init ( void )
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
// enable RCC clocks for timer dependencies
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/* DCMI PIXCLK source timer */
htim_dcmi_pixclkc_src.Instance = DCMI_PIXCLK_TIM_INSTANCE;
htim_dcmi_pixclkc_src.Init.Prescaler = 0;
htim_dcmi_pixclkc_src.Init.CounterMode = TIM_COUNTERMODE_UP;
htim_dcmi_pixclkc_src.Init.Period = (uint32_t)(272.0f / DCMI_CLKSRC_FREQ_MHZ / 2.0f);
htim_dcmi_pixclkc_src.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim_dcmi_pixclkc_src.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim_dcmi_pixclkc_src) != HAL_OK)
Error_Handler();
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim_dcmi_pixclkc_src, &sClockSourceConfig) != HAL_OK)
Error_Handler();
if (HAL_TIM_OC_Init(&htim_dcmi_pixclkc_src) != HAL_OK)
Error_Handler();
sMasterConfig.MasterOutputTrigger = TIM_TRGO_ENABLE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim_dcmi_pixclkc_src, &sMasterConfig) != HAL_OK)
Error_Handler();
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_OC_ConfigChannel(&htim_dcmi_pixclkc_src, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
Error_Handler();
__HAL_TIM_ENABLE_OCxPRELOAD(&htim_dcmi_pixclkc_src, TIM_CHANNEL_1);
/* ADC clock source */
htim_adc_clksrc.Instance = ADC_CLKSRC_TIM_INSTANCE;
htim_adc_clksrc.Init.Prescaler = 0;
htim_adc_clksrc.Init.CounterMode = TIM_COUNTERMODE_UP;
htim_adc_clksrc.Init.Period = (uint32_t)( 272.0f / DCMI_CLKSRC_FREQ_MHZ ) + 1U;
htim_adc_clksrc.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim_adc_clksrc.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim_adc_clksrc) != HAL_OK)
Error_Handler();
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim_adc_clksrc, &sClockSourceConfig) != HAL_OK)
Error_Handler();
if (HAL_TIM_OC_Init(&htim_adc_clksrc) != HAL_OK)
Error_Handler();
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_GATED;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim_adc_clksrc, &sSlaveConfig) != HAL_OK)
Error_Handler();
/* sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; */
/* sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE; */
/* if (HAL_TIMEx_MasterConfigSynchronization(&htim_adc_clksrc, &sMasterConfig) != HAL_OK) */
/* Error_Handler(); */
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_OC_ConfigChannel(&htim_adc_clksrc, &sConfigOC, ADC_CLKSRC_TIM_CHANNEL) != HAL_OK)
Error_Handler();
__HAL_TIM_ENABLE_OCxPRELOAD(&htim_adc_clksrc, ADC_CLKSRC_TIM_CHANNEL);
}
static void hw_dcmi_dma_init ( void )
{
/* enable the DCMI clock and read the register value back to introduce a delay */
RCC->AHB2ENR |= RCC_AHB2ENR_DCMI_PSSIEN;
tmp_reg = RCC->AHB2ENR & RCC_AHB2ENR_DCMI_PSSIEN;
/* /1* start with rising edge active on PCKPOL */
SET_BIT(DCMI->CR, DCMI_CR_PCKPOL);
/* configure the DCMI control registers */
DCMI->CR |= (DCMI_CR_EDM_1 & ~DCMI_CR_EDM_0); // 12-bit data
DCMI->CR |= (DCMI_CR_VSPOL | DCMI_CR_HSPOL); // VSYNC/HSYNC high
DCMI->CR |= (DCMI_CR_ENABLE); // enable the DCMI peripheral
/* SEGGER_RTT_printf(0, "DCMI CR: 0x%x\n", DCMI->CR); */
/************************************
* DMA CONFIGURATION
***********************************/
/* note: see section 15.3.19 in the reference manual */
/* enable the DMA2 clock and read the register value back to introduce a delay */
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
tmp_reg = RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN;
/* disable DMA stream and wait for it to actually disable */
DMA2_Stream1->CR &= ~(DMA_SxCR_EN);
while ( (DMA2_Stream1->CR & DMA_SxCR_EN) != 0 );
/* set the peripheral address to the DCMI DR register */
WRITE_REG(DMA2_Stream1->PAR, (uint32_t)&DCMI->DR);
/* set the memory address to point to the dest. buffer */
WRITE_REG(DMA2_Stream1->M0AR, (uint32_t)&adcBffer[0]);
/* configure the total number of items to be transferred */
MODIFY_REG(DMA2_Stream1->NDTR, DMA_SxNDT, DCMI_ADC_DATA_BUF_SZ/4);
/* use DMAMUX1 to route a DMA request line (DCMI has ID 75) to the DMA channel */
MODIFY_REG(DMAMUX1_Channel9->CCR, DMAMUX_CxCR_DMAREQ_ID, 75U);
/* configure stream priority to very high */
DMA2_Stream1->CR |= (DMA_SxCR_PL);
/* direction: peripheral-to-memory */
DMA2_Stream1->CR &= ~(DMA_SxCR_DIR);
/* enable memory increment */
DMA2_Stream1->CR |= DMA_SxCR_MINC;
/* disable peripheral inc */
DMA2_Stream1->CR &= ~(DMA_SxCR_PINC);
/* set memory burst to INC8 */
DMA2_Stream1->CR |= DMA_SxCR_MBURST_1;
DMA2_Stream1->CR &= ~(DMA_SxCR_MBURST_0);
/* set peripheral burst to single */
DMA2_Stream1->CR &= ~(DMA_SxCR_PBURST);
/* set peripheral data size to a word */
DMA2_Stream1->CR |= DMA_SxCR_PSIZE_1;
DMA2_Stream1->CR &= ~DMA_SxCR_PSIZE_0;
/* set memory data size to a word */
DMA2_Stream1->CR |= DMA_SxCR_MSIZE_1;
DMA2_Stream1->CR &= ~DMA_SxCR_MSIZE_0;
/* enable circular mode */
DMA2_Stream1->CR |= DMA_SxCR_CIRC;
/* disable peripheral flow controller */
DMA2_Stream1->CR &= ~DMA_SxCR_PFCTRL;
/* ensable DMA stream and wait for it to actually ensable */
DMA2_Stream1->CR |= (DMA_SxCR_EN);
while ( (DMA2_Stream1->CR & DMA_SxCR_EN) == 0 );
/* SEGGER_RTT_printf(0, "DMA2_Stream1 CR: 0x%x\n", DMA2_Stream1->CR); */
/* SEGGER_RTT_printf(0, "DMAMUX1_Channel9 CCR: 0x%x\n", DMAMUX1_Channel9->CCR); */
/* configure DMA2_Stream1 interrupt */
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
NVIC_SetPriority(DMA2_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0x01, 0x02));
NVIC_EnableIRQ(DMA2_Stream1_IRQn);
/* enable DMA stream interrupt */
DMA2_Stream1->CR |= DMA_SxCR_TCIE;
}
Чт авг 03, 2023 09:42:05
Чт авг 03, 2023 09:56:19
Чт авг 03, 2023 10:08:48
Чт авг 03, 2023 19:13:10
Если глобально, то зачем вообще внешний АЦП? Там и внутренний многим даст прикурить.
Необходимо сделать так, чтобы в момент считывания преобразователь гарантированно установил на шине нужные данные, в этом смысл.
Посмотрите в какой момент интерфейс контроллера производит выборку данных с линии и в какой момент АЦП выставляет нужные уровни.
Чт авг 03, 2023 20:19:30
Чт авг 03, 2023 20:23:01
Чт авг 03, 2023 21:01:15
Чт авг 03, 2023 21:50:27
dimaib писал(а):прерываний на этот случай нет.
dimaib писал(а):используется DCMI и DMA
dimaib писал(а):Частота тактирования должна быть не менее 4МГц.
dimaib писал(а):данные требуется считывать с частотой не менее 4МГц
Пт авг 04, 2023 00:48:55
Прерывания и не нужны. А если уж можно позволить себе прерывания, то всякие прочие интерфейсы и не требуются.
Это разные вещи. Что требуется всё-таки? А если 16-битные АЦП запустить в разрешении 12 бит, то всё равно мало скорости?
у Вас на картинке есть параметр tpd (задержка от соответствующего фронта на установку данных на шине - только после нее данные достоверны) по тексту у Вас о нем ни слова.
т.е. Вам надо не только удвоение частоты (захват чаще), но и фазовый сдвиг на этот самый tpd (для захвата).
или аппаратно вкарячивать n кол-во вентилей, или жужать двумя таймерами.
Пт авг 04, 2023 02:10:34
Пт авг 04, 2023 02:28:58
Зачем все эти сложности с параллельными шинами и т.п.? Почему не использовать АЦП встроенный в МК?
Пт авг 04, 2023 02:34:53
Пт авг 04, 2023 09:16:43
Пт авг 04, 2023 09:26:33
Пт авг 04, 2023 10:10:00
Пт авг 04, 2023 12:20:33
Пт авг 04, 2023 19:59:15
Тогда давайте актуальную схему. Подозреваю, что затык какой-то вовсе не великий.
Данные принимаются по спаду DCMI_PIXCLK
Это значит, что JK-триггер должен переворачиваться по нарастанию DCMI_PIXCLK
Проверьте.
И он должен быть максимально быстрым (инверторы на входе нежелательны из-за задержки).
Пт авг 04, 2023 23:15:36
If the ADCs are run with synchronized timing, this same clock can
be applied to the MUX_SELECT bit. After the MUX_SELECT
rising edge, either data port will have the data for its respective
channel; after the falling edge, the alternate channel’s data will be
placed on the bus.
Пт авг 04, 2023 23:34:11
Очевидно, это как раз желаемая ситуация. То есть за один период тактирования данные высовываются то одни, то другие. А интерфейс считывает только один раз за период, причём сразу по заднему фронту (по спаду). А уж успел там АЦП данные выставить или нет это уже его проблемы.