搜索
bottom↓
回复: 16

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

[复制链接]

出5入4汤圆

发表于 2014-5-17 23:33:15 | 显示全部楼层 |阅读模式
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. }
复制代码

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2014-5-18 00:19:32 | 显示全部楼层
你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时器中断了。

出0入0汤圆

发表于 2014-5-18 00:33:26 | 显示全部楼层
你的程序是2.5ms的高电平,17.5ms的低电平?周期50hz?

出5入4汤圆

 楼主| 发表于 2014-5-18 00:48:08 | 显示全部楼层
qqliyunpeng 发表于 2014-5-18 00:33
你的程序是2.5ms的高电平,17.5ms的低电平?周期50hz?

对啊,周期就是17.5+2.5=20ms,频率就是50Hz了啊,光是这样的都没法达到预期...

出5入4汤圆

 楼主| 发表于 2014-5-18 00:48:50 | 显示全部楼层
hithms 发表于 2014-5-18 00:19
你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时 ...

嗯嗯,这个得明天再去实验室看看了。光从程序上您能看出明显的问题吗?

出5入4汤圆

 楼主| 发表于 2014-5-18 15:20:48 | 显示全部楼层
hithms 发表于 2014-5-18 00:19
你可以用示波器看看定时器的中断周期是不是和你所设置的一样,假如没问题可以看看串口中断有没有干扰到定时 ...

问题已找到,我的TMOD在初始化串口定时器1的时候用了“=”而不是“|=“,结果可想而知。但是新的问题出现,我现在的频率是40Hz左右...用        TH0=(65535-100)/256;TL0=(65535-100)%256; 误差如此之大?

出5入4汤圆

 楼主| 发表于 2014-5-18 15:25:17 | 显示全部楼层
万分惊奇,程序中使用
  1.         TH0=(65535-78)/256;             
  2.         TL0=(65535-78)%256;
复制代码

并且循环200次,居然是20ms,和理论差太多了,求大神降临啊

出5入4汤圆

 楼主| 发表于 2014-5-18 15:25:41 | 显示全部楼层
万分惊奇,程序中使用
  1.         TH0=(65535-78)/256;             
  2.         TL0=(65535-78)%256;
复制代码

并且循环200次,居然是20ms,和理论差太多了,求大神降临啊

出0入0汤圆

发表于 2014-5-19 01:05:19 | 显示全部楼层
tim4146 发表于 2014-5-18 15:25
万分惊奇,程序中使用
并且循环200次,居然是20ms,和理论差太多了,求大神降临啊 ...

是65535还是65536?

出5入4汤圆

 楼主| 发表于 2014-5-19 12:44:19 | 显示全部楼层

都是(65535-X)/256这种计算啊,即使相差1 也不至于100设定成78才达到效果吧...

出0入93汤圆

发表于 2014-5-19 13:22:25 | 显示全部楼层
本帖最后由 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. }
复制代码

出0入0汤圆

发表于 2014-5-19 14:38:19 | 显示全部楼层
不是自装载的,在定时器里更新定时器计数值,要考虑指令消耗

出0入0汤圆

发表于 2014-5-19 14:58:54 | 显示全部楼层
换 stc12c 有记录硬件pwm输出,这样方便点

出0入0汤圆

发表于 2014-5-24 20:54:03 | 显示全部楼层
不知道 STC PWM 死区怎么设定?

出5入4汤圆

 楼主| 发表于 2014-5-25 23:10:48 | 显示全部楼层
wkman 发表于 2014-5-19 14:58
换 stc12c 有记录硬件pwm输出,这样方便点

够用的情况下我一直用STC89C52的,比较简单,后来试着用了STM32. 现在的STC12C价格怎么样?

出0入0汤圆

发表于 2014-5-25 23:44:03 | 显示全部楼层
You are very challenged.


  1.         TH0 = -100 >> 8;//TH0=(65535-100)/256;//0XFF              
  2.         TL0 = -100;        //TL0=(65535-100)%256;//0X9C
复制代码


  1. void Timer0_isr(void) interrupt 1
  2. {
  3.         TH0=(65535-100)/256;              
  4.         //TL0=(65535-100)%256;
  5.         TL0 += -100;
复制代码


  1. void Uart() interrupt 4                //串口中断响应程序
  2. {
  3.         ES=0;//关闭串口中断
复制代码


Speechless.

Just think about it.

出5入4汤圆

 楼主| 发表于 2014-5-27 18:57:58 | 显示全部楼层
millwood0 发表于 2014-5-25 23:44
You are very challenged.

It seems to be a good idea,use" TL0 += -100;" to offset the time spent before.
Thank u ,u r so helpful~
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 12:32

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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