目录
1.基础概念
2.原理:ADC采样过程分为四步:采样、保持、量化、编码。
3.采样定理
4.采样保持放大器(SHA)
5.ADC电压值转换
6.ADC轮询采样
1.基础概念
ADC 全称:Analog-to-Digital Converter,指模拟/数字转换器,就是将模拟信号转换成数字信号
①模拟信号:是连续变化的,具有电路简单,分辨率很高的特点,抗噪声能力弱
②数字信号:是离散变化的,抗噪声能力强,便于存储和交换,可用于加密
2.原理:ADC采样过程分为四步:采样、保持、量化、编码。
①采样是指将模拟波形在时域上进行切分,每个切片大小大致等于原来的波形的值,这过程往往回丢失一些信息
②采样保持:如果被采样的模拟信号的变化频率相对于A/D转换器的速度来说比较高,为保证转换精度,需要在A/D转换之前加上采样保持电路,使得在A/D转换期间保持输入模拟信号不变。
③量化:在采样完后给每个时间片分配一个数字,这样的过程称为量化
④编码: 量化后的数值还需通过编码用一个二进制代码表示出来,经过编码后得到的就是AD转换结果的数字量,二进制编码的位宽等于ADC的位宽。
3.采样定理
又称奈奎斯特采样定理,即当采样频率fs大于采样信号最高频率fmax的两倍时,采样后的数字信号完整地保留了原始信号中的信息。公式 :fs>2*fn
4.采样保持放大器(SHA)
采样保持过程将已采样的模拟电压在一段必要的时间内保持恒定,以便让ADC将模拟电压转换成数字形式。
一个基本的SHA如图,开始的时候模拟开关闭合,通过输入缓冲放大器对模拟电压进行采样,电容C存储或保存采样电压一段时间,输出缓冲放大器提供一个高输入阻抗来防止电容快速掉电。ADI要求输出缓冲器的输入阻抗足够高,以便电容可以保持时间内放电少于1LSB
5.ADC电压值转换
1.首先确定ADC是几位的,即确定最大数值是多少。比如一个8位的ADC,最大值是0xFF,就是255。
(一般芯片手册会有说明)
2.然后确定最大值时对应的参考电压值。一般而言最大值对应3.3V。这个你需要看这个芯片ADC模块的说明。寄存器中有对于输入信号参考电压的设置。
3.计算电压,读取的ADC数值除以最大数值再乘以参考电压值。比如你ADC值为0x55,那么实际值就是0x55/(0xFF+1)*3.3V = 1.65V
4.验证计算值。你可以用电压表量一下对应的引脚,看看计算值和实际值是否一样。
/********************************************************/
/** AT89C52+ADC0832+LCD1602 **/
/** 用ADC0832采集电压,并在1602上显示电压值 **/
/********************************************************/
#include <reg52.h>
#include <intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
bit RW=0;
sbit RS=P2^7;
sbit EN=P2^6;
sbit CLK=P1^0;
sbit DIO=P1^1;
sbit CS=P1^3;
u8 len;
u8 Display_Buffer[4];
void LcdInit();
void delay_us(u8 us);
void delay_ms(u8 ms);
void LcdDisplay(u8 x,u8 y, u8 *str);
void LcdSetCursor(u8 x,u8 y);
void LcdStar();
void write_con(u8 con);
void write_dat(u8 dat);
u8 Get_AD_Result()
{
u8 i;
u8 data1=0,data2=0;
CS=0;
//第一个下降沿到来前,DI需置1,起始控制位,开始转换
CLK=0;DIO=1; _nop_();
CLK=1;_nop_();
//第二个下降沿到来前,设D=1/0,选择单端/差分(SGL/DIF)模式中的单端输入模式
CLK=0;DIO=1; _nop_();
CLK=1; _nop_();
//第三个下降沿到来前,设D=0/1,选择CH0/CH1,这里选择单通道ch0
CLK=0;DIO=1; _nop_();
CLK=1;DIO=0; _nop_();
//第四个下降沿到来前,DI =1
CLK=0;DIO=1; _nop_();
//4-11,共8个下降沿 DO输出转换信号,读取数据(MSB-->LSB)
for(i=0;i<8;i++)
{
CLK=1;_nop_();
CLK=0;_nop_();
data1=(data1<<1)|(u8)DIO;
}
//11-18,共8个下降沿,读取数据(LSB)-->MSB)
for(i=0;i<8;i++)
{
data2=data2|((u8)DIO<<i);
CLK=1;_nop_();
CLK=0;_nop_();
}
CS=1;
//如果MSB-->LSB和LSB)-->MSB读取数据结果相同,返回读取结果,否则0
return(data1==data2)?data1:0;
}
//-----------------------------------------
// 主函数
//-----------------------------------------
void main()
{
u8 Data;
LcdInit();
LcdStar();
while(1)
{
//获取AD转换值 最大值255对应最高电压5.000v 显示三个数 使用500
Data =Get_AD_Result()*500.0/255;
Display_Buffer[0]= Data/100+'0';
Display_Buffer[1]= '.';
Display_Buffer[2]=Data/10%10+'0';
Display_Buffer[3]=Data%10+'0';
LcdDisplay(9,1, Display_Buffer);
}
}
//-----------------------------------------
// 延时us和1ms函数
//-----------------------------------------
void delay_us(u8 us)
{
while(us--);
}
void delay_ms(u8 ms)
{
while(ms--)
{
delay_us(248);
delay_us(248);
}
}
//-----------------------------------------
// lcd1602显示
//-----------------------------------------
//lcd初始化
void LcdInit()
{
write_con(0x01);//清屏
write_con(0x38);//设置16*2显示,配置8位数据接口
write_con(0x38);//设置16*2显示,配置8位数据接口
write_con(0x0C);//开显示,光标关,闪烁关,去黑块
write_con(0x06);//写数据时光标右移,画面不动
}
void LcdStar()
{
u8 code str[]="Voltage measure";
u8 tab[]="Voltage=";
LcdInit(); //初始化1602液晶
LcdDisplay(1,0,str);
LcdDisplay(1,1,tab);
LcdDisplay(9,1,"..."); //默认初始化温度00
LcdDisplay(13,1,"V"); //添加V电压
}
//设置显示RAM 起始地址,亦即光标位置,(x,y)对应屏幕上的起始坐标
void LcdSetCursor(u8 x,u8 y)
{
u8 addr;
if(y==0) //由输入的屏幕坐标计算显示RAM的地址
addr=0x00+x; //第一行字符地址从0x00起始
else
addr=0x40+x; //第二行字符地址从0x40起始
write_con(addr|0x80); //设置RAM地址
}
//设置显示RAM 起始地址,亦即光标位置,(x,y)对应屏幕上的起始坐标,str-字符串指针
void LcdDisplay(u8 x,u8 y,u8 *str)
{
LcdSetCursor(x,y); //设置起始地址
while(*str !='\0') //连续写入字符串数据,直到检测到结束符
{
write_dat(*str++); //先取str指向的数据,然后str自加1
delay_us(100);
}
}
//lcd1602写指令
void write_con(u8 con)
{
P0=con;
RS=0;
RW=0;
EN=1;
delay_us(200);
EN=0;
}
//lcd1602写数据
void write_dat(u8 dat)
{
P0=dat;
RS=1;
RW=0;
EN=1;
delay_us(200);
EN=0;
}
6.ADC轮询采样
在STM32 ADC轮询转换通道中,下一个通道开启时前一个通道不需要手动关闭,ADC会自动切换到下一个通道进行采集。
ADC轮询转换通道的原理和过程:
ADC(Analog-to-Digital Converter)是模拟信号转换为数字信号的电路。在STM32中,ADC是一个十分重要的外设,它可以将模拟信号转换为数字信号,供微处理器进行处理。
ADC的转换方式有多种,其中轮询转换通道是最基本的一种,它的原理是:在一次转换完成之后,自动切换到下一个通道进行采集,直到所有通道采集完成,然后产生一个转换结束的中断。
下面是一个简单的ADC轮询转换通道的代码示例:
初始化ADC模块:设置ADC通道和转换模式等参数,以及开启ADC时钟。
获取采样数据:通过设置ADC转换模式为单次转换或连续转换,轮流对3个通道进行采样,将采样结果存储到缓冲区中。
数据处理:将采样结果进行处理,比如进行滤波、校准等操作,得到最终的采样数据。
计算采样时间和定采样周期:采样时间是指从开始采样到采样结束所需的时间,可以通过软件延时或硬件定时器实现。采样周期是指两次采样之间的时间间隔,可以根据需要进行设置。
以下是单次采样的代码实现和注释,把三个通道的数据都放在一个缓冲区:
#include "bf7006_adc.h"
#define ADC_BUFFER_SIZE 16 // 缓冲区大小
#define ADC_SAMPLE_TIME 10 // 采样时间,单位为ms
#define ADC_SAMPLE_PERIOD 100 // 采样周期,单位为ms
static uint16_t adc_buffer[ADC_BUFFER_SIZE]; // 采样数据缓冲区
static uint8_t adc_buffer_index = 0; // 缓冲区索引
/**
* @brief 初始化ADC模块
*/
void adc_init(void)
{
HAL_ADC_ConfigChannel(&hadc, ADC_CHANNEL_0, ADC_MODE_SINGLE); // 配置ADC通道0为单次转换模式
HAL_ADC_ConfigChannel(&hadc, ADC_CHANNEL_1, ADC_MODE_SINGLE); // 配置ADC通道1为单次转换模式
HAL_ADC_ConfigChannel(&hadc, ADC_CHANNEL_2, ADC_MODE_SINGLE); // 配置ADC通道2为单次转换模式
HAL_ADC_Start(&hadc); // 开启ADC转换
}
/**
* @brief 获取采样数据
*/
void adc_sample(void)
{
HAL_ADC_Start(&hadc); // 开始转换
HAL_ADC_PollForConversion(&hadc, ADC_SAMPLE_TIME); // 等待转换完成
adc_buffer[adc_buffer_index++] = HAL_ADC_GetValue(&hadc); // 保存采样结果
if (adc_buffer_index >= ADC_BUFFER_SIZE) { // 缓冲区已满,重置索引
adc_buffer_index = 0;
}
}
/**
* @brief 数据处理
*/
uint16_t adc_process_data(void)
{
uint16_t result = 0;
for (uint8_t i = 0; i < ADC_BUFFER_SIZE; i++) { // 对所有采样数据进行求和
result += adc_buffer[i];
}
result /= ADC_BUFFER_SIZE; // 求平均值
return result;
}
/**
* @brief 计算采样周期
*/
uint32_t adc_calculate_period(void)
{
return ADC_SAMPLE_PERIOD - ADC_SAMPLE_TIME; // 采样周期减去采样时间即为等待时间
}
开发板的ADC模块有三个通道,可以通过轮询的方式进行连续转换采样。下面是采样原理和过程:
采样原理:
ADC模块将模拟信号转换为数字信号,采样过程中需要注意采样精度和采样速率。采样精度指的是数字信号的位数,采样速率指的是每秒钟采样的次数。
采样过程:
1. 初始化ADC模块,设置ADC通道和采样精度;
2. 设置ADC转换模式为连续转换模式;
3. 启动ADC转换;
4. 轮询ADC转换完成标志位,读取ADC转换结果;
5. 对采样数据进行处理和计算。下面是代码注释及解析:
#include "stm32f10x.h"
#define ADC1_DR_Address ((uint32_t)0x4001244C) // ADC1数据寄存器地址
uint16_t ADC_ConvertedValue[3]; // 存储ADC采样结果
void ADC_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能ADC1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA0、PA1、PA2为模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// ADC1配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 开启扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 开启连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不使用外部触发转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 3; // 采样通道数为3
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC1通道0、1、2为采样通道
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);
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// ADC1校准
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
void ADC_Sampling(void)
{
uint8_t i;
// 开始转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待转换完成
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 读取采样结果
for (i = 0; i < 3; i++)
{
ADC_ConvertedValue[i] = ADC_GetConversionValue(ADC1);
}
// 清除转换完成标志位
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
}
int main(void)
{
ADC_Configuration();
while (1)
{
ADC_Sampling();
}
}
在上面的代码中,我们通过`ADC_Configuration()`函数初始化ADC模块,设置ADC通道,ADC转换模式。然后,在`adc_sampling()`函数中,启动ADC转换,我们通过轮询ADC转换完成标志位,读取ADC转换结果,并将采样数据存储到数组中。最后,在`adc_process()`函数中,我们对采样数据进行处理和计算。
关于采样时间和采样周期的计算,可以根据采样精度和采样速率进行计算。采样时间可以通过以下公式计算:
采样时间 = 1 / 采样速率
采样周期可以通过以下公式计算:
采样周期 = 采样时间 x 采样次数文章来源:https://www.toymoban.com/news/detail-443804.html
在实际应用中,我们可以根据需要调整采样精度和采样速率,以满足应用的要求。文章来源地址https://www.toymoban.com/news/detail-443804.html
到了这里,关于STM32 ADC采样的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!