1. ADC基础
说明:
①STM32f429BI
②keil5
③stm32cubeMX
STM32f429中,有三个ADC(模拟/数字转换器),每个 ADC 有 12 位、 10 位、 8 位和 6 位可选,每个ADC有16个外部通道、2个内部通道和一个VBAT 通道的信号。下面,将根据单个的ADC框图来依次说明一下ADC的几个主要参数,手册上关于STM32的单个ADC原理框图如下,Stm32中关于ADC的数据流向如图中下标所示。
1.1数据采集
1.1.1通道
框图①,ADC的IN0-IN15是16个外部通道,分别连接芯片上的16个引脚,可以采集外部的模拟电压。其中ADC 输入电压范围为: VREF- ≤VIN ≤VREF+。由 VREF-、 VREF+ 、 VDDA (模拟电源)、 VSSA(模拟电源地)这四个外部引脚决定,一般把VREF+ 和 VDDA接3.3V,VREF-和VSSA接地,故能采集的电压为0~3.3V。每个ADC的通道并不是相互独立的,三个ADC之间会有一些通道共用引脚,在数据手册上可以看到,例如下图中,PC0对应ADC1、ADC2、ADC3的通道IN10。
ADC具有独立模式、双重模式(2个ADC交替采样同一个引脚电压)、三重模式(3个DAC交替采样同一个引脚电压),双重模式和三重模式可以提高采样率。
1.1.2分辨率(采样精度)
ADC有12位、10位、8位可选,这些即是采样精度的选择,它们分别对应转换的数值为
2
12
,
2
10
,
2
8
2^{12},2^{10},2^{8}
212,210,28。最大的采样电压为3.3.V,根据选择不同,则3.3V相应被分成
2
12
,
2
10
,
2
8
2^{12},2^{10},2^{8}
212,210,28。若转化器得到的数值为X,则相应的电压Y为:
Y
=
X
∗
3.3
/
2
12
(
o
r
2
10
,
2
8
)
Y=X*3.3/2^{12}(or 2^{10},2^8)
Y=X∗3.3/212(or210,28)
1.2数据转换
框图②中,分规则通道和注入通道,规则通道就是平常用的通道,注入通道可以当作中断理解,它是一种在规则通道转换的时候强行插入要转换的一种,如果在规则通道转换过程中,有注入通道插队,那么就要先转换完注入通道,等注入通道转换完成后,再回到规则通道的转换流程。所以,注入通道只有在规则通道存在时才会出现。框图③中则是相应的触发源,是用来触发数模转化的。
1.2.1 转换模式
在ADC运行方式中有几种模式:单次转换、连续转换、扫描模式
模式 | 运行 |
---|---|
单次转换 | 单通道转换一次数据 |
单次转换+扫描 | 多通道,每个通道转换一次数据 |
连续转换 | 单通道连续转换数据 |
连续转换+扫描 | 多通道,每个通道连续转换数据 |
由上述表格可知,扫描模式是针对多通道ADC而言的。
1.2.2 转换时间
由数据手册,我们知道ADC是挂在APB2上的,现在上面的时钟树,从左往右分析,STM32f429的外部晶振为25M的,系统时钟最大为180M。HSE:外部高速晶振,经过PLL倍频得到系统时钟SYSCLK,系统时钟经过AHB分频得到HCLK:高性能总线时钟,依此,我们可得到PCLK2,而ADCCLK则是由PCLK2再经过分频得到,由数据手册可知,ADCCLK最大值为36M。而对于STM32f429,一般如上图进行设置,设置PCLK2=HCLK/2=90MHz,然后对PCLK2再进行分频,一般设置为4分频或者6分频,即为22.5M和15M。
ADC 需要若干个 ADCCLK 周期完成对输入的电压进行采样,每个通道可以分别用不同的时间采样。其中采样周期最小是 3 个,即如果我们要达到最快的采样,那么应该设置采样周期为 3 个周期,这里说的周期就是 1/ADC_CLK。
ADC 的总转换时间跟 ADC 的输入时钟和采样时间有关,公式为:
Tconv = 采样时间 + 12(10 or 8) 个周期
上式中,第一个加数因子是跟ADC的分辨率有关的,根据选择的位数,对应周期。
当 ADCCLK = 30MHz,即 PCLK2 为 60MHz, ADC 时钟为 2 分频,采样时间设置为 3个周期,那么总的转换时为: Tconv = 3 + 12 = 15 个周期 =0.5us。一般我们设置 PCLK2=90MHz,经过 ADC 预分频器能分频到最大的时钟只能是 22.5M,采样周期设置期为 3 个周期,算出最短的转换时间为 0.6667us,这个是最常用的。
1.3数据传输
1.3.1 数据寄存器
ADC 转换后的数据根据转换组的不同,规则组的数据放在规则数据寄存器(ADC_DR),注入组的数据放在注入数据寄存器 (ADC_JDRx),规则通道有 16 个,可规则数据寄存器只有一个,如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走,或者开启 DMA 模式,把数据传输到内存里面,不然就会造成数据的覆盖。 最常用的做法就是开启 DMA 传输。
下面简单介绍一下DMA传输。DMA(Direct Memory Access,直接内存存取),用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无需CPU干预,节省CPU资源,ADC转换出来的值直接赋值给定义好的变量中,传输速度快。
我们知道,在单通道转换,一般不使DMA,是在中断服务函数中读取数据。所以一般使用多通道ADC时,会开启DMA,防止数据寄存器中的数据被覆盖。另外在双重、三种模式下,会配合DMA使用。
数据转换结束后,产生中断,如框图⑤所示,中断分为四种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断和溢出中断。
2.操作与结果
2.1 STM32cubeMX配置
①选择芯片型号
②设置RRC和USART1
③开ADC的IN5/IN6两个通道
④配置时钟
⑤ADC1相关配置
主要配置ADC1,其他的默认就好。
2.2 程序
使用STM32cubeMX生成代码
① printf的重定向
使用微库,在KEIL5中点击options for target,在Target标签下有个Use MicroLIB–勾选,使用微库。
在main.c中加入头文件 #include “stdio.h”,并加入以下代码
/* re-code printf */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
② 写main.c中的while循环
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t ADC_1, ADC_2;
uint32_t ADC_Value[100];
uint8_t i;
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC_Value,100); //使用DMA传输
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(500);
for(i=0,ADC_1=0,ADC_2=0;i<100;)
{
ADC_1=ADC_Value[i++];
ADC_2=ADC_Value[i++];
}
printf("double channel ADC test\n");
printf("ADC_1=%1.4f\r\n",ADC_1*3.3f/4096);
printf("ADC_2=%1.4f\r\n",ADC_2*3.3f/4096);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
偶数下标是IN5通道的数据,奇数下标是IN6通道的数据,这样便可分开存储两个通道的数据。在while之前,要开启HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC_Value,100), 其中,hadc1为结构体句柄,ADC_Value为存储数组,100为存储数据长度。
2.3 结果
其中PA5引脚接的3.3V电压,PA6引脚接的3.3V的 1/2分压。串口打印结果如下:
以上若有错误、不足之处,请多多指教!
附上代码:STM32cubeMX:双通道ADC(DMA)
参考:
https://blog.csdn.net/zdw6868/article/details/82562018
https://blog.csdn.net/weixin_42653531/article/details/81123770