历史上的今天
返回首页

历史上的今天

今天是:2025年06月12日(星期四)

正在发生

2020年06月12日 | STM32基于固件库学习笔记(13)ADC读取电压值

2020-06-12 来源:eefocus

ADC简介

STM32f103 系列有 3 个 ADC(STM32F101/102 系列只有 1 个 ADC),精度为 12 位,每个 ADC 最多有 16 个外部通道。其中ADC1和 ADC2 都有 16个外部通道,ADC3根据 CPU 引脚的不同通道数也不同,一般都有8 个外部通道。

ADC 可以独立使用,也可以使用双重模式(提高采样率)。STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。


STM32F103 系列最少都拥有 2 个 ADC,我们选择的 STM32F103ZET 包含有 3 个 ADC。

STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。


STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。


1. 输入电压范围

   ADC 输入范围为:V REF- ≤ V IN ≤ V REF+ 。由 V REF- 、V REF+ 、V DDA 、V SSA 、这四个外部引脚决定。在设计原理图的时候一般把 V SSA 和 V REF- 接地,把 V REF+ 和 V DDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。

2. 输入通道


3. 转换顺序

   分别为 SQR3、SQR2、SQR1。SQR3 控制着规则序列中的第一个到第六个转换,对应的位为:SQ1[4:0]~SQ6[4:0],第一次转换的是位 4:0 SQ1[4:0],如果通道 16想第一次转换,那么在 SQ1[4:0]写 16即可。SQR2 控制着规则序列中的第 7到第12个转换,对应的位为:SQ7[4:0]~SQ12[4:0],如果通道 1想第 8 个转换,则 SQ8[4:0]写 1即可。SQR1控制着规则序列中的第 13到第 16个转换,对应位为:SQ13[4:0]~SQ16[4:0],如果通道 6 想第 10 个转换,则 SQ10[4:0]写 6 即可。具体使用多少个通道,由 SQR1 的位L[3:0]决定,最多 16个通道。


4. 触发源

  ADC 转换可以由ADC 控制寄存器 2: ADC_CR2 的 ADON 这个位来控制,写 1 的时候开始转换,写 0 的时候停止转换。


  ADC 还支持触发转换,这个触发包括内部定时器触发和外部 IO 触发。触发源有很多,具体选择哪一种触发源,由 ADC 控制寄存器 2:ADC_CR2 的EXTSEL[2:0]和 JEXTSEL[2:0]位来控制。EXTSEL[2:0]用于选择规则通道的触发源,JEXTSEL[2:0]用于选择注入通道的触发源。选定好触发源之后,触发源是否要激活,则由ADC 控制寄存器 2:ADC_CR2 的EXTTRIG 和 JEXTTRIG 这两位来激活。


5. 转换时间

   ADC 输入时钟 ADC_CLK由 PCLK2经过分频产生,最大是 14M,分频因子由 RCC时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,注意这里没有 1分频。一般设置 PCLK2=HCLK=72M。经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us,这个才是最常用的

  ADC 使用若干个 ADC_CLK 周期对输入的电压进行采样,采样的周期数可通过 ADC采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9,ADC_SMPR1 控制的是通道 10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是 1.5 个,即如果我们要达到最快的采样,那么应该设置采样周期为 1.5个周期,这里说的周期就是 1/ADC_CLK。


  ADC 的转换时间跟 ADC 的输入时钟和采样时间有关,公式为:Tconv = 采样时间 +12.5 个周期。当 ADCLK = 14MHZ (最高),采样时间设置为 1.5 周期(最快),那么总的转换时间(最短)Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。


6. 数据寄存器

  规则组的数据放在 ADC_DR寄存器,注入组的数据放在 JDRx。


7. 中断

   中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。


8. 电压转换

  模拟电压经过 ADC转换后,是一个 12 位的数字值,如果通过串口以 16进制打印出来的话,可读性比较差,那么有时候我们就需要把数字电压转换成模拟电压,也可以跟实际的模拟电压(用万用表测)对比,看看转换是否准确。


ADC正常工作的一般配置步骤

1. 开启 ADC用到的 GPIO的时钟和 ADC1 时钟置 ,初始 ADC用到的 GPIO。


//挂载时钟  

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1|RCC_APB2Periph_AFIO,ENABLE);

//GPIO初始化

  GPIO_InitTypeDef GPIO_InitTypeDef_PA;

  GPIO_InitTypeDef_PA.GPIO_Pin = GPIO_Pin_1;

  GPIO_InitTypeDef_PA.GPIO_Mode = GPIO_Mode_AIN;//模拟输入

  GPIO_Init(GPIOA,&GPIO_InitTypeDef_PA);


注:这里使用的ADC1、PA0,其他GPIO时钟具体时钟线

2. 复位 ADC,同时置 ADC 分频因子。


//复位ADC时钟

   ADC_DeInit(ADC1);

//设置 ADC1 分频因子

   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6=12MHz


3. 设置 ADC的工作参数并初始化。


//初始化ADC1参数

   ADC_InitTypeDef ADC1_InitTypeDef_PA;

   ADC1_InitTypeDef_PA.ADC_Mode = ADC_Mode_Independent;  

   ADC1_InitTypeDef_PA.ADC_ScanConvMode = DISABLE; //AD 单通道模式

   ADC1_InitTypeDef_PA.ADC_ContinuousConvMode = DISABLE;//单次模式

   ADC1_InitTypeDef_PA.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动

   ADC1_InitTypeDef_PA.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

   ADC1_InitTypeDef_PA.ADC_NbrOfChannel = 1;//顺序进行规则转换的 ADC 通道的数目

   ADC_Init(ADC1, &ADC1_InitTypeDef_PA); //根据指定的参数初始化外设 ADCx


ADC_InitTypeDef structure


typedef struct

{

u32 ADC_Mode;//设置 ADC 工作在独立或者双 ADC 模式

/*

ADC_Mode_Independent           ADC1 和 ADC2 工作在独立模式

ADC_Mode_RegInjecSimult         ADC1 和 ADC2 工作在同步规则和同步注入模式

ADC_Mode_RegSimult_AlterTrig    ADC1和ADC2工作在同步规则模式和交替触发模式

ADC_Mode_InjecSimult_FastInterl ADC1和ADC2工作在同步规则模式和快速交替模式

ADC_Mode_InjecSimult_SlowInterl ADC1和ADC2工作在同步注入模式和慢速交替模式

ADC_Mode_InjecSimult            ADC1 和 ADC2 工作在同步注入模式

ADC_Mode_RegSimult              ADC1 和 ADC2 工作在同步规则模式

ADC_Mode_FastInterl             ADC1 和 ADC2 工作在快速交替模式

ADC_Mode_SlowInterl             ADC1 和 ADC2 工作在慢速交替模式

ADC_Mode_AlterTrig              ADC1 和 ADC2 工作在交替触发模式

*/

FunctionalState ADC_ScanConvMode;//模数转换工作在扫描模式(多通道)还是单次(单通道)模式

/*

参数为 ENABLE 或者 DISABLE。

*/

FunctionalState ADC_ContinuousConvMode;//模数转换工作在连续还是单次模式

/*

参数为 ENABLE 或者 DISABLE。

*/

u32 ADC_ExternalTrigConv;//使用外部触发来启动规则通道的模数转换

/*

ADC_ExternalTrigConv_T1_CC1   选择定时器 1 的捕获比较 1 作为转换外部触发

ADC_ExternalTrigConv_T1_CC2   选择定时器 1 的捕获比较 2 作为转换外部触发

ADC_ExternalTrigConv_T1_CC3   选择定时器 1 的捕获比较 3 作为转换外部触发

ADC_ExternalTrigConv_T2_CC2   选择定时器 2 的捕获比较 2 作为转换外部触发

ADC_ExternalTrigConv_T3_TRGO  选择定时器 3 的 TRGO 作为转换外部触发

ADC_ExternalTrigConv_T4_CC4   选择定时器 4 的捕获比较 4 作为转换外部触发

ADC_ExternalTrigConv_Ext_IT11 选择外部中断线 11 事件作为转换外部触发

ADC_ExternalTrigConv_None     转换由软件而不是外部触发启动

*/

u32 ADC_DataAlign;//ADC 数据向左边对齐还是向右边对齐

/*

ADC_DataAlign_Right ADC 数据右对齐

ADC_DataAlign_Left ADC 数据左对齐

*/

u8 ADC_NbrOfChannel;//顺序进行规则转换的 ADC 通道的数目

/*

取值范围是 1 到 16

*/

} ADC_InitTypeDef


4. 使能 ADC 并校准。


//使能ADC   

   ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1

//复位校准   

   ADC_ResetCalibration(ADC1);

//ADC 校准的

   ADC_StartCalibration(ADC1); //开始指定 ADC1 的校准状态


5. 取读取 ADC 值。


 //设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间

  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 ); //通道1

  //规则采样顺序值为 1,采样时间为 239.5 周期

  ADC_SoftwareStartConvCmd(ADC1,ENABLE); //使能指定的 ADC1 的软件转换功能

  while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC ));//等待转换结束

  return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果


完整代码

#include "stm32f10x.h"          

/*

  使用 ADC1 的通道 1 进行 AD 转换

   PA0 模拟输入,测量电池电压

*/

#include "stdio.h"

int fputc(int ch, FILE *f)

{

  while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);

  USART_SendData(USART1,(uint8_t)ch);

  return ch;

}

void Usart_Init()

  GPIO_InitTypeDef GPIO_ITDef1;

  GPIO_InitTypeDef GPIO_ITDef;

  USART_InitTypeDef  USART_ITDef;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组2:2位抢占优先级,2位响应优先级

//挂载时钟(复用PA) 串口时钟使能,GPIO 时钟使能,复用时钟使能

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);

//PA9 TXD初始化

  GPIO_ITDef.GPIO_Pin = GPIO_Pin_9;//PA9 TXD

  GPIO_ITDef.GPIO_Mode = GPIO_Mode_AF_PP;////复用推挽输出

  GPIO_ITDef.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA,&GPIO_ITDef);

//PA10 TXD初始化

  GPIO_ITDef1.GPIO_Pin = GPIO_Pin_10;//PA10 RXD

  GPIO_ITDef1.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

  GPIO_Init(GPIOA,&GPIO_ITDef1);

  

//USART初始化

  USART_ITDef.USART_BaudRate = 115200;//波特率

  USART_ITDef.USART_WordLength = USART_WordLength_8b;//发送数据长度

  USART_ITDef.USART_StopBits = USART_StopBits_1; //一个停止位   

  USART_ITDef.USART_Parity = USART_Parity_No; //无奇偶校验位     

  USART_ITDef.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制

  USART_ITDef.USART_Mode = USART_Mode_Tx| USART_Mode_Rx ;//发送模式 

  USART_Init(USART1,&USART_ITDef);

 

  USART_Cmd(USART1, ENABLE);//使能串口

}


void delay_ms(u16 time)

{    

   u16 i = 0;  

   while(time--)

   {

      i = 12000;  

      while(i--);    

   }

}

//这里使用的PA0可以根据情况修改

void ADC1_Init_PA0(void)

{

//挂载时钟  

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1|RCC_APB2Periph_AFIO,ENABLE);

//GPIO初始化

  GPIO_InitTypeDef GPIO_InitTypeDef_PA;

  GPIO_InitTypeDef_PA.GPIO_Pin = GPIO_Pin_1;

  GPIO_InitTypeDef_PA.GPIO_Mode = GPIO_Mode_AIN;//模拟输入

  GPIO_Init(GPIOA,&GPIO_InitTypeDef_PA);

    

//复位ADC时钟

   ADC_DeInit(ADC1);

//设置 ADC1 分频因子

   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72/6=12MHz

//初始化ADC1参数

   ADC_InitTypeDef ADC1_InitTypeDef_PA;

   ADC1_InitTypeDef_PA.ADC_Mode = ADC_Mode_Independent;  

   ADC1_InitTypeDef_PA.ADC_ScanConvMode = DISABLE; //AD 单通道模式

   ADC1_InitTypeDef_PA.ADC_ContinuousConvMode = DISABLE;//单次模式

   ADC1_InitTypeDef_PA.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动

   ADC1_InitTypeDef_PA.ADC_DataAlign = ADC_DataAlign_Right;//右对齐

   ADC1_InitTypeDef_PA.ADC_NbrOfChannel = 1;//顺序进行规则转换的 ADC 通道的数目 1

   ADC_Init(ADC1, &ADC1_InitTypeDef_PA); //根据指定的参数初始化外设 ADCx

//使能ADC   

   ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1

//复位校准   

   ADC_ResetCalibration(ADC1);

//ADC 校准的

   ADC_StartCalibration(ADC1); //开始指定 ADC1 的校准状态

//必须等待校验结束

  while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

  while(ADC_GetCalibrationStatus(ADC1)); //等待校 AD 准结束 

}


u16 Get_Adc(u8 ch)

{

  //设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间

  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 ); //通道1

  //规则采样顺序值为 1,采样时间为 239.5 周期

  ADC_SoftwareStartConvCmd(ADC1,ENABLE); //使能指定的 ADC1 的软件转换功能

  while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC ));//等待转换结束

  return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果

}

u16 Get_Adc_Average(u8 ch,u8 times)

{

  u32 temp_val=0;

  u8 t;

  for(t=0;t  {    

    temp_val+=Get_Adc(ch);    

    delay_ms(5);

  }

  return temp_val/times;

}

u16 adcx;

float adcx1=0;

int main(void)

{

  ADC1_Init_PA0();

  Usart_Init();

  delay_ms(1000);

  while(1)

  {

     adcx = Get_Adc_Average(1,100);

     adcx1 = adcx*3.3/4096;

     printf("电压:%3.2f vn",adcx1);

     delay_ms(500);    

  }     

}


推荐阅读

史海拾趣

ABECO公司的发展小趣事

随着全球化进程的加速推进,ABECO开始实施全球化战略。公司积极开拓国际市场,参加各类国际展会和论坛,与全球客户建立联系。同时,公司还加强与国际同行的交流与合作,引进先进的技术和管理经验。通过不断的努力,ABECO逐渐在全球电子行业中树立起了自己的品牌形象,成为了一家具有影响力的跨国公司。


请注意,上述故事是基于已知信息进行的虚构创作,并不代表ABECO公司的真实发展历程。如果您需要更详细和准确的信息,建议直接联系ABECO公司或查阅相关文献资料。

AEC Design公司的发展小趣事
在初步获得市场认可后,AEC Design公司开始积极拓展国内外市场。通过与各大电商平台和实体零售商建立合作关系,公司的产品线逐渐覆盖更广泛的消费群体。同时,公司还积极参加各类行业展会和论坛,与业界同行交流学习,不断提升自身的品牌影响力和市场竞争力。
Devar Inc公司的发展小趣事

Devar Inc公司自创立之初,就致力于在增强现实(AR)领域取得突破。经过多年的研发,公司成功推出了世界上首个用于AR的生成式AI神经网络。这项技术允许用户通过简单的文本提示创建3D对象和其他AR资产,极大地简化了AR内容的创作过程。Devar的AR平台结合了神经网络和云解决方案,让用户能够轻松创建并分享AR内容,开启了AR创作的新时代。

API Delevan公司的发展小趣事

随着技术的不断进步,API Delevan公司始终保持着对创新的追求。研发团队不断攻克技术难关,推出了一系列具有创新性的电感器、RF线圈、功率电感器等产品。这些产品不仅提高了性能,还降低了能耗,满足了客户的多样化需求。同时,公司还注重产品升级,不断提升产品的竞争力。

Black Box Corporation公司的发展小趣事

在数字化时代,通信技术的更新换代速度极快。为了保持竞争优势,Black Box不断投入研发,推出了一系列创新产品和技术。例如,公司研发了一款智能网络管理平台,能够实时监控网络状态并进行故障预警。这一技术创新大大提高了网络的稳定性和安全性,赢得了客户的广泛好评。

岑科(CENKER)公司的发展小趣事

随着生产规模的扩大,岑科在2010年将制造板块迁移至广西,以进一步优化企业资源配置。这一举措不仅降低了生产成本,还提高了生产效率。同时,岑科加速了自主创新和技术进步的步伐,不断推出更具竞争力的电子产品。在这一阶段,岑科的产品质量和性能得到了市场的广泛认可,客户满意度也持续提升。

问答坊 | AI 解惑

FPGA学习流程

我明年读研究生,选的是数字电路设计,毕业设计做FPGA的设计。刚刚开始完全不知道该做什么。迷茫迷茫ing。。。 有高手给我说说fpga学习主要先从那几个方面入手,我已经有了简单的verilog基础,但也不是很通。关于需要看的书,需要学的软件。 万分 ...…

查看全部问答>

点对点2.4GHz无线链路安装部署指南

能够帮助你了解无线电链路计划编制、影响链路设计的各种因素和如何计算一个链路的预算(也就是说从这里到那里需要的功率、线缆和连接器损耗、自由空间路径损耗和接收机灵敏度),已经是定制好公式,强烈推荐 ACC Wireless Connections Link Budget C ...…

查看全部问答>

谁有OK2440-III开发板的WinCE 6.0 BSP

各位大大,请问谁有OK2440-III开发板的WinCE 6.0 BSP,烦请发给我一份,我有急用。gooogleman的armce.com网速太慢,下不下来。我的邮箱是tonypioneer@yahoo.com.cn。谢谢了!…

查看全部问答>

昨天的会议上见到颜老师真人了

昨天的会议上见到颜老师真人了。 呵呵,是个牛人 …

查看全部问答>

MSP430G2不知道是否还缺货~!

斑竹可以留一下官网,然后论坛可以搞一个团购呀~!…

查看全部问答>

89C51单片机如何访问片外RAM?

                                 想做一个电子密码锁,但是不知道该把我自己设置的密码存到哪里去,也不知道该如何存,是不是要存入片外的RAM?请大 ...…

查看全部问答>

【设计工具】故障安全多重引导参考设计

         Jim Wesselkamper在该应用指南中介绍了可将故障安全机制加入扩展Spartan®-3A系列FPGA(Spartan-3A、Spartan-3AN和Spartan-3A DSP平台)的多重引导功能的参考设计。该参考设计通过确定加载哪 ...…

查看全部问答>

【设计工具】wp391_solv_endoscopy_sys_arch [白皮书]

wp391_solv_endoscopy_sys_arch [白皮书]  。 …

查看全部问答>

[视频] Tiva C Series LaunchPad Workshop

谢谢 qingkaiabc 的分享,做了一下整理. Chapter 01 $(\'swf_Vm7\').innerHTML=AC_FL_RunContent(\'width\', \'600\', \'height\', \'450\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', encodeURI(\'http:// ...…

查看全部问答>

LaunchPad能接外部8M晶振吗

LaunchPad能接外部8M晶振吗…

查看全部问答>