OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 640|回复: 0

《M144Z-M3最小系统板使用指南——STM32F103版》第三十四章 单通道ADC采集(DMA读取)实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2024-4-29 14:10:36 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-4-26 17:08 编辑

第三十四章 单通道ADC采集(DMA读取)实验
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章介绍使用STM32F103的DMA进行单通道的ADC采集。通过本章的学习,读者将学习到DMA、ADC的使用。
本章分为如下几个小节:
34.1 硬件设计
34.2 程序设计
34.3 下载验证

34.1 硬件设计
34.1.1 例程功能
1. LCD上不断刷新显示PA1引脚输入电压采样的数字量和模拟量。
2. LED0闪烁,提示程序正在运行

34.1.2 硬件资源
1. LED
       LED0 - PB5
2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块
3. ADC3
       Channel1 - PA1
4. DMA2
       Channel5

34.1.3 原理图
本章实验使用的ADC3为STM32F103的片上资源,因此没有对应的连接原理图。

34.2 程序设计
34.2.1 HAL库的ADC驱动
本章实验中使用DAM2 Channel5的外设到存储器模式,将ADC3的转换结果保存到指定的存储器中,其具体的步骤如下:
①:初始化ADC
②:配置ADC通道
③:开始ADC DMA传输
在HAL库中对应的驱动函数如下:
①:初始化ADC
请见第34.2.1小节中初始化ADC的相关内容。
②:配置ADC通道
请见第34.2.1小节中配置ADC通道的相关内容。
③:开始ADC DMA传输
该函数用于开始ADC DMA传输,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_ADC_Start_DMA(   ADC_HandleTypeDef* hadc,
  2.                                          uint32_t* pData,
  3.                                          uint32_t Length);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240426170807.png
表34.2.1.1 函数HAL_ADC_Start_DMA()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240426170814.png
表34.2.1.2 函数HAL_ADC_Start_DMA()形参描述
该函数的使用示例,如下所示:
  1. #include"stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     uint16_t buf;
  5.    
  6.     /* 开始ADC DMA传输 */
  7.     HAL_ADC_Start_DMA(&adc_handle, &buf, sizeof(buf) / sizeof(uint16_t));
  8. }
复制代码

34.2.2 ADC驱动
本章实验的ADC驱动主要负责向应用层提供ADC初始化和启动ADC的DMA采集的函数,同时实现DMA的中断回调函数。本章实验中,ADC的驱动代码包括adc.c和adc.h两个文件。
ADC驱动中,对DMA、GPIO、ADC的相关宏定义,如下所示:
  1. #define ADC_DMA_ADCX   ADC3
  2. #define ADC_DMA_ADCX_CLK_ENABLE()      \
  3.     do {                                \
  4.          __HAL_RCC_ADC3_CLK_ENABLE();   \
  5.     }while (0)
  6. #define ADC_DMA_ADCX_CHY           ADC_CHANNEL_1
  7. #define ADC_DMA_ADCX_CHY_GPIO_PORT GPIOA
  8. #define ADC_DMA_ADCX_CHY_GPIO_PIN  GPIO_PIN_1
  9. #define ADC_DMA_ADCX_CHY_GPIO_CLK_ENABLE() \
  10.     do {                                    \
  11.          __HAL_RCC_GPIOA_CLK_ENABLE();      \
  12.     }while (0)
  13. #define ADC_DMA_ADCX_DMACX DMA2_Channel5
  14. #define ADC_DMA_ADCX_DMA_CLK_ENABLE()  \
  15.     do {                                \
  16.          __HAL_RCC_DMA2_CLK_ENABLE();   \
  17.     }while (0)
  18. #define ADC_DMA_ADCX_DMACX_IRQn        DMA2_Channel4_5_IRQn
  19. #define ADC_DMA_ADCX_DMACX_IRQHandler  DMA2_Channel4_5_IRQHandler
  20. ADC驱动中,ADC的初始化函数,如下所示:
  21. /**
  22. *@brief   初始化ADC DMA读取
  23. *@param   memory_base: 读取目标内存基地址
  24. *@retval  无
  25. */
  26. void adc_dma_init(uint32_t memory_base)
  27. {
  28.     /* 配置ADC */
  29.    g_adc_dma_handle.Instance = ADC_DMA_ADCX;
  30.    g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  31.    g_adc_dma_handle.Init.ScanConvMode = DISABLE;
  32.    g_adc_dma_handle.Init.ContinuousConvMode = ENABLE;
  33.    g_adc_dma_handle.Init.NbrOfConversion = 1;
  34.    g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE;
  35.    g_adc_dma_handle.Init.NbrOfDiscConversion = 1;
  36.    g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  37.    HAL_ADC_Init(&g_adc_dma_handle);
  38.    
  39.    g_adc_dma_memory_base = memory_base;
  40. }
  41. /**
  42. *@brief   HAL库ADC初始化MSP函数
  43. *@param   hadc: ADC句柄
  44. *@retval  无
  45. */
  46. voidHAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  47. {
  48.    GPIO_InitTypeDef gpio_init_struct = {0};
  49.    
  50.     if (hadc->Instance == ADC_DMA_ADCX)
  51.     {
  52.          ADC_DMA_ADCX_CLK_ENABLE();
  53.          ADC_DMA_ADCX_CHY_GPIO_CLK_ENABLE();
  54.          ADC_DMA_ADCX_DMA_CLK_ENABLE();
  55.          
  56.          /* 初始化ADC采样引脚 */
  57.          gpio_init_struct.Pin = ADC_DMA_ADCX_CHY_GPIO_PIN;
  58.          gpio_init_struct.Mode = GPIO_MODE_ANALOG;
  59.          gpio_init_struct.Pull = GPIO_NOPULL;
  60.          gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  61.          HAL_GPIO_Init(ADC_DMA_ADCX_CHY_GPIO_PORT, &gpio_init_struct);
  62.          
  63.          /* 配置DMA */
  64.          g_adc_dma_dma_handle.Instance = ADC_DMA_ADCX_DMACX;
  65.          g_adc_dma_dma_handle.Init.Direction =DMA_PERIPH_TO_MEMORY;
  66.          g_adc_dma_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
  67.          g_adc_dma_dma_handle.Init.MemInc = DMA_MINC_ENABLE;
  68.          g_adc_dma_dma_handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;
  69.          g_adc_dma_dma_handle.Init.MemDataAlignment =DMA_MDATAALIGN_HALFWORD;
  70.          g_adc_dma_dma_handle.Init.Mode = DMA_NORMAL;
  71.          g_adc_dma_dma_handle.Init.Priority =DMA_PRIORITY_VERY_HIGH;
  72.          HAL_DMA_Init(&g_adc_dma_dma_handle);
  73.          
  74.          __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_adc_dma_dma_handle);
  75.          
  76.          HAL_NVIC_SetPriority(ADC_DMA_ADCX_DMACX_IRQn, 0, 0);
  77.          HAL_NVIC_EnableIRQ(ADC_DMA_ADCX_DMACX_IRQn);
  78.     }
  79. }
复制代码
从上面的代码中可以看出,该ADC初始化函数初始化了ADC,同时也使能了ADC的DMA请求,并且在HAL库ADC初始化MSP函数中配置了DMA。
ADC驱动中,启动ADC的DMA采集的函数,如下所示:
  1. /**
  2. *@brief   开启ADC DMA读取
  3. *@param   length: DMA读取次数
  4. *@retval  无
  5. */
  6. voidadc_dma_enable(uint32_t length)
  7. {
  8.    HAL_ADC_Stop_DMA(&g_adc_dma_handle);
  9.    
  10.    adc_channel_set( &g_adc_dma_handle,
  11.                      ADC_DMA_ADCX_CHY,
  12.                      ADC_REGULAR_RANK_1,
  13.                      ADC_SAMPLETIME_239CYCLES);
  14.    
  15.    HAL_ADC_Start_DMA(  &g_adc_dma_handle,
  16.                          (uint32_t *)g_adc_dma_memory_base,
  17.                          length);
  18. }
复制代码
从上面的代码中可以看到,开启ADC的DMA采集就是先配置ADC通道,然后调用函数HAL_ADC_Start_DMA()开始ADC DMA传输。
DAC驱动代码中,ADC转换完成回调函数,如下所示:
  1. /**
  2. *@brief   HAL库ADC转换完成回调函数
  3. *@param   hadc: ADC句柄
  4. *@retval  无
  5. */
  6. voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
  7. {
  8.     if (hadc->Instance == ADC_DMA_ADCX)
  9.     {
  10.          g_adc_dma_sta = 1;
  11.     }
  12. }
复制代码
ADC转换完成回调函数中可以看到,当ADC转换完成后,便将相应的标志位置1,在应用程序中便可以此来判断ADC的采集是否完成。

34.2.3 实验应用代码
本章实验的应用代码,如下所示:
  1. #define ADC_DMA_BUF_SIZE    50
  2. uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];
  3. extern uint8_t g_adc_dma_sta;
  4. int main(void)
  5. {
  6.     uint16_t adc_result;
  7.     uint16_t voltage;
  8.     uint16_t index;
  9.     uint32_t result_sum;
  10.    
  11.    HAL_Init();                             /* 初始化HAL库 */
  12.    sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
  13.    delay_init(72);                     /* 初始化延时 */
  14.    usart_init(115200);                     /* 初始化串口 */
  15.    led_init();                             /* 初始化LED */
  16.    lcd_init();                             /* 初始化LCD */
  17.    adc_dma_init((uint32_t)g_adc_dma_buf); /* 初始化ADC DMA读取 */
  18.    
  19.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  20.    lcd_show_string(30, 70, 200, 16, 16, "ADC DMATEST", RED);
  21.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  22.    
  23.    lcd_show_string(30, 110, 200, 16, 16, "ADC3_CH1_VAL:0", BLUE);
  24.    lcd_show_string(30, 130, 200, 16, 16, "ADC3_CH1_VOL:0.000V", BLUE);
  25.    
  26.     /* 开启ADC DMA读取 */
  27.    adc_dma_enable(ADC_DMA_BUF_SIZE);
  28.    
  29.     while (1)
  30.     {
  31.          if (g_adc_dma_sta == 1)
  32.          {
  33.              g_adc_dma_sta = 0;
  34.             
  35.              /* 对DMA读取的多个ADC数据进行均值滤波 */
  36.              for (result_sum = 0, index=0; index<ADC_DMA_BUF_SIZE; index++)
  37.              {
  38.                  result_sum += g_adc_dma_buf[index];
  39.              }
  40.              adc_result = result_sum / ADC_DMA_BUF_SIZE;
  41.              lcd_show_xnum(134, 110, adc_result, 5, 16, 0, BLUE);
  42.             
  43.              /* 计算实际电压值(扩大1000倍) */
  44.              voltage = (adc_result * 3300) / 4095;
  45.              lcd_show_xnum(134, 130, voltage / 1000, 1, 16, 0, BLUE);
  46.              lcd_show_xnum(150, 130, voltage % 1000, 3, 16, 0x80, BLUE);
  47.             
  48.              adc_dma_enable(ADC_DMA_BUF_SIZE);
  49.          }
  50.          
  51.          LED0_TOGGLE();
  52.          
  53.          delay_ms(100);
  54.     }
  55. }
复制代码
从上面的代码中可以看出,应用代码中定义了一个数组作为DMA传输的目的存储器,随后在完成相关初始化后,就开启ADC的DMA传输,随后便等待DMA传输完成,DMA传输完成后对所有ADC转换的结构进行均值滤波后在LCD上进行显示,同时显示转换的电压模拟量。

34.3 下载验证
在完成编译和烧录操作后,可以看到LCD上不断地刷新显示ADC3通道1(PA1引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA1引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 00:25

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表