stm32(十七)ADC

1、STM32F407 的 ADC 介绍


ADC(模数转换器),即将模拟信号转换成数字信号的电路。当然既然有模数转换器,那肯定也有数模转换
器 DAC,即将数字信号转换为模拟信号的电路称为数模转换器,其实就是芯片通过ADC来将模拟量转换为数字量。

例如:

假如3.3V的电压分成平均4096份,提问500份数字量对应的电压是多少?

Voltage = 3.3*500/4096。

ADC的作用就是采集数字量,根据数字量来判断当前的电压是多少。

1.1、基本概念

  • 参考电压

参考电压是指测量电压值时,用作参考点的电压值。我板子的参考电压是3.3V

  • 精度

8位:256,将参考电压分成256分

10位:1024,将参考电压分成1024分

12位:4096,将参考电压分成4096分

  • 转换公式

实际的电压= 采样的数字量*参考电压/采样精度。

1.2、常见的ADC转换器

传感器分为两种:①直接输出模拟电压量;②传感器内部带ADC,主控与这种传感器进行数据交互式,通常会以SPI或者IIC的通讯接口来进行通讯。

STM32内部有ADC控制器,可以实现对模拟电压信号的采集。

2、STM32F407的ADC控制器

12 位 ADC 是逐次趋近型模数转换器。它具有多达 19 个复用通道,可测量来自 16 个外部源,两个内部源和
VBAT 通道的信号。这些通道的 AD 转换可在单次,连续,扫描或不连续采样模式下进行。 ADC 的结果存储在一个
左对齐或右对齐的 16 位数据寄存器中。
ADC 具有模拟看门狗特性,允许应用检测输入电压是否超过了用户自定义的阈值上限或下限。


3、STM32F407 的 ADC 特征

  •  可配置 12 位, 10 位, 8 位或 6 位分辨率
  • 在转换结束,注入转换结束以及发生模拟看门狗或溢出事件时产生中断
  • 单次和连续转换模式
  • 用于自动将通道 0 转换为通道”n”的扫描模式
  • 数据对齐以保持内置数据一致性
  • 可独立设置各通道采样时间
  • 外部触发器选项,可为规则转换和注入转换配置极性
  • 不连续采样模式
  • 双重/三重模式(具有 2 个或更多 ADC 的器件提供)
  • 双重/三重 ADC 模式下可配置的 DMA 数据存储
  • 双重/三重交替模式下可配置的转换间延迟
  • ADC 转换类型
  • ADC 电源要求,全速运行时为 2.4V 到 3.6V,慢速运行时为 1.8V
  • ADC 输入范围: VREF-≤VIN≤VREF+
  • 规则通道转换期间可产生 DMA 请求
     

4、ADC时钟

ADC 具有两个时钟方案

  • 用于模拟电路的时钟: ADCCLK,所有 ADC 共用

此时钟来自于经可编程预分频器分频的 APB2 时钟,该预分频器允许 ADC 在 fPCLK2/2, /4, /6 或/8 下工作。

  • 用于数字接口的时钟

此时钟等效于 APB2 时钟。可以通过 RCC_APB2 外设时钟使能寄存器(RCC_APB2ENR)分别为每个 ADC使能/禁止数字接口时钟
 

5、ADC通道选择

有 16 条复用通道,可以将转换分为两组:规则转换和注入转换。每个组包含一个转换序列,该序列可
按任意顺序在任意通道上完成。

  • 一个规则转换组最多由 16 个转换构成,必须在 ADC_SQRx 寄存器中选择转换序列的规则通道以及顺序。

规则转换组中的转换总数必须写入 ADC_SQR1 寄存器中的 L[3:0]位。

  • 一个注入转换组最多由 4 个转换构成。必须在 ADC_JSQR 寄存器中选择转换序列的注入通道及其顺序。

注入转换组中的转换总数必须写入 ADC_JSQR 寄存器中的 L[1:0]位。
如果在转换期间修改 ADC_SQRx 或 ADC_JSQR 寄存器,将复位当前转换并向 ADC 发送一个新的启动脉冲,
以转换新选择的组

6、ADC外部通道与GPIO口的映射关系

STM32F407的ADC控制器的输入通道最多有19路,其中包括16个外部输入通道,3个内部通道。

在使用外部通道输入模拟电压信号时,需要知道ADCx控制器的通道所映射的管脚。

16个外部通道对应的管脚如下表:

举例:使用ADC1通道0采样光敏电阻的电压,需要将PA0配置为模拟输入,具体提代码如下:

RCC->AHB1ENR |= 1<<0;//使能PA时钟
GPIOA->MODER  |= 3<<0;//PA0配置为模拟输入

7、转换模式

转换模式有3种:单次模式、循环模式 、扫描模式,这三种模式可以组合使用。

单个通道单次转换

一个输入通道,转换模式为单次模式

多个通道单次转换

输入通道有多个,转换模式为单次模式+扫描模式

单个通道连续转换

一个输入通道,转换模式为循环次模式

多个通道连续转换

输入通道有多个,转换模式为循环模式+扫描模式

8、相关寄存器

8.1、ADC控制寄存器 1 (ADC_CR1) 

AD分辨率位数越大,精度越高,需要的转换时间越长。本章节,我们选择最高的分辨率,以ADC1控制器举例,对应的代码:ADC1->CR1 &=~(3<<24);

注入通道和规则通道都可以用扫描模式,具体扫描哪些通道的模拟信号,需要根据ADC_SQRx和ADC_JSQRx来确定

如果该位置1,当注入通道转换完成,会产生ADC中断异常

如果该位置1,当规则通道转换完成,会产生ADC中断异常

8.2、控制寄存器 2 (ADC_CR2) 

该位置1可以启动ADC的规则组模数转换,ADC控制器配置完成后,才可以启动模式转换,以ADC1控制器举例,对应的代码:ADC1->CR2 |=(1<<30);

该位置1可以启动ADC的注入组模数转换,ADC控制器配置完成后,才可以启动模式转换,以ADC1控制器举例,对应的代码:ADC1->CR2 |=(1<<22);

数据对齐常用的是右对齐,也就是AD采样结果从数据寄存器的位0开始存储

以ADC1控制器举例,对应的代码:ADC1->CR2 &=~(1<<11);

用于设置单次模式和连续模式,具体情况具体分析

配置AD的基本内容后,需要将该位置1来启动ADC。

以ADC1控制器举例,对应的代码:ADC1->CR2 |=(1<<0);

8.3、状态寄存器 (ADC_SR) 

ADC1->CR2 |=(1<<22);//启动注入通道转换

while(!(ADC1->SR &(1<<2))) ; //阻塞判断转换是否转换完成

ADC1->SR &=~(1<<2);//清零

//读取采样数据

ADC1->CR2 |=(1<<30);//启动规则组通道转换

while(!(ADC1->SR &(1<<1))) ; //阻塞判断转换是否转换完成

//读取采样数据

8.4、通用控制寄存器 (ADC_CCR) 

PCLK2 的时钟是84MHZ

举例:将ADC1~3的时钟配置 为84MHZ的8分频,代码如下:

ADC->CCR |= 3<<16;

 

8.5.​​​​​、ADC 采样时间寄存器 

STM32F407ZGT6支持19个模拟信号通道,每一个通道的采样时间都是可以单独设置的

采样时间寄存器 1 (ADC_SMPR1) 

采样时间寄存器 2 (ADC_SMPR2) 

通道0~18的采样时间设置与SMP0~SMP18一一对应,即3位对应一个通道,3位有8中情况,具体如下:

                       

举例①:ADC1的通道3设置采样键为480个周期,对应的代码如下:

        ADC1->SMPR2 |= 7<<9;

举例①:ADC1的通道16设置采样键为480个周期,对应的代码如下:

        ADC1->SMPR1 |= 7<<18;

8.6、ADC规则序列寄存器

规则序列寄存器 1 (ADC_SQR1) 

规则序列寄存器 2 (ADC_SQR2) 

规则序列寄存器 3 (ADC_SQR3) 

.

ADC_SQR1寄存器的位23:20

SQ1写入的是第1次转换的通道编号

SQ2写入的是第2次转换的通道编号

SQ3写入的是第3次转换的通道编号

SQ4写入的是第4次转换的通道编号

SQ5写入的是第5次转换的通道编号

SQ6写入的是第6次转换的通道编号

SQ7写入的是第7次转换的通道编号

SQ8写入的是第8次转换的通道编号

SQ9写入的是第9次转换的通道编号

SQ10写入的是第10次转换的通道编号

SQ11写入的是第11次转换的通道编号

SQ12写入的是第12次转换的通道编号

SQ13写入的是第13次转换的通道编号

SQ14写入的是第14次转换的通道编号

SQ15写入的是第15次转换的通道编号

SQ16写入的是第16次转换的通道编号

注意:通道编号为0~18

举例:

①规则通道转换序列只有一次转换,转换的通道编号是8,对应的代码如下:

ADC1->SQR1 &=~(0XF<<20);

ADC1->SQR3 |=(8<<0);

①规则通道转换序列有两次转换,第一转换的通道编号是12,第二次转换的通道编号是16,对应的代码如下:

ADC1->SQR1 |=(1<<20);

ADC1->SQR3 |=(12<<0);

ADC1->SQR3 |=(16<<5);

 

8.7、规则数据寄存器 (ADC_DR) 

8.8、注入序列寄存器 (ADC_JSQR) 

如果转换次数为1次,ADC 将仅转换 JSQ4[4:0] 通道 

如果转换次数为2次,ADC 转换通道的顺序为:先是 JSQ3[4:0],而后是 JSQ4[4:0]

如果转换次数为3次,ADC 将按以下顺序转换通道: JSQ2[4:0]JSQ3[4:0] JSQ4[4:0]

如果转换次数为4次,ADC 将按以下顺序转换通道: JSQ1[4:0]JSQ2[4:0]JSQ3[4:0] JSQ4[4:0] 

注意:通道编号为0~18

举例:

①注入通道转换序列只有一次转换,转换的通道编号是8,对应的代码如下:

ADC1->JSQR &=~(3<<20);

ADC1->JSQR |=(8<<15);

①注入通道转换序列有两次转换,第一转换的通道编号是12,第二次转换的通道编号是16,对应的代码如下:

ADC1->JSQR |=(1<<20);

ADC1->JSQR |=(12<<10);

ADC1->JSQR |=(16<<15);

8.9、注入数据寄存器 x (ADC_JDRx) (x= 1..4) 

当注入转换只有一次时,采样数据保存到JDR1

9、软件设计

管脚 PA5(STM_ADC)测试滑动变阻器的电压值

寄存器版

#include "stm32f4xx.h"
#include "stdio.h"
void Delay_us(u16 us)
{
	SysTick->CTRL &=~(1<<2);    //选择时钟源为21MHZ
	SysTick->CTRL &=~(1<<1);    //禁止滴答中断
	SysTick->LOAD = 21*us;      //设置重装载寄存器的值
	SysTick->CTRL |= (1<<0);    //使能滴答定时器
	while(!(SysTick->CTRL&(1<<16)));//阻塞判断定时时间是否到达,判断SysTick->CTRL的位
}
/******************** 串口打印函数 ***************************************/
/* fputc是printf最底层的调用函数 */
int fputc(int data,FILE *file)
{
	
	while( !(USART1->SR & (1 << 6)) );//等待发送完成
	USART1->DR = data;   //发送数据
	return data;
}
//初始化 ADC
//这里我们仅以规则通道为例
//我们默认仅开启 ADC1_CH5
void Adc_Init(void)
{
//先初始化 IO 口
RCC->APB2ENR|=1<<8; //使能 ADC1 时钟
RCC->AHB1ENR|=1<<0; //使能 PORTA 时钟
GPIO_Set(GPIOA,PIN5,GPIO_MODE_AIN,0,0,GPIO_PUPD_PU); //PA5,模拟输入,下拉
RCC->APB2RSTR|=1<<8; //ADCs 复位
RCC->APB2RSTR&=~(1<<8); //复位结束
ADC->CCR=3<<16; //ADCCLK=PCLK2/4=84/4=21Mhz,ADC 时钟不要超过 36Mhz
ADC1->CR1=0; //CR1 设置清零
ADC1->CR2=0; //CR2 设置清零
ADC1->CR1|=0<<24; //12 位模式
ADC1->CR1|=0<<8; //非扫描模式
ADC1->CR2&=~(1<<1); //单次转换模式
ADC1->CR2&=~(1<<11); //右对齐
ADC1->CR2|=0<<28; //软件触发
ADC1->SQR1&=~(0XF<<20);
ADC1->SQR1|=0<<20; //1 个转换在规则序列中 也就是只转换规则序列 1
//设置通道 5 的采样时间
ADC1->SMPR2&=~(7<<(3*5)); //通道 5 采样时间清空
ADC1->SMPR2|=7<<(3*5); //通道 5 480 个周期,提高采样时间可以提高精确度
ADC1->CR2|=1<<0; //开启 AD 转换器
}
//获得 ADC 值
//ch:通道值 0~16
//返回值:转换结果
u16 Get_Adc(u8 ch)
{
//设置转换序列
ADC1->SQR3&=0XFFFFFFE0;//规则序列 1 通道 ch
ADC1->SQR3|=ch;
ADC1->CR2|=1<<30; //启动规则转换通道
while(!(ADC1->SR&1<<1));//等待转换结束
return ADC1->DR; //返回 adc 值
}
//获取通道 ch 的转换值,取 times 次,然后平均
//ch:通道编号
//times:获取次数
//返回值:通道 ch 的 times 次转换结果平均值

void usart1_Init()
{
	u16 integer ;
	u16 fraction;
	float USARTDIV;
	RCC->AHB1ENR |= 1<<0;//使能GPIOA的时钟  RCC_AHB1ENR
	GPIOA->MODER &= ~(3<<18);//清零
	GPIOA->MODER |= 2<<18;//设置PA9为复用功能模式MODER
	GPIOA->AFR[1]&= ~(0XF<<4);//清零
	GPIOA->AFR[1]|= (7<<4);//设置PA9的复用功能为第7复用功能 RXD, AFRH
	GPIOA->MODER &= ~(3<<20);//清零
	GPIOA->MODER |= 2<<20;//设置PA10为复用功能模式MODER
	GPIOA->AFR[1]&= ~(0XF<<8);//清零
	GPIOA->AFR[1]|= (7<<8);//设置PA10的复用功能为第7复用功能 RXD, AFRH
	
	RCC->APB2ENR |= 1<<4;//使能串口1模块时钟 RCC_APB2ENR
	USART1->CR1  |= 1<<15;//设置串口OVER8为1
	USART1->CR1  &= ~(1<<12);//设置串口数据位长度 :1 起始位, 8 数据位
	USART1->CR2 &=~(3<<12);//设置串口停止位长度 :1 个停止位
	USART1->CR1 &= ~(1<<10);//无校验
	//中断不使能
	USART1->CR1 |= 1<<3;//发送使能
	USART1->CR1 |= 1<<2;//接收使能
	USARTDIV = 84000000/8/baudRate;//串口波特率设置:USARTDIV = fCK/(8*(2-OVER8) /波特率
	integer = (u16)USARTDIV;
	fraction = ((u16)(USARTDIV-integer))<<4;
	USART1->BRR |= integer<<4 | fraction;
	USART1->CR1 |= 1<<13;//使能串口
}

int main()
{
	float adc;
	u16 temp;
	
	uSART1_Init();
	Adc_Init();
	
	while(1)
	{
		temp = Get_Adc(ADC_Channel_5);
		adc = (temp * 3.3) /4096.0;
		printf("%1.4fV\r\n",adc);
		delay_ms(500);
	}
}

库函数版   

void  Adc_Init(void)
{    
  GPIO_InitTypeDef  GPIO_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	ADC_InitTypeDef       ADC_InitStructure;
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟

  //先初始化ADC1通道5 IO口
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化  
 
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);	  //ADC1复位
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);	//复位结束	 
 
	
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,
  ADC_CommonInit(&ADC_CommonInitStructure);//初始化
	
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式	
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐	
  ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1 
  ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
 
	ADC_Cmd(ADC1, ENABLE);//开启AD转换器	

}				  

u16 Get_Adc(u8 ch)   
{
	  	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );	//ADC1,ADC通道,480个周期,提高采样时间可以提高精确度			    
  
	ADC_SoftwareStartConv(ADC1);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}
int main()
{
	float adc;
	u16 temp;
	
	uSART1_Init();
	Adc_Init();
	
	while(1)
	{
		temp = Get_Adc(ADC_Channel_5);
		adc = (temp * 3.3) /4096.0;
		printf("%1.4fV\r\n",adc);
		delay_ms(500);
	}
}

 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值