细说MCU的ADC模块单通道单次采样的实现方法

目录

一、工程依赖的硬件

二、设计目的

三、建立工程

1、配置GPIO

2、配置中断

3、配置串口

4、配置ADC

5、选择时钟源和Debug

6、配置系统时钟和ADC时钟

四、设置采样频率

五、代码修改

1、重定义外部中断回调函数

2、启动ADC

3、配置printf函数

六、运行并查看结果


STM32G4系列MCU的模/数转换器(Analog to Digital Converter,ADC)功能比较强不同的型号所含ADC模块数量不同,最多有5个ADC(ADC1~5);但也并非完全独立,其ADC1和ADC2是一对,ADC3和ADC4是一对,ADC5可独立控制。每个ADC都包含一个12位逐次比较型模/数转换器。此外,每个ADC还有最多至19个通道,不同的通道具有单次、连续和扫描或断续等采样模式。

一、工程依赖的硬件

文章依赖的硬件及工程配置参考本文作者的其他文章:细说ARM MCU的串口接收数据的实现过程-CSDN博客 https://wenchm.blog.csdn.net/article/details/139541112

二、设计目的

在本例子中,使用ADC1的一个通道以单次采样的模式采集外部输入直流电压信号。使用NUCLEO-G474RE开发板上的按键B1来启动ADC采样。每按下一次B1键,进行一次A/D转换。在代码实现中,将通过查询方式判断是否转换完成;一旦转换成,主程序会从ADC的数据寄存器中读取转换结果,并将结果通过串口送出。此外,当输入信号的幅值大于一定值时,将会点亮板上发光二极管LD2。这个例子用到了ADC、串口入/输出等多个模块。此外,A/D转换虽采用查询模式,但对按键状态的识别,将采用中断方式。

ADC的输入电压范围是0~3.3 V,所以要确保外部施加的信号不超过此电压范围,否则会导致硬件损坏。

本例中,采用ADC1的第一个通道,对应STM32G474RE的引脚为PA0,在NUCLEG474RE板上通过CN7端子的第28引脚引出。此外,按键B1连接的引脚为PC13,LD2的控制引脚为PA5。

三、建立工程

1、配置GPIO

配置PA5为输出(GPIO_Output),默认输出电平Low,推挽输出,上拉,速度High,标识为LED;PA5引脚输出高电平时LD2点亮,默认的低电平时熄灭;

配置PC13为中断模式(GPIO_EXTI13),上升沿触发,下拉,用户标识为KEY。

2、配置中断

在NVIC中断表中,将EXTI line[15:10]interrupts使能,并将其抢占式优先级设为2(由于仅用到一个中断,级数选择可任意)。

3、配置串口

选择 Connectivity中的 USART2,其模式( Mode)选择异步( Asynchronous),其他参数设置均保持默认(波特率为115200 bit/ s),不开启中断。将 USART2的两个引脚 PA2和 PA3均设置为上拉。

4、配置ADC

选择Analog中的ADC1,在其模式(Mode)区,通道1(IN1)选择IN1 Single-ended(单端);其它参数设置可暂时均保持默认值。时钟预分频参数(Clock Prescaler)选择Asynchronous clock mode dividedby 1(其他选项亦可)。

5、选择时钟源和Debug

使用片外时钟晶体作为HSE的时钟源。在SYS中将Debug设置为Serial Wire。

6、配置系统时钟和ADC时钟

在STM32G474RE的说明文档中,给出了其ADC时钟频率的范围。

ADC的最大频率为60 MHz,而系统最高频率为170 MHz,如果系统频率配置较高,生成ADC时钟频率时就需要分频处理。

在本例中,没有使用低功耗模式,并且是让ADC进行单次采样的,所以最高时钟频以达到60 MHz。为了可靠起见,本例中配置ADC的时钟频率为34 MHz。

|-------------|-------------|---------------------------------|-------------|-------------|
| 符 号 | 参 数 | 条 件 | 最小值 | 最大值 |
| fADC/MHz | ADC的 时钟频率 | Rangel,单路ADC操作 | 0.14 | 60 |
| fADC/MHz | ADC的 时钟频率 | Range2 | ------ | 26 |
| fADC/MHz | ADC的 时钟频率 | Range1,所有ADCs操作,单端模式VDDA≥2.7 V | 0.14 | 52 |
| fADC/MHz | ADC的 时钟频率 | Range1,所有ADCs操作,单端模式VDDA≥1.62 V | 0.14 | 42 |
| fADC/MHz | ADC的 时钟频率 | Rangel,单路ADC操作,差分模式VDDA≥1.62 V | 0.14 | 56 |

四、设置采样频率

如果采样频率用符号fs表示,ADC的时钟频率用fADC表示,则在连续采样模式(continuous mode)下,它们之间的关系可以表示为:

式中:采样时间是指某个A/D通道的转换时间,单位是ADC时钟的周期数;分辨率通常以二进制位数表示,指ADC对输入信号的分辨能力(n位ADC,能区分输入电压的最小差异为:满量程输入的1/2^n)。STM32G474中,ADC是12位的,这只是可达到的最高分辨率,如果采样频率要求很高,也可以降低位数来使用。当分辨率为12位时,如果采样时间为2.5个ADC时钟周期,fADC为60 MHz,则采样频率为60×10⁶/(2.5+12+0.5)=4(MHz)。

当ADC的分辨率为12位时,转换时间Tconv=采样时间+12+0.5,单位为ADC的时钟周期。采样时间可配置为2.5、6.5、12.5、24.5、47.5、92.5、247.5和640.5(ADC时钟周期)。

五、代码修改

打开main.c,修改代码。

1、重定义外部中断回调函数

由于希望在产生按键中断时,启动ADC采样,所以,需要重定义外部中断EXTI的回调函数。这个回调函数可以写在main.c文件后面的一个注释对中。这里直接给出它的定义:

cpp 复制代码
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1,10);
	ADC1ConvertedValue = HAL_ADC_GetValue(&hadc1);
	if(ADC1ConvertedValue > 2048)
		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);
	else
		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);
	printf("ADCResult =%d \r\n",ADC1ConvertedValue);
}

int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xFFFF);
	return ch;
}
/* USER CODE END 4 */

2、启动ADC

在外部中断回调函数中调用了三个ADC相关的库幽数。

首先是启动ADC,用了库函数HAL_ADC_Start(ADC_HandleTypeDef *hadc)。此函数只有一个参数,就是ADC结构体变量。由于在硬件配置中用了ADC1,所以自动生成的代码中已经给出了它的结构体变量,即hadcl。

调用的第二个库函数是:

cpp 复制代码
HAL_ADC_PollForConversion(ADC_HandleTypeDef *hadc,uint32_t Timeout);

这个函数是以查询方式等待A/D转换过程的结束。该函数的第二个参数是Timeout,单位为ms。随后,就可以调用库函数HAL_ADC_GetValue(ADC_HandleTypeDef *hadc)来读取A/D转换的结果了。这里用了一个变量ADC1ConvertedValue来存放A/D转换的结果。需要在main.c中定义该变量,可以将其放到main函数前的注释对中:

cpp 复制代码
/* USER CODE BEGIN PV */
uint16_t ADC1ConvertedValue = 0;
/* USER CODE END PV */

接下来,在回调函数HAL_GPIO_EXTI_Callback()中根据A/D采样值的大小控制发光二极管的亮灭。

3、配置printf函数

在回调函数的最后,使用了 printf函数,将 A/ D转换的结果通过串口送出。

使用 printf函数从串口送出数据,需要在 main.c中将 stdio.h包含进来;此外,还要给出 putchar函数的定义。

cpp 复制代码
/* USER CODE BEGIN 4 */
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,0xFFFF);
	return ch;
}
/* USER CODE END 4 */

六、运行并查看结果

编译工程并下载到硬件中,将程序运行起来。

打开串口助手程序,设置好串口端口和波特率等参数,单击"打开串口。

分别用跳线将PA0连接到GND和VDD(3.3 V)上,并操作NUCLEO-G474RE板上B1键。可以看到,连接到GND时每次送的是0,连接到VDD时会每次送来一个接近4095的数,如图截图。

STM32G474RE上的ADC是12位的,输入电压3.3 V时,理论上对应最大转换值为4095。在将PA0连接到VDD上时,为什么 ADC的转换结果不是4095呢?这是因为板上的VDD并不是稳定的3.3 V,而是有偏差的。对于12位ADC,如果满量程输入电压为3.3 V,则转换结果的每一位对应的电压为3.3/4096 V,约为0.0008 V,即0.8 mV。从截图中的结果看,偏差了几十mV(不同的板子,偏差可能会有所不同)。

相关推荐
深圳市青牛科技实业有限公司1 小时前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比2 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie2 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
西瓜籽@3 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^138287988725 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源
极客小张6 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
m0_739312879 小时前
【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)
stm32·单片机·嵌入式硬件
嵌入式小章9 小时前
基于STM32的实时时钟(RTC)教学
stm32·嵌入式硬件·实时音视频
TeYiToKu9 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
基极向上的三极管10 小时前
【AD】3-4 在原理图中放置元件
嵌入式硬件