通过DCMI从并行ADC在STM32F4xx中进行数据输入

众所周知,STM32F4xx微控制器系列具有板载足够高的内核,非常适合“非切肉”任务,而DSP在“管道”(clk-data)模式下没有最简单的并行总线的完整数据输入接口。 抽了dm00037051.pdf后,我发现了一个非特定的但乍看之下合适的选项-DCMI(数码相机接口)接口。

当然,对于已加载的经典DSP(FIR,IIR,FFT)使用STM32微控制器不是最佳选择,但是如果卡突然放下而该微控制器的功能仍然足够,那么您还需要足够数量的低速接口。 关于这个削减。





在一个有“紧迫”期限和预算的项目中,有必要实施一个具有最佳选择的“铁杆”:质量,尺寸,消耗。 作为基本功能,需要以软实时模式来自ADC的数字信号处理(滤波和统计分析)。 对于处理,我希望有一个单精度浮点。 来自ADC的信号以48 MHz的中间频率接收。 1 MHz信号频带。 为了实现信号频谱从中频到零的传输,最好使用宽带ADC的二次采样。 同样,有必要通过以太网,SPI,UART,I2C接收和传输信息并处理中断。


实施截止日期和特定的DSP功能不允许将FPGA用于这些目的。 使用Blackfin系列的著名信号处理器,著名的Analog Devices没有通信经验,并且在附近访问时没有调试工具和演示板。 一旦使用了旗舰级DSP处理器ADSP-TS201 TigerSHARC,昂贵的产品就只能进行近距离和长时间的通信。 此外,Blackfin缺乏IEEE-754的硬件实现。 此外,必须使用128 KB的ADC加上30 KB的处理开销来接受连续的数据块,并且如果没有外部存储器,则很难将所有内容投入预算较少的内容。


通常,只有STM32F407板卡(来自中国的发现和定制)。 我怀疑,现在许多处理相关主题的人都可以得到这样的普遍帮助。 还有一个Terasic ADA-HSMC板,其上安装了AD9228双通道ADC(12位,Fd = 65 msps,带宽= 315 MHz)。


一个非特定但非常合适的选项是DCMI(数码相机接口)接口,该接口是在STM32F4中实现的硬件。


该接口的操作在RM0090,DocID018909,第454/1718页中进行了描述。 本文档提供了以下四个图。


因此,要求保护的输入频率高达54 MHz。 这就足够了-我们的子采样率是10 MHz。 这是所使用的DCMI接口信号集:





注意:D13,D14仅以144引脚封装提供。 我们有100针,但我们不需要它们。 (尽管ADI公司具有类似的14位ADC-AD9248)。


这是该接口的通用时序图:





这是JPEG帧格式模式下接口的时序图:





我们将使用此操作模式作为 他最适合我们。


在这种模式下,VSYNC信号被拉至电源。 我们将使用HSYNC作为外部信号来启用通过接口的数据接收。


我们在LQFP100封装中使用了STM32F407RGT6微控制器。


AD9238 ADC的输入用于对应通道PDWN_A(B)的关断(省电)模式,输出许可为OEB_A(B)。 从任何控制器引脚上获取它们都是合乎逻辑的。 结果,引脚连接图将如下所示:



由于此ADC没有输出时钟信号,因此必须使用乘法(时钟缓冲器)。 我们使用了德州仪器(TI)的LMK00101-价格合理,抖动低,而且最重要的是-马上就可以使用)。


在处理过程中,我们考虑到ADC并行总线上的数据相对于输入时钟信号的延迟为7个时钟周期。


我们将通过DMA接收数据(当然)。 这是用于初始化DCMI和DMA的源代码。


我们打开所需端口DCMI和DMA2的时钟


GPIO_InitTypeDef GPIO_InitStructure; /* Enable DCMI GPIOs clocks */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOD, ENABLE); /* Enable DCMI clock */ RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); 

该引脚(PA5)将模拟划分为帧-HSYNC。 退出时初始化


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_ResetBits(GPIOA, GPIO_Pin_5); //HSYNC_PA4 -PA5 -> GND 

在DCMI模式下配置相应的引脚


  /* PCLK */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI); /* D0-D7 */ GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_DCMI); //D0* GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_DCMI); //D1* GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_DCMI); //D2* GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_DCMI); //D3* GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_DCMI); //D4* GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI); //D5* GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_DCMI); //D6* GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_DCMI); //D7* GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_DCMI); //D8* GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_DCMI); //D9* GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_DCMI); //D10* GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_DCMI); //D11* /* VSYNC */ GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI); /* HSYNC */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI); /* DCMI GPIO configuration **************************************************/ /* D0,D1,D2,D3,D4,D8,D9 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(GPIOC, &GPIO_InitStructure); /* D6,D7*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; GPIO_Init(GPIOE, &GPIO_InitStructure); /* D10, D5, VSYNC(PB7) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); /* D11(PD2) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); /* PCLK(PA6) HSYNC(PA4)*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); 

这里最有趣的是将DCMI配置为与JPEG帧相对应的模式。


  DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous; DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Embedded; DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising; DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_Low; DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_High; DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame; DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_12b; 

DMA设定


  DCMI_Init(&DCMI_InitStructure); DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //0x50050028 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DCMI_PendingData; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = MAX_DOWBLE_BUF; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream1, &DMA_InitStructure); 

在从相应的DMA通道接收数据结束时设置中断


  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 

处理程序的代码,在其中关闭DCMI数据接收并设置数据就绪标志。


 void EXTI1_IRQHandler() { EXTI_ClearFlag(EXTI_Line1); DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TCIF1); DMA_Cmd(DMA2_Stream1, DISABLE); DCMI_Cmd(DISABLE); dma_recv_f = 1; //GPIO_ResetBits(GPIOE, GPIO_Pin_0); //VSYNC reset } 

一切都与设置有关。 现在,打开DMA通道DCMI块,开始以JPEG帧模式接收DCMI数据。


  /* Enable DMA transfer */ DMA_Cmd(DMA2_Stream1, ENABLE); /* Enable DCMI interface */ DCMI_Cmd(ENABLE); /* Start Image capture */ DCMI_CaptureCmd(ENABLE); DCMI_JPEGCmd(ENABLE); 

主程序的循环。 在这里,轮询标志并重新开始数据接收。


 uint8_t dma_recv_f = 0; //,     . uint16_t DCMI_PendingData[8500]; //    DCMI int main(void) { DCMI_Config(); //        __enable_irq(); //  while(1) { while(dma_recv_f!=1){}; //    dma_recv_f = 0; //  /*     */ Re_DMA_Config(DCMI_PendingData, glob_cnt); // DMA } } 

注意:如果您需要使用双缓冲来实时接收和处理数据,则stm32f4会在一半缓冲区已满时采用中断机制。 在DMA设置中,则必须设置数据接收的连续循环模式。 例如:


  // DMA2 Stream0 channel0 configuration ************************************** DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //0x50050028 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DCMI_PendingData; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_BufferSize = buf_size; <b>DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;</b> //     DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; <b>DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;</b> //  DMA     DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 

在中断处理程序中,只有在填满整个缓冲区和用户程序后,才需要复位DMA结束位,以指示数据接收已结束的当前缓冲区的编号。 像这样:


 if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1)){ DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF1); num_buf = 1; } else{ num_buf = 0; } 

基本上就是通过DCMI从ADC接收数据所需要的。


不幸的是,目前我无法现场演示整个机制。 铁片已经运行了大约3年)))。 从那时起,我只能携带已保存的考试注册数据。


这是来自SMB100A发生器的谐波信号,频率为48.001 MHz,等于我们的IF,偏移为1 KHz:





这是它的频谱:





为了检查ADA-HSMC和STM32F4 Discovery连接的长度约为200 mm的标准面包板线的最大性能,以40 MHz的时钟频率接收到正确的数据。
在为此目的而制造的“本机”板上,通过在室温下通过100毫米长的扁平电缆,事实证明它将采样频率提高到最大54 MHz。
在所需的10 MHz采样频率下,性能在-40至+60的工业范围内进行了测试。


其实一切。 感谢您的关注!




Source: https://habr.com/ru/post/zh-CN482506/


All Articles