ReCclay
认证:VIP会员
所在专题目录 查看专题
蓝桥嵌入式之 AT24C02
蓝桥嵌入式之 PWM波输出相关总结
蓝桥嵌入式之 输入捕获(一)
蓝桥嵌入式之 输入捕获(二)
蓝桥嵌入式之 实时时钟RTC
蓝桥嵌入式之 ADC电压采集与显示
作者动态 更多
【FPGA基础】基于 Pango Design Suite(PDS) 的FPGA开发流程
2021-05-14 11:22
【FPGA基础】基于Quartus Prime 17.1 的FPGA开发流程
2021-05-08 17:49
【AD快速入门】8051最小系统绘制
2021-04-22 10:03
蓝桥嵌入式之 ADC电压采集与显示
2021-04-14 11:48
蓝桥嵌入式之 实时时钟RTC
2021-04-13 14:50

蓝桥嵌入式之 ADC电压采集与显示

导读:《蓝桥杯嵌入式组》专栏文章是博主2019年参加蓝桥杯的嵌入式组比赛所做的学习笔记,在当年的比赛中,由于忙于准备考研及保研相关工作,博主仅仅参加了当年的省赛,并获得了省赛一等奖的成绩。成绩虽谈不上最好,但至少问心无愧。如今2021年回头再看该系列文章,仍然感触颇多。为了能更好地帮助到单片机初学者,今年特地抽出时间对当年的文章逻辑和结构进行重构,以达到初学者快速上手的目的。需要指出的是,由于本人水平有限,如有错误还请读者指出,非常感谢。那么,接下来让我们一起开始愉快的学习吧。

“一叶遮目,不见泰山”。不论何事,只有把握事情的总体趋势,才能做到心中有数。


1个留待补充的程序:


“为了确保不产生丢失数据的情况,开启 DMA 传输模式,将数据传输到内存中”尝试编程

一、ADC入门

1、基础知识

模数转换器(Analog To Digital Converter)简称 ADC(也可以写成 A/D),是指将连续变化的模拟信号转换为离散的数字信号的器件。 ADC 分为积分型、逐次逼近型、并行/串行比较型、Σ-Δ型等多种类型,STM32F103 自带的 ADC 属于逐次逼近型。

逐次逼近型 ADC 与天平称物重非常相似,从高位到低位逐位比较。首先从最重的砝码开始试放,与被称物体行进比较,若物体重于砝码,则该砝码保留,否则移去,然后用次重砝码继续比较,照此一直到最小一个砝码为止,将所有留下的砝码重量相加,就得此物体的重量。逐次逼近型 A/D 转换器,就是将输入模拟信号与不同的参考电压作多次比较,使转换所得的数字量在数值上逐次逼近输入模拟量对应值。


2、STM32F103RBT6的ADC时钟及转换时间

蓝桥板载STM32F103RBT6拥有2路12位(0~4096)ADC,ADC挂载在APB2总线上,且ADC最大时钟不超过14MHz。所以当APB2总线设置为72M,有必要对其分频再用于ADC,分频用到的库函数是:void ADC_ADCCLKConfig(u32 RCC_ADCCLKSource);

ADC的转换时间 = 采样时间 + 12.5周期

采样时间和实际电路有着莫大的关系,但是对于蓝桥的板子而言,对于转换速度没有太大要求我们一般设置为:ADC_SampleTime_239Cycles5239.5个周期即可。

3、ADC输入通道

注意到了两个ADC模块,共用了一些通道,不同ADC应用不同通道时,可以同时进行采样和转换,但不可以对相同通道同时采样。

板子对应ADC部分原理图

PB0管脚可复用为ADC_IN8

ADC1和ADC2两个通道的都欧克,所以本博客选用了ADC1。

4、ADC的注入组和规则组

在使用 ADC 外部通道时,可以设定为规则组和注入组。规则组就是设定好转换顺序后,按照规则正常转换;注入组类似于中断,可以插队,当触发信号触发注入组通道时,优先转换注入组,转换完后再继续转换规则组;如果正在转换规则通道期间,注入通道被触发,当前规则组转换被复位,注入通道序列被以单次扫描方式转换,完成转换后恢复上次被中断的规则通道转换。规则组最多可以使用 16 个通道,注入组最多可以使用 4 个通道。

蓝桥板子也没有用到多么复杂,我们就直接使用规则组就好了。

5、ADC的单通道和多通道

当“转换组”只有一个通道转换时称之为单通道模式,当有多个通道按顺序转换时称之为多通道模式或者扫描模式。

蓝桥板子,使用单通道即可。

6、ADC的单次转换和连续转换

当规则组或注入组的通道按照设定顺序执行一次采转换后即停止工作,这种模式称之为单次转换模式;如果执行完一次转换后,ADC 没有停止,而是立即启动新一轮转换,这种模式称之为连续转换模式。

蓝桥板子,使用单次转换即可。

7、触发源

触发启动转换:触发启动转换分为两种方式,分别是软件触发和外部事件触发(外部是相对于 ADC 外设来讲),其中外部事件触发又分为定时器触发和外部触发(这里的外部指的是芯片外部信号)。

蓝桥板子,使用软件触发即可

8、转换后的数据储存

①、规则组存储

在独立 ADC 模式下,规则组通道转换完成后,转换后的数据被存储在对应的 ADC 规则数据寄存器 ADC_DR 的低 16 位。

双 ADC 模式是 ADC1 和 ADC2 同时采集某些参数,比如要获取瞬时功率需要同时采集电压和电流参数才能准确计算结果,这种场合就必须使用双 ADC 模式。这种模式下 ADC1所对应的 ADC_DR 的高 16 位存储 ADC2 的规则数据,低 16 位存储 ADC1 的规则数据。

由于 ADC 的精度是 12 位,因此在将 ADC 的 12 位数据存入 16 位的数据寄存器中时,可以通过 ADC_CR2 寄存器的 ALIGN 位来选择数据是左对齐还是右对齐。

规则通道最多有 16 个通道,但规则数据寄存器只有一个,因此当使用多通道转换时,前一个转换的通道数据,会被后一个通道转换的数据覆盖掉,因此理论上必须在后一个通道转换完毕之前就把数据取走

为了确保不产生丢失数据的情况,开启 DMA 传输模式,将数据传输到内存中是一个好办法,这个到时候作为扩展例程写。

②、注入组存储

ADC 注入组最多有 4 个通道,每个通道都有对应的数据寄存器 ADC_JDRx(x=1..4),ADC_JDRx 是 32 位的,高 16 位保留,低 16 位用来保存数据,选择数据是左对齐还是右对齐都由 ALIGN 决定。

9、中断处理

如果打开相应的中断,有三种情况可以进入中断。 (1)规则通道转换完成中断

  1. 转换数据被存储在 16 位的 ADC_DR 寄存器中。
  2. EOC(转换结束)标志被置位。
  3. 如果设置了 EOCIE 位,则产生中断。

(2)注入通道转换完成中断

  1. 转换数据被存储在 16 位的 ADC_DRJx 寄存器中。
  2. JEOC(注入转换结束)标志被置位。
  3. 如果设置了 JEOCIE 位,则产生中断。

(3)模拟看门狗中断 如果开启了模拟看门狗中断,并且设置ADC低阈值ADC_LTR和高阈值ADC_HTR, 当采集到的电压高于高阈值或者低于低阈值时,就会产生模拟看门狗中断。

关于ADC中断这部分,这里也没有具体涉及到,等日后做题做到了再补充吧。目前也只用到了查询的方式。

10、ADC校准

ADC 有一个内置自校准模式,可以大幅减小由于内部电容的变化而造成的精准度误差。通过设置 ADC_CR2 寄存器的 CAL 位启动校准,一旦校准结束,CAL 位被硬件复位。建议在每次上电时执行一次 ADC 校准,启动校准前,ADC 必须处于上电状态(ADON=‘1’)(言外之意必须先使能ADC),至少超过两个 ADC 时钟周期。

校准代码是固定的,参考下面的代码即可。

11、 使能通道触发转换

规则组和注入组触发方式方式分为软件触发和外部触发,其中外部触发包括定时器触发和外部信号触发。如果设置了规则组或注入组触发方式,还需要使能相应触发,保证在触发到来时启动转换。

规则组软件触发相应函数为void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

12、转换结束标志位

看手册介绍

可以由软件清除或者读取数据寄存器清除!

13、总结配置流程

1、初始化 ADC1 通道引脚(内部温度传感器和参照电压通道不需要进行引脚初始化) 2、配置 ADC1 中断优先级(如果使用 ADC 中断,本节未使用) 3、使能 ADC1 外设时钟 4、设置 ADC1 预分频系数 5、复位 ADC1 6、配置 ADC1 初始化结构体 7、设置规则组转换顺序和采样时间 8、使能 ADC1 中断(本节未使用) 9、使能 ADC1 外设 10、校准 ADC1 11、使能 ADC1 软件触发或外部触发转换 12、编写中断服务函数(如果使用 ADC 中断,本节未使用)

二、主要代码

main.c

/*******************************************************************************
* 文件名:main.c
* 描  述:
* 作  者:CLAY
* 版本号:v1.0.0
* 日  期: 2019年1月27日
* 备  注:ADC转换实现电压测量
*         
*******************************************************************************
*/

#include "config.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
#include "rtc.h"
#include "adc.h"

u8 ADC_Flag = 0;
u8 string[20];
float adc_val;

int main(void)
{

	STM3210B_LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	
	LEDInit();
	KeyInit();
	BeepInit();
	TIM2Init(2000, 72);//定时2ms
	
	ADC1Init();
	
	while(1)
	{	
		KeyDriver();
		if(ADC_Flag )
		{
			ADC_Flag = 0;
			adc_val = Get_ADC(8) * 3.3 / 4096;
			sprintf((char*)string,"ADC_VAL : %.2f    ",adc_val);
			LCD_DisplayStringLine(Line2, string);
		
		}
	}
}

void KeyAction(int code)
{
	if(code == 1)//按下B1,切换灯状态,蜂鸣器鸣叫0.1s
	{
		GPIOC->ODR ^= (1<<8);//PC8不断取反
		GPIOD->ODR |= (1<<2);//PD2置1,使能573锁存器
		GPIOD->ODR &= ~(1<<2);//PD2清0,关闭573锁存器
		Beep(100);
	}
	else if(code == 2)
	{
		Beep(-1);
	}
	else if(code == 3)
	{
		Beep(0);
	}
	else if(code == 4)
	{
		
	}
}



adc.c

#include "adc.h"

void ADC1_IOInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB口时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//选中PB0引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化IO
}

void ADC_Cali(void)
{
	ADC_ResetCalibration(ADC1);//使能复位校准
	while(ADC_GetResetCalibrationStatus( ADC1));//等待复位校准结束
	ADC_StartCalibration( ADC1);//开启AD校准
	while(ADC_GetCalibrationStatus(ADC1));//等待校准结束
}

void ADC1Init(void)
{
	ADC_InitTypeDef ADC_InitStructure;
	
	ADC1_IOInit();//ADC的GPIO配置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC时钟,APB2总线
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADC最大时钟不超过14M,这里尽心了对APB2时钟进行6分频=12M
	ADC_DeInit(ADC1);//复位ADC
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//通道模式选择单通道
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//转换模式选择单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//规则组触发转换方式选择转换由软件触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据格式选择右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数目为1
	ADC_Init(ADC1,&ADC_InitStructure);//初始化结构体
	
	ADC_Cmd(ADC1, ENABLE);//使能ADC
	ADC_Cali();//ADC校准
}

u16 Get_ADC(u8 channel)
{
	u16 temp;
	
	ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5);//ADC采样时间239.5周期 总转换时间=采样时间+12.5周期
	ADC_SoftwareStartConvCmd( ADC1,ENABLE);//使能ADC1软件触发转换,触发一次ADC转换
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == 0);//等待ADC转换完毕
	temp = ADC_GetConversionValue( ADC1);//获取转换结果,注意此时已经自动清除EOC位
	ADC_SoftwareStartConvCmd( ADC1,DISABLE);//失能ADC转换
	
	return temp;
}




adc.h

#ifndef _ADC_H
#define _ADC_H


#include "config.h"

void ADC1Init(void);
u16 Get_ADC(u8 channel);

#endif


stm32f10x_it.c

extern u8 ADC_Flag;

void TIM2_IRQHandler(void)
{
	static u16 tmr500ms = 0;
	
	if(TIM_GetITStatus(TIM2, TIM_FLAG_Update))
	{
		TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
		
		tmr500ms++;
		
		KeyScan();
		BeepScan(2);//2ms扫描
		if(tmr500ms >= 250)
		{
			tmr500ms = 0;
			ADC_Flag = 1;
		}
	}
}

三、注意事项

定时器设定500ms读取一次AD值

结语:以上就是本篇文章的全部内容啦,希望大家可以多多支持我的原创文章。如有错误,请及时指正,非常感谢。

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 146
收藏 146
关注 431
成为作者 赚取收益
全部留言
0/200
  • dy-i2UfRuvP 2021-05-19 13:17
    对我很有帮助
    回复
  • dy-iipPZRPN 2021-05-13 22:38
    思路清晰,受益匪浅
    回复
  • dy-3EbVR6Ei 2021-05-13 22:27
    比论文强一万倍
    回复
  • dy-Xq2JxpfN 2021-05-13 22:12
    比论文强一万倍
    回复
  • dy-9g42stbW 2021-05-13 21:55
    什么时候更新
    回复
  • dy-mLj7kl5v 2021-05-13 20:31
    大开眼界,真是好文
    回复
  • dy-apcih3c1 2021-05-13 20:11
    感谢分享
    回复
  • dy-k78ZHtFD 2021-05-13 20:01
    学习了
    回复
  • dy-nAWdnPGS 2021-05-13 18:49
    比论文强一万倍
    回复
  • dy-prSX6RBY 2021-05-13 18:38
    围观学习
    回复
  • dy-jqGVYqsF 2021-05-13 16:05
    大开眼界,真是好文
    回复
  • dy-VIQ9auhf 2021-05-13 14:54
    围观学习
    回复
  • dy-7mura2gg 2021-05-13 14:44
    对我很有帮助
    回复
  • dy-YN3DYTeH 2021-05-13 14:34
    讲的真好!
    回复
  • dy-9hjGevyn 2021-05-13 13:32
    佩服楼主
    回复
  • dy-H1WY5jXH 2021-05-13 13:13
    什么时候更新
    回复
  • dy-wVQjSHHX 2021-05-13 12:59
    对我很有帮助
    回复
  • dy-88VlYaFf 2021-05-12 15:35
    不亚于看了一篇高质量论文
    回复
  • dy-9QTV6UZW 2021-05-12 15:23
    精彩,很多东西还没接触到
    回复
  • dy-WYS5BCmB 2021-05-12 15:10
    大开眼界,真是好文
    回复