STM32 ADC实战指南:从原理到代码实现的全方位解析

知识点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、没有配置通道

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值