|
原创发表于hellocq.net论坛,转帖到此,有改动。 ---------------------------------------------------------------
经常做其他的单片机实验,还没做过DDS的实验。最近打算写个程序试验一下。
看了N久datasheet和别人的例子,就开始写程序了。可到了控制字的计算,发现还真是麻烦。由于我是用C写程序,所以一开始定义了long型变量,让它算,几行代码就解决了。不过一想,单片机这样算也太慢了吧,要是一转编码器,频率变化几十次上百次,单片机还不累死了
于是想换个查表方式吧。搞了一上午,把算法搞好,表也算好做出来了,一编译,晕!长出了好几百字节(这其中包括表本身约200字节,再加上算法中多句的加法代码),看来2051里面别想放了。
看来,鱼和熊掌,不可兼得。要速度,代码量就大。要代码精简,速度就慢。这大概就是单片机的真谛吧。
我的查表算法思路是这样的 : 1、构造N个二维数组(N=你需要输入的最大频率值位数,例如你需要精确到10HZ,最高30MHZ,那么就有10M位、1M位、100K位、10K位、1K位、100HZ位、10HZ位,共7位,所以N=7)。 2、根据你所用的芯片型号,和晶振频率,计算出每个频率位0-9时的控制字。 3、使用时,把你频率的每一位控制字,查表读出,并相加(特别需要注意进位也需要处理)。 4、把加出的4字节控制字,送DDS。
此方法,理论最大控制字误差为N。一般9851或9850DDS,常用的晶振频率值条件下,此误差实际表现在频率上时,小于1HZ的1到2个数量级。
查表部分具体程序(尚未进行硬件测试)
/******************************************* AD9850 DDS控制子程序 ******************************************** 编写:BG4UVR 描述: MCS51单片机控制AD9850/AD9851子程序。 提供如下5个常用DDS控制函数。例子中 DDS控制字表的DDS型号为AD9850,晶振 为16MHZ。 注意: 使用前请修改程序内相应硬件端口 ********************************************/ void dds_reset(void); //DDS主复位程序 void dds_serialmode(void); //DDS串行方式设置 void dds_set(void); //DDS设置数据发送 void fre2word(void); //DDS控制字计算 void freupdata(void); //DDS频率输出更新 /*******************************************/
//其他函数此处省略,只留下了表的结构和控制字计算部分……
/********************************************/ //控制字表,DDS=9850,晶振=16MHZ //word=0x100000000*fre/16000000; /********************************************/ unsigned char code dds_word_10mhz[2][4]={ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xa0 };
unsigned char code dds_word_1mhz[10][4]={ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x10, 0x00,0x00,0x00,0x20, 0x00,0x00,0x00,0x30, 0x00,0x00,0x00,0x40, 0x00,0x00,0x00,0x50, 0x00,0x00,0x00,0x60, 0x00,0x00,0x00,0x70, 0x00,0x00,0x00,0x80, 0x00,0x00,0x00,0x90 }; unsigned char code dds_word_100khz[10][4]={ 0x00,0x00,0x00,0x00, //0 0x99,0x99,0x99,0x01, //1 0x33,0x33,0x33,0x03, //2 0xcc,0xcc,0xcc,0x04, //3 0x66,0x66,0x66,0x06, //4 0x00,0x00,0x00,0x08, //5 0x99,0x99,0x99,0x09, //6 0x33,0x33,0x33,0x0b, //7 0xcc,0xcc,0xcc,0x0c, //8 0x66,0x66,0x66,0x0e //9 }; unsigned char code dds_word_10khz[10][4]={ 0x00,0x00,0x00,0x00, //0 0xc2,0xf5,0x28,0x00, //1 0x85,0xeb,0x51,0x00, //2 0x47,0xe1,0x7a,0x00, //3 0x0a,0xd7,0xa3,0x00, //4 0xcc,0xcc,0xcc,0x00, //5 0x8f,0xc2,0xf5,0x00, //6 0x51,0xb8,0x1e,0x01, //7 0x14,0xae,0x47,0x01, //8 0xd7,0xa3,0x70,0x01, //9 }; unsigned char code dds_word_1khz[10][3]={ 0x00,0x00,0x00, //0 0x93,0x18,0x04, //1 0x26,0x31,0x08, //2 0xba,0x49,0x0c, //3 0x4d,0x62,0x10, //4 0xe1,0x7a,0x14, //5 0x74,0x93,0x18, //6 0x08,0xac,0x1c, //7 0x9b,0xc4,0x20, //8 0x2f,0xdd,0x24, //9 }; unsigned char code dds_word_100hz[10][3]={ 0x00,0x00,0x00, //0 0xdb,0x68,0x00, //1 0xb7,0xd1,0x00, //2 0x92,0x3a,0x01, //3 0x6e,0xa3,0x01, //4 0x49,0x0c,0x02, //5 0x25,0x75,0x02, //6 0x00,0xde,0x02, //7 0xdc,0x46,0x03, //8 0xb7,0xaf,0x03 //9 }; unsigned char code dds_word_10hz[10][2]={ 0x00,0x00, //0 0x7c,0x0a, //1 0x14,0xf8, //2 0x75,0x1f, //3 0xf1,0x29, //4 0x6d,0x34, //5 0xea,0x3e, //6 0x66,0x49, //7 0xe2,0x53, //8 0x5f,0xfe //9 }; unsigned char code dds_word_1hz[10][2]={ 0x00,0x00, //0 0x0c,0x01, //1 0x18,0x02, //2 0x25,0x03, //3 0x31,0x04, //4 0x3e,0x05, //5 0x4a,0x06, //6 0x57,0x07, //7 0x63,0x08, //8 0x6f,0x09 //9 };
/********************************************/
//控制字计算 void fre2word(void){ unsigned int temp1,temp2; //计算W0的值。注意,本例DDS工作在串行方式,这里W0是指控制字低8位,请区别于并行方式 temp1=dds_word_1hz[fre[7]][0]+dds_word_10hz[fre[6]][0]+dds_word_100hz[fre[5]][0]+dds_word_1khz[fre[4]][0]+dds_word_10khz[fre[3]][0]+dds_word_100khz[fre[2]][0]+dds_word_1mhz[fre[1]][0]+dds_word_10mhz[fre[0]][0]; W[0]=(unsigned char)temp1; //计算W0相加时的进位 temp2=(unsigned char)(temp1>>8);
//计算W1值 temp1=dds_word_1hz[fre[7]][1]+dds_word_10hz[fre[6]][1]+dds_word_100hz[fre[5]][1]+dds_word_1khz[fre[4]][1]+dds_word_10khz[fre[3]][1]+dds_word_100khz[fre[2]][1]+dds_word_1mhz[fre[1]][1]+dds_word_10mhz[fre[0]][1]+temp2; W[1]=(unsigned char)temp1; temp2=(unsigned char)(temp1>>8);
//计算W2值 temp1=dds_word_100hz[fre[5]][2]+dds_word_1khz[fre[4]][2]+dds_word_10khz[fre[3]][2]+dds_word_100khz[fre[2]][2]+dds_word_1mhz[fre[1]][2]+dds_word_10mhz[fre[0]][2]+temp2; W[2]=(unsigned char)temp1; temp2=(unsigned char)(temp1>>8);
//计算W3值 temp1=dds_word_10khz[fre[3]][3]+dds_word_100khz[fre[2]][3]+dds_word_1mhz[fre[1]][3]+dds_word_10mhz[fre[0]][3]+temp2; W[3]=(unsigned char)temp1; } |
|