ADC多通道采样基于双轴推杆-stm32f103c8t6HAL库
ADC多通道采集、双轴推杆、
这篇笔记以及学习参考了大佬的文章STM32CubeMX | HAL库的ADC多通道数据采集(轮训、DMA、DMA+TIM)、读取内部传感器温度_tim+adc+dma采集-CSDN博客
江科大笔记—ADC模式转换_adc左对齐和右对齐区别-CSDN博客
一、基本原理
1、ADC采样关键参数
-
ADC的分辨率:
是指它能够将输入信号分成多少个离散的量化级别(份数)。通常用比特数来表示,例如stm32f103c8t6就是12位ADC,则分辨率为2^12=4096个量化级,(也可以一定程度上理解成精度)
-
ADC采样时间
:完成一次采样的时间,这里以ADC时钟周期为单位
例:我使用的这款芯片ADC的时钟频率最高不能超过14Mhz,当我们的系统时钟频率为72Mhz时,ADC通道则必须使用6分频或者8分频,来保证ADC的时钟频率最高不能超过14Mhz
计算采样时间
TCONV = 采样时间(由用户配置)+ 12.5个周期

-
采样间隔时间
如果ADC的采样触发选用事件触发时,为保证采样准确性,间隔时间要大于采样时间
例如:选用定时器触发时,定时器的计数时间要大于采样时间。


-
采样时间计算
计算得采样时间=(1/12M)*(55.5+12.5)=6.5us


2、cubemax参数配置解读
- Data alignment: 数据对齐模式:分为左对齐和右对齐Right alignment
STM32中的ADC是12位(0-4095)的,但是数据寄存器拥有16位,故存在数据对齐的问题。
数据右对齐,即作为转换结果的12位数据向右靠,高位补0。
数据左对齐,即作为转换结果的12位数据向左靠,低位补0。
一般通常使用数据右对齐,这样在读取16位寄存器,直接就是转换结果。
如果选择左对齐,直接读取,得到的数据会比实际的数据大,因为数据左对齐就是把数据左移4次,二进制特点:就是数据左移一次,就等效于把数据乘以2。这里左移4次,相当于乘以16,所以直接读取,得到的数据会比实际的数据大16倍。
左对齐用处:当对分辨率的要求不高时(对电压仅作大概的判断即可)可以采用左对齐,将数据寄存器的高8位取出,就相当于舍弃了转换结果的4位的精度,12位的ADC退化位为8位的ADC。
- Scan Conversion Mode: 扫描模式(勾选了多通道后会默认打开)
- Continuous Conversion Mode: 连续转换模式
- DisContinuousConversion Mode: 间断转换模式
这三个参数需要放一起进行理解
- 单次转换 非扫描模式 单次转换 扫描模式
- 连续转换 非扫描模式 连续转换 扫描模式
-
单次转换:
每触发一次,转换结束就会停下来,下次转换就得再触发才能开始。
-
连续转换:
一次转换完成后不会停止,而是立刻开始下一轮的转换,并持续下去。
-
非扫描模式:
只对存放在序列1的通道起作用。
-
扫描模式:
可以根据配置的顺序依次采集多个ADC通道
小结:转换作用的是完整的采集之间是否的连续关系,扫描则是一次完整采集中的通道是否连续关系
-
间断模式:
在扫描模式的情况下,还可以使用间断模式。它的作用是在扫描的过程中,每隔几次转换就暂停,需要再次触发才能继续。
-
注入通道和看门狗
这个项目暂不涉及,先做了解即可
根据数据手册,可将注入通道理解为ADC里的中断,当注入通道触发时,暂停正在转换的规则通道,优先处理注入通道转换,完成后回到规则通道暂停的位置继续执行。
看门狗可以理解为事件中断,当转换的值低于或者高于配置的值时,就会触发中断。


二、硬件连接
根据原理图连接硬件,要注意的是5V接VCC,因为stm32f103c8t6 的ADC采集范围只有0-3.3V,如果为5V的话,就会导致推杆有死区,在3.3-5V范围时数据皆为4095不改变。导致无法判定位置,所以接VCC就好,这样就不会有死区。

实物照片

三、cubemax项目配置
这里我选择DMA的方式接收数据,配置ADC分频为6分频,这时ADC时钟频率为12M

选ADC1的IN0和IN1以及一个内部温度通道

-
勾选ADC1 通道
勾选ADC1的IN0和IN1以及一个内部温度通道
这里我们只用到了ADC1,所以ADC通用设置默认为独立(Independent)模式
-
数据对齐
数据对齐为右对齐
-
连续模式
连续模式关闭,在程序中手动循环开启达到循环的效果,还有一个因素是开启连续模式后会一直进行采集,而且DMA数据地址是递增的,假如存放地址的数组不够大的话就会导致数据溢出,程序 卡蹦,而且这样的话采集到的数据也难以辨认对应的哪条通道;
-
间断模式
间断模式也关闭,执行几次转换后关闭,暂时不需要,我们配置的三个通道转换完成后自动停止,和这个功能重复,不需要再配置;
-
触发源选择
触发源选择为软件触发,在程序中使用相应函数开启即可;
-
将规则通道数配置
将规则通道数改为3条,然后根据需求配置三个通道顺序,采样时间均设置为55.5个周期
DMA配置,添加ADC1通道,配置为循环模式,字宽为半字

ok,配置完成生成工程。
四、代码
main.c
#define ADC_CHANNELS 3 // ADC通道数量
uint16_t adc_buffer[ADC_CHANNELS]; // 数组存储三个通道的数据
float voltage[ADC_CHANNELS]; // 转换后的电压值
char buff[20]; //用来给OLED显示
float temp; //温度
//声明类型转换函数
void Float_To_Str(float num, char *buffer);
int main()
{
/*
省略cubemax生成的初始化代码
*/
//校正(初始化)ADC通道,让数据更精确
HAL_ADCEx_Calibration_Start(&hadc1);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/*
手动开启ADC、DMA转换,转换完成会自动停止,
放循环里就实现了连续转换的效果
*/
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_buffer,ADC_CHANNELS);
/*
我这里因为是使用OLED进行显示带有小数的数据
所以封装了一个将float类型转换为字符的函数
*/
Float_To_Str(adc_buffer[0],buff);
OLED_ShowString(1,1,buff);
//通过OLED显示数据,要使用的话记得配置OLED模块
HAL_UART_Transmit(&huart2,buff,sizeof(buff),1000);
//通过串口阻塞模式将数据发送出去,要使用的话记得配置和使能串口
Float_To_Str(adc_buffer[1],buff);
HAL_UART_Transmit(&huart2,buff,sizeof(buff),1000);
OLED_ShowString(2,1,buff);
Float_To_Str(adc_buffer[2],buff);
HAL_UART_Transmit(&huart2,buff,sizeof(buff),1000);
OLED_ShowString(3,1,buff);
//根据内部电压对应温度关系计算温度
voltage[2]=(adc_buffer[2]*3.3)/4095;
temp = (1430-voltage[2]*1000)/4.3+25;
Float_To_Str(temp,buff);
HAL_UART_Transmit(&huart2,buff,sizeof(buff),1000);
OLED_ShowString(4,1,buff);
}
/* USER CODE END 3 */
}
void Float_To_Str(float num, char *buffer)
void Float_To_Str(float num, char *buffer)
{
int integer = (int)num;
int decimal = (int)((num - integer) * 100 + 0.5); // +0.5实现四舍五入
if(decimal >= 100)
{ // 处理进位(如3.999→4.00)
decimal -= 100;
integer++;
}
// 格式化为字符串(如"123.45"),要包含头文件#include "stdio.h"
sprintf(buffer, "%d.%02d ", integer, decimal);
}
五、实际效果演示


更多推荐



所有评论(0)