嵌入式——ADC
ADC初始化步骤:①开启RCC时钟,包括ADC和GPIO的时钟,ADC的CLK也需要配置//开启RCC时钟, 包括ADC和GPIO的时钟,ADC的CLK也需要配置②配置GPIO,将用到的GPIO口配置成模拟输入的模式// 配置GPIO// ADC专属模式③配置多路开关,把左边的通道接入到右边的规则组列表(选择规则组的输入通道)④配置ADC转换器连续转换模式:ENABLE——连续模式、DISABLE
一、ADC简介
1.ADC简介
小玩笑:ADC是指物理输出核心也就是游戏中常说的射手,哈哈哈哈哈开个玩笑,正式开始。
ADC(Analog-Digital Converter)模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁(将模拟信号转换为数字信号)。
STM32的ADC是12位逐次逼近型ADC(工作模式),1us转换时间
分辨率:指 ADC 能区分的最小模拟信号变化,一般用多少位来表示,12位的AD值,表示范围为0~-1, 量化结果为0~4095,位数越高,量化结果越精细,对应分辨率越高,分辨率越高,转换后的数字信号越接近原始模拟信号,但电路复杂度和成本也会增加。
转换时间(频率):从AD转换开始到产生结果,需要花1us的时间,对应AD转换的频率就是1MHz。
输入电压范围:0~3.3V,转换结果范围:0~4095
ADC有18个输入通道,可测量16个外部信号源和2个内部信号源
16个外部信号:16个GPIO口
2个内部信号:内部温度传感器(CPU温度)、内部参考电压(1.2V的基准电压)
ADC增强单元:规则组(常规事件)和注入组(突发事件)两个转换单元
模拟看门狗自动监测输入电压范围
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。
2.逐次逼近型ADC(内部结构)
既然STM32的ADC工作模式是逐次逼近型,那么我们首先来了解一下ADC逐次逼近型内部结构,注意这里不是STM32中的内部结构,而是独立的8位逐次逼近型ADC芯片。

首先输入通道选择:下面地址锁存和译码,ADDA,ADDB,ADDCADDA、ADDB、ADDC 是 3 根地址线,它们通过二进制编码来指定要选择的模拟输入通道。例如, ADDC = 0、ADDB = 1、ADDA = 0 时,根据表格可知,此时选中的是 IN2 通道,然后给一个ALE锁存信号,即 IN2 通道上的模拟信号将被送入后续的转换电路进行处理。IN0~IN7是8路输入通道,通过通道选择开关,选中一路。

如何知道这个电压对应的编码数据是多少?这就需要使用逐次逼近的方法来一一比较,输入通道选择完毕后,进入输入比较器,它可以判断两个输入信号电压的大小关系,输出一个高低电平指示谁大谁小,现在输入比较器有两个输入端,一个是待测的电压(外部通道输入的,未知编码的电压),另一个是DAC电压输出端(DAC输出的已知编码的电压),如果DAC输出的电压较大,那就调小DAC数据,如果DAC输出的电压较小,那就增大DAC数据,直到DAC输出的电压和外部通道输入的电压近似相等,这样DAC输入的数据就是外部电压的编码器数据。
AD转换结束后,DAC的输入数据,就是外部电压的编码器数据,通过DAC右边箭头输出,进入8位三态锁存缓冲器,然后通过D0~D7输出。EOC是转换结束信号。START是开始转换,给一个输入脉冲,开始转换。CLOCK是ADC时钟,因为ADC内部是一步一步进行判断的,所以需要时钟来进行推动。下面VREF(+)和VREF(-)是DAC的参考电压。VVC和GND是整个芯片电路的固定。
为了最快找到未知电压的编码,通常使用二分法进行寻找,比如图上为8位的ADC,那么编码就是0~255,第一次比较的时候,给ADC输入255的一半,进行比较,依次进行比较。
3.STM32逐次逼近型ADC(内部结构)

左边是ADC的输入通道,包括16个GPIO口IN0~IN15和两个内部的通道,一个是内部温度传感器,另一个是内部参考电压,总共18个输入通道 ,然后到达模拟多路开关(指定想要选择的通道),右边是多路开关的输出,进入模数转换器(这个转换寄存器就是上面的逐次逼近型ADC),转换结果会直接放在数据寄存器里,模拟多路开关这里分为规则通道和注入通道,规则通道最多可以进行16个模数转换,但是规则组寄存器一次只能存取一个值,所以需要搭配DMA使用。注入通道最多可以进行4个模数转换,注入寄存器可以存取四个值,所以可以直接使用。
接下来是外围信号电路的讲解,左下角是触发转换的部分,也就是逐次逼近型ADC结构图中的START信号,那么STM32触发ADC转换的信号有两种,一种是软件触发,就是程序中手动调用一条代码,就可以启动转换。另一种是硬件触发,就是图中的TIM1_TRGO一部分为注入组的触发源和TIM_CH1一部分为规则组的触发源。
左上角为VREF+,VREF-,VDDA,VSSA,前两个为ADC的参考电压,决定了ADC输入电压的范围,后面两个为ADC的供电引脚,一般情况下VREF+接VDDA,VREF-接VSSA。
右边ADCCLK,是ADC的时钟也就是逐次逼近型ADC结构图中的CLOCK,用于驱动内部逐次比较的时钟,来自于ADC预分频器,ADC预分频器来源于RCC。
上面部分EOC是规则组的完成信号,JEOC是注入组的完成信号,这两个信号会在状态寄存器里置一个标志位,读取标志位,就知道是否转换结束。
4.ADC基本结构

这幅图就是对STM32ADC最基本的结构总结,首先数据选择器左边有16个GPIO口和两个内部通道,经过数据选择器选择需要ADC转换的通道,上面的16代表 规则组转换,规则通道最多可以进行16个模数转换,但是规则组寄存器一次只能存取一个值,下面的四对应注入通道最多可以进行4个模数转换,注入寄存器可以存取四个值。下面为触发控制和时钟选择,如果需要中断,则配置中断控制输出及其NVIC,最后使能ADC。
5.规则组的四种转换模式
①单次转换,非扫描模式
每次ADC转换只能选择一个通道并进行一次转换,转换完成后置EOC标志位,如果需要再次转换,就需要重新触发。如果需要换一个通道,则需要将开始的通道换为新通道,然后启动转换。
②连续转换,非扫描模式
每次ADC转换只能选择一个通道,但在一次转换结束后不会停止,而是立刻开启下一轮的转换,也就是说,如果需要再次转换,就不需要重新触发。但是如果需要换一个通道,则需要将开始的通道换为新通道,然后启动转换。
③单次转换,扫描模式
每次ADC转换可以选择多个通道,并依次对多个通道进行转换,转换结果都放在数据寄存器中,为了防止数据覆盖,就需要使用DMA及时将数据挪走,转换完成后置EOC标志位,如果需要再次转换,就需要重新触发。
④连续转换,扫描模式
每次ADC转换可以选择多个通道,并依次对多个通道进行转换,在一次转换结束后不会停止,而是立刻开启下一轮的转换。
6.规则组的触发源

7.转换时间
AD转换的步骤:采样,保持,量化,编码(ADC逐次比较过程)
STM32 ADC的总转换时间为:
TCONV = 采样时间 + 12.5个ADC周期
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs
8.校验
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
建议在每次上电后执行一次校准。
启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期。
二、ADC库函数
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
参数:RCC_PCLK2_Div2、RCC_PCLK2_Div4、RCC_PCLK2_Div6、RCC_PCLK2_Div8
作用:配置ADCCLK分频器
void ADC_DeInit(ADC_TypeDef* ADCx);
作用:恢复缺省配置
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
作用:ADC初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
作用:StructInit结构体初始化
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
作用:给ADC通电,开关控制
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
作用:开启DMA输出信号(使用DMA转运数据,调用该函数)
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
作用:中断输出控制,能不能通往NVIC
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
作用:复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
作用:获取复位校准状态
void ADC_StartCalibration(ADC_TypeDef* ADCx);
作用:开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
作用:获取开始校准状态
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
作用:ADC软件触发函数,也就是结构图触发控制(给SWSTART位置1,以开始转换)
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
作用:返回SWSTART的状态
(配置间断模式)
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
作用:每隔几个通道间断一次
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
作用:是不是启用间断模式
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
参数1:ADCx
参数2:指定的通道
参数3:序列几的位置
参数4:通道的采样时间
作用:给序列的每个位置填写指定的通道
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
作用:是否允许外部触发转换
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
作用:获取AD转换的数据寄存器(读取转换结果)
uint32_t ADC_GetDualModeConversionValue(void);
作用:双ADC模式读取转换结果
模拟看门狗配置
Void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog);
作用:是否启动模拟看门狗
Void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
作用:配置高低阈值
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);
作用:配置看门的通道
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
作用:开启内部两个通道
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
作用:获取标志位状态,(第二个参数给EOC的标志位,如果转换结束,EOC标志位置1)
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
作用:清除标志位
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
作用:获取中断状态
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);
作用:清除中断挂起位
三、ADC代码介绍
1.代码1——AD单通道
ADC初始化步骤:
①开启RCC时钟,包括ADC和GPIO的时钟,ADC的CLK也需要配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启RCC时钟, 包括ADC和GPIO的时钟,ADC的CLK也需要配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADCCLK=72MHz/6=12MHz
②配置GPIO,将用到的GPIO口配置成模拟输入的模式
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // ADC专属模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
③配置多路开关,把左边的通道接入到右边的规则组列表(选择规则组的输入通道)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
④配置ADC转换器
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
连续转换模式:ENABLE——连续模式、DISABLE——单次模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 对齐方式
数据对齐:ADC_DataAlign_Right——右对齐、ADC_DataAlign_Left——左对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
外部触发转换选择:ADC_ExternalTrigConv_None——内部软件触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC的工作模式:ADC_Mode_Independent——独立模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
通道数目,指定扫描模式下,总共会用到几个通道
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
扫描转换模式:ENABLE——扫描模式、DISABLE——非扫描模式
ADC_Init(ADC1, &ADC_InitStructure);
⑤开启ADC电源
ADC_Cmd(ADC1, ENABLE);
⑥校准
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准状态 SET=0
ADC_StartCalibration(ADC1); // 开始校准
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准状态
AD.c
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启RCC时钟,包括ADC和GPIO的时钟,ADC的CLK也需要配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 对齐方式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 外部触发信号
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 四种模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准状态
ADC_StartCalibration(ADC1); // 开始校准
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准状态
}
uint16_t AD_GetValue(void) // 启动转换,获取结果
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发转换
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Voltage:0.00V");
AD_Init();
while (1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue / 4095 * 3.3;
OLED_ShowNum(1, 9, ADValue, 4);
OLED_ShowNum(2, 9, Voltage, 1); // 显示整数
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
Delay_ms(100);
}
}
2.AD多通道
AD.c
#include "stm32f10x.h" // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启RCC时钟,包括ADC和GPIO的时钟,ADC的CLK也需要配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
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;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 对齐方式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 外部触发信号
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 四种模式
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准状态
ADC_StartCalibration(ADC1); // 开始校准
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准状态
}
uint16_t AD_GetValue(uint8_t ADC_Channel) // 启动转换,获取结果
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发转换
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0, AD1, AD2, AD3;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
while (1)
{
AD0 = AD_GetValue(ADC_Channel_0);
AD1 = AD_GetValue(ADC_Channel_1);
AD2 = AD_GetValue(ADC_Channel_2);
AD3 = AD_GetValue(ADC_Channel_3);
OLED_ShowNum(1, 5, AD0, 4);
OLED_ShowNum(2, 5, AD1, 4);
OLED_ShowNum(3, 5, AD2, 4);
OLED_ShowNum(4, 5, AD3, 4);
Delay_ms(100);
}
}
3.DMA+AD多通道
AD.c
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4]; // 将数据转运到里面
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启RCC时钟,包括ADC和GPIO的时钟,ADC的CLK也需要配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
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;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
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 = ENABLE; // 单次模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 4;
ADC_Init(ADC1, &ADC_InitStructure);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 外设站点基地址——ADC_DR寄存器的地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设站点的传输宽度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设站点的地址自增
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; // 存储器站点起始地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 存储器站点数据宽度
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 存储器站点地址是否自增
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 传输方向
DMA_InitStructure.DMA_BufferSize = 4; // 传输计数器
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 触发方式
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 自动重装
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC_DMACmd(ADC1, ENABLE); //DMA到ADC的输出,将数据从ADC搬出
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准状态
ADC_StartCalibration(ADC1); // 开始校准
while(ADC_GetCalibrationStatus(ADC1) == SET); // 获取校准状态
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发转换
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, "AD0:");
OLED_ShowString(2, 1, "AD1:");
OLED_ShowString(3, 1, "AD2:");
OLED_ShowString(4, 1, "AD3:");
while (1)
{
OLED_ShowNum(1, 5, AD_Value[0], 4);
OLED_ShowNum(2, 5, AD_Value[1], 4);
OLED_ShowNum(3, 5, AD_Value[2], 4);
OLED_ShowNum(4, 5, AD_Value[3], 4);
Delay_ms(100);
}
}
四、总结
嵌入式系统的核心(如微控制器 MCU、微处理器 MPU)只能直接处理数字信号(0 和 1 组成的二进制数据),但现实世界中的许多物理量(如温度、声音、光强、压力等)都是通过传感器转换为模拟电信号(连续变化的电压或电流)存在的。ADC 的作用就是搭建 “模拟世界” 与 “数字世界” 之间的桥梁,让嵌入式系统能够感知和处理这些物理量。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)