找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1509|回复: 13
打印 上一主题 下一主题
收起左侧

为什么我的单片机程序输出波形的频率最大只能30HZ

[复制链接]
跳转到指定楼层
楼主
ID:494908 发表于 2022-4-14 13:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  1. #include<reg52.h>
  2. #include<intrins.h>
  3. #define uint unsigned int
  4. #define uchar unsigned char
  5. #define AddWr 0x90                           //PCF8591地址1001 0000


  6. sbit SCL=P3^6;      
  7. sbit SDA=P3^7;
  8. sbit RS=P2^0;
  9. sbit RW=P2^1;
  10. sbit E =P2^2;
  11. sbit sin=P1^0;
  12. sbit square=P1^1;
  13. sbit triangle=P1^2;
  14. sbit juchi=P1^3;
  15. sbit key1=P1^4;
  16. sbit key2=P1^5;
  17. sbit key3=P1^6;
  18. sbit key4=P1^7;
  19. bit ack;

  20. uchar pinlv,shi,ge;                 //频率变量//
  21. uchar change;                        //波形切换变量//
  22. uchar num;                                 //波形表输出个数变量//
  23. uint TH0_PL,TL0_PL;                   //重设初值//
  24. uint timer_value;                        //初值变量//
  25. uchar fd;
  26. uchar code table[]="0123456789";  

  27. void delay_ms(unsigned int n)  //12MHZ晶振时延时1ms,若用11.0592MHZ,则j=110
  28. {
  29.   unsigned int i=0,j=0;
  30.    for(i=n;i>0;i--)
  31.     for(j=0;j<123;j++);
  32. }

  33. void delay_8591()
  34. {
  35.    _nop_();   _nop_(); _nop_(); _nop_();           //起始条件建立时间大于4.7us,延时
  36. }         


  37. uchar code sin_table[]={0x7F,0x98,0xB0,0xC6,0xD9,0xE9,0xF5,0xFC,
  38.                                                 0xFE,0xFC,0xF5,0xE9,0xD9,0xC6,0xB0,0x98,
  39.                                                 0x7F,0x66,0x4E,0x38,0x25,0x15,0x09,0x02,
  40.                                                 0x00,0x02,0x09,0x15,0x25,0x38,0x4E,0x66};

  41. uchar code square_table[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  42.                                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  43.                                                    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  44.                                                    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

  45. uchar code triangle_table[]={0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,
  46.                                                         0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0,
  47.                                                         0xff,0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,
  48.                                                         0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10};

  49. uchar code juchi_table[]={0x00,0x07,0x10,0x17,0x20,0x27,0x30,0x37,
  50.                                                         0x40,0x47,0x50,0x57,0x60,0x67,0x70,0x77,
  51.                                                         0x80,0x87,0x90,0x97,0xa0,0xa7,0xb0,0xb7,
  52.                                                         0xc0,0xc7,0xd0,0xd7,0xe0,0xe7,0xf0,0x00};



  53. /*******************************************PCF8591DA转换模块**************************/////////////////////
  54. /****************************************************************************************///////////
  55.    void start()//开始信号  
  56. {  
  57.     SDA=1;            //确保SDA线为高电平,开始        之前都确保为高电平
  58.     delay_8591();         
  59.     SCL=1;                             //确保SCL为高电平
  60.      delay_8591();                    
  61.     SDA=0;                     //在SCL为高时拉低SDA线,即为起始信号
  62.     delay_8591();  
  63.         SCL=0;                                   //将SCL线拉低,为后面SDA的传数据时电平变化做准备
  64. }  

  65. void stop() //停止信号  
  66. {  
  67.     SDA=0;                    //确保SDA线为低电平
  68.      delay_8591();   
  69.     SCL=1;                          //确保SCL线为高电平
  70.      delay_8591();   
  71.     SDA=1;                      //在SCL为高时拉高SDA线,即为停止信号,此时SDA和SCL均为高,同时完成IIC初始化过程
  72.      delay_8591();  
  73. }  

  74. void ack_check(bit a)
  75. {
  76.         if(a==0)
  77.                   SDA=0;              //*在此发出应答或非应答信号 *//
  78.     else
  79.                 SDA=1;
  80.                 _nop_();   
  81.                 SCL=1;
  82.                 delay_8591();  
  83.                 SCL=0;
  84.                 _nop_();
  85.                 _nop_();                     //*清时钟线,钳住I2C总线以便继续接收*//
  86.                 SDA=1;        
  87. }
  88. void send_byte(uchar date)
  89. {
  90.         uchar i;
  91.         for(i=0;i<8;i++)  //*要传送的数据长度为8位*//
  92.     {
  93.      if((date<<i)&0x80)
  94.                     SDA=1;                                           //*判断发送位*//
  95.      else  
  96.                          SDA=0;               
  97.                         _nop_();
  98.                SCL=1;                               //*置时钟线为高,通知被控器开始接收数据位*/
  99.                delay_8591();         
  100.                SCL=0;
  101.             }
  102.                     SDA=1;                //*8位发送完后释放数据线,准备接收应答位*/
  103.                         _nop_();
  104.                     SCL=1;
  105.                     if(SDA==1)
  106.                                 ack=0;     
  107.                     else ack=1;        //*判断是否接收到应答信号*/
  108.                             SCL=0;
  109.                            
  110. }
  111. void write_add()
  112. {
  113.         start();
  114.         send_byte(AddWr);
  115.         if(ack==0)                                                //判断有无应答信号
  116.         {                                                                //有应答信号应该给低电平
  117.                  stop();
  118.                 return;
  119.         }
  120.          send_byte(0x40);                         //01000000 写入控制位,使能DA输出
  121. }




  122. /*********************************************************显示模块*///////////////////////////////////////////////
  123. /**********************************************************************************************//////////            
  124. void LCD12864_BusyCheck()        //读一下当前代码是否在忙,最高位为0为不忙,1为忙
  125.     {
  126.        unsigned char temp;
  127.        P0=0xff;        //释放整个口,全部为高电平
  128.        RS=0;         //RS为0是读状态 ,为1是写数据
  129.        RW=1;          //高读低写
  130.        delay_ms(10);
  131.      while(1)             //一直检测知道不忙为止
  132.       {
  133.           E=1;             //E是使能口,由1到0才可写入数据
  134.           temp=P0;
  135.           E=0;
  136.           if((temp&0x80)==0)  //如果等于零意味着最高位为0,不忙
  137.           break;          //结束(如果上面代码不为0,则循环一直检测
  138.      }
  139.    }

  140. void LCD12864_wcmd(unsigned char cmd)      //命令函数
  141. {
  142.     LCD12864_BusyCheck();
  143.     RS=0;           //高数低命
  144.     RW=0;
  145.     delay_ms(1);
  146.     P0=cmd;  //命令把想写的内容送到数据口
  147.     delay_ms(1);
  148.     E=1;
  149.     E=0;          //产生一个下降沿写数据
  150.    }

  151. void LCD12864_wdat(unsigned char dat)     //写数据函数
  152. {
  153.     LCD12864_BusyCheck();
  154.     RS=1;   //写数据
  155.     RW=0;
  156.     delay_ms(1);
  157.     P0=dat;                 //把想写的内容送到数据口
  158.     E=1;
  159.     delay_ms(1);
  160.     E=0;
  161. }

  162. void LCD12864_SetPos(unsigned char x,unsigned char y)     //x是行,y是列
  163. {
  164.    unsigned char pos;

  165.    switch(x)
  166.      {
  167.       case 0:x=0x80;break;     //第一行从0x80开始
  168.       case 1:x=0x90;break;
  169.       case 2:x=0x88;break;
  170.       case 3:x=0x98;break;
  171.       default:break;
  172.       }
  173.       pos=x+y;    //x范围:0-3,y范围:0-7 (因为一行只能写八个汉字)
  174.       LCD12864_wcmd(pos);
  175.       }

  176. void LCD12864_DisHZ_Str(unsigned char*s)
  177.     {
  178.      while(*s)
  179.      LCD12864_wdat(*s++);  //指针++,指针指向下一个
  180.      }

  181. void LCD12864_init()
  182.    {
  183.      LCD12864_wcmd(0x01);     //清屏指令
  184.      delay_ms(3);
  185.      LCD12864_wcmd(0x30);
  186.      delay_ms(5);
  187.      LCD12864_wcmd(0x30);
  188.      delay_ms(5);
  189.      LCD12864_wcmd(0x0c);
  190.      delay_ms(3);
  191.      LCD12864_wcmd(0x01);
  192.      delay_ms(3);
  193.      LCD12864_wcmd(0x80);
  194.      delay_ms(3);
  195.      LCD12864_wcmd(0x06);                          //游标右移,地址自动加一
  196.      delay_ms(3);
  197.     }



  198. //*******定时器程序*********//
  199. //*定时次数=(1/pinlv*32)/(1/12/12)=31250/pinlv*//  更改频率原理:单片机12MHZ晶振,所以产生1s的时钟,也就是1000000us,而输出波形表内有32个数据,
  200.                                                                                                 //           所以用1000000/(频率*32),得出数值作为定时器初值,每隔一个时间输出一个数值。
  201. void timer0_init()
  202. {
  203.         uchar a;                        //频率储存
  204.         shi=pinlv/10;
  205.         ge=pinlv%10;
  206.         a=shi*10+ge;
  207.         timer_value=31250/a;                //定时次数计算
  208.         timer_value=timer_value-28;           //误差补偿
  209.         TMOD=0x01;
  210.         TH0_PL=(65535-timer_value)/256;
  211.         TL0_PL=(65535-timer_value)%256;
  212.         TH0=TH0_PL;
  213.         TL0=TL0_PL;
  214.         TF0=0;
  215.         EA=1;
  216.         ET0=1;
  217.         TR0=1;
  218. }



  219. /***************************************按键模块********************************************/
  220. /******************************************************************************************/
  221. void key()
  222. {        
  223.                 if(key1==0)//按下键盘
  224.                         {        
  225.                                 delay_ms(10);        //消抖动
  226.                                 if(key1==0);//在确认一次
  227.                         {        pinlv++;//发光二极管点亮
  228.                                 if(pinlv==51)                   //*设置频率调节范围1~50
  229.                                 pinlv=1;
  230.                                 }
  231.                                 while(!key1)//松手
  232.                                 delay_ms(10);          ////消抖动
  233.                                 while(!key1);                }
  234.                 if(key2==0)//按下键盘
  235.                         {        
  236.                                 delay_ms(10);        //消抖动
  237.                                 if(key2==0);//在确认一次
  238.                         {
  239.                                 pinlv--;//发光二极管点亮
  240.                                 if(pinlv==1)                                  //*设置频率调节范围1~50
  241.                                 pinlv=50;
  242.                                 }
  243.                                 while(!key2)//松手
  244.                                 delay_ms(10);          ////消抖动
  245.                                 while(!key2);                }
  246.                 if(key3==0)//按下键盘
  247.                         {        
  248.                                 delay_ms(10);        //消抖动
  249.                                 if(key3==0);//在确认一次
  250.                         {
  251.                                 fd++;
  252.                                 if(fd==11)                                                //*设置幅度调节范围1~10
  253.                                 fd=1;
  254.                                 }
  255.                                 while(!key3)//松手
  256.                                 delay_ms(10);          ////消抖动
  257.                                 while(!key3);                }
  258.            if(key4==0)//按下键盘
  259.                         {        
  260.                                 delay_ms(10);        //消抖动
  261.                                 if(key4==0);//在确认一次
  262.                         {
  263.                             fd--;
  264.                                 if(fd==1)
  265.                                 fd=10;
  266.                                 }
  267.                                 while(!key4)//松手
  268.                                 delay_ms(10);          ////消抖动
  269.                                 while(!key4);                }
  270. }
  271. void wave_KEY()
  272. {
  273.         if(square==0)//按下键盘
  274.                         {        
  275.                                 delay_ms(10);        //消抖动
  276.                                 if(square==0);//在确认一次
  277.                         {        
  278.                                 change=2;        
  279.                                 }
  280.                                 while(square)//松手
  281.                                 delay_ms(10);          ////消抖动
  282.                                 while(square);                }
  283.         else if(sin==0)//按下键盘
  284.                         {        
  285.                                 delay_ms(10);        //消抖动
  286.                                 if(sin==0);//在确认一次
  287.                         {        
  288.                                 change=1;        
  289.                                 }
  290.                                 while(sin)//松手
  291.                                 delay_ms(10);          ////消抖动
  292.                                 while(sin);                }
  293.         else if(triangle==0)//按下键盘
  294.                         {        
  295.                                 delay_ms(10);        //消抖动
  296.                                 if(triangle==0);//在确认一次
  297.                         {        
  298.                                 change=3;        
  299.                                 }
  300.                                 while(triangle)//松手
  301.                                 delay_ms(10);          ////消抖动
  302.                                 while(triangle);                }
  303.         else if(juchi==0)//按下键盘
  304.                         {        
  305.                                 delay_ms(10);        //消抖动
  306.                                 if(juchi==0);//在确认一次
  307.                         {        
  308.                                 change=4;        
  309.                                 }
  310.                                 while(juchi)//松手
  311.                                 delay_ms(10);          ////消抖动
  312.                                 while(juchi);                }               
  313. }



  314. void display()                             //显示子程序
  315. {

  316.    LCD12864_init();
  317.    LCD12864_wcmd(0x0c);    //开显示
  318.    LCD12864_wcmd(0x30);

  319.       LCD12864_SetPos(0,0);                  //在第一行第一个汉字输出
  320.       delay_ms(1);
  321.       LCD12864_DisHZ_Str("  波形发生器");
  322.           LCD12864_SetPos(1,0);                  //第二行
  323.       delay_ms(1);
  324.       LCD12864_DisHZ_Str("波形:");
  325.                                             
  326.          LCD12864_SetPos(2,0);           //第三行         
  327.      delay_ms(1);
  328.      LCD12864_DisHZ_Str("频率:");
  329.                                     
  330.                                                             LCD12864_SetPos(2,7);                       
  331.                                 LCD12864_DisHZ_Str("HZ");
  332.          LCD12864_SetPos(3,0);                          //第四行
  333.      delay_ms(1);
  334.      LCD12864_DisHZ_Str("幅度:");
  335.                                                                
  336.                                      LCD12864_SetPos(3,7);                       
  337.                                 LCD12864_DisHZ_Str("V");
  338.                                                                 }

  339. void state_init()                        //*初始波形数据设置(正弦波,频率5hz,幅度5v*//
  340. {
  341.         pinlv=5;
  342.         change=1;
  343.         fd=10;
  344.         write_add();        
  345. }


  346. /****************************************主程序*********************************//////
  347. /************************************************************************///////////////
  348. void main()
  349. {        
  350.                 state_init();
  351.             P0=0xff;
  352.         display();           
  353.                    while(1)
  354.                 {
  355.                  if(change==1)                                                                                                   // 此处数据是随按键改变的数据程序,因此放入while循环中,随波形转换而改变显示
  356.                                                                                   {
  357.                                                                                                   LCD12864_SetPos(1,4);

  358.                                                    LCD12864_DisHZ_Str("sin");
  359.                                                                                                    }
  360.                                                                  if(change==2)  
  361.                                                    {
  362.                                                                                                    LCD12864_SetPos(1,4);

  363.                                                    LCD12864_DisHZ_Str("方波");
  364.                                                                                                    }
  365.                                                                  if(change==3)   {
  366.                                                                                    LCD12864_SetPos(1,4);

  367.                                                    LCD12864_DisHZ_Str("角波");
  368.                                                                                                    }
  369.                                                                  if(change==4)   {
  370.                                                                                     LCD12864_SetPos(1,4);

  371.                                                    LCD12864_DisHZ_Str("锯齿");        
  372.                                                                                                    }  
  373.                                                                                                    LCD12864_SetPos(2,4);
  374.                                                                  shi=pinlv/10;
  375.                                                                  ge=pinlv%10;
  376.                                                                  if(shi==0)
  377.                                                                  LCD12864_wdat(' ');
  378.                                                                  else
  379.                                                             LCD12864_wdat(table[shi]);
  380.                                                                  if(shi==0&&ge==0)
  381.                                                                  LCD12864_wdat(' ');
  382.                                             else
  383.                                                             LCD12864_wdat(table[ge]);
  384.                                                                  LCD12864_SetPos(3,4);
  385.                                                             LCD12864_wdat(table[fd*5/10]);                                               
  386.                                 LCD12864_DisHZ_Str(".");
  387.                                                                 LCD12864_wdat(table[fd*5%10]);
  388.                         wave_KEY();
  389.                          key();
  390.                         delay_ms(10);
  391.                         timer0_init();
  392.                         delay_ms(10);
  393.                         }
  394. }
  395. void timer0() interrupt 1        //改变频率
  396. {        
  397.         TH0=TH0_PL;
  398.         TL0=TL0_PL;
  399.         if(change==1)
  400.                 send_byte(sin_table[num]*0.1*fd);                         //单片机输出为5V,因此FD变化1~10,在乘以0.1后,可编程0~5的调节
  401.         else if(change==2)
  402.                 send_byte(square_table[num]*0.1*fd);                //假如不加幅度程序,则输出频率可灵活调制1~40hz!!,即去掉*0.1*fd
  403.         else if(change==3)
  404.                 send_byte(triangle_table[num]*0.1*fd);
  405.         else if(change==4)
  406.                 send_byte(juchi_table[num]*0.1*fd);           
  407.         if(num>=31)
  408.                 num=0;
  409.         else
  410.                 num++;

  411. }


复制代码
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:161164 发表于 2022-4-14 17:30 | 只看该作者
因为89C52太慢了
当50Hz时
定时器的更新时间是31250/50 = 625us
但计算  square_table[num]*0.1*fd  耗时394us
传送数据  send_byte(XXX)  又耗时331
加起来共耗时725us
已经超出定时器的溢出时间了
于是不断进出中断
回复

使用道具 举报

板凳
ID:494908 发表于 2022-4-14 17:44 | 只看该作者
lkc8210 发表于 2022-4-14 17:30
因为89C52太慢了
当50Hz时
定时器的更新时间是31250/50 = 625us

那这要怎么改呀,求大神帮帮忙
回复

使用道具 举报

地板
ID:494908 发表于 2022-4-14 18:45 | 只看该作者
lkc8210 发表于 2022-4-14 17:30
因为89C52太慢了
当50Hz时
定时器的更新时间是31250/50 = 625us

怎么省去square_table[num]*0.1*fd  这个时间
回复

使用道具 举报

5#
ID:161164 发表于 2022-4-14 22:56 | 只看该作者
米霁113 发表于 2022-4-14 17:44
那这要怎么改呀,求大神帮帮忙

换芯片吧~
回复

使用道具 举报

6#
ID:494908 发表于 2022-4-14 23:03 | 只看该作者

你说的这个耗时725us是怎么计算的??
回复

使用道具 举报

7#
ID:494908 发表于 2022-4-14 23:14 | 只看该作者

不换芯片的话,程序可以做改动吗?
回复

使用道具 举报

8#
ID:491577 发表于 2022-4-14 23:16 | 只看该作者
中断函数要尽量简单,复杂运算放在主函数中运行。晶振用30M的。最好换单片机,新一点的单片机都有定时器对外输出管脚,定时器可以直接对外输出方波,连中断都不需要。输出10MHz都可以。89C52只是学习用,做产品扔了吧。
回复

使用道具 举报

9#
ID:494908 发表于 2022-4-14 23:58 | 只看该作者
hhh402 发表于 2022-4-14 23:16
中断函数要尽量简单,复杂运算放在主函数中运行。晶振用30M的。最好换单片机,新一点的单片机都有定时器对 ...

那我这个square_table[num]*0.1*fd 运算改怎么在主函数改,求求大神帮帮忙!!做设计的,感谢!!
回复

使用道具 举报

10#
ID:123289 发表于 2022-4-15 11:29 | 只看该作者
波形极限周期:T=DAC转换时间 * 波形周期点数
点数32是死的,就看DA的时间了,需要查手册,看看DA的时间。
升频要点:
下达DA转换命令后,不要死等转换完成!!!,浪费时间,而是利用这段时间准备好下一个转换数据,这样DA完成后,下个数据就可以立即给到DA了。这样最快!

回复

使用道具 举报

11#
ID:625730 发表于 2022-4-15 11:48 | 只看该作者
米霁113 发表于 2022-4-14 23:58
那我这个square_table[num]*0.1*fd 运算改怎么在主函数改,求求大神帮帮忙!!做设计的,感谢!!

你最好把你的设计要达到的目标讲一讲,你放到主函数也未必能解决你的问题,设计上有空间的话,单独用一个单片机来输出波形,别让一个单片机干这么多事。
回复

使用道具 举报

12#
ID:494908 发表于 2022-4-16 18:12 | 只看该作者
yzwzfyz 发表于 2022-4-15 11:29
波形极限周期:T=DAC转换时间 * 波形周期点数
点数32是死的,就看DA的时间了,需要查手册,看看DA的时间 ...

这样的话程序应该怎么改呢?
回复

使用道具 举报

13#
ID:494908 发表于 2022-4-16 18:14 | 只看该作者
TEC 发表于 2022-4-15 11:48
你最好把你的设计要达到的目标讲一讲,你放到主函数也未必能解决你的问题,设计上有空间的话,单独用一个 ...

设计目的就是实现一个可以调节频率、幅度的波形发生器,使用PCF8591芯片,STC89C52,频率越高越好。我现在做的最高只有20HZ,单片机的话应该没有时间改了
回复

使用道具 举报

14#
ID:123289 发表于 2022-4-18 14:37 | 只看该作者
DA有无中断信号(查DA手册)
有:DA用中断做,下达开始转换后,立即退出。转去执行下个数据的输出准备工作(准备好输出数据)。
       中断响应后,立即将(准备好的输出数据)送DA,重复上述过程。
无:查手册给出DA转换时间TS,下达开始转换后,立即执行下个数据的输出准备工作(准备好输出数据)并统计准备工作用了多少时间,如果不足TS,用延时去补。达到TS时间时,立即立即将(准备好的输出数据)送DA,重复上述过程。
不懂汇编的人,做这种对时序要求严的程序,吃亏许多。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表