ADC模拟-数字转换

1.ADC的简介

ADC是Analog-to-Digital Converter的缩写,意为“模数转换器”,他的作用呢,就是将连续变化的模拟量,类似于

例如左侧的,他会一直连续变化,他就是模拟信号,而左侧的数字信号只有0和1两种情况.

好的既然大家知道了,模拟信号和数字信号的样子,那么他们是如何在STM32内部如何转换的呢,接下来就要介绍一下

逐次逼近型ADC(SAR ADC),

逐次逼近型ADC(SAR ADC)

在我们的STM32内部有ADC外设,我们就是通过这个ADC外设来进行转换的,那么我们的这个ADC是逐次逼近型(SAR)ADC,就是采取二分法,逐次逼近待测量的电压值.

SAR ADC图示:

那么这个SAR ADC是如何工作的呢,假设我们的基准电压是32mv的话,假设测量21.5mv,我们的输入电压第一次32mv的一半进行比较,也就是16mv,比16mv大,我们就输出1,第二次我们就与32mv与16mv之间的一半进行比较,那么那么这个值就是24mv,我们的电压小于24mv,则输出0,依次比下去,得出结果为10101,在这里我们的基准电压就是外部的需要测量的电压,我们这里需要通过不断地逼近测量出电压,然后输出给芯片

如图所示:

规则组与注入组的区别

image-20240912154552830

规则组上菜,就是一次只能上一个菜,假如上了第十六个菜,那么之前的十五个菜,都会被挤掉,相当于桌子比较小

,那么想要数据不被覆盖,就得及时用DMA运走

注入组相当于VIP餐厅的座位,他一次能上四个菜,相当于老板桌

转换模式

第一个模式 :

单次转换非扫描模式,在非扫描模式下,只有第一个序列1的位置有效,比如说我们想获取通道2的数据,我们就在序列1的位置写上通道2,假如我们还想再次转换的话,我们就还得在触发

第二个模式:

连续转,换非扫描模式 他与上一次,单次转换的不同的是,他在转换一次后不会停止,而是立刻开始新一轮的转换,这样就可以只出发一次,然后一直转换了

第三个模式:

单次转换,扫描模式,这个则是与第一种模式完全相反了

第四个模式:

连续扫描转换模式 在扫描模式下,前七个序列都是有效的,都是可以被读取数据的,比如说我们想获取任意几个通道的数据,我们就在序列1-7的位置写依次写入想转换的数据2,假如我们还想再次转换的话,就不用触发了,他会连续起来,这样就只需要最看是触发一次,然后就可以一直转换了,这个模式的好处就是开始转换后,不需要等待一段时间了,因为他再一直触发

触发控制

触发控制分为硬件触发和软件触发

数据对齐模式

数据对齐 ,因为我们的ADC是12位的,但是寄存器有16位数据,那么就产生了,数据对齐,是左对齐还是右对齐

数据右对齐就是这些数据都向右靠齐,高位多出来的,直接补零

数据左对齐就是数据都向左对齐,低位多出来的补零,这样一来跟我们真是采集的数据就有所不同了,还需通过计算来算出真实的采集值,二进制有个特点就是数据左移一次,相当于把数据乘以2,那我们的数据左移了四位,那么就是相当于乘以16了,

转换时间

介绍完转换时间后在介绍一下时钟源的选择

  1. 选择时钟源:ADC需要一个稳定的时钟源来进行采样。这里选择了APB2总线时钟作为ADC的时钟源。

  2. 调整时钟频率:通过将APB2总线时钟除以6,可以得到一个适合ADC工作的较低频率。这是因为ADC对时钟频率有特定的要求,过高的时钟频率可能会导致采样不稳定或其他问题。

校准

ADC的预分频器来自RCC ,ADC 的ADCCLK最大为14MHZ,所以说他只能用6分频,8分频

2.代码的书写

下面我们将利用ADC来采集电压,通过USART DMA来显示在屏幕上

我们主要是根据这个图片来书写代码,

第一步初始化GPIO,初始化时钟

    //首先开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化时钟
    //ADC分频
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //采用6分频
    //初始化模拟输入的引脚
    GPIO_InitTypeDef LED_InitStruct;   //定义GPIO结构体
    LED_InitStruct.GPIO_Pin = GPIO_Pin_0;   //确定引脚
    LED_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  //确定速度
    LED_InitStruct.GPIO_Mode = GPIO_Mode_AIN;  //确定为模拟输入
​
    GPIO_Init(GPIOA,&LED_InitStruct);

第二步选择规则组

​
    //选择规则组的输入通道
    //首先选择为ADC1,然后选择ADC通道0,接着选择规则组里面的次序号为1,最后这个就是采样时间的参数
    //ADC_SampleTime_55Cycles5决定了ADC在开始转换之前对输入信号的采样时间长度
    //较长的采样时间可以提高ADC的精度,特别是在处理快速变化的信号或存在噪声的情况下。
    //然而,较长的采样时间也会增加每次转换的总时间,从而影响ADC的吞吐量。
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);

给大家翻译了一份ADC结构体的中文注释版

typedef struct
{
  uint32_t ADC_Mode;                      /*!< 配置ADC以独立模式或双模式运行。
                                               此参数可以是 @ref ADC_mode 中定义的值 */
​
  FunctionalState ADC_ScanConvMode;       /*!< 指定转换是在扫描模式(多通道)还是单通道模式下执行。
                                               此参数可以设置为 ENABLE 或 DISABLE */
​
  FunctionalState ADC_ContinuousConvMode; /*!< 指定转换是在连续模式还是单次模式下执行。
                                               此参数可以设置为 ENABLE 或 DISABLE */
​
  uint32_t ADC_ExternalTrigConv;          /*!< 定义用于启动常规通道组模拟到数字转换的外部触发源。
                                               此参数可以是 @ref      ADC_external_trigger_sources_for_regular_channels_conversion 中定义的值 */
​
  uint32_t ADC_DataAlign;                 /*!< 指定ADC数据是对齐到左侧还是右侧。
                                               此参数可以是 @ref ADC_data_align 中定义的值 */
​
  uint8_t ADC_NbrOfChannel;               /*!< 指定使用常规通道组进行转换的ADC通道数量。
                                               此参数必须在1到16之间。 */
}ADC_InitTypeDef;

第三部开始配置ADC结构体

    ADC_InitTypeDef ADC_Init_Strct;
​
    ADC_Init_Strct.ADC_ContinuousConvMode = DISABLE;    //选择单次模式
    ADC_Init_Strct.ADC_DataAlign = ADC_DataAlign_Right;  //选择右对齐模式
    ADC_Init_Strct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //选择软件触发
    ADC_Init_Strct.ADC_Mode = ADC_Mode_Independent;  //选择独立模式,这个就是ADC1和ADC2各自转换
    ADC_Init_Strct.ADC_NbrOfChannel = 1;
    ADC_Init_Strct.ADC_ScanConvMode = DISABLE;    //选择非扫描模式
​
    ADC_Init(ADC1,&ADC_Init_Strct);
    ADC_Cmd(ADC1,ENABLE);//使能ADC 

最后一步进行校准

    //第一步调用复用校准函数
    ADC_ResetCalibration(ADC1);  
    //第二步等待校准完成
    while(ADC_GetResetCalibrationStatus(ADC1) == 1);
    //第三步开始校准
    ADC_StartCalibration(ADC1);
    //第四步等待校准完成
    while(ADC_GetCalibrationStatus(ADC1) == 1);

我们现在完成了按照上述流程图配置ADC的初始化了,接下来还得写一个获取ADC数据函数,按照下面这个流程

uint16_t ADC_GetVaul()
{
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);   //软件启动ADC开始转换
​
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == 0);  //等待转换完成,EOC标志位置1
​
    return ADC_GetConversionValue(ADC1);  //返回获取的值
​
}

接下来就开始书写main函数了

float Valu = 0;
​
int main(void)
{
    Usart_Init();
​
    ADC_Init_Star();
​
    Valu = (float)ADC_GetVaul() / 4095 *3.3; // 假设这里返回的是一个 uint16_t 类型的值
    printf("%f\n    ", Valu); // 在格式字符串中指明要打印一个无符号短整型
    // 添加 \n 以换行,使输出更加清晰
​
​
}

在按照这样接线,就可以在屏幕上成功显示电压了

image-20240916174157881

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值