STM32模拟-数字转换(ADC)
如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的
stm32学习笔记: stm32学习笔记源码
如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用
git的使用(下载及上传_gitcode怎么下载文件_是小刘不是刘的博客-CSDN博客
ADC(模拟转数字):本次代码是采集端口电压,并且通过可变电阻改变电压值观测
使用非扫描单次和非扫描连续来进行多通道和单通道的采集
为什么没有扫描呢(因为规则组只有一个数据寄存器,所以一次采集多少个通道他也只会保存一个,如果想保存多个最后用DMA将数据转运,DMA的知识会到下一章写,这个也就刚好当作DMA的实践了)扫描模式连续采集文章如下。
stm32 DMA的使用_是小刘不是刘的博客-CSDN博客
目录
一、理论部分
1. ADC简介
2. 逐次逼近型ADC
3 stm32ADC框图
4 ADC引脚定义
5 规则组的转换模式
6 触发控制
7 数据对齐
8 转换时间
9 校准
10 硬件电路
二 代码部分
1 AD单通道单次转换非扫描
1 初始化引脚
2 之后就是配置ADC了
3 ADC校准
4 开启ADC转换
5 主函数测试
2 单通道连续转换非扫描
3 AD多通道
一、理论部分
1. ADC简介
先大致了解下,后面会写到为什么是1us转换时间、也会写转换结果精度的问题、通道数什么的也会详细介绍、模拟单元后面也会具体讲、模拟看门狗自动检测可以减少进看门狗中断的检测次数减少硬件的压力。(然后每个板子的adc数量是不一样的,f103的好像都是2个ADC,具体有几个就得去看芯片手册了。
2. 逐次逼近型ADC
这里我们先说说一个8位的ADC,stm32内部的为12位的,只能说精度更高,但是原理差不多,所以我们先了解一下这个远古芯片ADC0809。因为stm32是ADC外设,所以没有具体画出adc的工作方式,这里正好可以了解一下。
1) 首先第一部分就是选择器:这个很简单,总不能18个通道给你18个选择引脚把,这里通过3位选择器来控制8个通道的选择
2) 第二部分 一个DAC 通过DAC的输入范围Vref+和Vref-来确定DAC和ADC的参考电压,所以这里如果是5v那ADC的输入也要是5V,然后ADC通过每次取值(二分法取值,假如输入范围是0-255,我们就会拿126与输入值进行比较,如果不对就继续减小或者加大)这里刚好二分法的取值是二进制的权值,所以只需要8次就能判断出8位ADC的值
之后是上面的那一部分使能和时钟了,ADC内部是需要时钟来控制的,EOC转换结束信号
3 )第三部分是输出,8位8个输出,12位12个输出
3 stm32ADC框图
这个看着就比刚刚说的那个芯片复杂很多了
但是逻辑方面差不多,首先是16个通道的输入IN0-IN15 还有两个内部源 一个温度传感器一个参考电压的输入(总共18个通道
然后中间有一个模拟多路开关,这个一个4选16的开关
然后是输入到两个通道:一个注入通道,一个规则通道,这两个通道分别由自己的使用方面
1:注入通道:可以一次选择四个通道,并且也能存储4个处理结果。
2:规则通道:可以一次选中16个通道但是只能存储一个量,一般配合DMA使用,存一个就转移一个走,这里右侧有个ADCCLK就是ADC的时钟,前面说过ADC内部的比较是需要时钟的干预的·。这个时钟源是来自ADC的预分频器,如图所示,经过APB2后分屏(将72M分2 4屏很显然超过了最大14M的限制这里就最大只能6 分下来为12M。
然后是这一块,1 为一个模拟看门狗,如果我们打开了这个模拟看门狗他就会一直帮我们检测这个adc的转换结果,分别有上限为和下下限位,如果超出了阈值就会输出一个·AWD信号标志位
2 为通道的输出,当注入组和规则组转换完成,也会输出一个标志位出去。
然后是下面这一部分是触发源:ADC可以软件触发,也可以硬件触发
上面是注入组的触发源下面是规则组的触发源,有一些时钟通道和TRGO(这个东西就是可以硬件自动完成转换的,减少代码频繁进入中断,产生不确定因素的可能)吧时钟触发源选择为TRGO然后这里触发源选择TRGO就行。
总结一下(直接找的中科大的PPT
从左往右看
1)首先和上面的图一样,16个外部输出脚,加1个内部测量芯片温度的,和一个参考电压
2) 然后是AD转换器,分为规则组和注入组,这里规则组可以选择16个通道,但是只有1个数据寄存器可以存放数据,一个为注入组,有4个AD数据寄存器,
3)下面两个输入一个为触发控制(触发控制分为软件触发和硬件触发,软件就是调用代码触发转换,硬件就是时钟触发和外部中断触发这类),一个为ADC时钟。
4)上方有个输出EOC:转换完成之后置标志位可以输出到NVIC
5) 可以部署一个模拟看门狗,这个看门狗可以设定一个上下阈值,检测转换值,超出范围就会置一个标志位输出到NVIC
6 )右下角的开关控制:这个基本外设都有哦就是cmd启用外设
4 ADC引脚定义
ADC的引脚定义,有3个ADC,理论上是3个ADC每个16个通道,但是可以看见这里其实1 2基本都是一样的引脚,这里ADC1 2就能交叉采样,让其处理的速度更快。
5 规则组的转换模式
转换模式 1 单次转换非扫描 2连续转换非扫描 3 单次转换扫描 4 连续转换扫描
然后这就是通过两个位可以控制选项的嘛(很经典的2选4)
具体讲解一下吧,因为用的比较多的就是规则组,所以这里先说规则组吧,注入组后面再说
这里先说单次扫描是什么意思:就是ADC转换器可以一次转换16个通道,但是我们这里只让他第一位有效,就只检测第一位,第一位输入的是通道几他就会检测通道几
然后是连续和非连续:这个也好理解,就是转换完一次需要再次去启动转换就是非连续
1 单次转换非扫描
第一种模式就是单次非扫描:就是只采集一个存储位,并且只转换一次,如果你还想第二次转换,你就需要重新去启动转换。
2 连续非扫描
还是第一个存储位有效,但是不需要手动去启动转换,会一直自动的去完成转换。
3 单次转换扫描模式
还是每次都需要开启转换,但是这次是一次转换了多个通道的数据了,你可以指定通道的数目,这样转换的时候就只会转换前7个通道,转换结束后产生EOC信号。
4 连续转换扫描模式
转换完成之后自动完成下一次转换,这样我们就只需要读数据寄存器就好了。
6 触发控制
这些就是刚刚说的触发源了,首先是定时器信号,然后是外部引脚和软件控制位(软件触发),他的触发模式可以通过控制EXTSEL寄存器来控制,如果是使用固件库编程的话直接选择模式就好。
7 数据对齐
因为数据寄存器是16位的,但是STM32的ADC是12位精度的
右对齐:这里直接读出来就行
左对齐:向左移动了4位,就比原值扩大了16倍。所以最后处理的时候最好除以16,并且如果你不需要这么高的精度,你可以只取高八位
8 转换时间
A采用过后需要将采样的信号保持一会再进行量化编码,不然采样的信号保持不到量化的时间怎么 能采样(信号系统一般只说采样量化编码,但是这里因为需要时钟配合是需要时间的)
采样时间+保持量化编码所需要的12.5周期就是总的转换时间,这里采样时间根据周期来定,若时钟为14MHZ(前面说了这个是有最大时钟限制的嘛14M就是最大时钟了)这时候采样时间就是1.5个周期,加上12.5就是14个周期(这时候时钟是14M所以处理14个周期就只需要1us了.
9 校准
每次上电都需要校准,这里直接通过几条代码就能让他校准(不需要自己计算)
10 硬件电路
1 第一种就是最简单的滑动变阻器,通过改变阻止来调节电压
2 各种传感器,比如光敏,温湿度之类的,直接影响传感器阻值
3 电压转换电路 输入0-5 经过分压 就是5×50\33最后就是0-3-3的范围了
二 代码部分
1 AD单通道单次转换非扫描
首先看看自己的硬件连接(我用的开发板,开发板是PC1接的电位器,也可以自己网上买一个电位器想接哪个ADC接口都行)这个就属于刚刚说的第一种直接接的滑动变阻器了。
之后就是代码部分
首先写一个驱动我们就先写初始化void AD_Iint(void) 老朋友了每个固件都有
代码如下:
1 初始化引脚
首先初始化引脚,然后配置ADC(这里看你是不是C99 如果是你的结构体声明随意,如果不是就只能放在最前面我是放在了最前面的(我的变阻器接的是PC1,所以初始化GPIOC)然后是开启时钟,还是那句话没有时钟你后面的所有操作都是无效的
void AD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
2 之后就是配置ADC了
首先我们需要配置ADC的时钟,这个我们之前讲过(这里是来自APB2的72M的分屏,我们只需要配置分屏系数,之后就是配置ADC的结构体。
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_RegularChannelConfig(ADC1,ADC_Channel_11,1,ADC_SampleTime_1Cycles5);
ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;
ADC_InitStruct.ADC_NbrOfChannel=1;
ADC_InitStruct.ADC_ScanConvMode=DISABLE;
ADC_Init(ADC1,&ADC_InitStruct);
ADC_Cmd(ADC1,ENABLE);
首先:1)配置时钟(6分屏
2) 为所选ADC常规通道及其对应通道配置,排序器中的排序及其采样时间。
3)配置ADC结构体
4) 启动ADC
1)这里直接调用ADCCLKConfig:很明显的配置时钟的函数 参数选择了6分屏
2) 配置ADC通道以及排序器(这个就是前面图所说的序列)
这里我们使用的为ADC1,然后通道11(这个得根据自己接的引脚号去查表哦,我用的PC1就是通道11),序列1,采样时间随便配置一个1.5
3) 配置ADC结构体
首先是第一个:前面所说的连续模式或者单一模式(ENABLE是开启连续,这里我是关闭
第二个:数据对齐:左对齐右对齐,我这里选择了右对齐(前面也讲过哈忘记了去看看
第三个:触发模式(外部触发、软件触发)这里我选择软件触发
第四个:ADC模式选择:可以选双AD和单AD(我这里选择了单AD
第五个:开启多少个序列
第六个:扫描或者非扫描:我选择了非扫描
4 ) 启动ADC
3 ADC校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
这就是前面说的校准ADC了(直接调用函数即可)
1 初始化ADC校准
2 等待校准寄存器初始化(若为1则一直等待 为0时代表初始化完成)
3 开始ADC校准
4 开始校准(若为1则一直等待 为0代表校准完成 ,这边建议先看看手册,然后去找对应的函数
4 开启ADC转换
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
这里写一个函数带16位的返回值
1 首先 启动软件触发ADC转换
2 判断EOC标志位,这个前面也一直说过要判断这个标志位
3 然后我们直接返回数据寄存器的值,这样主函数直接调用就能开启一次转换了
5 主函数测试
void Delay(int delay)
{
while(delay--);
}
uint16_t Value=0;
int main(void)
{
Usart_Config();
AD_Init();
while(1)
{
Value=AD_GetValue();
printf("%d\r\n",Value);
Delay(5000000);
}
}
这里懒得写定时器了- -直接递减延时了
首先调用AD的初始化:AD_Init()
然后创建一个16位的变量保存数据,之后我们进行打印,转动变阻器采集量正常变化,也能正常变换为0 和4091
测试完成
成功采集之后我们就能通过计算得到电压了,我们可以直接除以4096然后乘以3.3这样就是吧范围限定到0-3.3v了代码如下
uint16_t Value=0;
float voltage=0;
int main(void)
{
Usart_Config();
AD_Init();
while(1)
{
Value=AD_GetValue();
voltage=(float)Value / 4096 * 3.3;
printf("%.2fv\r\n",voltage);
Delay(5000000);
}
}
测试结果:前面是没加位数限制,这里如果只想精确到第二位可以像我一样输个.2f就好
2 单通道连续转换非扫描
这里连续转换就不需要每次等待转换完成了,直接开启之后调用查看数据寄存器就好,这不需要等待转换完成了,硬件会不断地将转换的结果放到数据存储器,数据会一直被刷新。
之后开启连续转换(这里改为ENBALE
测试结果(结果是一样的
3 AD多通道
AD多通道其实和单通道是一样的,只不过多开了几个序列和几个通道,我现在手上没有多的会输出模拟量的器件了,只能说做个简单测试(分别吧电位器接两次接到了两个引脚,所以不会两个引脚都有数据)实在没有电位器的也能直接往3.3V上面直接怼,过几天买一个五向遥感回来给大家测试
因为前面说过有规则组和注入组,规则组虽然能一次转换16个通道,但是数据寄存器只能存放一个数据这里如果要使用扫描模式来完成多通道的话就会有数据被覆盖(所以需要配合DMA来实现数据的搬移)
我们首先采用单次非扫描的方式实现多通道采集(这里其实就是一次次的将采集的通道替换采集一次保存一次= =虽然有点瓜但是确实能采集多个他通道不是
代码实现
吧函数改为有输入的函数,等下好直接选择通道,然后吧前面配置通道的函数移到这个里面来,再加上一个ADC采样的引脚进来
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_1Cycles5);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
这样就配置完成了
主函数如下,定义两个变量分别存储两次转换值
测试结果
第1张为将电位器接到PC1 第2张为接到PC2 第3张是电位器接到PC1,PC2直接接了3.3V
到这里ADC的基础知识就写完了,下一章会写扫描模式连续采样了(因为要用DMA,所以会新开一个帖子了)
作者:是小刘不是刘