51单片机IO口模拟PWM,问题看了一晚上找不到原因

2020-01-23 14:40发布

PWM周期:20ms,也就是50Hz频率
由于用于控制舵机,高电平是0.5ms~2.5ms,所以打算把20ms分为200份,没份时间就是20ms/200=0.1ms,这样5份高电平就代表0.5ms了
单片机是常用的STC89C52RC,烧录程序后从示波器看波形却傻眼了....波形上面频率是170Hz左右,看了一晚上的程序居然没看出问题。所谓旁观者清,各位来指点指点吧。

程序如下,一看就懂
  1. /*********************************************************************************
  2. ====舵机驱动程序=========
  3. 编写:小刘
  4. 晶振:12MHz
  5. 舵机型号:MG996
  6. 舵机周期驱动信号周期:20ms
  7. 电压:4.8-7.2V
  8. **********************************************************************************/

  9. #include<reg52.h> //包含特殊功能寄存器的定义
  10. sbit p11=P1^1;//输出pwm脉冲

  11. #define uchar unsigned char
  12. #define uint unsigned int
  13. /* 函数&变量申明 -----------------------------------------------*/
  14. void Init_Timer0(void);        //定时器定时20ms,分为200份,每份时间0.1ms=100us
  15. void Init_Uart() ;
  16. uchar receive;
  17. uchar pwm=25;//阀值,25就是2.5ms 的高电平时间
  18. uchar period=200;//舵机信号周期20ms,分成200份
  19. uchar i=0;//用于计数
  20. bit temp=0;
  21. /*
  22. ********************************************************************************
  23. ** 函数名称 : main(void)
  24. ** 函数功能 : 主函数
  25. ********************************************************************************
  26. */
  27. main()
  28. {
  29.         Init_Timer0();
  30.     Init_Uart() ;
  31.         while(1);
  32. }


  33. /*
  34. ********************************************************************************
  35. ** 函数名称 : Init_Timer0(void)
  36. ** 函数功能 : 定时器初始化子程序
  37. ********************************************************************************
  38. */
  39. void Init_Timer0(void)                  //定时器初始化子程序
  40. {
  41.         TMOD |= 0x01;          //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响                     
  42.         //给定初值,每份定时时间0.1ms,一个周期200份=20ms
  43.         TH0=(65535-100)/256;//0XFF             
  44.         TL0=(65535-100)%256;//0X9C
  45.         EA=1;            //总中断打开
  46.         ET0=1;           //定时器中断打开
  47.         TR0=1;           //定时器开关打开
  48. }
  49. /*
  50. ********************************************************************************
  51. ** 函数名称 : void Init_Uart()
  52. ** 函数功能 : 串口初始化子程序
  53. ********************************************************************************
  54. */
  55. void Init_Uart()
  56. {
  57.           TMOD=0x20;                   //用定时器设置串口波特率
  58.         TH1=0xfd;
  59.         TL1=0xfd;
  60.         TR1=1;
  61.         REN=1;          //串口初始化
  62.         SM0=0;
  63.         SM1=1;
  64.         EA=1;           //开启总中断
  65.         ES=1; //开串口中断
  66. }
  67. /*
  68. ********************************************************************************
  69. ** 函数名称 : Timer0_isr(void) interrupt 1 using 1
  70. ** 函数功能 : 定时器中断程序
  71. ********************************************************************************
  72. */
  73. void Timer0_isr(void) interrupt 1
  74. {
  75.         TH0=(65535-100)/256;             
  76.         TL0=(65535-100)%256;
  77.         i++;
  78.         if (i>=pwm)p11=0; //如果超过了阀值就是电平

  79.         if (i==200){i=0;p11=1;}        //0.1ms*200=20ms 周期时间到,默认给输出高电平


  80. }
  81. /*
  82. ********************************************************************************
  83. ** 函数名称 : void Uart()
  84. ** 函数功能 : 串口接收中断程序
  85. ********************************************************************************
  86. */
  87. void Uart() interrupt 4                //串口中断响应程序
  88. {
  89.         ES=0;//关闭串口中断
  90.         if(RI)
  91.         {
  92.         RI=0;//清除接收中断标志位
  93.         receive=SBUF;//读取SBUF
  94.         }
  95.         ES=1;//打开串口中断

  96. }
复制代码
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
17条回答
takashiki
2020-01-25 01:47
本帖最后由 takashiki 于 2014-5-19 13:28 编辑
tim4146 发表于 2014-5-18 15:25
万分惊奇,程序中使用
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 ...
必须的,你要知道,51很慢,尤其对于你这个应用来说,100个周期就要计时一次,进出中断的的时间无可忽略,而且赋值也是需要时间的,你需要进行补偿。
最精确的解决方案:将T0或T1工作于8位自动重载模式,中断中无需重新赋值,精确多了。

补充一下说明:
另外,你的UART中断写得过于复杂,浪费了很多CPU,这样就行了,嘿嘿:
  1. void Uart() interrupt 4 {        //串口中断响应程序
  2.     if(_testbit_(RI))            //清除接收中断标志位
  3.         receive=SBUF;            //读取SBUF
  4. }
复制代码

一周热门 更多>