第2-17讲:PCA实现数模转换(DAC)
-
- 学习目的
- 了解DAC数模转换原理及RC积分电路原理。
- 掌握STC8A8K64D4系列单片机实现DAC功能的硬件和软件设计。
- DAC简介
DAC (全称是Digital to Analog Convertor)数模转换器是一种将数字信号转换为模拟信号(以电流、电压或)的设备或电路。在很多数字系统中(例如计算机、单片机),信号以数字方式(0或者1)存储和传输,而数模转换器DAC可以将这样的信号转换为模拟信号,从而使得他们能够被外界(人或其他非数字系统)识别。数模转换器DAC的常见用法是在音乐播放器中将数字形式存储的音频信号输出为模拟的声音。
T型电阻网络方式是一种常见的DAC实现方法,由T型电阻网络和运算放大器组成,下图是8位DAC的原理示意图。输入数字量中的每位都按其权值分别转换为模拟量,之后通过运算放大器求和相加。
图1:T型电阻网络示意图
上图中,由于运算放大器的反相输入端为“虚地”,所以无论模拟开关连接到“0”还是 “1”,从T形电阻网络节点对“地”往右看的等效电阻均为R,由此可计算出基准电流I=VREF/R。再根据电流可计算出流过各个分支的电流从右向左(I0~I7)依次是I/2、I/4、I/8、I/16、I/32、I/64、I/128和、I/256。
由此,每一位数字量都发挥了有效的位权,流向运算放大器反相输入端的总电流如下:
该电流经过运算放大器换成模拟电压输出,从而实现由数字信号到模拟信号的转换。这里以8位DAC示例,输出电压有256种变化,当然,这种T形电阻网络的转换原理可以推广到n位,实现n位DAC。
市面上很多单片机片内集成了DAC外设,并且也有专用独立DAC芯片供用户选择使用,那为什么还需要使用PWM实现DAC?
这是因为虽然市面上很多集成了DAC的单片机和独立DAC芯片,他们使用起来更方便,并且精度也高,但是很多时候,我们并不需要很高的精度,我们选择使用的单片机可能没有DAC外设,但是一般都有PWM,因此,出于对成本的考虑,我们会使用PWM实现DAC,从而节省成本。
-
- 硬件电路设计
STC8A8K64D4系列单片机片内没有集成DAC外设,因此,IK-64D4开发板通过PCA工作于PWM模式输出高速PWM脉冲配合RC滤波电路实现DAC功能。
实现DAC转换是基于将高速PWM信号通过RC电路整合成比较平缓的电压信号作为模拟输出,通过改变高速PWM信号的占空比达到改变输出电平信号幅度的目的。为了达到比较理想的电压信号输出,P7.0口输出的PWM信号经二阶RC滤波电路整合,如下图所示。
图2:PWM实现DAC电路
- 注:开发板J27端子需使用短路帽短接P06和DAC_O以及P70和DAC_I,并且将J27端子的P06和ADC以及P70和DHT11的短路帽去掉。
-
- 软件设计
- PWM实现DAC实验
- 软件设计
- 注:本节的实验是在“实验2-11-1:ADC采样电位器电压(查询方式)”的基础上修改,本节对应的实验源码是:“实验2-17-1: PWM实现DAC实验”。
- 关于PWM的内容,读者可以参阅《第2-16讲:可编程计数器阵列PCA》中的PCA实现PWM部分的内容。
-
-
- 实验内容
-
-
配置PCA模块0工作于PWM模式, PCA配置如下:
- 系统时钟:24MHz。
- PCA时钟源:系统时钟= 24Mhz。
- 功能引脚:P7.0。
- 中断:不开启中断。
- PWM位数:8位。
程序运行后,在主循环中不断改变PWM的占空比,从而改变P7.0输出的电压值。实验中为了方便观察实验现象,配置P0.6为ADC功能引脚,并通过跳线帽将其连接到DAC电路的输出端,这样,就可以实时获取DAC输出的电压值并通过串口输出,以便于我们观察实验现象(DAC输出电压值的改变)。
-
-
-
- 代码编写
-
-
- 新建一个名称为“pca.c”的文件及其头文件“pca.h”并保存到工程的“Source”文件夹,并将“pca.c”加入到Keil工程中的“SOURCE”组。
- 引用头文件
因为在“pca.c”文件中使用了“pca.c”文件中的函数,所以需要引用下面的头文件“pca.h”。
代码清单:引用头文件
- //引用pca的头文件
- #include " pca.h"
- PCA初始化
本例中,我们配置PCA模块0工作于PWM模式,PCA时钟源使用系统时钟(24Mhz),功能引脚为P7.0,代码清单如下。
代码清单:PCA初始化
- /***************************************************************************
- * 描 述 : PCA初始化
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_init(void)
- {
- CCON = 0x00; //CF、CR、CCF1、CCF0位均清零
- P_SW1 &= 0xEF; //PCA模块0功能引脚选择P7.0
- P_SW1 |= 0x20;
- /*--------------------PCA模式寄存器CMOD配置---------------------------
- 位7 位6 位5 位4 位3~位1 位0
- CIDL x x x CPS[2~0] BCF
- 0 x x x 100 0
- CIDL=0:空闲模式下仍然计数
- CPS[2~0]=100:PCA时钟源选择:系统时钟
- BCF=0:禁止PCA计数器溢出中断
- -----------------------------END------------------------------------*/
- CMOD = 0x08;
- CL = 0x00; //PCA计数器赋初值
- CH = 0x00; //PCA计数器赋初值
- IP &= ~0x02; //中断优先级配置为2(较高优先级)
- IPH |= 0x02;
- /*------------------PCA模块0模式控制寄存器CCAPM0配置------------------
- 位7 位6 位5 位4 位3 位2 位1 位0
- x ECOM0 CCAPP0 CCAPN0 MAT0 TOG0 PWM0 ECCF0
- x 0 0 0 0 0 1 0
- ECOM0=0:关闭PCA模块0的比较功能
- CCAPP0=0:关闭PCA模块0的上升沿捕获
- CCAPN0=0:关闭PCA模块0的下降沿捕获
- MAT0=0:关闭PCA模块0的匹配功能
- TOG0=0:关闭PCA模块0的高速脉冲输出功能
- PWM0=1:开启PCA模块0的脉宽调制输出功能
- ECCF0=0:禁止PCA模块0的匹配/捕获中断
- -----------------------------END------------------------------------*/
- CCAPM0 = 0x02;
- PCA_PWM0 &= 0x3F; //PCA模块0工作于8位PWM功能
- PCA_PWM0 &= 0xFC; //EPC0H位和EPC0L位置0
- CCAP0L = 0x00; //PCA比较值寄存器赋初值
- CCAP0H = 0x00; //PCA比较值寄存器赋初值
- CR = 1; //启动PCA计数器阵列计数
- }
- 主函数
主函数中初始化PCA和ADC,PCA计数启动后,在主循环中不断改变PWM的占空比,使得DAC输出不同的电压。
ADC采样DAC输出的电压值后,通过串口输出采样值及其对应的电压值,代码清单如下。
代码清单:主函数
- /***************************************************************************
- * 描 述 : 主函数
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************/
- int main(void)
- {
- u16 adc_value; //存放ADC采样值
- float voltage; //存放ADC采样值计算后的电压值
- u16 TempData;
- P2M1 &= 0xBF; P2M0 &= 0xBF; //设置P2.6为准双向口(LED1)
- P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD)
- P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD)
- P7M1 &= 0xFE; P7M0 |= 0x01; //设置P7.0为推挽输出
- pca_init();
- uart1_init(); //串口1初始化
- adc_config(); //初始化ADC
- EA = 1; //使能总中断
- while(1)
- {
- TempData++;
- CCAP0H = (u8)(256 - TempData); //P7.0引脚输出频率不变但占空比不断变化的脉冲信号
- if(TempData>138)TempData=1; //占空比达到很大时重新设定占空比
- delay_ms(5);
- adc_value = get_adc_value(); //读取ADC采样值
- voltage = (2.5*adc_value)/4096; //将ADC采样值转换为电压(单位V)
- printf("ADC采样值: %d\r\n",adc_value); //串口打印上传的采集的原始值
- printf("采样值对应的电压:%.2fV\r\n",voltage);//串口打印ADC采样电压
- delay_ms(200); //延时200ms,方便观察数据
- led_toggle(LED_1); //翻转指示灯D1状态,指示一次ADC采样完成
- }
- }
-
-
- 硬件连接
-
-
本实验通过P7.0输出PWM信号实现DAC输出,P0.6配置为ADC用于采样DAC电路输出的电压,按照下图所示连接跳线帽。
图3:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-17-1: PWM实现DAC实验”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\pca_dac\project”目录下的工程文件“pca_dac.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“pca_dac.hex”位于工程的“…\pca_dac\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。程序运行后,在串口接收窗口可以看到开发板上报的ADC采样值及其对应的电压值,如下图所示。
图4:串口调试助手中观察电压值