本帖最后由 正点原子运营 于 2024-4-25 11:32 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章介绍使用STM32F103模数转换器(ADC)进行带通道的电压采集。通过本章的学习,读者将学习到单通道ADC的使用。 本章分为如下几个小节: 33.1 硬件设计 33.2 程序设计 33.3 下载验证
33.1 硬件设计 33.1.1 例程功能 1. LCD上不断刷新显示PA1引脚输入电压采样的数字量和模拟量。 2. LED0闪烁,提示程序正在运行
33.1.2 硬件资源 1. LED LED0 - PB5 2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块 3. ADC2 Channel1 - PA1
33.1.3 原理图 本章实验使用的ADC1为STM32F103的片上资源,因此没有对应的连接原理图。
33.2 程序设计 33.2.1 HAL库的ADC驱动 本章实验将使用ADC1的通道1(PA1引脚)采集外部输入电压的模拟量,并将其转换为数字量,其具体的步骤如下: ①:初始化ADC ②:配置ADC通道 ③:开启ADC ④:ADC轮询转换 ⑤:获取ADC值 在HAL库中对应的驱动函数如下: ①:初始化ADC 该函数用于初始化ADC,其函数原型如下所示: - HAL_StatusTypeDef HAL_ADC_Init(ADC_HandleTypeDef* hadc);
复制代码该函数的形参描述,如下表所示: 表33.2.1.1 函数HAL_ADC_Init()形参描述 该函数的返回值描述,如下表所示: 表33.2.1.2 函数HAL_ADC_Init()返回值描述 该函数需要传入ADC的句柄指针,该句柄中就包含了ADC的初始化配置参数结构体,该结构体的定义如下所示: - typedef struct
- {
- uint32_t DataAlign; /* 数据对齐方式 */
- uint32_t ScanConvMode; /* 扫描模式 */
- FunctionalState ContinuousConvMode; /* 连续转换 */
- uint32_t NbrOfConversion; /* 规则序列 */
- FunctionalState DiscontinuousConvMode; /* 不连续采样模式 */
- uint32_tNbrOfDiscConversion; /* 不连续采样序列 */
- uint32_t ExternalTrigConv; /* 外部触发转换 */
- }ADC_InitTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- ADC_HandleTypeDef adc_handle = {0};
-
- /* 初始化ADC */
- adc_handle.Instance = ADC1;
- adc_handle.Init.DataAlign =ADC_DATAALIGN_RIGHT;
- adc_handle.Init.ScanConvMode = DISABLE;
- adc_handle.Init.ContinuousConvMode= DISABLE;
- adc_handle.Init.NbrOfConversion= 1;
- adc_handle.Init.DiscontinuousConvMode= DISABLE;
- adc_handle.Init.NbrOfDiscConversion= 1;
- adc_handle.Init.ExternalTrigConv=ADC_SOFTWARE_START;
- HAL_ADC_Init(&adc_handle);
- }
复制代码②:配置ADC通道 该函数用于配置ADC通道,其函数原型如下所示: - HAL_StatusTypeDef HAL_ADC_ConfigChannel( ADC_HandleTypeDef* hadc,
- ADC_ChannelConfTypeDef* sConfig);
复制代码该函数的形参描述,如下表所示: 表33.2.1.3 函数HAL_ADC_ConfigChannel()形参描述 该函数的返回值描述,如下表所示: 表33.2.1.4 函数HAL_ADC_ConfigChannel()返回值描述 该函数使用ADC_ChannelConfTypeDef类型结构体指针传入了ADC通道的配置参数,该结构体的定义如下所示: - typedef struct
- {
- uint32_t Channel; /* 通道 */
- uint32_t Rank; /* 规则组中的序号 */
- uint32_t SamplingTime; /* 采样时间 */
- }ADC_ChannelConfTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- ADC_ChannelConfTypeDef config = {0};
-
- /* 配置ADC通道 */
- config.Channel = ADC_CHANNEL_0;
- config.Rank = 1;
- config.SamplingTime =ADC_SAMPLETIME_3CYCLES;
- HAL_ADC_ConfigChannel(&adc_handle, &config);
- }
复制代码③:开启ADC 该函数用于开启ADC,其函数原型如下所示: - HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
复制代码该函数的形参描述,如下表所示: 表33.2.1.5 函数HAL_ADC_Start()形参描述 该函数的返回值描述,如下表所示: 表33.2.1.6 函数HAL_ADC_Start()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* 开启ADC */
- HAL_ADC_Start(&adc_handle);
- }
复制代码④:ADC轮询转换 该函数用于ADC轮询转换,其函数原型如下所示: - HAL_StatusTypeDef HAL_ADC_PollForConversion( ADC_HandleTypeDef* hadc,
- uint32_t Timeout);
复制代码该函数的形参描述,如下表所示: 表33.2.1.7 函数HAL_ADC_PollForConversion()形参描述 该函数的返回值描述,如下表所示: 表33.2.1.8 函数HAL_ADC_PollForConversion()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* ADC轮询转换 */
- HAL_ADC_PollForConversion(&adc_handle, HAL_MAX_DELAY);
- }
复制代码⑤:获取ADC值 该函数用于获取ADC值,其函数原型如下所示: - uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
复制代码该函数的形参描述,如下表所示: 表33.2.1.9 函数HAL_ADC_GetValue()形参描述 该函数的返回值描述,如下表所示: 表33.2.1.10 函数HAL_ADC_GetValue()返回值描述 该函数的使用示例,如下所示: - #include"stm32f1xx_hal.h"
- void example_fun(void)
- {
- uint32_t value;
-
- value = HAL_ADC_GetValue(&adc_handle);
-
- /* Do something.*/
- }
复制代码
33.2.2ADC驱动 本章实验的ADC驱动主要负责向应用层提供ADC的初始化和获取ADC转换结果的函数。本章实验中,ADC的驱动代码包括adc.c和adc.h两个文件。 ADC驱动中,对ADC、GPIO的相关宏定义,如下所示: - #define ADC_ADCX ADC2
- #define ADC_ADCX_CLK_ENABLE() \
- do { \
- __HAL_RCC_ADC2_CLK_ENABLE(); \
- }while (0)
- #define ADC_ADCX_CHY ADC_CHANNEL_1
- #define ADC_ADCX_CHY_GPIO_PORT GPIOA
- #define ADC_ADCX_CHY_GPIO_PIN GPIO_PIN_1
- #define ADC_ADCX_CHY_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOA_CLK_ENABLE(); \
- }while (0)
- ADC驱动中,ADC的初始化函数,如下所示:
- /**
- *@brief 初始化ADC
- *@param 无
- *@retval 无
- */
- void adc_init(void)
- {
- /* 配置ADC */
- g_adc_handle.Instance = ADC_ADCX;
- g_adc_handle.Init.DataAlign =ADC_DATAALIGN_RIGHT;
- g_adc_handle.Init.ScanConvMode = DISABLE;
- g_adc_handle.Init.ContinuousConvMode= DISABLE;
- g_adc_handle.Init.NbrOfConversion= 1;
- g_adc_handle.Init.DiscontinuousConvMode= DISABLE;
- g_adc_handle.Init.NbrOfDiscConversion= 1;
- g_adc_handle.Init.ExternalTrigConv=ADC_SOFTWARE_START;
- HAL_ADC_Init(&g_adc_handle);
- }
复制代码从上面的代码中可以看出,初始化ADC就是调用函数HAL_ADC_Init()来初始化ADC。 ADC驱动中,配置ADC通道的函数,如下所示: - /**
- *@brief 设置ADC通道
- *@param adc_handle: ADC句柄
- *@param channel: ADC通道
- *@param rank: 规则采样的编号
- *@param sampling_time: 采样时间
- *@retval 无
- */
- voidadc_channel_set( ADC_HandleTypeDef *adc_handle,
- uint32_t channel,
- uint32_t rank,
- uint32_t sampling_time)
- {
- ADC_ChannelConfTypeDef adc_channel_conf_struct = {0};
-
- /* 配置ADC通道 */
- adc_channel_conf_struct.Channel = channel;
- adc_channel_conf_struct.Rank = rank;
- adc_channel_conf_struct.SamplingTime = sampling_time;
- HAL_ADC_ConfigChannel(adc_handle, &adc_channel_conf_struct);
- }
- ADC驱动中,获取ADC转换结果的函数,如下所示:
- /**
- *@brief 获取ADC结果
- *@param channel: ADC通道
- *@retval ADC结果
- */
- uint16_t adc_get_result(uint32_t channel)
- {
- uint16_t result;
-
- adc_channel_set(&g_adc_handle, channel, 1,ADC_SAMPLETIME_480CYCLES);
- HAL_ADC_Start(&g_adc_handle);
- HAL_ADC_PollForConversion(&g_adc_handle, HAL_MAX_DELAY);
- result =HAL_ADC_GetValue(&g_adc_handle);
-
- return result;
- }
- /**
- *@brief 均值滤波获取ADC结果
- *@param channel: ADC通道
- *@param times: 均值滤波的原始数据个数
- *@retval ADC结果
- */
- uint16_t adc_get_result_average(uint32_t channel, uint8_t times)
- {
- uint32_t sum_result = 0;
- uint8_t index;
- uint16_t result;
-
- for (index=0; index<times; index++)
- {
- sum_result += adc_get_result(channel);
- }
-
- result = sum_result / times;
-
- return result;
- }
复制代码以上两个函数都是用于获取ADC转换结果的函数,其中函数adc_get_result()会配置并开启ADC指定通道的规则通道转换,并等待其转换结束后,读取其转换的1次结果;而函数adc_get_result_averagr()则是多次调用啊含糊adc_get_result()获取多次ADC的转换结果,然后进行均值滤波。
33.2.3 实验应用代码 本章实验的应用代码,如下所示: - int main(void)
- {
- uint16_t adc_result;
- uint16_t voltage;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- lcd_init(); /* 初始化LCD */
- adc_init(); /* 初始化ADC */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "ADCTEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
-
- lcd_show_string(30, 110, 200, 16, 16, "ADC2_CH1_VAL:0", BLUE);
- lcd_show_string(30, 130, 200, 16, 16, "ADC2_CH1_VOL:0.000V", BLUE);
-
- while (1)
- {
- /* 均值滤波获取ADC结果 */
- adc_result = adc_get_result_average(ADC_ADCX_CHY, 10);
- lcd_show_xnum(134, 110, adc_result, 5, 16, 0, BLUE);
-
- /* 计算实际电压值(扩大1000倍) */
- voltage = (adc_result * 3300) / 4095;
- lcd_show_xnum(134, 130, voltage / 1000, 1, 16, 0, BLUE);
- lcd_show_xnum(150, 130, voltage % 1000, 3, 16, 0x80, BLUE);
-
- LED0_TOGGLE();
-
- delay_ms(100);
- }
- }
复制代码从上面的代码中可以看出,在进行完包括ADC的所有初始化工作后,便不断地获取ADC2通道1进行10次转换后经过均值滤波后的结果,并将该原始值显示在LCD上,同时还通过该电压的原始值计算出了电压的模拟量,并在LCD上进行显示。
33.3 下载验证 在完成编译和烧录操作后,可以看到LCD上实时刷新显示着ADC2通道1(PA1引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA1引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。
|