当前位置:   article > 正文

STM32(七):ADC电位检测 (标准库函数)_adc的“规则组”是指可以用来执行一个

adc的“规则组”是指可以用来执行一个

前言

上一篇文章已经介绍了如何用STM32单片机中的定时器的PWM波来实现LED的“呼吸”。这篇文章我们来介绍一下如何用STM32单片机中ADC进行电位检测,并发送到XCOM串口中显示。

一、实验原理

1.ADC模数转换的介绍

首先,我们先介绍一下AD模数模块(Analog-to-Digital Converter)。AD模数模块即模拟/数字转换器,主要功能是将连续变化的模拟信号转换为离散的数字信号。

由于单片机只能处理数字信号,因此,在对外部的模拟信号进行分析、处理的过程中,必须使用ADC模块将外部的模拟信号转换成单片机所能处理的数字信号。

A/D转换器的主要类型有以下几种:

  • 积分型
  • 逐次比较型
  • 并行比较型
  • \Sigma -\bigtriangleup调制型
  • 电容阵列逐次比较型
  • 压频变换型
A/D转换器的主要技术指标主要有以下几种:
  • 转换范围 :A/D转换器能够转换的模拟电压范围。
  • 分辨率:输出二进制数的位数表示,位数越多,分辨率越高。
  • 绝对精度:对应一个给定数字量的理论模拟输入与实际输入之差。
  • 转换速度:A/D转换器完成一次转换所需的时间。转 换时间是指从接到转换控制信号开始,到输出端得到稳定的 数字输出信号所经过的这段时间。

典型ADC结构性能比较如下:

2.STM32中的ADC

STM32拥有1~3个ADC(STM32F101/102系列只有1个ADC,STM32F103系列最少都拥有2个ADC ),这些ADC可以独立使用,也可以使用双重模式(提高采样率)。

STM32的ADC是12位逐次逼近型的模拟数字转换器。它有18个通道,可测量16个外部和2个内部信号源(温度传感器、内部参考电压)。ADC的输入时钟不得超过14MHz,其时钟频率由PCLK2分频产生。

ADC 特点
  • STM32单片机有2个独立的ADC控制器,有18个通道,可测量16个外部信号和2个内部信号源:内部温度传感器和内部参考电压(Bandgap voltage) 。
  • ADC 供电要求: 2.4V to 3.6 V 。
  • ADC 输入范围: VREF- ≤ VIN ≤ VREF+ (VREF+ and VREF- available only in LQFP100 package)
  • 精度:12位。结果可按左对齐或右对齐的方式存放在16位寄存器中。
  • A/D转换的过程:采用、保持、量化、编程。采样时间越长,转换结果越稳定。采样时间可设置为:1.5个/7.5个/13.5个/28.5个ADC时钟周期。
  • ADC转换时间 : 采用时间+转换时间(12.5个时钟周期)
  • DMA 功能 (ADC1)

要想搞清楚ADC的原理,那么首先还是先要学会看图,原理图如下图所示:

1.电压输入范围

ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VDDA和VREF+接 3.3V,得到ADC的输入电压范围为: 0~3.3V。

2.输入通道

STM32 16 个外部 ADC 通道可分为两组 : 规则的和注入的。
  • 每个组可以是这16个通道中的任意一些通道以任意顺序进行的组合。
  • 规则组最多有16个通道,通道和转换顺序在ADC规则系列寄存器x(ADC_JSQR)中选择。
  • 注入组最多有4个通道。通道和转换顺序在ADC注入系列寄存器(ADC_JSQR)中选择。

各通道的A/D 转换可以单次、连续、扫描或间断模 式执行, stm32f103zet6通道如下:

3.转换顺序

由于规则转换通道只有一个数据寄存器,使用多个通道进行转换需要考虑转换顺序。多个通道的使用顺序分为俩种情况:规则通道的转换顺序和注入通道的转换顺序。

可以看下参考博客,这边就不多加赘述。

4.触发源

触发有两种方式:配置寄存器和通过内部定时器或者外部IO触发转换。

  • 配置寄存器触发,通过配置控制寄存器CR2的ADON位,写1时开始转换,写0时停止转换。在程序运行过程中只要调用库函数,将CR2寄存器的ADON位置1就可以进行转换。
  • 通过内部定时器或者外部IO触发转换,也就是说可以利用内部时钟让ADC进行周期性的转换,也可以利用外部IO使ADC在需要时转换,具体的触发由控制寄存器CR2决定。

5.转换时间

转换时间由输入时钟和采样周期来决定。每个通道总的转换时间=TSampling+Tconversion Tsampling(采样时间)

转换时间=采样时间+12.5个周期

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

6.数据寄存器 

数据转换完成后的存放在2种数据寄存器中,分别是注入通道数据寄存器和规则通道数据寄存器。

注入通道数据寄存器

数据寄存器有4个,由于注入通道最多有4个,所以注入通道转换的数据都有固定的存放位置,不会跟规则寄存器那样产生数据覆盖的问题。

规则通道数据寄存器

负责存放规则通道转换的数据,通过32位寄存器ADC_DR来存放。

7.中断

规则和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断。它们都有 独立的中断使能位。
数据转换完成之后可以产生中断,有三种情况,如下图所示:

二、实验步骤

1.配置NVIC

主要是配置ADC中断优先级,这边的对于优先级配置不懂的可以看之前的文章

STM32(三):外部中断 (标准库函数)_stm32外部中断的库函数程序怎么写-CSDN博客

  1. static void ADC_NVIC_Config(void)
  2. {
  3. NVIC_InitTypeDef NVIC_InitStructure;
  4. /* 配置中断优先级 */
  5. NVIC_InitStructure.NVIC_IRQChannel = ADCx_IRQ;
  6. /* 设置抢占式优先级为0 */
  7. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  8. /* 设置子优先级为0 */
  9. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  10. /* 使能外部中断通道 */
  11. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  12. /* 初始化配置嵌套向量中断控制器 */
  13. NVIC_Init(&NVIC_InitStructure);
  14. }

2.GPIO 初始化

主要是对ADC IO口引脚的配置,同时还要使能端口时钟

  1. static void ADCx_GPIO_Config(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStructure;
  4. /* 打开 ADC IO端口时钟 */
  5. ADC_APBxClock_FUN(ADC_GPIO_CLK, ENABLE );
  6. /* 配置 ADC IO 引脚模式 */
  7. GPIO_InitStructure.GPIO_Pin = ADC_PIN;
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  9. /* 初始化 ADC IO */
  10. GPIO_Init(ADC_PORT, &GPIO_InitStructure);
  11. }

3.配置工作模式

  1. static void ADCx_Mode_Config(void)
  2. {
  3. ADC_InitTypeDef ADC_InitStructure;
  4. /* 打开ADC时钟 */
  5. ADC_APBxClock_FUN(ADC_CLK,ENABLE );
  6. /* ADC 模式配置 */
  7. /* 只使用一个ADC,属于单模式 */
  8. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  9. /* 禁止扫描模式,多通道才要,单通道不需要 */
  10. ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
  11. /* 连续转换模式 */
  12. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  13. /* 不用外部触发转换,软件开启即可 */
  14. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  15. /* 转换结果右对齐 */
  16. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  17. /* 转换通道1个 */
  18. ADC_InitStructure.ADC_NbrOfChannel = 1;
  19. /* 初始化ADC */
  20. ADC_Init(ADCx, &ADC_InitStructure);
  21. /* 配置ADC时钟为PCLK2的8分频,即9MHz */
  22. RCC_ADCCLKConfig(RCC_PCLK2_Div8);
  23. /* 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期 */
  24. ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
  25. /* ADC 转换结束产生中断,在中断服务程序中读取转换值 */
  26. ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);
  27. /* 开启ADC ,并开始转换 */
  28. ADC_Cmd(ADCx, ENABLE);
  29. /* 初始化ADC 校准寄存器 */
  30. ADC_ResetCalibration(ADCx);
  31. /*等待校准寄存器初始化完成 */
  32. while(ADC_GetResetCalibrationStatus(ADCx));
  33. /* ADC开始校准*/
  34. ADC_StartCalibration(ADCx);
  35. /*等待校准完成 */
  36. while(ADC_GetCalibrationStatus(ADCx));
  37. /* 由于没有采用外部触发,所以使用软件触发ADC转换 */
  38. ADC_SoftwareStartConvCmd(ADCx, ENABLE);
  39. }

记得配置ADC初始化函数,将上述配置放在一个函数ADCx_Init()

  1. void ADCx_Init(void)
  2. {
  3. ADCx_GPIO_Config();
  4. ADC_NVIC_Config();
  5. ADCx_Mode_Config();
  6. }

4.中断函数

但到这里我们的中断配置还没结束!!重点!!易踩坑!!

我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 ADC_IRQHandler_FUN(),记得加上头文件,例如:

  1. #include "bsp/adc/bsp_adc.h"
  2. __IO uint16_t ADC_ConvertedValue;
  3. void ADC_IRQHandler_FUN(void)
  4. {
  5. if(ADC_GetITStatus(ADCx,ADC_IT_EOC)!=RESET)
  6. {
  7. ADC_ClearITPendingBit(ADCx,ADC_IT_EOC);
  8. /* 读取ADC的转换值 */
  9. ADC_ConvertedValue = ADC_GetConversionValue(ADCx);
  10. }
  11. }

5.串口发送函数

主要是讲电压数据发送到XCOM串口助手中,运用到printf()函数,这边可以看发过的这篇文章

STM32(九):USART串口通信 (标准库函数)-CSDN博客

三、实操代码

程序分为3个文件:bsp_adc.c、bsp_adc.h、main.c

1.bsp_adc.c 

  1. /* 包含头文件 ----------------------------------------------------------------*/
  2. #include "bsp/adc/bsp_adc.h"
  3. /**
  4. * 函数功能: ADC GPIO 初始化
  5. */
  6. static void ADCx_GPIO_Config(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. /* 打开 ADC IO端口时钟 */
  10. ADC_APBxClock_FUN(ADC_GPIO_CLK, ENABLE );
  11. /* 配置 ADC IO 引脚模式 */
  12. GPIO_InitStructure.GPIO_Pin = ADC_PIN;
  13. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  14. /* 初始化 ADC IO */
  15. GPIO_Init(ADC_PORT, &GPIO_InitStructure);
  16. }
  17. /**
  18. * 函数功能: 配置ADC工作模式
  19. */
  20. static void ADCx_Mode_Config(void)
  21. {
  22. ADC_InitTypeDef ADC_InitStructure;
  23. /* 打开ADC时钟 */
  24. ADC_APBxClock_FUN(ADC_CLK,ENABLE );
  25. /* ADC 模式配置 */
  26. /* 只使用一个ADC,属于单模式 */
  27. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  28. /* 禁止扫描模式,多通道才要,单通道不需要 */
  29. ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
  30. /* 连续转换模式 */
  31. ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  32. /* 不用外部触发转换,软件开启即可 */
  33. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  34. /* 转换结果右对齐 */
  35. ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  36. /* 转换通道1个 */
  37. ADC_InitStructure.ADC_NbrOfChannel = 1;
  38. /* 初始化ADC */
  39. ADC_Init(ADCx, &ADC_InitStructure);
  40. /* 配置ADC时钟为PCLK2的8分频,即9MHz */
  41. RCC_ADCCLKConfig(RCC_PCLK2_Div8);
  42. /* 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期 */
  43. ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
  44. /* ADC 转换结束产生中断,在中断服务程序中读取转换值 */
  45. ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);
  46. /* 开启ADC ,并开始转换 */
  47. ADC_Cmd(ADCx, ENABLE);
  48. /* 初始化ADC 校准寄存器 */
  49. ADC_ResetCalibration(ADCx);
  50. /*等待校准寄存器初始化完成 */
  51. while(ADC_GetResetCalibrationStatus(ADCx));
  52. /* ADC开始校准*/
  53. ADC_StartCalibration(ADCx);
  54. /*等待校准完成 */
  55. while(ADC_GetCalibrationStatus(ADCx));
  56. /* 由于没有采用外部触发,所以使用软件触发ADC转换 */
  57. ADC_SoftwareStartConvCmd(ADCx, ENABLE);
  58. }
  59. /**
  60. * 函数功能: NVIC配置:ADC中断优先级配置
  61. */
  62. static void ADC_NVIC_Config(void)
  63. {
  64. NVIC_InitTypeDef NVIC_InitStructure;
  65. /* 配置中断优先级 */
  66. NVIC_InitStructure.NVIC_IRQChannel = ADCx_IRQ;
  67. /* 设置抢占式优先级为0 */
  68. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  69. /* 设置子优先级为0 */
  70. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  71. /* 使能外部中断通道 */
  72. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  73. /* 初始化配置嵌套向量中断控制器 */
  74. NVIC_Init(&NVIC_InitStructure);
  75. }
  76. /**
  77. * 函数功能: ADC初始化
  78. */
  79. void ADCx_Init(void)
  80. {
  81. ADCx_GPIO_Config();
  82. ADC_NVIC_Config();
  83. ADCx_Mode_Config();
  84. }

2.bsp_adc.h

  1. #ifndef __ADC_H__
  2. #define __ADC_H__
  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include <stm32f10x.h>
  5. // 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
  6. /********************ADC输入通道(引脚)配置**************************/
  7. #define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
  8. #define ADCx ADC3
  9. #define ADC_CLK RCC_APB2Periph_ADC3
  10. #define ADCx_IRQ ADC3_IRQn
  11. #define ADC_IRQHandler_FUN ADC3_IRQHandler
  12. #define ADC_GPIO_CLK RCC_APB2Periph_GPIOC
  13. #define ADC_PORT GPIOC
  14. #define ADC_PIN GPIO_Pin_0 // 连接至板载精密可调电阻(需加跳帽)
  15. #define ADC_CHANNEL ADC_Channel_10 // 连接至板载精密可调电阻(需加跳帽)
  16. //#define ADC_PIN GPIO_Pin_1 // 连接至板载光敏电阻(需加跳帽)
  17. //#define ADC_CHANNEL ADC_Channel_11 // 连接至板载光敏电阻(需加跳帽)
  18. /* 扩展变量 ------------------------------------------------------------------*/
  19. /* 函数声明 ------------------------------------------------------------------*/
  20. void ADCx_Init(void);
  21. #endif /* __ADC_H__ */

3.main.c

  1. /* 包含头文件 ----------------------------------------------------------------*/
  2. #include "stm32f10x.h"
  3. #include "stm32f10x.h"
  4. #include "bsp/led/bsp_led.h"
  5. #include "bsp/key/bsp_key.h"
  6. #include "bsp/delay/delay.h"
  7. #include "bsp/systick/bsp_SysTick.h"
  8. #include "bsp/GeneralTIM/bsp_GeneralTIM.h"
  9. #include "bsp/adc/bsp_adc.h"
  10. /* 用于保存转换计算后的电压值 */
  11. float ADC_ConvertedValueLocal;
  12. /* 扩展变量 ------------------------------------------------------------------*/
  13. extern __IO uint16_t ADC_ConvertedValue;
  14. /* 私有函数原形 --------------------------------------------------------------*/
  15. static void Delay(uint32_t time);
  16. /**
  17. * 函数功能: 主函数.
  18. */
  19. int main(void)
  20. {
  21. /* 调试串口初始化配置,115200-N-8-1.使能串口发送和接受 */
  22. USARTx_Init();
  23. /* ADC 初始化 */
  24. ADCx_Init();
  25. /* 调用格式化输出函数打印输出数据 */
  26. printf("----这是一个ADC单通道电压采集实验-----\n");
  27. /* 无限循环 */
  28. while (1)
  29. {
  30. ADC_ConvertedValueLocal =(float)ADC_ConvertedValue*3.3/4096;
  31. printf("AD转换原始值 = 0x%04X \r\n", ADC_ConvertedValue);
  32. printf("计算得出电压值 = %f V \r\n",ADC_ConvertedValueLocal);
  33. Delay(1000);
  34. }
  35. }

四、实验效果

ADC电压检测

参考博客:

STM32—ADC详解_stm32最好可设置几个模拟通道-CSDN博客

结束语

本文以STM32VET6为例讲解了如何用STM32单片机中ADC进行电位检测,并发送到XCOM串口中显示,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/676663
推荐阅读
相关标签
  

闽ICP备14008679号