刘刘 stm32学习日记 (九)ADC

一、简介

二、逐次逼近型ADC:(ADC0809、独立的adc芯片)

        in0-7为输入通道(stm32内有18个通道), 通过通道选择开关(地址锁存和译码),选中一路,输入到比较器左端进行转换,通过主次逼近的方式(通常用二分法)和已知的DAC点进行比较,如果DAC输出的电压比较大,就调小DAC数据,反之,直到DAC输出的电压和外部通道输入的电压近视相等,这样DAC输入的数据就是外部电压的编码数据了,AD转换结束后,DAC的输入数据,就是未知电压的编码,通过锁存器进行输出

三、stm内部的ADC

原理基本一样:输入口输入后通过模拟多路开关进行通道选择,然后经过进行模数转换比较然后输出,通过读取寄存器便可以知道结果。

stm32的模拟开关可以选择多个通道,还分成了两个组,规则通道组(可以选择16个通道)和注入通道(一次最多4个),

 举个例子:

普通的单通道 :指定一个菜,老板给你做,然后做好了给你

规则通道组:指定一个菜单,这个菜单最多可以填16个菜,老板就按照菜单的顺序依次做好,一次性给端上来,但是桌子上只能放一个,多的话,菜就会被挤掉,如果使用这个菜单的话,最好配合DMA(数据传送,来一个之后就立刻将数据传递走,避免被覆盖)来实现

注入通道  :一次性最多可以点4个菜,可以同时上菜,桌子大

继续上图,

        左下角为触发转换部分,一种为软件触发,一种为通过硬件触发源(触发定时器,可以给TIM3定个1ms的时间并且把TIM3的更新事件选择为TRGO输出,将其选为触发信号,整个过程不需要进中断,节省了中断资源(通过设置右边这个寄存器来完成)

        左上角为ADC的参考电压决定ADC输入电压的范围,下面两个是ADC的供电引脚, 一般情况下,VREF+要接VDDA,VREF-要接VSSA,

        右下角对于ADC预分频器;只能选择6分频,结果是12M,和8分频,结果是9M,这两个值

         右上角两个数据寄存器,用于存放转换结果的

 开门狗:关注它着门的通道,一但超过这个阈值范围了,它就会乱叫,就会在上面,会有一个EOC转换完成的信号 申请一个模拟看门狗的中断,

规则组和注入组转换完成后也会有一个EOC转换完成的信号(EOC是规则组的完成信号,JEOC是注入组完成的信号),可以通过读取标志位判断是否准换完成,同时这两个标志位也可以去到NVIC,申请中断

adc引脚图:

ADC12引脚: ADC1和ADC2可以配合使用的双ADC模式

  • 规则组的4种转换模式

第一种,单次转换,非扫描模式:

在非扫描的模式下,这个菜单就只有第一个序列1的位置有效,即每次转化一个,可自由指定通道,过一会,转换结果放在数据寄存器里,同时给EOC标志位置1,通过判断标志位看如果转换完了就可以在寄存器读结果了,如果想再触发一次转换,进行下次转换 。

第二种,连续转换,非扫描模式:

它在一次转换结束后不会停止,而是立刻开始下一轮的转换,然后一直持续下去,只需要最开始触发一次,之后就可以一直转换了, 开始转换之后不需要等待一段时间,想要读AD值的时候,直接从数据寄存器取

第三种,单次转换,扫描模式:

也是单次转换,所以每触发一次,然后每次触发之后,它就依次对这前7个(可以选择)位置进行AD转换,同样需要使用DMA,7个通道转换完成之后,产生EOC信号,转换结束

第四种,多次转换,扫描模式:

不解释

  • 数据对齐

左对齐数据会大16倍

  • 转换时间

一般AD转换都很快,如果不需要非常高速的转换频率,那转换时间就可以忽略 

  • 校准

四、硬件

ADC只能0-3.3V,通过分压才能正常读。

VIN/(R1+R2)*R2

五、实战

1、实验1

 

2、建库

adc初始化

第一步,开启RCC时钟,包括ADC和GPIO的时钟,ADCCLK的分频器,也需要配置

第二步,配置GPIO,把需要用的GPIO配置成模拟输入的模式

第三步,配置多路开关,把左边的通道接入到右边的规则组列表里

第四步,配置ADC转换器(包括ADC转换方式:单次转换还是连续转换、扫描还是非扫描、通道个数,触发源,数据对齐方式)

最后、调用一下ADC_Cmd函数,开启ADC

ad.c


#include "stm32f10x.h"                  // 设备头文件

void AD_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);   // 使能ADC1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA时钟
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 设置ADC时钟分频为PCLK2的6分频
    
    GPIO_InitTypeDef GPIO_InitStructure;  // 定义GPIO初始化结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  // 设置引脚为模拟通道0
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置引脚速率为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);  // 初始化GPIOA
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);  // 配置ADC1的规则通道,使用通道0,采样时间为55.5个周期
    
    ADC_InitTypeDef ADC_InitStructure;  // 定义ADC初始化结构体
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // 独立模式
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 不使用外部触发转换
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 禁用扫描模式
    ADC_InitStructure.ADC_NbrOfChannel = 1;  // 转换通道数为1
    ADC_Init(ADC1, &ADC_InitStructure);  // 初始化ADC1
    
    ADC_Cmd(ADC1, ENABLE);  // 使能ADC1
    
    ADC_ResetCalibration(ADC1);  // 复位校准寄存器
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);  // 等待校准寄存器复位完成
    ADC_StartCalibration(ADC1);  // 启动ADC1校准
    while (ADC_GetCalibrationStatus(ADC1) == SET);  // 等待校准完成
}

uint16_t AD_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 软件启动ADC1转换
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  // 等待转换完成标志置位
    return ADC_GetConversionValue(ADC1);  // 读取转换结果
}

ad.h

#ifndef __AD_H
#define __AD_H

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

#endif

 main.c

#include "stm32f10x.h"                  // 设备头文件
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t ADValue;  // 存储ADC转换的数值
float Voltage;     // 存储计算得到的电压值

int main(void)
{
    OLED_Init();  // 初始化OLED显示屏
    AD_Init();    // 初始化ADC模块
    
    OLED_ShowString(1, 1, "ADValue:");     // 在OLED上显示ADValue标签
    OLED_ShowString(2, 1, "Volatge:0.00V"); // 在OLED上显示Voltage标签
    
    while (1)
    {
        ADValue = AD_GetValue();  // 读取ADC数值
        Voltage = (float)ADValue / 4095 * 3.3;  // 计算电压值,假设ADC为12位,电压范围为0-3.3V
        
        OLED_ShowNum(1, 9, ADValue, 4);  // 在OLED上显示ADC数值,占4位
        OLED_ShowNum(2, 9, Voltage, 1);   // 在OLED上显示电压整数部分,占1位
        OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);  // 在OLED上显示电压小数部分,占2位
        
        Delay_ms(100);  // 延时100毫秒,控制刷新速率
    }
}

 

3、实验2

4、建库

AD.c


#include "stm32f10x.h"                  // 设备头文件

void AD_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);   // 使能ADC1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能GPIOA时钟
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 设置ADC时钟分频为PCLK2的6分频
    
    GPIO_InitTypeDef GPIO_InitStructure;  // 定义GPIO初始化结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;  // 设置引脚为模拟通道0、1、2、3
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 设置引脚速率为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);  // 初始化GPIOA
    
    ADC_InitTypeDef ADC_InitStructure;  // 定义ADC初始化结构体
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // 独立模式
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 不使用外部触发转换
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 禁用扫描模式
    ADC_InitStructure.ADC_NbrOfChannel = 1;  // 每次转换的通道数为1
    ADC_Init(ADC1, &ADC_InitStructure);  // 初始化ADC1
    
    ADC_Cmd(ADC1, ENABLE);  // 使能ADC1
    
    ADC_ResetCalibration(ADC1);  // 复位校准寄存器
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);  // 等待校准寄存器复位完成
    ADC_StartCalibration(ADC1);  // 启动ADC1校准
    while (ADC_GetCalibrationStatus(ADC1) == SET);  // 等待校准完成
}

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);  // 配置ADC1的规则通道,采样时间为55.5个周期
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 软件启动ADC1转换
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  // 等待转换完成标志置位
    return ADC_GetConversionValue(ADC1);  // 读取转换结果
}

 AD.h


#ifndef __AD_H
#define __AD_H

void AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);

#endif

main.c

 #include "stm32f10x.h"                  // 设备头文件
#include "Delay.h"
#include "OLED.h"
#include "AD.h"

uint16_t AD0, AD1, AD2, AD3;  // 存储四个模拟通道的ADC转换结果

int main(void)
{
    OLED_Init();  // 初始化OLED显示屏
    AD_Init();    // 初始化ADC模块
    
    OLED_ShowString(1, 1, "AD0:");  // 在OLED上显示AD0标签
    OLED_ShowString(2, 1, "AD1:");  // 在OLED上显示AD1标签
    OLED_ShowString(3, 1, "AD2:");  // 在OLED上显示AD2标签
    OLED_ShowString(4, 1, "AD3:");  // 在OLED上显示AD3标签
    
    while (1)
    {
        AD0 = AD_GetValue(ADC_Channel_0);  // 读取ADC_Channel_0通道的数值
        AD1 = AD_GetValue(ADC_Channel_1);  // 读取ADC_Channel_1通道的数值
        AD2 = AD_GetValue(ADC_Channel_2);  // 读取ADC_Channel_2通道的数值
        AD3 = AD_GetValue(ADC_Channel_3);  // 读取ADC_Channel_3通道的数值
        
        OLED_ShowNum(1, 5, AD0, 4);  // 在OLED上显示AD0数值,占4位
        OLED_ShowNum(2, 5, AD1, 4);  // 在OLED上显示AD1数值,占4位
        OLED_ShowNum(3, 5, AD2, 4);  // 在OLED上显示AD2数值,占4位
        OLED_ShowNum(4, 5, AD3, 4);  // 在OLED上显示AD3数值,占4位
        
        Delay_ms(100);  // 延时100毫秒,控制刷新速率
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值