信号源是许多电子设备必不可少的部件,可以采用模拟集成电路如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+2048 j=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 <c8051io20.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仿真开发板上调试通过,若要产生高精度的信号,必须考虑四舍五入近似、系统时钟精度及程序响应延时造成的误差。
|