Keil C51编程控制倍频正弦信号的产生
2013-02-11
陈红仙

MCS51单片机是我国应用最为广泛的单片机种。以往单片机应用程序主要用汇编语言编写,由于汇编语言程序的可读性和可移植性都较差,采用汇编语言编写单片机应用程序不但周期长,而且调试和排错也比较困难。为了提高编制单片机应用程序的效率,改善程序的可读性和可移植性,采用高级语言无疑是一种好的选择。C语言是一种通用的计算机程序设计语言,既具有一般高级语言的特点,又能直接对计算机的硬件进行操作,表达和运算能力也较强,许多以往只能采用汇编语言来解决的问题现在都可以改用c语言来解决。

德国Keil Software公司多年来致力于单片机c语言编译器的研究。该公司开发的Keil C51是一种专为8051单片机设计的高效率c语言编译器,符合ANSI标准,生成的程序代码运行速度极高,所需要的存储器空间极小,完全可以和汇编语言相媲美。Keil C51具有丰富的函数库,包含100多种功能函数,为用户编程提供了极大的方便。C51程序可以实现与汇编语言的接口,两者相互之间的调用十分方便。。高版本的Keil C51编译器,尤其是Keil Vision2(基于Windows操作系统的C51集成编译环境),以其性能优秀、使用方便,受到了众多单片机爱好者的欢迎。

在有些测量仪表中,常需要提供不同频率的低频正弦波信号源,它们的频率完全成整数倍关系。如测量电厂工业用水的电导为防止电极传感器的极化,要用低频正弦波信号作为激励源,双频测导法就要求提供精确双倍频率的正弦信号。常用的正弦波信号倍频或分频采用的方法有:

  • 方波信号分频后滤波;
  • 锁相合成法;
  • 单片机控制的D/A转换后再滤波等。

方法① 由于基频的谐波分量大,滤波效果差而很少采用;方法②采用的是压控振荡原理,常用于高频正弦信号的倍频或分频;方法③ 由于高频谐波分量远小于基波分量,滤波效果好且能精确定时,容易实现信号的倍频或分频。本文采用单片机AT89C2051控制D/A转换实现倍频正弦波信号的产生,编程语言采用的就是Keil C51。

1 硬件电路

图1为倍频正弦波信号发生电路,U2为l0位串行DA集成电路TLC5615(TEXAS仪器公司生产),VREF为2.5v的标准参考电压。U3 MAX813为看门狗复位集成电路,在U1(AT89C2051)出现程序跑飞时可自动复位。U1控制DA输出正弦变化的阶梯电压,经R1、C3滤波,C4隔直后即可得到波形较理想的正弦波,只要在一个周期内保证有足够多的输出点数。

图1倍频正弦波发生电路

U1根据P1.3和P1.4(标号分别为SWF0和SWF1)的状态控制正弦波的产生与停止及基频与倍频,它们的组合关系为:SWF1=1、SWF0=x时DA无正弦信号输出;SWF1=0、SWF0=0时DA输出基本频率的正弦信号;SWF1=0、SWF0=1时DA输出双倍基频的正弦信号。DA转换器TLC5615遵从SPI标准的三线串行通信协议,三线分别是:/CS片选线,低电平有效;SCLK时钟线;DIN数据线。SPI串行总线上数据传送时序如图2所示,图中在/CS低电平有效时,时钟线SCLK上升沿时数据线DIN上的数据必须稳定,方可保证数据的正确传送,当/CS高电平时器件不接受数据,这样可在SPI串行总线上挂多个支持SPI标准的器件。有关SPI串行总线的具体参数请参阅器件资料。

图2 SPI串行数据总线数据传送时序图

2 Keil C51程序

单片机程序采用Keil C51编程语言编写,经编译后生成HEX文件即可固化在AT89C2051中。一个C,51工程(project)的源程序由C文件和H头文件等组成。本文下面给出的C文件wave.c中有主程序"void main(void)"、DA转换输出函数"void da.out(void)"和定时器中断函数"void timer0(void)interrupt 1 using 2"组成。两次DA转换之间采用定时器0进行定时。在产生基频正弦信号或倍频正弦信号时不改变定时器的定时时间,而是通过传送给DA不同的数字量来实现,即头文件中的产生基频正弦信号的wavel数组和产生倍频信号的waveh数组的长度一样,均为128,但wavel是一个周期内正弦波的数字量,waveh是两个周期内的数字量,前64个数值与后64个数值相同。这样可以避免执行重置定时常数的指令而引起的时间误差,从而得到精确的双倍频关系。另外,wavel数组中的峰-峰值(最大值-最小值)约为waveh数组中峰-峰值的一半,这样使得经RC滤波后两种频率的正弦波幅度近似相等,以满足使用要求。如程序中的定时常数(TH0=0xff,TL0=0x00),在晶振为20MHz时,测得基频为50.6Hz,倍频为101.2Hz。以下是C51源程序wave.c和H头文件wave.h,在Keil C51 V6.12下编译通过生成HEX文件。

C51源程序wave.c

#include <reg51.h>
#include "wave.h"
void da_out(void); //声明函数
sbit DIN = 0x97; //P1.7位定义
sbit SCLK = 0x96; //P1.6位定义
sbit DACS = 0x95; //P1.5位定义
sbit SWF1 = 0x94; //P1.4位定义
sbit SWF0 = 0x93; //P1.3位定义
sbit WDI = 0x92; //P1.2位定义
sbit FLAG = 0x90;
IUI word; //IUI即idata unsigned int,在wave.h中预定义
void main(void)
{
    IUI i;
    TMOD = 0x01; //定时器0方式1;
    TH0 = 0xff;
    TL0 = 0x00; //置定时器0常数;
    TR0 = 1; //启动定时器0
    ET0 = 1;
    EA = 1; //开定时器中断及总中断
    SWF0 = 1;
    SWF1 = 1; //设P1.3,P1.4为输入
    WDI = 1; //看门狗输入置高电平
    while (1) {
        for (i = 0; i < 128; i++) {
            FLAG = 1; //置标志,FLAG在定时器0中断程序中被清除
            WDI = 0;
            _nop_();
            WDI = 1; //看门狗复位
            if (SWF1) {
                word = 512;    //SWF1=1时,DA输出同一量,无正弦信号输出
            }
            else {
                if (SWF0) {
                    word = waveh[i];    //取倍频数字量
                }
                else {
                    word = wavel[i];    //取基频数字量
                }
            }
            word = word << 6; //10位数字量移至高位
            while (FLAG); //等待,直至定时器中断程序中清FLA G
            da_out(); //调用DA输出子程序
        }
    }
}
void timer0(void) interrupt 1 using 2
{
    TH0 = 0xff;
    TL0 = 0x00; //重置定时器常数
    FLAG = 0; //清主程序中的等待标志
}
void da_out(void)
{
    IUI i;
    SCLK = 0;
    _nop_();
    DACS = 0; //准备传送数据
    for (i = 0; i < 10; i++) {
        DIN = (bit)(word & Ox80); //取最高位送数据线
        word = word << 1; //左移,准备下一位传送
        SCLK = 1;
        _nop_();
        SCLK = 0; //一个CLK信号
    }
    DACS = 1;
    _nop_();
    SCLK = 1; //传送结束
}
H头文件(wave.h):
    typedef idata unsigned int IUI;
int code wavel[] = {
    512, 524, 537, 550, 563, 698, 707, 715, 723, 731, 775, 775, 774, 772, 770, 698, 689, 679, 669, 658,
    512, 499, 486, 473, 460, 325, 316, 308, 300, 292, 248, 248, 249, 251, 253, 325, 334, 344, 354, 365,
};
int code waveh[] = {
    512, 562, 611, 660, 707, 753, 796, 836, 874, 907, 937, 963, 985, 1001, 1014, 1021,
    1023, 1021, 1014, 1001, 985, 963, 937, 907, 874, 836, 796, 753, 707, 660, 611, 562,
    512, 46l, 412, 363, 316, 270, 227, 187, 149, 116, 86, 60, 38, 22, 9, 2,
    0, 2, 9, 22, 38, 60, 86, 116, 149, 187, 227, 270, 316, 363, 412, 461,
    512, 562, 611, 660, 707, 753, 796, 836, 874, 907, 937, 963, 985, 1001, 1014, 1021,
    1023, 1021, 1014, 1001, 985, 963, 937, 907, 874, 836, 796, 753, 707, 660, 611, 562,
    512, 461, 412, 363, 316, 270, 227, 187, 149, 116, 86, 60, 38, 22, 9, 2,
    0, 2, 9, 22, 38, 60, 86, 116, 149, 187, 227, 270, 316, 363, 412, 461
}:

3小结

笔者有多年的单片机汇编语言编程经历,改用Keil C51后感觉很好,编程效率大为提高。本文是Keil C51在正弦波产生中的应用,由C源程序可见,程序较汇编语言程序可读性大为提高,非常简炼。本文介绍的倍频正弦波信号发生电路已用于某型电导率表中,效果很好。

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