当前位置: 首页 > news >正文

STM32基础教程——AD单通道

目录

前言

技术实现

连线图

代码实现 

内容要点 

ADC基本结构配置

读取ADC数据

电压值计算 

实验结果 

问题记录 


前言

ADCAnalog-Digital Converter模拟-数字转换器,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁.12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和两个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐对齐方式存储在16位数据寄存器中。

逐次逼近型ADC通过二进制搜索算法逐步确定输入模拟信号的数值,具体步骤如下:

  1. 初始化
    • 逐次逼近寄存器(SAR)将最高有效位(MSB)设为1,其余位设为0,形成一个初始猜测值。
    • 该值通过内部DAC(数模转换器)转换为模拟电压,并与输入信号进行比较。
  2. 比较与调整
    • 比较器将DAC输出的电压与输入模拟信号进行比较:
      • 若DAC电压 < 输入电压 → 保持该位为1,继续测试下一位(次高位)。
      • 若DAC电压 > 输入电压 → 将该位清零(设为0),继续测试下一位。
    • 重复上述步骤,逐位确定每一位的值(从MSB到LSB),直到所有位完成判断。
  3. 输出结果
    • 最终,SAR寄存器中保存的二进制数值即为输入模拟信号的数字表示。

ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。

ADC主要特征:

        ● 12位分辨率
        ● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
        ● 单次和连续转换模式
        ● 从通道0到通道n的自动扫描模式
        ● 自校准
        ● 带内嵌数据一致性的数据对齐
        ● 采样间隔可以按通道分别编程
        ● 规则转换和注入转换均有外部触发选项
        ● 间断模式
        ● 双重模式(带2个或以上ADC的器件)
        ● ADC转换时间:
        ─
        STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
        ─
        STM32F101xx基本型产品:时钟为28MHz时为1μs(时钟为36MHz为1.55μs)
        ─
        STM32F102xxUSB型产品:时钟为48MHz时为1.2μs
        ─
        STM32F105xx和STM32F107xx产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
        ● ADC供电要求:2.4V到3.6V
        ● ADC输入范围:VREF- ≤ VIN ≤ VREF+
        ● 规则通道转换期间有DMA请求产生。

ADC功能描述:

        

        

技术实现

连线图

代码实现 

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"						//延时函数
#include "OLED.h"
#include "AD.h"

uint16_t AD_Value;						//AD转换值	
float Voltage;

int main(void)
{
	/*
		OLED初始化
	*/
	OLED_Init();
	AD_Init();

	OLED_ShowString(1,1,"ADValue:");
	OLED_ShowString(2,1,"Voltage:0.00V");

	while(1)
	{
		AD_Value = AD_GetValue();
		Voltage = (float)AD_Value / 4095 * 3.3;
		OLED_ShowNum(1,9,AD_Value,5);
		OLED_ShowNum(2,9,Voltage,1);
		OLED_ShowNum(2,11,(uint16_t)(Voltage*100)%100,2);
	}
}

AD.h

#ifndef AD_H
#define AD_H

#include "stm32f10x.h"

void AD_Init(void);
uint16_t AD_GetValue(void);

#endif

AD.c 

#include "AD.h"

/**
 * @brief  AD Initialization 
 * @param  None
 * @retval None
 * @note   Initialize AD basic structure
 */
void AD_Init(void)
{
    //开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

    //配置ADC时钟 12MHz
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;                         //模拟输入
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

    //选择规则组的输入通道
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);

    //初始化ADC
    ADC_InitTypeDef ADC_InitStruct;
    ADC_InitStruct.ADC_Mode                 = ADC_Mode_Independent;		//独立ADC模式
    ADC_InitStruct.ADC_ScanConvMode         = DISABLE;                  //非扫描模式
    ADC_InitStruct.ADC_ContinuousConvMode   = DISABLE;                  //单次转换
    ADC_InitStruct.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_None;//软件触发,非外部触发
    ADC_InitStruct.ADC_DataAlign            = ADC_DataAlign_Right;      //数据右对齐
    ADC_InitStruct.ADC_NbrOfChannel         = 1;                        //序列1
    ADC_Init(ADC1,&ADC_InitStruct);
    
    //关闭ADC电源,且超过两周期以上
    ADC_Cmd(ADC1,DISABLE);

	//维持ADC处于掉电状态并且持续两周期,满足校准要求,一个周期约为71.4ns,两个周期约为142.8ns
	Delay_us(1);
	
    //ADC校准
    ADC_ResetCalibration(ADC1);                                         //复位校准,将ADC_CR2寄存器中的RSTCAL位置1,初始化校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));                         //等待复位校准完成,校准寄存器被初始化后RSTAL位由硬件清零
    ADC_StartCalibration(ADC1);                                         //AD校准,将ADC_CR2寄存器中的CAL位置1,开始校准
    while(ADC_GetCalibrationStatus(ADC1));                              //等待AD校准完成,校准完成后CAL位由硬件清零
	
	//开启ADC电源
    ADC_Cmd(ADC1,ENABLE);
}

/**
 * @brief  获取AD转换的结果
 * @param  None
 * @retval None
 * @note   启动转换,返回AD转换的结果
 */
uint16_t AD_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);                              //软件触发AD转换
    while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));                       //等待AD转换完成,读取SR寄存器EOC位的状态,转换完成后硬件设置
    return ADC_GetConversionValue(ADC1);                                //返回获取的ADC转换结果,读取DR寄存器会自动清除EOC标志位
}

 OLED部分代码参照文章《STM32基础教程——OLED显示》http://【STM32基础教程 ——OLED显示 - CSDN App】https://blog.csdn.net/2301_80319641/article/details/145837521?sharetype=blog&shareId=145837521&sharerefer=APP&sharesource=2301_80319641&sharefrom=link

内容要点 

ADC基本结构配置

//开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

实现AD转换要选择GPIO端口作为AD通道,ADC1和GPIO同属APB2外设,故应先开启APB2外设时钟。

//配置ADC时钟 12MHz
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

RCC_PCLK2_Div6: ADC clock = PCLK2/6 ,PCLK为72MHz,计算后ADC时钟为12MHz。

ADC时钟要求不得超过14MHz,当系统时钟为72MHz时,RCC_ADCCLKConfig()的参数分频因子只能选择RCC_PCLK2_Div6或RCC_PCLK2_Div8

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;                         //模拟输入
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStruct);

由于进行AD转换,GPIO的输入量为模拟量,故应将GPIO的输入模式配置为模拟输入模式, 由于使用AD转换通道1,这里GPIO配置PA0引脚。AD通道与GPIO引脚对应见下图:

//选择规则组的输入通道
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);

 配置ADC输入通道,配置为ADC通道1,将其置于转换序列1,采样周期设置为55.5个采样周期,

AD转换时间为68/14/1000000,即4.86us,即最大采样频率为205.8kHz.

STM32 ADC的总转换时间为:TcoNv=采样时间+12.5个ADC周期

//初始化ADC
    ADC_InitTypeDef ADC_InitStruct;
    ADC_InitStruct.ADC_Mode                 = ADC_Mode_Independent;		//独立ADC模式
    ADC_InitStruct.ADC_ScanConvMode         = DISABLE;                  //非扫描模式
    ADC_InitStruct.ADC_ContinuousConvMode   = DISABLE;                  //单次转换
    ADC_InitStruct.ADC_ExternalTrigConv     = ADC_ExternalTrigConv_None;//软件触发,非外部触发
    ADC_InitStruct.ADC_DataAlign            = ADC_DataAlign_Right;      //数据右对齐
    ADC_InitStruct.ADC_NbrOfChannel         = 1;                        //序列1
    ADC_Init(ADC1,&ADC_InitStruct);

初始化ADC,ADC模式有双ADC模式(包含多种模式)和单ADC模式,这里使用单ADC模式,同时转换模式设置为单次转换,非扫描模式(即转换序列只进行一次转换,并只转换指定的转换序列)。本实验使用软件触发AD转换的模式,不使用AD的硬件触发。数据对齐方式使用右对齐(右对齐不会改变数据的大小,使用方便,但精度不如数据左对齐,左对齐对高位操作方便,但数据读取不如右对齐方式。)。

//关闭ADC电源,且超过两周期以上
    ADC_Cmd(ADC1,DISABLE);

	//维持ADC处于掉电状态并且持续两周期,满足校准要求,一个周期约为71.4ns,两个周期约为142.8ns
	Delay_us(1);

AD转换后要进行校准,关闭ADC电源且维持至少两个周期,满足AD转换的要求。

//ADC校准
    ADC_ResetCalibration(ADC1);                                         //复位校准,将ADC_CR2寄存器中的RSTCAL位置1,初始化校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));                         //等待复位校准完成,校准寄存器被初始化后RSTAL位由硬件清零
    ADC_StartCalibration(ADC1);                                         //AD校准,将ADC_CR2寄存器中的CAL位置1,开始校准
    while(ADC_GetCalibrationStatus(ADC1));

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差 。先启用复位校准,复位校准寄存器,复位校准完成后会将ADC_CR2 寄存器中的RSTCAL位置0,标志校准寄存器已经初始化完成。在启用AD转换,当AD转换完成后,应急那会将CAL位置零。启动AD转换可以消除误差,确保数据的稳定性。

//开启ADC电源
    ADC_Cmd(ADC1,ENABLE);

启动AD转换

读取ADC数据

/**
 * @brief  获取AD转换的结果
 * @param  None
 * @retval None
 * @note   启动转换,返回AD转换的结果
 */
uint16_t AD_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);                              //软件触发AD转换
    while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));                       //等待AD转换完成,读取SR寄存器EOC位的状态,转换完成后硬件设置
    return ADC_GetConversionValue(ADC1);                                //返回获取的ADC转换结果,读取DR寄存器会自动清除EOC标志位
}

使用软件触发的方式触发AD转换,AD转换完成后会将ADC_SR寄存器的EOC位置1,使用 while()循环等待AD转换,完成后读取ADC_DR寄存器的值,将ADC的值返回。由于ADC_DR寄存器的低半字为ADC1规则组ADC的数据,ADC_GetConversionValue()将ADC_DR寄存器的值强转为了uint16_t,自动截断高半字。

电压值计算 

        AD_Value = AD_GetValue();
		Voltage = (float)AD_Value / 4095 * 3.3;

实验结果 

旋转电位器,可以观察到OLED上显示的经转化后的电压数字量(电压值)在改变

问题记录 

1.江科大教学视频中对AD校准未严格遵守“启动校准前,ADC必须处于关电状态(ADON=’0’)超过至少两个ADC时钟周期。”

相关文章:

  • 一款安全好用的企业即时通讯平台,支持统一门户
  • 单链表各种操作实现(数据结构C语言多文件编写)
  • 中介者模式:理论、实践与 Spring 源码解析
  • MDP 值迭代算法
  • AI推理强,思维模型也有功劳【60】启发式偏差思维
  • WITRAN_2DPSGMU_Encoder 类中,门机制
  • 高级语言调用C接口(四)结构体(2)-Python
  • 如何在本地修改 Git 项目的远程仓库地址
  • 智算启新篇 安全筑新基 ——中国移动举办智算基础设施及安全分论坛
  • C++ 智能指针底层逻辑揭秘:优化内存管理的核心技术解读
  • Java 中常见的数据结构
  • 3、组件:魔法傀儡的诞生——React 19 组件化开发全解析
  • 【Python爬虫】详细入门指南
  • UNet深度学习实战遥感航拍图像语义分割
  • Java雪花算法
  • RabbitMQ的应用
  • mysql和mongodb
  • React 之 Redux 第三十二节 Redux 常用API及HOOKS,以及Redux Toolkit核心API使用详解
  • 62. 评论日记
  • java 实现文件编码检测的多种方式
  • 浙能集团原董事长童亚辉被查,还是杭州市书法家协会主席
  • 习近平会见哥伦比亚总统佩特罗
  • 金正恩观摩朝鲜人民军各兵种战术综合训练
  • 寒武纪陈天石:公司的产品力获得了行业客户广泛认可,芯片市场有望迎来新增量需求
  • 季子文化与江南文化的根脉探寻与融合
  • 著名学者黄修己去世,享年90岁