论坛风格切换切换到宽版
  • 13180阅读
  • 36回复

讨论一下DDS控制字的算法 [复制链接]

上一主题 下一主题
在线BG4UVR
 
发帖
11207
只看楼主 倒序阅读 0楼 发表于: 2006-10-22
经常做其他的单片机实验,还没做过dds的实验。最近打算写个程序试验一下。

看了n久datasheet和别人的例子,就开始写程序了。可到了控制字的计算,发现还真是麻烦。由于我是用c写程序,所以一开始定义了long型变量,让它算,几行代码就解决了。不过一想,单片机这样算也太慢了吧,要是一转编码器,频率变化几十次上百次,单片机还不累死了

于是想换个查表方式吧。搞了一上午,把算法搞好,表也算好做出来了,一编译,晕!长出了好几百字节(这其中包括表本身约200字节,再加上算法中多句的加法代码),看来2051里面别想放了

dds的控制字计算,大家有什么好的想法么?请各位指点
离线ding
发帖
3484
只看该作者 1楼 发表于: 2006-10-22
人呢.我这里有MZU译好的DDS9851的稿子!!!还有什么算法的.我看不懂
人呢.我这里有mzu译好的dds9851的稿子!!!还有什么算法的.我看不懂.
所以给你看看了.
在线BG4UVR
发帖
11207
只看该作者 2楼 发表于: 2006-10-22
没人感兴趣,没趣……
离线ahzds
发帖
7423
只看该作者 3楼 发表于: 2006-10-22
完全有讨论的必要,不同的算法和思路会产生大不一样的程序!程序代码历来追求短小精悍,高效无错,支持把这个问题多讨论讨论!建议大家把自己的程序都贴出来共同探讨!
离线BG7TBL
发帖
2963
只看该作者 4楼 发表于: 2006-10-22
我的程序供参考:
//******************计算fsw,结果在tmp2中
      tmp1=fre; //频率
     tmp2=0;
     for (unsigned char i=0; i<32; i++)
        {
          if((tmp1>180000000)|(tmp1==180000000))
          {
                tmp2++;
                tmp1=tmp1-180000000;
            }
            tmp1=tmp1<<1;
            tmp2=tmp2<<1;
  }
在线BG4UVR
发帖
11207
只看该作者 5楼 发表于: 2006-10-23
'
我的程序供参考:
//******************计算fsw,结果在tmp2中
      tmp1=fre; //频率
     tmp2=0;
     for (unsigned char i=0; i<32; i++)
        {
          if((tmp1>180000000)|(tmp1==180000000))
          {
                tmp2++;
                tmp1=tmp1-180000000;
            }
            tmp1=tmp1<<1;
            tmp2=tmp2<<1;
  }
'

不错!有启发!这个程序没有用直接除的方式,而是用了移位的方式。但循环要执行32次,所以速度上表现估计和用除法是相似的(没有实际测试过)。

看来,鱼和熊掌,不可兼得。要速度,代码量就大。要代码精简,速度就慢。这大概就是单片机的真谛吧

btw:
楼上这句:
          if((tmp1>180000000)|(tmp1==180000000))
不知为何不用
if(tmp1>=180000000)呢?
在线BG4UVR
发帖
11207
只看该作者 6楼 发表于: 2006-10-23
我的查表算法思路是这样的:
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个数量级。

查表部分具体程序(尚未进行硬件测试)

  1. /*******************************************
  2.     ad9850 dds控制子程序
  3. ********************************************
  4. 编写:bg4uvr
  5. 描述:   mcs51单片机控制ad9850/ad9851子程序。
  6.     提供如下5个常用dds控制函数。例子中
  7.     dds控制字表的dds型号为ad9850,晶振
  8.     为16mhz。
  9. 注意:   使用前请修改程序内相应硬件端口
  10. ********************************************/
  11. void dds_reset(void);     //dds主复位程序
  12. void dds_serialmode(void);   //dds串行方式设置
  13. void dds_set(void);         //dds设置数据发送
  14. void fre2word(void);     //dds控制字计算
  15. void freupdata(void);     //dds频率输出更新
  16. /*******************************************/
  17. //其他函数此处省略,只留下了表的结构和控制字计算部分……
  18. /********************************************/
  19. //控制字表,dds=9850,晶振=16mhz
  20.   //word=0x100000000*fre/16000000;
  21. /********************************************/
  22. unsigned char code dds_word_10mhz[2][4]={
  23.   0x00,0x00,0x00,0x00,
  24.   0x00,0x00,0x00,0xa0
  25. };
  26. unsigned char code dds_word_1mhz[10][4]={
  27.   0x00,0x00,0x00,0x00,
  28.   0x00,0x00,0x00,0x10,
  29.   0x00,0x00,0x00,0x20,
  30.   0x00,0x00,0x00,0x30,
  31.   0x00,0x00,0x00,0x40,
  32.   0x00,0x00,0x00,0x50,
  33.   0x00,0x00,0x00,0x60,
  34.   0x00,0x00,0x00,0x70,
  35.   0x00,0x00,0x00,0x80,
  36.   0x00,0x00,0x00,0x90
  37. };
  38. unsigned char code dds_word_100khz[10][4]={
  39.   0x00,0x00,0x00,0x00,         //0
  40.   0x99,0x99,0x99,0x01,     //1
  41.   0x33,0x33,0x33,0x03,     //2
  42.   0xcc,0xcc,0xcc,0x04,     //3
  43.   0x66,0x66,0x66,0x06,     //4
  44.   0x00,0x00,0x00,0x08,     //5
  45.   0x99,0x99,0x99,0x09,     //6
  46.   0x33,0x33,0x33,0x0b,     //7
  47.   0xcc,0xcc,0xcc,0x0c,     //8
  48.   0x66,0x66,0x66,0x0e         //9
  49. };
  50. unsigned char code dds_word_10khz[10][4]={
  51.   0x00,0x00,0x00,0x00,     //0
  52.   0xc2,0xf5,0x28,0x00,     //1
  53.   0x85,0xeb,0x51,0x00,     //2
  54.   0x47,0xe1,0x7a,0x00,     //3
  55.   0x0a,0xd7,0xa3,0x00,     //4
  56.   0xcc,0xcc,0xcc,0x00,     //5
  57.   0x8f,0xc2,0xf5,0x00,     //6
  58.   0x51,0xb8,0x1e,0x01,     //7
  59.   0x14,0xae,0x47,0x01,     //8
  60.   0xd7,0xa3,0x70,0x01,     //9
  61. };
  62. unsigned char code dds_word_1khz[10][3]={
  63.   0x00,0x00,0x00,     //0
  64.   0x93,0x18,0x04,     //1
  65.   0x26,0x31,0x08,     //2
  66.   0xba,0x49,0x0c,     //3
  67.   0x4d,0x62,0x10,     //4
  68.   0xe1,0x7a,0x14,     //5
  69.   0x74,0x93,0x18,     //6
  70.   0x08,0xac,0x1c,     //7
  71.   0x9b,0xc4,0x20,     //8
  72.   0x2f,0xdd,0x24,     //9
  73. };
  74. unsigned char code dds_word_100hz[10][3]={
  75.   0x00,0x00,0x00,     //0
  76.   0xdb,0x68,0x00,     //1
  77.   0xb7,0xd1,0x00,     //2
  78.   0x92,0x3a,0x01,     //3
  79.   0x6e,0xa3,0x01,     //4
  80.   0x49,0x0c,0x02,     //5
  81.   0x25,0x75,0x02,     //6
  82.   0x00,0xde,0x02,     //7
  83.   0xdc,0x46,0x03,     //8
  84.   0xb7,0xaf,0x03     //9
  85. };
  86. unsigned char code dds_word_10hz[10][2]={
  87.   0x00,0x00,     //0
  88.   0x7c,0x0a,     //1
  89.   0x14,0xf8,     //2
  90.   0x75,0x1f,     //3
  91.   0xf1,0x29,     //4
  92.   0x6d,0x34,     //5
  93.   0xea,0x3e,     //6
  94.   0x66,0x49,     //7
  95.   0xe2,0x53,     //8
  96.   0x5f,0xfe     //9
  97. };
  98. unsigned char code dds_word_1hz[10][2]={
  99.   0x00,0x00,     //0
  100.   0x0c,0x01,     //1
  101.   0x18,0x02,     //2
  102.   0x25,0x03,     //3
  103.   0x31,0x04,     //4
  104.   0x3e,0x05,     //5
  105.   0x4a,0x06,     //6
  106.   0x57,0x07,     //7
  107.   0x63,0x08,     //8
  108.   0x6f,0x09     //9
  109. };
  110. /********************************************/
  111. //控制字计算
  112. void fre2word(void){
  113.   unsigned int temp1,temp2;
  114.   //计算w0的值。注意,本例dds工作在串行方式,这里w0是指控制字低8位,请区别于并行方式
  115.   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];
  116.   w[0]=(unsigned char)temp1;
  117.   //计算w0相加时的进位
  118.   temp2=(unsigned char)(temp1>>8);
  119.   //计算w1值
  120.   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;
  121.   w[1]=(unsigned char)temp1;
  122.   temp2=(unsigned char)(temp1>>8);
  123.   //计算w2值
  124.   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;
  125.   w[2]=(unsigned char)temp1;
  126.   temp2=(unsigned char)(temp1>>8);
  127.   //计算w3值
  128.   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;
  129.   w[3]=(unsigned char)temp1;
  130. }
在线BG4UVR
发帖
11207
只看该作者 7楼 发表于: 2006-10-23
呵呵,今天改进了频率显示时的算法,只改了一句,目标代码少了143个字节。晕!看来还是要加强学习啊……
离线bg4iww
发帖
8529
只看该作者 8楼 发表于: 2006-10-23
用查表的方法程序很容易写的,并且查询速度很快,代码长度也很短,大概也就是三十行左右吧(我用的汇编)。就是表格的体积有些大,不过2051应该是绰绰有余的,如果另加了很多功能的话就另当别论了
离线BG7TBL
发帖
2963
只看该作者 9楼 发表于: 2006-10-23
'

btw:
楼上这句:
          if((tmp1>180000000)|(tmp1==180000000))
不知为何不用
if(tmp1>=180000000)呢?
'

因为我写程序时候记不清楚究竟是>=还是=>了,写反了编译也能通过,
怕错,就那样写了。
我最讨厌那种 --++a++--,的写法,害人半天都看不懂!

要好好学习楼主的查表法才行,速度快啊!
离线awakening
发帖
795
只看该作者 10楼 发表于: 2006-10-23
恩,搂主的方法不错.不过运算量貌似也不小.为了看着简化点我还是用了直接除法.用的是avr的片子,自带乘法器,速度也快.
离线BG7TBL
发帖
2963
只看该作者 11楼 发表于: 2006-10-23
'
恩,搂主的方法不错.不过运算量貌似也不小.为了看着简化点我还是用了直接除法.用的是avr的片子,自带乘法器,速度也快.
'

我也是用avr,程序经过测试,ok的!
离线ncradio
发帖
11344
只看该作者 12楼 发表于: 2006-10-23
查什么表啊,c51直接计算就行了
很快的放心好了
离线ba1ka
发帖
1044
只看该作者 13楼 发表于: 2006-10-24
楼主本来使用c51 直接算就行了,何必舍近求远。到是n年前用汇编嫌乘除麻烦,用过查表法,程序虽简单了些但误差较大得不偿失,后还是改回直接计算,在乘除算法上下些工夫,其实也不麻烦。包括lcd驱动,码盘处理,dds控制字等全加起来在2k之内,完全可以在89c2051里跑。
在线BG4UVR
发帖
11207
只看该作者 14楼 发表于: 2006-10-24
'
楼主本来使用c51 直接算就行了,何必舍近求远。到是n年前用汇编嫌乘除麻烦,用过查表法,程序虽简单了些但误差较大得不偿失,后还是改回直接计算,在乘除算法上下些工夫,其实也不麻烦。包括lcd驱动,码盘处理,dds控制字等全加起来在2k之内,完全可以在89c2051里跑。
'

学习了
离线BA5SBA
发帖
3088
只看该作者 15楼 发表于: 2006-10-24
需要向各位学习!!!!
离线wycx
发帖
1497
只看该作者 16楼 发表于: 2006-10-24
用户被禁言,该主题自动屏蔽!
在线BG4UVR
发帖
11207
只看该作者 17楼 发表于: 2006-10-24
'
http://www.hellocq.net/forum/showthread.php?t=124374&12m时钟882us计算dds控制字源程序
12m时钟882us计算dds控制字源程序
这个速度够用么????\
我试过..时间好像与频率的数值无关...
'

您这段代码:
1、写法漂亮
2、方法简单
3、速度更快
4、代码较少
5、误差更大(控制字最大误差为9×8=72)

btw:这段代码真是漂亮,长见识啊…… 喜欢代码的人,看到漂亮的代码,就像色狼看见plmm一样 单看这段代码,也不惘我抛砖开了本帖 玉来了,赚了

  1. //每位的控制字---111.518000mhz dds时钟
  2. #define ff7 385136686
  3. #define ff6 38513669
  4. #define ff5 3851367
  5. #define ff4 385137
  6. #define ff3 38514
  7. #define ff2 3851
  8. #define ff1 385
  9. #define ff0 39
  10. /***********************************************************/
  11. /***计算控制字************/
  12. /***入口:频率数组指针***出口,控制字*****/
  13. unsigned long jisuan(unsigned char data *fno)
  14. {
  15. unsigned long dds_no ;
  16. dds_no=
  17. (*(fno+7))*ff7+
  18. (*(fno+6))*ff6+
  19. (*(fno+5))*ff5+
  20. (*(fno+4))*ff4+
  21. (*(fno+3))*ff3+
  22. (*(fno+2))*ff2+
  23. (*(fno+1))*ff1+
  24. (*fno)*ff0 ;
  25. return(dds_no);
  26. }
离线wycx
发帖
1497
只看该作者 18楼 发表于: 2006-10-24
用户被禁言,该主题自动屏蔽!
在线BG4UVR
发帖
11207
只看该作者 19楼 发表于: 2006-10-24
'
我早就发了,,可就没人看......................
不过,,误差没你说的那么大....我实验的结果是误差小于1hz...真的...不信你试试
'

恩,我说的那个72,也是最坏的情况下的情况,一般不会碰得到的。就算碰上了,最多估计也只差1、2hz。所以你这个程序,实用性还是相当的不错的