版权声明:本文为博主原创文章,转载请附上原文出处链接。
今天介绍下STC8A8K64S4A12系列单片机片ADC外设的原理,掌握ADC采样硬件电路设计及相关应用程序设计。
实际应用中,我们经常需要将模拟量转换为数字量供CPU处理,如电池电压检测、温度检测等等,对于CPU来说,他能处理的是数字量,所以,需要通过A/D转换(模数转换)将时间连续、幅值也连续的模拟量转换为时间离散、幅值也离散的数字信号,从而实现CPU对模拟信号的处理,能够实现A/D转换功能的电路称之为模数转换器(ADC:Analog-to-digital converter)。
ADC的结构和实现原理有多种方式,常见的ADC的类型有积分型、逐次逼近型、并行比较型/串并行型、Σ-Δ调制型等。逐次逼近型ADC又称为逐位比较型ADC,是模数转换器中应用很广泛的一种实现方式。其原理就是将输入的模拟信号与不同的参考电压按规律进行多次比较,使转换所得的数字量在数值上逐次逼近输入模拟量,最后得到转换后的数字量。供比较的数字量之间的最小变化值决定了ADC转换器精度。
ADC转换器有几个常见且重要的性能参数,如转换精度、转换速度和通道数等。现市场上多数MCU芯片内部是集成了ADC外设的,也有专用ADC芯片,比如TI公司的TLC548、ADI公司的AD7705等。一般使用外部ADC芯片会提高ADC采集的精度,但成本会大大增加。片外ADC芯片与单片机之间的通信接口多是串行口(SPI或I2C接口),这样占用的就是单片机的串行口外设资源。(不再是单片机的ADC通道了)
待采集的模拟信号无论是进单片机的ADC引脚还是接入片外专用ADC芯片的采集引脚,理论上都是要求具有尽可能大的输出阻抗。尤其是市场上微处理器厂家多、型号多,其片内ADC外设的结构会各不相同,这些ADC输入口对地的等效电阻数量级上也有差别,如果输入的待采集信号输出阻抗也在这个数量级上,那么很容易影响采集效果。所以,很多情况下会采用在待采集信号后加电压跟随器的办法解决这个问题。下面截图NTC热敏电阻模块(带电压跟随器)的原理图帮助大家直观理解下。
☆注:集成电路U2实现了电压跟随功能,AO电平信号输出阻抗极大,可以满足不同微处理器ADC口对输入阻抗的要求。
关于AD和ADC的区别:一般说ADC指的就是模数转换器,不会有歧义。而说到AD,除了可以表示模数转换外(少数文档使用),还可以表示单片机访问外部扩展存储器的地址总线和数据总线合在一起的简称。举个例子,STC8A8K64S4A12系列单片机的P0口有复用功能是AD0AD7,AD0AD7除了表示访问外部扩展存储器的地址总线A0A7外,还可以表示访问外部扩展存储器的地址总线低8位D0D7。关键要看下手册描述,注意推敲语境,一般在STC的手册中AD指的就是地址总线和数据总线合在一起的简称,ADC指的才是模数转换器,所以看芯片原理图时AD0和ADC0是完全不同的含义。
☆注:STC8A8K64S4A12系列单片机各个引脚的功能都有表示,可以看到AD0AD7,还有ADC0ADC7,含义完全不同,须知。
STC8A8K64S4A12系列单片机集成的是15路12位逐次逼近型ADC,速度可达80万次/秒。该系列单片机设计有专用的ADC电源脚和电压基准引脚,需注意基准引脚电压要小于ADC电源引脚电压。下表介绍下ADC通道关联的引脚分布情况。
☆注:STC8A8K64S4A12系列单片机每一路ADC都有且只有一个IO端口供选择使用。
■ STC8A8K64S4A12开发板ADC硬件电路
STC8A8K64S4A12开发板上设计了2.5V基准电压电路和ADC采集电路,如下图:
ADC | 功能描述 | 对应IO口 | 说明 |
---|---|---|---|
P0.6 | ADC采集通道 | P0.6 | 非独立GPIO |
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO。P0.6还可能会被用于DAC实验中,所以是非独立GPIO口。
■ STC8A8K64S4A12系列单片机ADC内部结构图
STC8A8K64S4A12系列单片机ADC由多路选择开关、比较器、逐次比较寄存器、12位DAC、转换结果寄存器以及控制寄存器构成。
STC8A8K64S4A12系列单片机ADC是逐次比较型ADC,逐次比较型ADC由一个比较器和DA转换器构成,通过逐次比较逻辑,从最高位开始,依次对每一输入电压与内置DA转换器输出进行比较,经过多次比较,使转换所得的数字量逐次逼近输入模拟量对应值。逐次比较型AD转换具有速度高、功耗低的优点。
☆注:从上图ADC内部结构图可知DA转换器是12位的,所以了解了ADC逐次比较的原理后不难得出这样的结论:正是这个内部的12位DA转换器精度决定了ADC精度也是12位的。
ADC内部结构图的多路选择开关是由ADC控制寄存器ADC_CONTR的B0~B3位控制,该寄存器B6位和B7位分别控制ADC转换启动和ADC电源,该寄存器B5位是转换结束标志位(需软件清零),如下图所示。
针对STC8A8K64S4A12系列单片机ADC,软件的配置过程如下:
☆注:ADC中断方式对ADC转换结果的读取一般是在中断服务函数中进行,艾克姆提供的例程是ADC查询方式。
STC8A8K64S4A12系列单片机进行ADC操作时会用到4个寄存器,如下表所示:
序号 | 寄存器名 | 读/写 | 功能描述 |
---|---|---|---|
1 | ADC_CONTR | 读/写 | ADC控制寄存器 |
2 | ADCCFG | 读/写 | ADC配置寄存器 |
3 | ADC_RES | 读/写 | ADC转换结果高8位寄存器 |
4 | ADC_RESL | 读/写 | ADC转换结果低8位寄存器 |
ADC配置寄存器ADCCFG用于配置ADC转换速度和转换结果格式,该寄存器B5位设置ADC转换12位结果以什么顺序存放到ADC转换结果寄存器(ADC_RES和ADC_RESL)中,B0~B3位控制ADC转换速度。
ADC转换结果寄存器由ADC_RES和ADC_RESL两个8位寄存器实现,因为ADC转换位数是12位,必然会有4位用不到。至于哪4位用不到,需要参考ADCCFG配置寄存器的RESFMT位决定。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 包含与用户uart有关的用户自定义函数 |
2 | adc | .c | ADC有关的用户自定义函数 |
3 | delay | .c | 包含用户自定义延时函数 |
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
#include "adc.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、adc.h和delay.h头文件在该路径,所以要包含 |
2 | …\Use | STC8.h头文件在该路径,所以要包含 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在adc.c文件中编写操作ADC外设会用到的函数,如下表所示。
序号 | 函数名 | 功能描述 |
---|---|---|
1 | ADC_config | ADC口的初始化操作 |
2 | Get_ADC12bitResult | 读取ADC转换原始值 |
关于上面2个ADC基本操作函数,下面详细给出代码。
程序清单:ADC口初始化函数
/**************************************************************************
功能描述:ADC口初始化
入口参数:无
返回值:无
*************************************************************************/
void ADC_config(void)
{
ADC_CONTR|=0x80; //开AD转换电源
delay_ms(10); //适当延时等待AD转换供电稳定
ADC_CONTR|=0x0E; //选择P0.6作为模拟功能AD使用
ADC_CONTR&=0xFE; //选择P0.6作为模拟功能AD使用
ADCCFG&=0xFC; //AD转换速度为416个时钟数转换一次
ADCCFG|=0x0C; //AD转换速度为416个时钟数转换一次
ADC_CONTR&=0xDF; //清AD转换完成标志
EADC=0; //禁止ADC转换中断
ADCCFG|=0x20; //ADC转换结果ADC_RES存高4位,ADC_RESL存低8位
ADC_CONTR|=0x40; //启动AD转换,ADC_START=1
}
程序清单:读取ADC转换原始值函数
/**************************************
功能描述:ADC口检测AD转换值函数
入口参数:无
返回值:ADC 12位数据
***************************************/
uint16 Get_ADC12bitResult(void)
{
uint16 AD_Dat=0;
ADC_CONTR&=0xDF; // 将ADC_FLAG清0
ADC_CONTR&=0xBF; //关闭AD转换,ADC_START=0
//12位AD结果的高4位放ADC_RES的低4位,低8位在ADC_RESL
AD_Dat = ADC_RES; //将ADC_RES低4位移到应在的第9位至第12位
AD_Dat <<= 8;
AD_Dat|= ADC_RESL; //将ADC_RESL的8位移到应在的低8位
ADC_CONTR|=0x40; //启动AD转换,ADC_START=1
return AD_Dat;
}
然后,对P0.6引脚ADC原始值的处理方式是直接返回该原始值,处理函数代码如下。
程序清单: ADC转换原始值处理函数
/***************************************************************************
功能描述:读取ADC采集的原始值
入口参数:无
返回值:实时原始值
**************************************************************************/
uint16 HandleADC(void)
{
uint16 Temp_signal;
//读取采集的原始值
Temp_signal=Get_ADC12bitResult();
//返回采集的原始值
return Temp_signal;
}
最后,在主函数中对串口1进行初始化,主循环中每1000ms通过串口1发送读取的ADC原始值。
代码清单:主函数
int main()
{
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
ADC_config(); //ADC初始化
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while (1)
{
printf("\r\n ADC采集原始值: %d\r\n",HandleADC()); //串口打印上传的ADC采集原始值信息
delay_ms(1000);
}
}
本实验需要使用短路帽将J27端子的P06与ADC短接,如下图所示。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-12-1:ADC电位器实验(P0.6读取原始值)”部分。
在adc.c文件中编写操作ADC外设会用到的基本函数,请参考“实验2-12-1:ADC电位器实验(P0.6读取原始值)”部分。
然后,对光敏电阻引脚ADC原始值的处理方式是根据返回的原始值来计算出对应ADC口的电压值,处理函数代码如下。
程序清单: ADC转换原始值处理函数
/***************************************************************************
功能描述:将采集的原始值转换为电压值
入口参数:无
返回值:实测电压值
**************************************************************************/
float HandleADC(void)
{
uint16 Temp_signal;
float g_voltage;
//读取采集的原始值
Temp_signal=Get_ADC12bitResult();
//因为开发板J6选择2V5为ADC基准电压。所以计算电压值公式:V=Temp_signal/4096*2.5。(单位V)
if((Temp_signal<TEMPMAX)&&(Temp_signal>TEMPMIN))
{
g_voltage=(2.5*Temp_signal)/4096; //(单位V)
}
//返回实测电压值
return g_voltage;
}
最后,在主函数中对串口1进行初始化,主循环中每1000ms通过串口1发送读取的电压值。
代码清单:主函数
int main()
{
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
ADC_config(); //ADC初始化
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while (1)
{
printf("\r\n ADC端口电压值: %.1f\r\n",HandleADC()); //串口打印上传的采集的电压值
delay_ms(1000);
}
}
本实验需要使用短路帽将J27端子的P06与ADC短接,如下图所示。
以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!