[APM32F4] APM32F411 TINY板实现TMR触发ADC采样+DMA传输

[复制链接]
1230|1
 楼主| DKENNY 发表于 2023-9-16 13:49 | 显示全部楼层 |阅读模式
本帖最后由 DKENNY 于 2023-9-18 14:05 编辑

最近看了看ADC采样,想着把ADC和DMA以及TMR结合起来研究看看,达到使用定时器触发ADC采样,并使用DMA传输的功能,最近正好收到了APM32F411开发板,就想着使用这款开发板实现一下这个功能。

1. APM32F411简介
APM32F411.jpg
-带有FPU的32位ARM CortexM4F内核
-支持最高120MHz的工作频率
-具有512KB+NVR 32KB的flash容量,128KB的SRAM容量
-支持睡眠、停机、待机三种工作模式
-支持CF卡 、SRAM、PSRAM、NOR和NAND存储器
-具有多个通信外设以及模拟外设
更多详细资料可以访问他们的官网(https://geehy.com)。

2. APM32F411系统工作主频
在调试时,我发现这款芯片的官方SDK里面,芯片的运行主频不是120MHz,而是使用的是100MHz。当然,这个主频我们是可以直接算出来的。我们进入Debug视图,运行到main函数,打开RCM模块视图。
image001.png

我们可以看到PLL1CFG寄存器中的各项值,这时,就可以参考官方手册给出的计算公式,计算出它的主频。
image002.png

通过查看官方用户手册,上面有关于这部分的详细阐述,最后算得系统实际工作主频为8M*(100/4)/2= 100MHz。这对后面我配置TMR2定时触发是有一定影响的,所以在这里提一下。

3. 代码
1、定时器
这里我选择TMR2定时器的时钟更新事件触发ADC采样,也就是TMR的重装载寄存器置位1次,则触发一次ADC数据采集。
image005.jpg
  1. /*
  2.     定时器初始化
  3. */
  4. void TMR_Init(void)
  5. {
  6.     TMR_BaseConfig_T timBaseConfig;
  7.    
  8.     //系统工作主频为100M,由于APB1是2分频,所以时基计算如下
  9.     timBaseConfig.period = 3; //设置定时器时基为4us,(3+1)*(99+1)/(50M*2) = 4us
  10.     timBaseConfig.division = 99;
  11.     timBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
  12.     timBaseConfig.countMode = TMR_COUNTER_MODE_UP;
  13.     TMR_ConfigTimeBase(TMR2,&timBaseConfig);

  14.     //选择外部触发源
  15.     TMR_SelectOutputTrigger(TMR2, TMR_TRGO_SOURCE_UPDATE);
  16.   
  17.     TMR_EnableInterrupt(TMR2,TMR_INT_UPDATE);
  18.     NVIC_EnableIRQRequest(TMR2_IRQn,0,0);

  19.     TMR_Enable(TMR2);
  20. }

  21. /*
  22.     TMR2中断服务函数
  23. */
  24. void TMR2_IRQHandler(void)
  25. {
  26.     if (TMR_ReadIntFlag(TMR2, TMR_INT_UPDATE) != RESET)
  27.     {
  28.         TMR_ClearIntFlag(TMR2, TMR_INT_UPDATE);

  29.         if(count)
  30.         {
  31.             count--;
  32.         }
  33.         else
  34.         {
  35.               //定时1s,打印一次数据
  36.              count = 250000;
  37.               if(DMA_ReadCurrentMemoryTarget(DMA2_Stream0))
  38.               {
  39.                   printf("The memory currently accessed by DMA : 1 \r\n");
  40.                   printf("Volatage = %.2f V\r\n",(double)adc_data[1][0]/4096*3.3);
  41.               }
  42.               else
  43.               {
  44.                   printf("The memory currently accessed by DMA : 0 \r\n");
  45.                   printf("Volatage = %.2f V\r\n",(double)adc_data[0][0]/4096*3.3);
  46.                }
  47.                 APM_TINY_LEDToggle(LED2);
  48.          }
  49.     }
  50. }

2、ADC
APM32F411有2个ADC源,我使用的是ADC1,并且设置的是TMR2定时器触发,我们可以看看官方手册对于ADC外部触发的描述。
image006.jpg
  1. /**
  2.     ADC初始化代码
  3. */
  4. void ADC_Init(void)
  5. {
  6.     ADC_Config_T adcConfig;
  7.     ADC_CommonConfig_T adcCommonConfig;

  8.     ADC_Reset();

  9.     /*
  10.         ADC采样:50M,(112+12)/50M = 2.48us,2.48us进行一次ADC采样
  11.     */
  12.     adcCommonConfig.accessMode = ADC_ACCESS_MODE_DISABLED;//ADC不采用直接访问DMA模式
  13.     adcCommonConfig.mode = ADC_MODE_INDEPENDENT;     //ADC设置为独立模式
  14.     adcCommonConfig.prescaler = ADC_PRESCALER_DIV2; //ADC 2分频:100M/2=50M
  15.     adcCommonConfig.twoSampling = ADC_TWO_SAMPLING_5CYCLES;        //2个采样阶段之间的延迟:只在双重或三重交错模式下使用
  16.     ADC_CommonConfig(&adcCommonConfig);

  17.     adcConfig.resolution = ADC_RESOLUTION_12BIT;//设置ADC分辨率
  18.     adcConfig.scanConvMode = DISABLE;//不使能ADC循环扫描模式
  19.     adcConfig.continuousConvMode = DISABLE;//设置ADC单次转换模式,否则除了第一次是由外部TMR触发以外,其他的都是软件触发了
  20.     adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_RISING; //设置ADC外部上升沿触发
  21.     adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TMR2_TRGO;//设置ADC使用TMR2触发
  22.     adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT; //ADC数据右对齐
  23.     adcConfig.nbrOfChannel = 1; //设置ADC转换通道个数
  24.     ADC_Config(ADC1, &adcConfig);

  25.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
  26.     ADC_EnableDMA(ADC1);
  27.     ADC_EnableDMARequest(ADC1);

  28.     ADC_Enable(ADC1);
  29. }

3、DMA
我这里只用了一路ADC一个通道数据传输到DMA,也可以ADC多通道传输,只需要稍微改一下DMA程序并定义几个空间数据用来存放数据即可。
  1. /*DMA初始化*/
  2. void DMA_Configuration(void)
  3. {
  4.     DMA_Config_T dmaConfig;

  5.     DMA_Reset(DMA2_Stream0);
  6.     dmaConfig.channel = DMA_CHANNEL_0;
  7.     dmaConfig.peripheralBaseAddr = (uint32_t) & (ADC1->REGDATA);//配置DMA的外设基地址为ADC1数据寄存器
  8.     dmaConfig.memoryBaseAddr = (uint32_t)adc_data[0];//配置DMA的主存基地址
  9.     dmaConfig.dir = DMA_DIR_PERIPHERALTOMEMORY;//配置DMA传输方向:外设->主存
  10.     dmaConfig.bufferSize = 1;
  11.     dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;//DMA外设基地址不自增
  12.     dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;//DMA主存基地址自增
  13.     dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;//DMA以半字为单位进行传输
  14.     dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
  15.     dmaConfig.loopMode = DMA_MODE_NORMAL;//设置DMA为NORMAL模式,也就是说,当外部TMR触发ADC采样时,DMA开始传输
  16.     dmaConfig.priority = DMA_PRIORITY_HIGH;
  17.     dmaConfig.fifoMode = DMA_FIFOMODE_DISABLE;
  18.     dmaConfig.fifoThreshold = DMA_FIFOTHRESHOLD_HALFFULL;
  19.     dmaConfig.memoryBurst = DMA_MEMORYBURST_SINGLE;
  20.     dmaConfig.peripheralBurst = DMA_PERIPHERALBURST_SINGLE;
  21.     DMA_Config(DMA2_Stream0, &dmaConfig);

  22.     //开启DMA双缓冲模式
  23.     DMA_ConfigBufferMode(DMA2_Stream0,(uint32_t)adc_data[1],DMA_MEMORY_0);
  24.     DMA_EnableDoubleBufferMode(DMA2_Stream0);
  25.     DMA_Enable(DMA2_Stream0);

  26.     //使能DMA完成中断
  27.     DMA_EnableInterrupt(DMA2_Stream0,DMA_INT_TCI**);
  28.     NVIC_EnableIRQRequest(DMA2_STR0_IRQn,0,0);
  29. }

  30. /*
  31.     DMA_Stream0中断服务函数
  32. */
  33. void DMA2_STR0_IRQHandler(void)
  34. {
  35.     if(DMA_ReadIntFlag(DMA2_Stream0,DMA_INT_TCI**0) != RESET)
  36.     {
  37.         DMA_ClearIntFlag(DMA2_Stream0,DMA_INT_TCI**0);
  38.     }
  39. }

4、main函数
  1. volatile uint32_t adc_data[2][1]; // 存储ADC采集的数据

  2. void RCM_Init(void);
  3. void GPIO_Init(void);
  4. void TMR_Init(void);
  5. void ADC_Init(void);
  6. void DMA_Configuration(void);

  7. volatile uint32_t count = 250000;

  8. /*
  9.     时钟初始化
  10. */
  11. void RCM_Init(void)
  12. {
  13.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA2 | RCM_AHB1_PERIPH_GPIOA);
  14.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  15.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
  16. }

  17. /*
  18.     GPIO初始化
  19. */
  20. void GPIO_Init(void)
  21. {
  22.     GPIO_Config_T gpioConfig;

  23.     gpioConfig.pin = GPIO_PIN_0;
  24.     gpioConfig.mode = GPIO_MODE_AN;
  25.     gpioConfig.pupd = GPIO_PUPD_NOPULL;
  26.     GPIO_Config(GPIOA, &gpioConfig);
  27. }


  28. int main(void)
  29. {
  30.     USART_Config_T usartConfig;
  31.    
  32.     usartConfig.baudRate = 115200;
  33.     usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;
  34.     usartConfig.mode = USART_MODE_TX_RX;
  35.     usartConfig.parity = USART_PARITY_NONE;
  36.     usartConfig.stopBits = USART_STOP_BIT_1;
  37.     usartConfig.wordLength = USART_WORD_LEN_8B;
  38.    
  39.     USART_Config(USART1,&usartConfig);
  40.     APM_TINY_COMInit(COM1,&usartConfig);
  41.     APM_TINY_LEDInit(LED2);

  42.     RCM_Init();
  43.     GPIO_Init();
  44.     TMR_Init();
  45.     ADC_Init();
  46.     DMA_Configuration();

  47.     while (1)
  48.     {
  49.     }
  50. }



4. 运行效果
image004.jpg


5. 例程源码
DMA_TMR2_ADC.zip (734.51 KB, 下载次数: 9)


欢迎大家讨论交流。





评论

不错不错,学习了  发表于 2023-9-22 10:58
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

108

帖子

17

粉丝
快速回复 在线客服 返回列表 返回顶部