一、简介
二、逐次逼近型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毫秒,控制刷新速率
}
}