模拟数字转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小。故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小。
CW32F003 内部集成一个 12 位精度、最高 1M SPS 转换速度的逐次逼近型模数转换器 (SAR ADC),最多可将 16 路模拟信号转换为数字信号。现实世界中的绝大多数信号都是模拟量,如光、电、声、图像信号等,都要由 ADC 转换成数字信号,才能由 MCU 进行数字化处理。
分辨率:表示ADC转换器的输出精度,通常以位数(bit)表示,比如8位、10位、12位等,位数越高,精度越高。
采样率:表示ADC对模拟输入信号进行采样的速率,通常以每秒采样次数(samples per second,SPS)表示,也称为转换速率,表示ADC能够进行多少次模拟到数字的转换。
采样范围:指ADC可以采集到的模拟输入信号的电压范围,范围见下:
0 ≤ ADC ≤ Vref
Vref 为参考电压,CW32F003有四路电压参考源见上文。
CW32F003采用的是逐次逼近型的12位ADC,逐次逼近型ADC是一种常见的ADC工作原理,它的思想是通过比较模拟信号与参考电压之间的大小关系来逐步逼近输入信号的数字表示。在逐次逼近型ADC中,输入信号和参考电压被加入一个差分放大器中,产生一个差分电压。然后,这个差分电压被输入到一个逐步逼近的数字量化器中,该量化器以逐步递减的方式将其与一系列参考电压进行比较。具体来说,在每个逼近阶段,量化器将输入信号与一个中间电压点进行比较,将该电压点上方或下方的参考电压作为下一个逼近阶段的参考电压。这个过程一直持续到量化器逼近到最终的数字输出值为止。
我们数字电压电流表的采样电路原理图如下图所示,
图6-1 电压采样电路原理图
如果使用1.5V作为参考电压,根据R8和R7的阻值配比可以得到最高采样电压为:
1.5 / 10 * (220 + 10)= 34.5V
电流采样的电路原理图见图6-2,对电流采样本质上是对检流电阻的电压进行采样。
图6-2 电流采样原理图
1.数字信号具有良好的抗干扰性。数字信号是由一系列离散的数字表示,因此可以抵抗模拟信号受到的各种干扰,如噪声、漂移等。
2.方便数字信号的存储、处理和传输。由于数字信号是离散的,因此它们可以轻松存储在计算机内存或其他数字设备中,方便进行处理和传输。
3.具有可编程性。现代的ADC出现了很多可编程的功能,例如可编程增益、采样率和滤波器等,可以根据不同的应用场景进行优化。
4.适用性广泛。ADC被广泛应用于工业、通信、医疗、电子测量、音频、视频等领域,可转换各种不同类型的模拟信号,包括电压、电流、声音、光信号等。
ADC的应用非常广泛。例如,我们可以用ADC将传感器的模拟信号转换为数字信号,然后通过计算机进行处理和分析;ADC在音频处理中也起着重要的作用,将模拟声音信号转换为数字信号,并接下来进行数字信号处理;无线电通信中的信号调制也需要使用ADC等。总的来说,ADC在现代电子工程中非常重要,是数字信号处理和控制技术的关键部分。
在下面我们对CW32F003的ADC通道进行配置,输入5V电压给电压表,CW32将采样得到的值输入数码管显示,对ADC通道的配置代码如下;
#include "ADC.h"
uint16_t Volt_Buffer; //存放ADC采样值
void ADC_init(void)
{
ADC_InitTypeDef ADC_InitStructure; //ADC配置结构体
ADC_SerialChTypeDef ADC_SerialChStructure; //ADC序列通道结构体
GPIO_InitTypeDef GPIO_Init_Struct;
__RCC_GPIOB_CLK_ENABLE(); //打开ADC对应引脚时钟
__RCC_ADC_CLK_ENABLE(); // 打开ADC时钟
GPIO_Init_Struct.IT = GPIO_IT_NONE;
GPIO_Init_Struct.Mode = GPIO_MODE_ANALOG;//将GPIO的模式配置成模拟功能
GPIO_Init_Struct.Pins = GPIO_PIN_1; // PB01是电压采集引脚
GPIO_Init(CW_GPIOB, &GPIO_Init_Struct);
PB01_ANALOG_ENABLE(); //使能模拟引脚
ADC_StructInit(&ADC_InitStructure); // ADC默认值初始化
ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div4; //ADC工作时钟配置 PCLK/4 = 6/4 = 1.5Mhz
/*信号电压较低时,可以降低参考电压来提高分辨率。改变参考电压后,同样二进制表示的电压值就会不一样,
最大的二进制(全1)表示的就是你的参考电压,在计算实际电压时,就需要将参考电压考虑进去。*/
ADC_InitStructure.ADC_VrefSel = ADC_Vref_BGR1p5;//参考电压设置为1.5V
//由于电压信号为慢速信号,ADC采样时间为十个ADC采样周期以确保准确
ADC_InitStructure.ADC_SampleTime = ADC_SampTime10Clk;
//Sqr为序列配置寄存器,这里只用到了序列0的通道,所以配置成0表示只转换Sqr0序列
ADC_SerialChStructure.ADC_SqrEns = ADC_SqrEns0;
ADC_SerialChStructure.ADC_Sqr0Chmux = ADC_SqrCh9;//配置ADC序列,PB01是ADC的第9通道
ADC_SerialChStructure.ADC_InitStruct = ADC_InitStructure; //ADC初始化
ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure); //ADC序列连续转换模式配置
ADC_ClearITPendingAll(); //清除ADC所有中断状态
ADC_Enable(); // ADC使能
ADC_SoftwareStartConvCmd(ENABLE); //ADC转换软件启动命令
}
void Get_ADC_Value(void) //取得ADC采样的值传给全局变量Volt_Buffer
{
ADC_GetSqr0Result(&Volt_Buffer);
}
在主函数中初始化ADC后在BTIM1的中断服务程序中调用 Get_ADC_Value 得到ADC采样的值,再在主函数的 while 循环中调用数码管显示函数 Display 将ADC采样值显示到数码管上。下图为数字电压电流表接入5V电压时的采样显示图。可以看到接入5V时ADC采样得到669,我们可以计算:
(669/4096)* [(1.5/10)*(200+10)] = 5.145 V
其中4096代表CW32的ADC采样精度12位为2^12=4096,由于我们的测试样品中220KΩ的电阻被替换成了200KΩ,所以计算公式如上,与万用表测量数值相符。(各位学员最终收到的版本是220KΩ的电阻)
图6-3 ADC采样显示
图6-4 万用表测量5V
6.5. ADC采样计算
根据上文,ADC所采样的值虽然准确地显示在数码管上,但采样值仍需要转换成标准值。计算思路与上述公式类似,只是显示到数码管上需要将数值扩大100倍。因此采样计算的思路为:将采样得到的值(比如在5V输入的情况下ADC采样得到668)用上述计算公式计算得到的结果后乘以100:
(668/4096)* [(1.5/10)*(200+10)] * 100 = 513.7 V
由于变量为整形,最终输入给显示函数 Display 的值为513,在 Display 函数里对输入的值进行判定,如果输入值大于1000,则数码管只能显示xx.x V,所以我们只取输入值的千百十位;如果输入值小于1000,比如现在输入值为513,则数码管可显示x.xx V,分别将513的百十个位存入 Seg_Reg 数组中。
最终需要添加一个 Cal_Buffer 变量来存储 Volt_Buffer 的值、一个电压计算函数,再修改 Display 函数见下文:
uint16_t Cal_Buffer; //存储 Volt_Buffer 的值
#define ADC_REF_VALUE (1500) //扩大1000倍 1.5 * 1000 = 1500
#define R2 (200) //单位:KΩ
#define R1 (10)
void Volt_Cal(void) //将ADC采样值转化为标准值
{
Cal_Buffer = Volt_Buffer; //存储中断服务程序中取得的ADC采样值
Cal_Buffer = (Cal_Buffer * ADC_REF_VALUE > > 12) * (R2 + R1)/R1;//计算得到的值为标准值的1000倍
if(Cal_Buffer % 10 >= 5) // 四舍五入
{
Cal_Buffer = Cal_Buffer / 10 + 1;
}
else
{
Cal_Buffer = Cal_Buffer / 10; //此时的值为标准值的100倍
}
}
在while循环中调用数码管显示函数Display之前先调用Volt_Cal函数。
int main()
{
RCC_Configuration();
Seg_Init();
Btim1_Init();
ADC_init();
while(1)
{
Volt_Cal();
Display(Cal_Buffer);
}
}
Display函数的更新如下:
void Display(uint32_t value)
{
uint8_t Thousands; //千位
uint8_t Hundreds; //百位
uint8_t Tens; //十位
uint8_t Units; //个位
Thousands = value / 1000; //如果输入值大于1000,只取输入值的千百十位
if(Thousands > 0) //大于0则说明输入值的千位有值
{
Units = value % 10;
value = Units > 5 ? (value + 10) : value; // 根据后一位四舍五入
Thousands = value / 1000 % 10; //只取千百十位
Hundreds = value / 100 % 10;
Tens = value / 10 % 10;
// 显示xx.x伏
Seg_Reg[0] = Thousands;
Seg_Reg[1] = Hundreds + 10; // 加dp显示
Seg_Reg[2] = Tens;
}
else //如果输入值的千位没有值,则取百十个位
{
Units = value % 10;
Tens = value / 10 % 10;
Hundreds = value / 100 % 10;
// 显示x.xx伏
Seg_Reg[0] = Hundreds + 10; // 加dp显示
Seg_Reg[1] = Tens;
Seg_Reg[2] = Units;
}
}
最终显示效果如下图(输入接5V):
图6-5 采样计算后显示值
此时万用表测得电压如下:
图6-6 万用表测量值(5V)
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !