采用C8051F020 DAC产生任意波形
2013-02-01
周乃明 施乾信 王兵
标签: C8051F020 DAC

信号源是许多电子设备必不可少的部件,可以采用模拟集成电路如8038、单片机控制D/A转换器或DDS芯片如AD9850等方法实现。对于内部具有D/A转换器的单片机,采用其自备的D/A转换器产生需要的信号是最经济的方法。内部具有D/A转换器的单片机种类很多,这里介绍采用Cygnal公司最新的一款功能强大的内部具有D/A 转换器的单片机C8051F020产生任意波形的详细方法。该方法及部分程序也可以用于其他型号的单片机芯片。

1 采用C8051F020 D/A转换器产生正弦信号的基本原理

单片机指令不支持正弦函数运算,所以要产生正弦信号只能通过查正弦函数表的方法,再经过D/A转换成模拟量而输出正弦波。其波形的频率可以通过改变定时器的初值,即改变查表输出的时间来控制。由于本例中采用了C8051F020的一个12位D/A转换器产生正弦信号,所以平均值为2048幅度为0~4096(212)的64点正弦函数表可以通过如下公式计算:

输出=sin(2πj/64)×2048+2048j=0,1,……,63

计算得到的64项16位二进制数结果,以2个8位二进制数的形式存放在code段(ROM中)具有128项的一维数组SINE_TABLE[128]中,每2项合成一个16位数,取低12位送D/A转换器进行D/A转换。

以下面程序数表中前2项为例:0x08,0x00合并成0x0800,取低12位为800 H,经过DAC0数据转换电压为1.20 V(约为满幅2.40 V的一半)。以产生100 Hz正弦信号为例,设SYSCLK系统振荡频率为2MHz,定时器的初值为:

216 - 2MHz/12 × 1/100Hz × 1/64 =65536-26.041666 =65509.958 ≈ 65510

其中216是因为采用的定时器3是16位的。

2 硬件电路

采用C8051F020 D/A转换器产生正弦信号的硬件电路图如图1所示。

电源部分电路如图2所示。

3 软件编程

// sinwave.c
#include <reg51.h>
//C8051F020特殊功能寄存器声明
//16位特殊功能寄存器定义
sfr16 DP = 0x82; //数据指针
sfr16 TMR3RL = 0x92; //定时器3重装值
sfr16 TMR3 = 0x94; //定时器3计数器
sfr16 ADC0 = 0xbe; //ADC0
sfr16 ADC0GT = 0xc4; //ADC0窗口1
sfr16 ADC0LT = 0xc6; //ADC0窗口2
sfr16 RCAP2 = 0xca; //定时器2捕捉/重载
sfr16 T2 = 0xcc; //定时器2
sfr16 RCAP4 = 0xe4; //定时器4捕捉/重载
sfr16 T4 = 0xf4; //定时器2
sfr16 DAC0 = 0xd2; //DAC0
sfr16 DAC1 = 0xd5; //DAC1
//全局常量
#define SYSCLK 2000000 //系统复位自动使用内部时钟,为2MHz
sbit LED = P1 ^ 6; //定义P1.6名称为LED,监视系统运行
unsigned char i = 0; //声明无符号字符变量i(初值为0)
void PORT_Init(void); //端口初始化函数原型
void Timer3_Init(int counts); //定时器3初始化函数原型
void Timer3_ISR(void); //定时器3中断服务函数原型
void Dac0_Init(void); //DAC0初始化函数原型
//正弦函数表,定义具有128项的无符号字符型一维数组
unsigned char code SINE_TABLE[128] = {0x08, 0x00, 0x08,
                                      0xc9, 0x09, 0x90, 0x0a, 0x53, 0x0b, 0x10, 0x0b, 0xc5, 0x0c,
                                      0x72, 0x0d, 0x13, 0x0d, 0xa8, 0x0e, 0x2f, 0x0e, 0xa7, 0xxf,
                                      0xxe, 0xxf, 0x64, 0xxf, 0xa8, 0xxf, 0xd9, 0xxf, 0xf6, 0x0f, 0xff,
                                      0x0f, 0xf6, 0xxf, 0xd9, 0x0f, 0xa8, 0x0f, 0x64, 0x0f, 0x0e, 0x0e,
                                      0xa7, 0x0e, 0x2f, 0x0d, 0xa8, 0x0d, 0x13, 0x0c, 0x72, 0x0b,
                                      0xc5, 0x0b, 0x10, 0x0a, 0x53, 0x09, 0x90, 0x08, 0xc9, 0x08,
                                      0x00, 0x07, 0x37, 0x06, 0x70, 0x05, 0xad, 0x04, 0xf0, 0x04,
                                      0x3b, 0x03, 0x8e, 0x02, 0xed, 0x02, 0x58, 0x01, 0xd1, 0x01,
                                      0x59, 0x00, 0xf2, 0x00, 0x9c, 0x00, 0x58, 0x00, 0x27, 0x00,
                                      0x0a, 0x00, 0x00, 0xxx, 0x0a, 0x00, 0x27, 0x00, 0x58, 0x00,
                                      0x9c, 0x00, 0xf2, 0x01, 0x59, 0x01, 0xd1, 0x02, 0x58, 0x02,
                                      0xed, 0x03, 0x8e, 0x04, 0x3b, 0x04, 0xf0, 0x05, 0xad, 0x06,
                                      0x70, 0x07, 0x37,
                                     };
void main(void) //主函数
{
    WDTCN = 0xde; //关闭看门狗定时器,使其无效
    WDTCN = 0xad:
            PORT_Init(); //调用端口初始化函数
    Timer3_Init(65510); //见下面注释*
    Dac0_Init(); //调用DAC0初始化函数
    EA = 1; //开中断允许总开关,允许中断
    while (1) { //主函数在此循环等待
    }
}
void PORT_Init(void) //端口初始化函数
{
    XBR2 = 0x40; //交叉网络设定为弱上拉并生效
    P1 MDOUT |= 0x40; //设置P1.6(LED)为推挽输出方式
}
//定时器3初始化函数
//定义定时器3为自动重装载方式,以系统时钟的1/12为时钟源
void Timer3_Init(int counts) //“counts”为计数重载值。由调用函数传递过来
{
    TMI13CN = 0x00; //定时器3停止,清TF3,
//使用SYCCLK/12为时钟源
    TMR3RL = counts; //设置重载值为“counts”
    TMR3 = 0xffff; //设置立即重载
    EIE2 |= 0x01; //开启定时器3中断允许开关,允许定时器3中断
    TMR3CN |= 0x04; //开启定时器3,使其运行
}
void Dac0_Init(void) //DAC0初始化函数
{
    DAC0CN = 0x80; //DAC0使能,且为立即更新方式,写DAC0H寄存器
//将立即启动DAC0工作,取DAC0H和DAC0L组
//成的16位数据的低12位数据为DAC0的转换数据
    REFOCN |= 0x03; //DAC参考电压设定为使用内部电压基准
}
void Timer3_ISR(void)interrupt 14 //T3中断服务函数
{
    TMR3CN &= ~(0x80); //清TF3
    LED = ~LED; //使LED状态改变,
    DAC0L = SINE_TABLE[i * 2 + 1]; //查SINE_TABLE表,将第i*2+1项送给
//DAC0L,i=0,1,2,……63
    DAC0H = SINE_TABLE[i * 2]; //查SINE_TABLE表,将第i*2项送给
//DAC0H,i=0,1,2,……63
    i = i + 1;
    if (i >= 64) {
        i = 0;    //循环输出64点
    }
}

调用定时器3初始化函数,调节其中的计数器初值可以得到不同频率的正弦信号。这里以产生100Hz正弦信号为例,计数器初值为65510,具体的计算方法如前文所述。

4 产生任意波形的方法

若想产生方波、三角波或任意波形,可以简单地通过修改正弦函数表来得到。

5 结束语

本文中的程序已经在新华龙公司C8051F020仿真开发板上调试通过,若要产生高精度的信号,必须考虑四舍五入近似、系统时钟精度及程序响应延时造成的误差。

可能会用到的工具/仪表
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号