知识点1【ADC的介绍】
ADC是指 模拟量转为数字量
模拟量:在时间上和数量上都是离散的物理量称为数字量——离散的,不连续的。
在生活中,误差是一直存在的,我们可以无限缩小误差,但是不能消除;
**数字传感器:**价格较贵,操作简单,可以直接输出数组量
**模拟传感器:**价格较便宜,但是需要自己配置AD转换,最终还需要根据算法优化数据
知识点2【单片机中的ADC】
在STM32F103ZET6中,ADC的精度是12位(12位分辨率)的,是一种逐次逼近型数字转换器。
每个ADC都有18个通道,可测量16个外部信号源,2个内部信号源。
各通道的A/D转换可以 单次、连续、扫描和间断 模式执行。
1、单次、连续、扫描和间断
单次转换:每次软件触发,只完成一次规则通道序列的转换,然后停止。
连续转换:一次触发后,硬件不停重复执行规则序列(N通道)转换,直到手动停止。
扫描模式:针对多通道场景,按设定的序列(Rank 1…N)依次完成N路规则通道的转换
间断模式:在扫描模式基础上,将N通道序列拆分成若干小段(每段M各通道),每次触发只转换一次,完成后暂停,待下次触发再转换下一段。
注意:单次+间断组合下,每次触发仅完成 M 个通道,剩余通道要下次触发才会转换,不会“自动”一次扫完所有 N 个。需要搭配循环使用
2、中断事件
转换结束,注入转换结束 和 发生 模拟看门狗事件 时产生中断
转换结束:触发EOC中断
注入转换结束:触发JEOC中断
(1)EOC触发条件
**启动扫描模式:**触发条件是所有通道转换完成
禁用扫描模式:每次仅转换单通道,EOC立即触发
(2)JEOC触发条件
注意:注入组无需启用扫描模式,其通道转换默认按顺序执行,JEOC始终在所有通道转换完成后触发。
3、注入模式
注意:
在触发注入模式下,若注入组转换被触发,规则组当前转换会被暂停,注入组转换完成后规则组从中断点继续。JEOC的触发不干扰规则组的流程,仅标志注入组完成。
4、校准
在使用ADC之前可以先对ADC控制器进行校准,以确认数据转换的精度
这是一个可选的步骤,加上校准肯定时更完善的。
解释
电容是有一个充电放电的过程的,会影响我们对电压的采集,因此需要校验。
5、通道采样时间
在STM32F1系列的ADC中,采样 + 转换这两个阶段是分开计时的
(1)采样阶段
是由 ADC_SMPR1/2 寄存器中的 SMP[2:0] 决定,最短1.5个ADC时钟周期,最长239.5个周期
(2)转换阶段
长度固定,12.5个ADC时钟周期
(3)12.5的来源
12个周期
对应12位分辨率的逐次逼近决策,每一个 Bit 1次比较,一次比较一个时钟周期
0.5个周期
用来做 采样电容切换,结果锁存 等 微小的内部延迟
6、ADC规则 通道顺序配置
当我们使用扫描模式,及多通道模式的基础上,多通道执行的顺序是如何的呢?
我们直到 每一个ADC中有16个外部通道,
(1)寄存器
这三个寄存器中是用来配置 通道执行顺序的
注意
这里我只介绍 比较特殊的ADC→SQR1 中的L[3:0]寄存器,它是用来说明规则通道转换序列中通道数目的。
(2)库函数ADC_RegularChannelConfig()
参数 RANK 就是配置 规则通道的转换 顺序的。
(3)注意
配置顺序的时候 rank必须是连续的,比如
rank——1 rank——2 rank——3 rank——5 rank——6
是不行的,4没有配置 不连续
7、数据对齐
(1)数据右对齐
数据右对齐是比较常用的,它不需要人为的去调整。
(2)数据左对齐
数据左对齐,我们一般在处理小数据的时候会进行移位放大,这时候我们进行左对齐即可。
左对齐数据转换后 需要对数据进行相应的处理
知识点3【代码练习】
1、单通道
(1)配置步骤
/*
1、打开时钟 ADC端口 与ADC外设
2、端口配置
3、ADC初始化
4、校准
5、开启通道
6、开启ADC转换
7、中断
8、中断使能
9、定时器使能
在中断服务函数中
*/
(2)代码演示
#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include <stdio.h>
void Systick_Init(u32 ticks);
void Usart2_Init(u32 Baud);
void GPIOF_Pin8_Init(void);
void ADC3_Interrupt_Init(void);
int fputc(int c, FILE * stream);
void Delay_ms(u32 ms);
u32 delay_ms = 0;
float ADC3_Recv_Data;
int main(void)
{
SystemInit();// 初始化系统时钟(例如 HSE 8MHz 通过 PLL 倍频到 72MHz)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Systick_Init(72000);
Usart2_Init(9600);
GPIOF_Pin8_Init();
ADC3_Interrupt_Init();
while(1)
{
}
}
//系统定时器初始化函数
void Systick_Init(u32 ticks)
{
SysTick_Config(ticks);
}
//串口初始化
void Usart2_Init(u32 Baud)
{
GPIO_InitTypeDef GPIOA_Pin2_InitStruct;
GPIO_InitTypeDef GPIOA_Pin3_InitStruct;
USART_InitTypeDef USART2_InitStruct;
//时钟配置 USART,收(PA3)发(PA2)端口
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_StructInit(&GPIOA_Pin3_InitStruct);
GPIOA_Pin3_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIOA_Pin3_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIOA_Pin3_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//收PA3端口配置
GPIO_Init(GPIOA,&GPIOA_Pin3_InitStruct);
GPIO_StructInit(&GPIOA_Pin2_InitStruct);
GPIOA_Pin2_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIOA_Pin2_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIOA_Pin2_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//发PA2端口配置
GPIO_Init(GPIOA,&GPIOA_Pin2_InitStruct);
//串口初始化
USART_StructInit(&USART2_InitStruct);
USART2_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART2_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART2_InitStruct.USART_BaudRate = Baud;
USART2_InitStruct.USART_Parity = USART_Parity_No;
USART2_InitStruct.USART_StopBits = USART_StopBits_1;
USART2_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART2,&USART2_InitStruct);
//使能串口
USART_Cmd(USART2,ENABLE);
}
//PF8引脚配置
void GPIOF_Pin8_Init(void)
{
GPIO_InitTypeDef GPIOF_Pin8_InitStruct;
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
//配置引脚模式
GPIO_StructInit(&GPIOF_Pin8_InitStruct);
GPIOF_Pin8_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIOF_Pin8_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIOF_Pin8_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOF,&GPIOF_Pin8_InitStruct);
}
//ADC+中断 初始化
void ADC3_Interrupt_Init(void)
{
ADC_InitTypeDef ADC3_IN6_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//配置ADC3 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);
//配置ADC3_IN6 循环模式 单通道
ADC_StructInit(&ADC3_IN6_InitStruct);
ADC3_IN6_InitStruct.ADC_ContinuousConvMode = ENABLE;
ADC3_IN6_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC3_IN6_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC3_IN6_InitStruct.ADC_NbrOfChannel = 1;
ADC3_IN6_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC3,&ADC3_IN6_InitStruct);
//校准
ADC_ResetCalibration(ADC3);
while(ADC_GetResetCalibrationStatus(ADC3) == SET);
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC3) == SET);
//配置通道
ADC_RegularChannelConfig(ADC3, ADC_Channel_6, 1, ADC_SampleTime_55Cycles5); // 通道6,采样周期55.5周期
//启动转换
ADC_SoftwareStartConvCmd(ADC3, ENABLE); // 启动转换
//ADC3使能
ADC_Cmd(ADC3,ENABLE);
//中断使能
ADC_ITConfig(ADC3,ADC_IT_EOC,ENABLE);
//NVIC配置
NVIC_InitStruct.NVIC_IRQChannel = ADC3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
//重定向fputc
int fputc(int c, FILE * stream)
{
USART_SendData(USART2,c);
while(USART_GetFlagStatus(USART2,USART_FLAG_TC) == RESET);
return c;
}
//延时函数
void Delay_ms(u32 ms)
{
u32 ticks = delay_ms + ms;
while(ticks > delay_ms);
}
//系统定时器中断服务函数
void SysTick_Handler(void)
{
delay_ms++;
}
//ADC3中断处理函数
void ADC3_IRQHandler(void)
{
if(ADC_GetITStatus(ADC3,ADC_IT_EOC) == SET)
{
//开启转换
//ADC_SoftwareStartConvCmd(ADC3,ENABLE);
//获取数据
ADC3_Recv_Data = ADC_GetConversionValue(ADC3);
printf("%f\\n",ADC3_Recv_Data);
//清空中断
ADC_ClearITPendingBit(ADC3,ADC_IT_EOC);
}
}
代码错误:
1、这里 因为是连续模式 不需要再次开启转换,当是单次模式的时候可以开启,以实现不断收取的状态
2、没有配置通道
结束
代码重在练习!
代码重在练习!
代码重在练习!
今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!