文章目录
最近看了一张图觉得很有意思,可以看看我们目前的阶段hhh
adc是很重要的外部设备,使用adc可以进行很多方面的测量等,但是基本的教程如正点原子,野火等这方面都讲的比较单一,只是知道怎么用就够了,而没有让我们更好的去了解这样的一个设备,因此这里我来将我的理解做一个总结。
其实at官方也有一本很好的资料讲了这些东西,概述了st的adc的一些功能,如下所示:
我上传到gitee了,链接:STM32的ADC应用笔记
本文用到的实验平台:
- 野火MINI-stm32开发板
- STM32CUBE-IDE开发工具
一、DAC
1、直接触发
在初始化中编辑如下代码
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);
HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, 4095);
HAL_DAC_Start(&hdac,DAC_CHANNEL_2);
注意这里初始化代码不可以写成:
这样两个io口输出的都是DAC2的参数,具体原因还不太清楚,实验结果是这样的
2、使用波形发生器
定时器还是配置为事件更新
初始化中加入代码
HAL_TIM_Base_Start(&htim2);
HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
效果如下
关于这里频率的计算:
前面是定时器周期,然后他是单次累加上去的,所以就是从0-4095这样加上去,再从4095-0这样一个来回,就是一次正弦波,所以计数4096*2次,所以周期大概就在439HZ
3、DAC使用DMA来配置输出
这里不需要用他的内部波形功能了
开启dma并设置循环模式
这里定时器还是选择事件来更新
这里我们准备好需要的正弦波的数据
uint16_t sin_Data[] = {
2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668, 3382, 3042, 2661,
2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127, 310, 564, 878, 1240, 1636, 2048
};
添加初始化函数
HAL_TIM_Base_Start(&htim2);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)sin_Data, 32, DAC_ALIGN_12B_R);
使用示波器得到下面的结果
这里计算方法还是一样的,就大概是112khz
二、ADC
1、stm32adc的理解
首先来看下正点原子的教程中写的:
- 这里其实就是一个很好的总结了,首先是独立模式和双重模式,就是ADC外设单独使用,比如ADC1,ADC2这样的,不同的外设使用起来互不干扰,双重模式是啥呢,就是两个ADC一起使用,交替进行。
- 然后是单次,连续,扫描或者间断进行,这就是下面要讲的各个模式的特点了
- 然后是左右对齐,st的ADC是12位的逐次逼近型的ADC,所以采集的有效数据就是12位的,但是确保存在16位的寄存器中,这样不就有了左对齐和右对齐了吗,当然这样还会在比如我们使用DMA传输的时候,传输什么样大小的字节有关系喽
下面提到了ADC的时钟,可以看到ADC转换速率最快能有1Mhz,这个结果又是怎么出来的呢,因为ADC转换一次的时间一般就是采样时间+转换时间,而转换时间官方说明了是12.5个周期,而采样时间最小是1.5个周期,这样在最快的时钟下不就是14个周期吗,然后始终是14M,这样就是1Mhz的采样速率了
下面讲到了注入组和规则组,通俗理解注入组就像是中断一样,会打断规则组,完成注入组的采样,之后结束在进入规则组
下面的部分讲到了他的这个两个比较重要的寄存器,一个是使能发送的寄存器,一个是接收数据的寄存器
下面看下ADC的硬件部分
- 首先就讲清楚了作为一个逐次逼近型的ADC的本质,就是和VREF+还有VREF-进行比较接近的,另外还有两个模拟电源接口,就是通过这两个引脚来进行逐次逼近的,当然采样用的还是ADC外设连接的引脚,这个在2中体现出来了,3就是上面讲到的注入通道和规则通道了,4就是我们st给他设置的时钟。这里我们一般使用12Mhz即可,如下所示:
- 5就是使用外部触发来转换,前面提到了ADC_CR2这个寄存器,ADC转换的开始与结束可以由这个寄存器来控制,也可以使用外部事件来触发,常用的就是定时器了,这样效果比再循环中不断地启用定时器效果要好点!
2、ADC在CUBEMX中的配置了解
打开cube配置好基本时钟后,选择ADC,使能ADC可以看到如下内容,值得关注的是他也提供了关于内部温度传感器还有参考电压引脚的接口
下面是ADC的配置接口,这里可以很快的看到三个模式(注意F4的话和这个页面不太一样)
关于这个的理解:
- 扫描模式只在多通道ADC的时候有效,这样配置扫描模式使得通道按照配置的循环次序进行依次转换,而单次模式(就是关闭连续转换模式)无论是单通道还是多通道都是进行一次,连续模式就是不停的扫描
- 关于不连续转换模式,也称为间断模式,这里官方解释是:该设置只在连续转换失能的时候该参数才有效,否则这个参数就被抛弃
- 另一种解释就是不连续转换就是不一定要转换完所有通道(连续模式是转换完所有的哦),而是指定这个序列中的n的通道
下面看看采样周期相关的,在下面的设置中显示了采样周期,如果使用多个通道的话这里就要设置好先后已经周期
同样根据常识,st外设的三种模式,轮询,中断还有DMA
HAL_ADC_Start(hadc);
HAL_ADC_Start_IT(hadc);
HAL_ADC_Start_DMA(hadc, pData, Length);
在这里还有一个比较重要的自校准函数,虽然不知道能有啥用,但校准总比不校准好啊
HAL_ADCEx_Calibration_Start(&hadc1); //adc的自校准
上面的这几个函数将在下面广泛出现
3、单通道采集
直接轮询采集
相对比较方便,直接设置即可
初始化代码
HAL_ADC_Start(&hadc1);
采集代码
HAL_ADC_PollForConversion(&hadc1, 50); // 转换函数,等待时间50ms
singel_adc = HAL_ADC_GetValue(&hadc1); // 获取ADC采样值
HAL_ADC_Start(&hadc1); // 再次开启采集
//可适当自行延迟
我们查看效果,采集很成功!
现在我们对配置做一些小修改,打开连续转换模式
因为使能了连续转换,故而不在需要我们手动的去使能转换了
使用中断触发
在前面的配置中加入中断
使用中断采集
中断采集函数,可以看到这里我又注释了启动中断采集函数,因为前面已经使能了连续转换,所以ADC本身就会不断地触发中断,就不需要我们自己在中断函数里面手动采集了
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_PollForConversion(&hadc1, 50);
singel_adc = HAL_ADC_GetValue(&hadc1);
// HAL_ADC_Start_IT(&hadc1);
}
使用DMA来传输
下面我们关掉中断,并打开DMA传输,这里使用半字即可,注意开启循环模式,因为我们前面使能了连续转换模式,所以这里要使能循环模式,那么如果不使能循环模式咋办呢,那就直接也不使能连续转换模式,然后在while循环里面轮询呗,但这样DMA存在的意义就很弱了!!!
下面是代码设置
可以看到单次转换跟是否使用循环模式息息先关啊
4、使用定时器触发
无需设置任何中断,事件触发后不断地将数据转移到内存中,所以这里就需要配置DMA来传输,DMA配置如下,这里因为传输大小是16位的,所以设置为半字节即可。
ADC设置如下,无需设置任何中断
定时器设置如下
在main.c中添加代码
这里接收的数据我们根据我们在DMA中设置的来就行DMA中设置的是半字,所以这里设置为uint16_t即可
但是他这里用的DMA接收函数,接收的数据参数是32位的,所以要强制转换下,如下所示
之后我们启用debuge,将变量放到现场表达式查看,可以看到功能正常
5、多通道采集
多通道轮询采集
配置多通道轮询模式,这里我关闭连续转换,这里配置各个通道要改为对应的通道1234哦
编写采集函数代码如下:
for(uint8_t i=0;i<4;i++)
{
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 50);
adc_value[i] = HAL_ADC_GetValue(&hadc1);
}
HAL_Delay(10);
下面我们开启连续转换模式试试
代码部分如下所示,经过实测这种方式采样的话,十分混乱,就是各个ad的值在不停的移位,暂不清楚原因,等以后知道了在更新上!!!
下面尝试下多通道中断采集
使用连续转换试试,然后这样就不用写启动函数
使能中断触发
编写中断采集函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
for(uint8_t i=0;i<4;i++)
{
HAL_ADC_PollForConversion(&hadc1, 50);
adc_value[i]=HAL_ADC_GetValue(&hadc1);
}
}
这里我发现两个问题:
- 采样周期不可以设置的过大,设置的过大,中断会出问题,比如我设置为239.5个周期的时候出现了通道混乱的情况,就是几个通道的值一模一样,同时改变
- 本身这个通道会错位,就是通道号码和采集数据的数组不对应
- 然后这个似乎只能用连续采样,选择单次采样的话一直失败,无法进入中断函数
下面用DMA来采集
DMA这里使能连续转换,然后DMA开启循环传输,就跟上面的单次采样一样了
之后我们只需要写一个DMA开启的函数即可
我看其他人博客的时候有看到关于DMA传输的时候关于半字还是一个字节的问题,这个我觉得还是有点意思的,这里做一个说明:
- 使用半字还是一个字节都是可以的,因为收到的数据是12位的,所以都行
- 影响在于如果使用半字的话,那我们用来存的数组也要定义为uint16_t的,如果是一个字节的就是uint32_t
- 然后一点就是不管是半字还是一个字节启动函数要用32位强制转换,因为这个函数就这么写的
6、注入通道的使用
注入通道类似中断处理,不过我感觉挺鸡肋的,不知道啥地方能用上,这里只是做个记录,使用四个规则,两个注入
编写函数如下所示
使用的代码如下:
HAL_ADCEx_InjectedStart(&hadc1);
HAL_ADCEx_InjectedPollForConversion(&hadc1, 1000);
adc_val[0] = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1);
adc_val[1] = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_2);
HAL_Delay(50);
使用debug查看仿真
当然除了软件启动之外还可以使用定时器来触发,这个其实就跟其他的一样了,就不继续试验了
7、使用双路ADC采集
前面有提到ADC除了独立模式就是双ADC模式,使用双ADC可以交替采样从而提高精度,F1系列的芯片最高能使用两路ADC,而f4系列的芯片可以同时使用三路ADC
将这些模式翻译下如下所示
当然其实这些笔记里面都讲过了
下面来进行配置,双路ADC的模式太多,这里仅介绍常用的,其他的就不介绍了,可以自行查看手册翻阅,这里使用的是快速交替采样模式
开启DMA传输
采集函数的编写,一定要注意顺序!!!
这样就OK了,点击调试就能看到结果了!!!