版权声明:本文为博主原创文章,转载请附上原文出处链接。
今天介绍下STC8A8K64S4A12系列单片机片DAC数模转换原理及RC积分电路原理,掌握掌握STC8A8K64S4A12系列单片机实现DAC功能的硬件和软件设计。
DAC (全称是Digital to Analog Convertor)数模转换器是一种将数字信号转换为模拟信号(以电流、电压或电荷的形式)的设备,在很多数字系统中(例如计算机、单片机),信号以数字方式(0或者1)存储和传输,而数模转换器DAC可以将这样的信号转换为模拟信号,从而使得他们能够被外界(人或其他非数字系统)识别。
数模转换器DAC的常见用法是在音乐播放器中将数字形式存储的音频信号输出为模拟的声音。有的电视机的显像也有类似的过程。数模转换器DAC有时会降低原有模拟信号的精度,因此转换细节常常需要筛选,使得误差可以忽略。
DAC有几个常见且重要的性能参数,如分辨率、线性度、建立时间、绝对精度和相对精度等。这里简单说下分辨率和线性度的概念。DAC的分辨率是输入数字量的最低有效位(LSB)发生变化时,所对应的输出模拟量(电压或电流)的变化量。其反映了输出模拟量的最小变化值。DAC的线性度(也称非线性误差)是实际转换特性曲线与理想直线特性之间的最大偏差。常以相对于满量程的百分数表示。
现市场上越来越多的MCU芯片内部会集成DAC外设,也有专用DAC芯片,包括一些音频芯片都属于DAC芯片,比如PCM5102A等。说到单片机DAC外设,一定会给出这个DAC外设是多少位的,比如单片机片内有16位DAC,就意味着数字信号的范围是065535,假设该DAC输出是03.3V的电压型模拟信号,那DAC转换可分辨的最小电压(分辨率)就是3300mV/65535=0.05mV,这也就意味DAC数字信号输出有1bit的抖动,那么实际输出的模拟信号就有0.05mV的偏差。
DAC核心部分是由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器所组成。这里之所以说核心部分是因为有些专用DAC芯片,比如音频芯片可能还有I2S接口相关的外围电路等。下面简单介绍下倒T型电阻网络。解析这个原理图时我们必须知道戴维南等效电源定理,然后从右侧向左去等效可分析出I7是I的1/2,I6是I的1/4,I5是I的1/8,I4是I的1/16,I3是I的1/32,I2是I的1/64,I1是I的1/128,I0是I的1/256,如此每一位都发挥了有效的位权,输出电压有256种变化,该T型电阻网络等效输出表达式为D0Vref/256+D1Vref/128+D2Vref/64+D3Vref/32+D4Vref/16+D5Vref/8+D6Vref/4+D7Vref/2。
☆注:本T型电阻网络是8位DAC举例,若是10位、12位、16位依次按此方法增加R电阻和2R电阻电路部分。
STC8A8K64S4A12系列单片机内部没有集成DAC外设,所以STC8A8K64S4A12开发板实现DAC转换是基于将高速PWM信号通过RC电路整合成比较平缓的电压信号作为模拟输出,而改变高速PWM信号的占空比可改变输出电平信号的幅值。为了达到比较理想的电压信号输出,P7.0口输出的PWM信号经2级RC电路整合,如下图。
☆注:开发板J27端子需使用短路帽短接P06||DAC_O和P70||DAC_I,并且将J27端子的P06||ADC和P70||DHT11短路帽去掉。
DAC电路占用的单片机的引脚如下表:
DAC | 功能描述 | 对应IO口 | 说明 |
---|---|---|---|
PCA | DAC触发信号 | P7.0 | 非独立GPIO |
ADC | DAC输出信号 | P0.6 | 非独立GPIO |
☆注:非独立GPIO表示开发板有其他的电路使用这个GPIO。
■ RC积分电路简介
由电阻R和电容C构成的电路称为阻容电路,简称RC电路。RC电路是电子电路中非常常见的一种电路,但RC电路的种类和变化很多,首先我们介绍下RC积分电路。
在RC电路分析中,有时要用到时间常数这一概念。时间常数为电容量与电阻值的乘积。在电容量大小不变时,电阻值决定了时间常数的大小。电阻值不变时,电容量的大小决定了时间常数的大小。输入信号加在电阻R1上,输出信号取自电容C1。输入信号是矩形脉冲,在积分电路中,要求RC电路中的时间常数远大于脉冲宽度。
☆注:RC积分电路的作用是:消减变化量,突出不变量。故RC积分电路可将矩形脉冲波转换为锯齿波或三角波,还可将锯齿波转换为抛物波。
RC积分电路的条件是时间常数远大于输入信号的脉冲宽度,STC8A8K64S4A12开发板的RC电路中,R1取值3.3K、C1取值100nF,可计算时间常数为R1C1=3.3KΩ10010-9F=3.310-4。如果时间常数远大于输入信号的脉冲周期,则也一定满足RC积分电路的条件。换句话说如果时间常数的倒数远小于输入信号的频率,则也一定满足RC积分电路的条件。时间常数的倒数可计算出为3.3*10-4的倒数约为3030,所以输入信号的频率只要远大于3.03KHZ即可满足积分电路的条件(一般按照10倍来作为基本条件)。
STC8A8K64S4A12开发板控制P7.0口输出PWM信号的频率约是43.2KHZ(具体不详述),该频率满足RC积分电路的条件,所以可将P7.0口输出的PWM信号整合成比较平缓的电压信号(可使用示波器辅助测量之)。又因为单片机控制P7.0口输出的PWM信号频率不变、占空比变化,所以最后整合出的电压信号的幅值会不同,从而实现输出不同模拟量的目的。
■ RC微分电路简介
RC微分电路和RC积分电路在电路形式上相近,RC微分电路输出电压取自电阻,而且RC时间常数与积分电路不同。RC微分电路中,要求RC时间常数远小于输入信号的脉冲宽度。如下图所示。
☆注:RC微分电路的作用是:消减不变量,突出变化量。故RC微分电路可把矩形波转换为尖脉冲波,电路的输出波形只反映输入波形的突变,即只有输入波形发生突变的瞬间才有输出。而对恒定部分则没有输出。
除了上面描述的RC微分电路和RC积分电路,RC电路还有很多不同应用,比如RC耦合电路、RC脉冲分压器以及RC滤波电路等。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | uart | .c | 包含与用户uart有关的用户自定义函数 |
2 | adc | .c | ADC有关的用户自定义函数 |
3 | pca | .c | PCA有关的用户自定义函数。 |
4 | delay | .c | 包含用户自定义延时函数 |
■ 需要引用的头文件
#include "delay.h"
#include "uart.h"
#include "adc.h"
#include "pca.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | uart.h、adc.h、pca.h和delay.h头文件在该路径,所以要包含。 |
2 | …\Use | STC8.h头文件在该路径,所以要包含 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在adc.c文件中编写操作ADC外设会用到的函数,如下表所示。
序号 | 函数名 | 功能描述 |
---|---|---|
1 | ADC_config | ADC口的初始化操作 |
2 | Get_ADC12bitResult | 读取ADC转换原始值 |
3 | HandleADC | 处理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;
}
然后,在pca.c文件中编写PCA的初始化函数PCAInit,代码如下。
程序清单:PCA初始化函数
/**************************************************************************
功能描述:PCA初始化
入口参数:无
返回值:无
*************************************************************************/
void PCAInit(void)
{
P_SW1 &= 0xEF; //选择PCA模块0为引脚P7.0,选择PCA模块1为引脚P7.1
P_SW1 |= 0x20; //选择PCA模块2为引脚P7.2,选择PCA模块3为引脚P7.3
CCON = 0x00; //CF、CR、CCF1、CCF0位均清零
CMOD &= 0x7F; //CIDL位置0,空闲模式下PCA计数器仍然工作
CMOD &= 0xF1; //CP2、CP1、CP0设置为100,PCA时钟源选择为系统时钟
CMOD |= 0x08; //CP2、CP1、CP0设置为100,PCA时钟源选择为系统时钟
CMOD &= 0xFE; //ECF位置0,禁止寄存器CCON中CF位中断(禁止PCA计时中断)
CL = 0x00; //PCA计数器赋初值
CH = 0x00; //PCA计数器赋初值
//PCA模块0初始化部分
CCAPM0 |= 0x40; //ECOM0位置1,允许比较器功能
CCAPM0 &= 0xDF; //CAPP0位置0,禁止上升沿捕获
CCAPM0 &= 0xEF; //CAPN0位置0,禁止下降沿捕获
CCAPM0 &= 0xF7; //MAT0位置0,禁止匹配控制位
CCAPM0 &= 0xFB; //TOG0位置0,禁止翻转控制位
CCAPM0 |= 0x02; //PWM0位置1,开启PWM模式
CCAPM0 &= 0xFE; //ECCF0位置0,禁止CCF0中断
PCA_PWM0 &= 0x3F; //PCA模块0工作于8位PWM功能
PCA_PWM0 &= 0xFC; //EPC0H位和EPC0L位置0
CCAP0L = 0x00; //PCA比较值寄存器赋初值
CR = 1; //启动PCA计数器阵列计数
}
最后,在主函数中对串口1进行初始化,主循环中每200ms通过串口1发送读取的端口P06的ADC原始值信息。
代码清单:主函数
int main()
{
uint16 TempData=0;
P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口
P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出
PCAInit(); //PCA初始化
ADC_config(); //ADC初始化
Uart1_Init(); //串口1初始化
EA = 1; //使能总中断
delay_ms(10); //初始化后延时
while (1)
{
TempData++;
CCAP0H = (uint8)(256 - TempData); //P7.0引脚输出频率不变但占空比不断变化的脉冲信号
if(TempData>138) //占空比达到很大时重新设定占空比
TempData=1;
delay_ms(20);
printf("\r\n ADC_P06端口原始值: %d\r\n",Get_ADC12bitResult()); //串口打印上传的采集的原始值
delay_ms(200);
}
}
以上是今天要讲的内容,希望对大家有帮助,如果有啥不明白的,欢迎讨论哦!