本帖最后由 正点原子运营 于 2024-4-29 14:13 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章介绍STM32F103的DMA进行多通道的ADC采集。通过本章的学习,读者将学习到DMA、ADC的使用。 本章分为如下几个小节: 35.1 硬件设计 35.2 程序设计 35.3 下载验证
35.1 硬件设计 35.1.1 例程功能 1. LCD上不断刷新显示PA5和PA6引脚输入电压采样的数字量和模拟量。 2. LED0闪烁,提示程序正在运行
35.1.2 硬件资源 1. LED LED0 - PB5 2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块 3. ADC1 Channel5 - PA5 Channel6 - PA6 4. DMA1 Channel1
35.1.3 原理图 本章实验使用的ADC1为STM32F103的片上资源,因此没有对应的连接原理图。
35.2 程序设计 35.2.1 HAL库的ADC驱动 本章实验与上一章实验中对ADC的操作十分类似,不过本章实验是配置、使能和读取ADC1的Channel5和Channel6,因此请见第35.2.1小节中HAL库的ADC驱动的相关内容。
35.2.2 ADC驱动 本章实验的ADC驱动主要负责向应用层提供ADC的初始化和启动ADC的DMA采集函数,同时实现了DMA的中断回调函数。本章实验中,ADC的驱动代码包括adc.c和adc.h两个文件。 ADC驱动中,对DMA、GPIO、ADC的相关宏定义,如下所示: - #define ADC_NCH_DMA_ADCX ADC1
- #define ADC_NCH_DMA_ADCX_CLK_ENABLE() \
- do { \
- __HAL_RCC_ADC1_CLK_ENABLE(); \
- }while (0)
- #define ADC_NCH_DMA_ADCX_CH_NUM 2
- #define ADC_NCH_DMA_ADCX_CHA ADC_CHANNEL_5
- #define ADC_NCH_DMA_ADCX_CHA_GPIO_PORT GPIOA
- #define ADC_NCH_DMA_ADCX_CHA_GPIO_PIN GPIO_PIN_5
- #define ADC_NCH_DMA_ADCX_CHA_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOA_CLK_ENABLE(); \
- }while (0)
- #define ADC_NCH_DMA_ADCX_CHB ADC_CHANNEL_6
- #define ADC_NCH_DMA_ADCX_CHB_GPIO_PORT GPIOA
- #define ADC_NCH_DMA_ADCX_CHB_GPIO_PIN GPIO_PIN_6
- #define ADC_NCH_DMA_ADCX_CHB_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOA_CLK_ENABLE(); \
- }while (0)
- #define ADC_NCH_DMA_ADCX_DMACX DMA1_Channel1
- #define ADC_NCH_DMA_ADCX_DMA_CLK_ENABLE() \
- do { \
- __HAL_RCC_DMA1_CLK_ENABLE(); \
- }while (0)
- #define ADC_NCH_DMA_ADCX_DMACX_IRQn DMA1_Stream1_IRQn
- #define ADC_NCH_DMA_ADCX_DMACX_IRQHandler DMA1_Stream1_IRQHandler
复制代码ADC驱动中,ADC的初始化函数,如下所示: - /**
- *@brief 初始化多通道ADC DMA读取
- *@param memory_base: 读取目标内存基地址
- *@retval 无
- */
- voidadc_nch_dma_init(uint32_t memory_base)
- {
- /* 配置ADC */
- g_adc_nch_dma_handle.Instance = ADC_NCH_DMA_ADCX;
- g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- g_adc_nch_dma_handle.Init.ScanConvMode = ENABLE;
- g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE;
- g_adc_nch_dma_handle.Init.NbrOfConversion = ADC_NCH_DMA_ADCX_CH_NUM;
- g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE;
- g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 1;
- g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
- HAL_ADC_Init(&g_adc_nch_dma_handle);
-
- g_adc_nch_dma_memory_base = memory_base;
- }
- /**
- *@brief HAL库ADC初始化MSP函数
- *@param hadc: ADC句柄
- *@retval 无
- */
- voidHAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
-
- if (hadc->Instance == ADC_NCH_DMA_ADCX)
- {
- ADC_NCH_DMA_ADCX_CLK_ENABLE();
- ADC_NCH_DMA_ADCX_CHA_GPIO_CLK_ENABLE();
- ADC_NCH_DMA_ADCX_CHB_GPIO_CLK_ENABLE();
- ADC_NCH_DMA_ADCX_DMA_CLK_ENABLE();
-
- /* 初始化ADC采样引脚 */
- gpio_init_struct.Pin = ADC_NCH_DMA_ADCX_CHA_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_ANALOG;
- gpio_init_struct.Pull = GPIO_NOPULL;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(ADC_NCH_DMA_ADCX_CHA_GPIO_PORT, &gpio_init_struct);
-
- gpio_init_struct.Pin = ADC_NCH_DMA_ADCX_CHB_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_ANALOG;
- gpio_init_struct.Pull = GPIO_NOPULL;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(ADC_NCH_DMA_ADCX_CHB_GPIO_PORT, &gpio_init_struct);
-
- /* 配置DMA */
- g_adc_nch_dma_dma_handle.Instance =ADC_NCH_DMA_ADCX_DMACX;
- g_adc_nch_dma_dma_handle.Init.Direction =DMA_PERIPH_TO_MEMORY;
- g_adc_nch_dma_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
- g_adc_nch_dma_dma_handle.Init.MemInc = DMA_MINC_ENABLE;
- g_adc_nch_dma_dma_handle.Init.PeriphDataAlignment=
- DMA_PDATAALIGN_HALFWORD;
- g_adc_nch_dma_dma_handle.Init.MemDataAlignment =DMA_MDATAALIGN_HALFWORD;
- g_adc_nch_dma_dma_handle.Init.Mode = DMA_NORMAL;
- g_adc_nch_dma_dma_handle.Init.Priority =DMA_PRIORITY_VERY_HIGH;
- HAL_DMA_Init(&g_adc_nch_dma_dma_handle);
-
- __HAL_LINKDMA( &g_adc_nch_dma_handle,
- DMA_Handle,
- g_adc_nch_dma_dma_handle);
-
- HAL_NVIC_SetPriority(ADC_NCH_DMA_ADCX_DMACX_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(ADC_NCH_DMA_ADCX_DMACX_IRQn);
- }
- }
复制代码从上面的代码中可以看出,本章实验的ADC初始化与上一章实验中的ADC初始化步骤基本一致,不过本章实验的ADC初始化函数中配置了ADC1的Channel5和Channel6。 ADC驱动中,启动ADC的DMA采集的函数和ADC转换完成回调函数均与上一章实验中的函数一致,请见第35.2.2小节中的相关内容。
35.2.3 实验应用代码 本章实验的应用代码,如下所示: - #define ADC_NCH_DMA_BUF_SIZE (50 *ADC_NCH_DMA_ADCX_CH_NUM)
- uint16_t g_adc_nch_dma_buf[ADC_NCH_DMA_BUF_SIZE];
- extern uint8_t g_adc_nch_dma_sta;
- int main(void)
- {
- uint16_t adc_result[ADC_NCH_DMA_ADCX_CH_NUM];
- uint16_t voltage;
- uint16_t index;
- uint32_t result_sum;
- uint8_t ch_index;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- lcd_init(); /* 初始化LCD */
- adc_nch_dma_init((uint32_t)g_adc_nch_dma_buf); /* 初始化多通道ADC DMA读取 */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "ADC NCH DMATEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
-
- lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH5_VAL:0", BLUE);
- lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH5_VOL:0.000V", BLUE);
- lcd_show_string(30, 150, 200, 16, 16, "ADC1_CH6_VAL:0", BLUE);
- lcd_show_string(30, 170, 200, 16, 16, "ADC1_CH6_VOL:0.000V", BLUE);
-
- /* 开启多通道ADC DMA读取 */
- adc_nch_dma_enable(ADC_NCH_DMA_BUF_SIZE);
-
- while (1)
- {
- if (g_adc_nch_dma_sta == 1)
- {
- g_adc_nch_dma_sta = 0;
-
- for (ch_index=0; ch_index<ADC_NCH_DMA_ADCX_CH_NUM; ch_index++)
- {
- /* 对DMA读取的多个ADC数据进行均值滤波 */
- for (result_sum = 0, index=0;
- index<(ADC_NCH_DMA_BUF_SIZE / ADC_NCH_DMA_ADCX_CH_NUM);
- index++)
- {
- result_sum +=
- g_adc_nch_dma_buf[(ADC_NCH_DMA_ADCX_CH_NUM* index) +
- ch_index];
- }
- adc_result[ch_index] =
- result_sum /
- (ADC_NCH_DMA_BUF_SIZE / ADC_NCH_DMA_ADCX_CH_NUM);
- }
-
- /* 计算实际电压值(扩大1000倍) */
- voltage = (adc_result[0] * 3300) / 4095;
- lcd_show_xnum(134, 110, adc_result[0], 5, 16, 0, BLUE);
- lcd_show_xnum(134, 130, voltage / 1000, 1, 16, 0, BLUE);
- lcd_show_xnum(150, 130, voltage % 1000, 3, 16, 0x80, BLUE);
-
- /* 计算实际电压值(扩大1000倍) */
- lcd_show_xnum(134, 150, adc_result[1], 5, 16, 0, BLUE);
- voltage = (adc_result[1] * 3300) / 4095;
- lcd_show_xnum(134, 170, voltage / 1000, 1, 16, 0, BLUE);
- lcd_show_xnum(150, 170, voltage % 1000, 3, 16, 0x80, BLUE);
-
- adc_nch_dma_enable(ADC_NCH_DMA_BUF_SIZE);
- }
-
- LED0_TOGGLE();
-
- delay_ms(100);
- }
- }
复制代码本章实验的应用代码与上一章实验中的引用代码基本一致,只不过本章实验要处理的是2个ADC通道的数据,因此DMA传输的目的存储器容量的定义变大了,随后便可在DMA传输完成后,采用与上一章实验一样的处理方式处理ADC采集并转换后的数据,最后将每个通道采集到电压的数字量和模拟量显示置LCD上。
35.3 下载验证 在完成编译和烧录操作后,可以看到LCD上不断地刷新显示ADC1的Channel5和Channel6(PA5引脚和PA6引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA5和PA6中的任意一个或多个引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。 |