找回密码
 立即注册
查看: 14514|回复: 51

驱动舵机, STC8H8K64U高级PWM, STC驱动教程系列

[复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:135
  • 最近打卡:2024-12-25 10:15:05

47

主题

1350

回帖

8058

积分

荣誉版主

冲哥视频教程和各种开源资料QQ交流群884047237,可群

积分
8058
QQ
发表于 2023-2-3 11:27:58 | 显示全部楼层 |阅读模式
驱动舵机:  STC驱动教程系列——STC8H8K64U高级PWM驱动舵机
一、前言
  看到最近有朋友在群里问下图的这种舵机(SG90)怎么驱动,有小伙伴直接毫不犹豫脱口而出了一句定时器,当然早期的MCU用这个当然没问题,但是新款的MCU还有这个就显得有那么点过时了,这里我们分享一种新的思路——PWM驱动(当然驱动方式包括但不限于PWM,这里只提供一种比较常见的解决方案)。
舵机.png





二、舵机驱动原理及使用PWM驱动的意义

  众所周知,舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。在高档遥控玩具,如飞机、潜艇模型,遥控机器人中已经得到了普遍应用。他可以根据输入脉冲信号的高电平时间直接输出对应的角度,高电平时间和角度的关系如下图所示:

驱动时序.png

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-1.png


  可以看到上图中每一个脉冲的周期是20ms(相邻两个上升沿信号之间的时间差为20ms),即频率为50HZ,其次脉冲宽度为0.5ms舵机处于0度位置,脉冲宽度为2.5ms,舵机处于180度位置,脉冲宽度为1.5ms舵机处于90度位置,中间的角度就可以按照这个关系去映射咯。



  当然上述的波形直接用软件的延时也是可以时间的对吧,例如我要转到0度,我也可以这样弄。现在ISP软件的软件延时计算器里生成如下两个延时:

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-2.png

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-3.png

  然后假设使用P60端口那么将端口初始化为准双向口或者推挽输出口,随后写如下代码就可以将舵机保持在0度了对不。

  1. P60=1;
  2. Delay500us();
  3. P60=0;
  4. Delay500us();
  5. Delay19ms();
复制代码
  当然,用延时也能输出的波形,定时器当然也可以,但是用延时太浪费MCU的性能,尤其是多路舵机同时以不同的角度控制的话,简直生不如dead;这里强烈不建议,定时器的话虽然能解决CPU占用的问题,但是他会频繁的进入中断,对于我们做产品的人来说,如果能有最优解那么定时器的方法并不可取。这里直接使用硬件的PWM,我们只要配置好周期,往比较寄存器写入比较值就能直接输出PWM了,不需要进入中断,更不需要延时,只要MCU不断电,这个PWM能跑到地老天荒,毕竟这是硬件实现的,稳定性更高,精度更高。



三、代码的实现
  这里直接贴上我们的测试代码再来分段讲解:
  1. #include    "reg51.h"       //包含此头文件后,里面声明的寄存器不需要再手动输入,避免重复定义
  2. #include    "intrins.h"
  3. #define     MAIN_Fosc       24000000L   //定义主时钟
  4. typedef     unsigned char   u8;
  5. typedef     unsigned int    u16;
  6. typedef     unsigned long   u32;
  7. //手动输入声明"reg51.h"头文件里面没有定义的寄存器
  8. sfr TH2  = 0xD6;
  9. sfr TL2  = 0xD7;
  10. sfr IE2   = 0xAF;
  11. sfr INT_CLKO = 0x8F;
  12. sfr AUXR = 0x8E;
  13. sfr P_SW1 = 0xA2;
  14. sfr P_SW2 = 0xBA;
  15. sfr P4   = 0xC0;
  16. sfr P5   = 0xC8;
  17. sfr P6   = 0xE8;
  18. sfr P7   = 0xF8;
  19. sfr P1M1 = 0x91;    //PxM1.n,PxM0.n     =00--->Standard,    01--->push-pull
  20. sfr P1M0 = 0x92;    //                  =10--->pure input,  11--->open drain
  21. sfr P0M1 = 0x93;
  22. sfr P0M0 = 0x94;
  23. sfr P2M1 = 0x95;
  24. sfr P2M0 = 0x96;
  25. sfr P3M1 = 0xB1;
  26. sfr P3M0 = 0xB2;
  27. sfr P4M1 = 0xB3;
  28. sfr P4M0 = 0xB4;
  29. sfr P5M1 = 0xC9;
  30. sfr P5M0 = 0xCA;
  31. sfr P6M1 = 0xCB;
  32. sfr P6M0 = 0xCC;
  33. sfr P7M1 = 0xE1;
  34. sfr P7M0 = 0xE2;
  35. sbit P00 = P0^0;
  36. sbit P01 = P0^1;
  37. sbit P02 = P0^2;
  38. sbit P03 = P0^3;
  39. sbit P04 = P0^4;
  40. sbit P05 = P0^5;
  41. sbit P06 = P0^6;
  42. sbit P07 = P0^7;
  43. sbit P10 = P1^0;
  44. sbit P11 = P1^1;
  45. sbit P12 = P1^2;
  46. sbit P13 = P1^3;
  47. sbit P14 = P1^4;
  48. sbit P15 = P1^5;
  49. sbit P16 = P1^6;
  50. sbit P17 = P1^7;
  51. sbit P20 = P2^0;
  52. sbit P21 = P2^1;
  53. sbit P22 = P2^2;
  54. sbit P23 = P2^3;
  55. sbit P24 = P2^4;
  56. sbit P25 = P2^5;
  57. sbit P26 = P2^6;
  58. sbit P27 = P2^7;
  59. sbit P30 = P3^0;
  60. sbit P31 = P3^1;
  61. sbit P32 = P3^2;
  62. sbit P33 = P3^3;
  63. sbit P34 = P3^4;
  64. sbit P35 = P3^5;
  65. sbit P36 = P3^6;
  66. sbit P37 = P3^7;
  67. sbit P40 = P4^0;
  68. sbit P41 = P4^1;
  69. sbit P42 = P4^2;
  70. sbit P43 = P4^3;
  71. sbit P44 = P4^4;
  72. sbit P45 = P4^5;
  73. sbit P46 = P4^6;
  74. sbit P47 = P4^7;
  75. sbit P50 = P5^0;
  76. sbit P51 = P5^1;
  77. sbit P52 = P5^2;
  78. sbit P53 = P5^3;
  79. sbit P54 = P5^4;
  80. sbit P55 = P5^5;
  81. sbit P56 = P5^6;
  82. sbit P57 = P5^7;
  83. /****************************** 用户定义宏 ***********************************/
  84. #define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))       //Timer 0 中断频率, 1000次/秒
  85. #define PWMA_ENO     (*(unsigned char  volatile xdata *)  0xFEB1)
  86. #define PWMA_PS      (*(unsigned char  volatile xdata *)  0xFEB2)
  87. #define PWMB_ENO     (*(unsigned char  volatile xdata *)  0xFEB5)
  88. #define PWMB_PS      (*(unsigned char  volatile xdata *)  0xFEB6)                              
  89. #define PWMA_CR1     (*(unsigned char  volatile xdata *)  0xFEC0)
  90. #define PWMA_CR2     (*(unsigned char  volatile xdata *)  0xFEC1)
  91. #define PWMA_SMCR    (*(unsigned char  volatile xdata *)  0xFEC2)
  92. #define PWMA_ETR     (*(unsigned char  volatile xdata *)  0xFEC3)
  93. #define PWMA_IER     (*(unsigned char  volatile xdata *)  0xFEC4)
  94. #define PWMA_SR1     (*(unsigned char  volatile xdata *)  0xFEC5)
  95. #define PWMA_SR2     (*(unsigned char  volatile xdata *)  0xFEC6)
  96. #define PWMA_EGR     (*(unsigned char  volatile xdata *)  0xFEC7)
  97. #define PWMA_CCMR1   (*(unsigned char  volatile xdata *)  0xFEC8)
  98. #define PWMA_CCMR2   (*(unsigned char  volatile xdata *)  0xFEC9)
  99. #define PWMA_CCMR3   (*(unsigned char  volatile xdata *)  0xFECA)
  100. #define PWMA_CCMR4   (*(unsigned char  volatile xdata *)  0xFECB)
  101. #define PWMA_CCER1   (*(unsigned char  volatile xdata *)  0xFECC)
  102. #define PWMA_CCER2   (*(unsigned char  volatile xdata *)  0xFECD)
  103. #define PWMA_CNTRH   (*(unsigned char  volatile xdata *)  0xFECE)
  104. #define PWMA_CNTRL   (*(unsigned char  volatile xdata *)  0xFECF)
  105. #define PWMA_PSCRH   (*(unsigned char  volatile xdata *)  0xFED0)
  106. #define PWMA_PSCRL   (*(unsigned char  volatile xdata *)  0xFED1)
  107. #define PWMA_ARRH    (*(unsigned char  volatile xdata *)  0xFED2)
  108. #define PWMA_ARRL    (*(unsigned char  volatile xdata *)  0xFED3)
  109. #define PWMA_RCR     (*(unsigned char  volatile xdata *)  0xFED4)
  110. #define PWMA_CCR1H   (*(unsigned char  volatile xdata *)  0xFED5)
  111. #define PWMA_CCR1L   (*(unsigned char  volatile xdata *)  0xFED6)
  112. #define PWMA_CCR2H   (*(unsigned char  volatile xdata *)  0xFED7)
  113. #define PWMA_CCR2L   (*(unsigned char  volatile xdata *)  0xFED8)
  114. #define PWMA_CCR3H   (*(unsigned char  volatile xdata *)  0xFED9)
  115. #define PWMA_CCR3L   (*(unsigned char  volatile xdata *)  0xFEDA)
  116. #define PWMA_CCR4H   (*(unsigned char  volatile xdata *)  0xFEDB)
  117. #define PWMA_CCR4L   (*(unsigned char  volatile xdata *)  0xFEDC)
  118. #define PWMA_BKR     (*(unsigned char  volatile xdata *)  0xFEDD)
  119. #define PWMA_DTR     (*(unsigned char  volatile xdata *)  0xFEDE)
  120. #define PWMA_OISR    (*(unsigned char  volatile xdata *)  0xFEDF)
  121. /*****************************************************************************/
  122. #define PWM1_1      0x00        //P:P1.0  N:P1.1
  123. #define PWM1_2      0x01        //P:P2.0  N:P2.1
  124. #define PWM1_3      0x02        //P:P6.0  N:P6.1
  125. #define PWM2_1      0x00        //P:P1.2/P5.4  N:P1.3
  126. #define PWM2_2      0x04        //P:P2.2  N:P2.3
  127. #define PWM2_3      0x08        //P:P6.2  N:P6.3
  128. #define PWM3_1      0x00        //P:P1.4  N:P1.5
  129. #define PWM3_2      0x10        //P:P2.4  N:P2.5
  130. #define PWM3_3      0x20        //P:P6.4  N:P6.5
  131. #define PWM4_1      0x00        //P:P1.6  N:P1.7
  132. #define PWM4_2      0x40        //P:P2.6  N:P2.7
  133. #define PWM4_3      0x80        //P:P6.6  N:P6.7
  134. #define PWM4_4      0xC0        //P:P3.4  N:P3.3
  135. #define ENO1P       0x01
  136. #define ENO1N       0x02
  137. #define ENO2P       0x04
  138. #define ENO2N       0x08
  139. #define ENO3P       0x10
  140. #define ENO3N       0x20
  141. #define ENO4P       0x40
  142. #define ENO4N       0x80
  143. #define PWM_PSCR    99     //设置分频
  144. #define PWM_PERIOD  4799    //设置周期值
  145. #define PWM_Min     120
  146. #define PWM_Max     600
  147. /*************  本地变量声明    **************/
  148. bit B_1ms;          //1ms标志
  149. u16 PWM1_Duty = PWM_Min;
  150. u16 PWM2_Duty = PWM_Min;
  151. u16 PWM3_Duty = PWM_Min;
  152. u16 PWM4_Duty = PWM_Min;
  153. bit PWM1_Flag;
  154. bit PWM2_Flag;
  155. bit PWM3_Flag;
  156. bit PWM4_Flag;
  157. void UpdatePwm(void);
  158. /******************** 主函数 **************************/
  159. void main(void)
  160. {
  161.     P_SW2 |= 0x80; //扩展寄存器(XFR)访问使能
  162.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
  163.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
  164.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
  165.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
  166.     P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
  167.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
  168.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
  169.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  170.     PWM1_Flag = 0;
  171.     PWM2_Flag = 0;
  172.     PWM3_Flag = 0;
  173.     PWM4_Flag = 0;
  174.     PWM1_Duty = 0;
  175.     PWM2_Duty = 256;
  176.     PWM3_Duty = 512;
  177.     PWM4_Duty = 1024;
  178.     //  Timer0初始化
  179.     AUXR = 0x80;    //Timer0 set as 1T, 16 bits timer auto-reload,
  180.     TH0 = (u8)(Timer0_Reload / 256);
  181.     TL0 = (u8)(Timer0_Reload % 256);
  182.     ET0 = 1;    //Timer0 interrupt enable
  183.     TR0 = 1;    //Tiner0 run
  184.     PWMA_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
  185.     PWMA_CCER2 = 0x00;
  186.     PWMA_CCMR1 = 0x68; //通道模式配置
  187.     PWMA_CCMR2 = 0x68;
  188.     PWMA_CCMR3 = 0x68;
  189.     PWMA_CCMR4 = 0x68;
  190.     PWMA_CCER1 = 0x55; //配置通道输出使能和极性
  191.     PWMA_CCER2 = 0x55;
  192.     PWMA_PSCRH = (u8)(PWM_PSCR>>8);     //设置分频
  193.     PWMA_PSCRL = (u8)(PWM_PSCR);
  194.     PWMA_ARRH = (u8)(PWM_PERIOD >> 8);  //设置周期时间
  195.     PWMA_ARRL = (u8)PWM_PERIOD;
  196.     PWMA_ENO = 0x00;
  197.     PWMA_ENO |= ENO1P; //使能输出
  198.     PWMA_ENO |= ENO1N; //使能输出
  199.     PWMA_ENO |= ENO2P; //使能输出
  200.     PWMA_ENO |= ENO2N; //使能输出
  201.     PWMA_ENO |= ENO3P; //使能输出
  202.     PWMA_ENO |= ENO3N; //使能输出
  203.     PWMA_ENO |= ENO4P; //使能输出
  204.     PWMA_ENO |= ENO4N; //使能输出
  205.     PWMA_PS = 0x00;  //高级 PWM 通道输出脚选择位
  206.     PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
  207.     PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
  208.     PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
  209.     PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
  210.     PWMA_BKR = 0x80; //使能主输出
  211.     PWMA_CR1 |= 0x01; //开始计时
  212.     P40 = 0;                //给LED供电
  213.     EA = 1;     //打开总中断
  214.     while (1)
  215.     {
  216.     }
  217. }
  218. /********************** Timer0 1ms中断函数 ************************/
  219. void timer0(void) interrupt 1
  220. {
  221.     static u8 count=0;
  222.     count++;
  223.     if( count>=50 )
  224.     {
  225.         count = 0;
  226.         if(!PWM1_Flag)
  227.         {
  228.             PWM1_Duty+=10;
  229.             if(PWM1_Duty >  PWM_Max) PWM1_Flag = 1;
  230.         }
  231.         else
  232.         {
  233.             PWM1_Duty-=10;
  234.             if(PWM1_Duty <= PWM_Min) PWM1_Flag = 0;
  235.         }
  236.         if(!PWM2_Flag)
  237.         {
  238.             PWM2_Duty+=10;
  239.             if(PWM2_Duty > PWM_Max) PWM2_Flag = 1;
  240.         }
  241.         else
  242.         {
  243.             PWM2_Duty-=10;
  244.             if(PWM2_Duty <= PWM_Min) PWM2_Flag = 0;
  245.         }
  246.         if(!PWM3_Flag)
  247.         {
  248.             PWM3_Duty+=10;
  249.             if(PWM3_Duty > PWM_Max) PWM3_Flag = 1;
  250.         }
  251.         else
  252.         {
  253.             PWM3_Duty-=10;
  254.             if(PWM3_Duty <= PWM_Min) PWM3_Flag = 0;
  255.         }
  256.         if(!PWM4_Flag)
  257.         {
  258.             PWM4_Duty+=10;
  259.             if(PWM4_Duty > PWM_Max) PWM4_Flag = 1;
  260.         }
  261.         else
  262.         {
  263.             PWM4_Duty-=10;
  264.             if(PWM4_Duty <= PWM_Min) PWM4_Flag = 0;
  265.         }
  266.         
  267.         UpdatePwm();
  268.     }
  269. }
  270. //========================================================================
  271. // 函数: UpdatePwm(void)
  272. // 描述: 更新PWM占空比.
  273. // 参数: none.
  274. // 返回: none.
  275. // 版本: V1.0, 2012-11-22
  276. //========================================================================
  277. void UpdatePwm(void)
  278. {
  279.     PWMA_CCR1H = (u8)(PWM1_Duty >> 8); //设置占空比时间
  280.     PWMA_CCR1L = (u8)(PWM1_Duty);
  281.     PWMA_CCR2H = (u8)(PWM2_Duty >> 8); //设置占空比时间
  282.     PWMA_CCR2L = (u8)(PWM2_Duty);
  283.     PWMA_CCR3H = (u8)(PWM3_Duty >> 8); //设置占空比时间
  284.     PWMA_CCR3L = (u8)(PWM3_Duty);
  285.     PWMA_CCR4H = (u8)(PWM4_Duty >> 8); //设置占空比时间
  286.     PWMA_CCR4L = (u8)(PWM4_Duty);
  287. }
复制代码
  注:以上代码基于开天斧的试验箱程序“25-高级PWM1-PWM2-PWM3-PWM4,驱动P6口呼吸灯实验程序”改编。


  上述代码:
  1.main函数之前就是头文件应用,寄存器定义,变量定义等,具体的可以查阅手册对照哈,这里暂时先不赘述了。
  2.main函数开始先将打开这个P_SW2的寄存器的最高位,因为手册说过要用一些特殊寄存器必须打开这,我们的PWM的寄存器都在这里面。

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-4.png

  3.然后就是PWMx_Flag和PWMx_Duty(x取值0-3)这两个变量的初始化了,PWMx_Flag是用来设定当前PWM的高电平时间是越来越大还是越来越小的,PWMx_Duty就是当前PWM的高电平计数值。PWMx_Flag为0,PWMx_Duty会往上计数,数值越来越大,角度也越来越大,反之亦然。

  4.然后是定时器的初始化,我们想要他每隔一定的时间转动一定的角度,那么这个“一定的时间”怎么来的,我们就用定时器来实现咯,定时器初始化如上,这里初始化为1ms一次,当然自己不会使用定时器代码的话,我们依旧可以借助工具——ISP软件

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-5.png


  5.然后开始是PWM的初始化部分,

    5.1 先是PWMA_CCER1 = 0x00;  PWMA_CCER2 = 0x00;这里先把捕获/比较寄存器的数值全部清空,在写模式

    5.2 然后PWMA_CCMR1 = 0x68; 这一步主要是为了将PWM设置为“PWM模式1”

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-6.png

    上图看起来可能不太清晰,这里直接上图,如下图,运行的时候有一个计数器PWM_CNT,如果是向上计数他会从0开始计数到我们设定的PWM_ARR的值,到了设定值会变成0重新开始计数,这个计数值我们称之为PWM_CNT,然后我们设定一个比较值PWM_CCR,只要CNT小于CCR,就输出高电平,反之输出低电平,那么我们只要设置好ARR就可以调节输出的频率,调节CCR就可以改变输出的高电平时间对不对。(一个PWM有4组通道,4组占用一个ARR也就是四组PWM频率相同,每组通道有独立的CCR,也就是每组通道的占空比都可以自由设定,且每组通道都有PWMA_CCMR寄存器,也就是每组PWM都可以设置独立的工作模式

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-7.png

    5.3这里PWMA_CCER1 = 0x55; 这里主要是为了开启捕获/比较的通道和开启比较输出。

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-8.png

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-9.png


    5.4 这里四行是为了设定我们的频率,和上图的5.2图中所讲的一样,设定ARR的值。

      PWMA_PSCRH = (u8)(PWM_PSCR>>8);     //设置分频
      PWMA_PSCRL = (u8)(PWM_PSCR);
      PWMA_ARRH = (u8)(PWM_PERIOD >> 8);  //设置周期时间
      PWMA_ARRL = (u8)PWM_PERIOD;

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-10.png


    计算的频率公式如上所示,这里PWM_PSCR = 99,PWM_PERIOD = 4799,主频24M,这里我们设置为边沿对齐模式(后面讲)所以输出频率= 24000000/(99+1)/(4799+1)=50hz刚刚好:lol。

   

    5.5下面几行代码就是控制哪几个通道输出。我们之前提过一个PWM有四组通道,一组有2个输出通道,这里把八个都打开了,其实我们驱动一舵机只要一个就够了,这里我们就先不去动他(我懒!)

      PWMA_ENO = 0x00;
      PWMA_ENO |= ENO1P; //使能输出
      PWMA_ENO |= ENO1N; //使能输出
      PWMA_ENO |= ENO2P; //使能输出
      PWMA_ENO |= ENO2N; //使能输出
      PWMA_ENO |= ENO3P; //使能输出
      PWMA_ENO |= ENO3N; //使能输出
      PWMA_ENO |= ENO4P; //使能输出
      PWMA_ENO |= ENO4N; //使能输出

    5.6 下面几行代码就是选择输出的通道,因为我们有好多组通道可以选择,
如果这时候刚巧某个通道的某一个IO口被占用了,我们就可以换一个,具体可以如图

      PWMA_PS = 0x00;  //高级 PWM 通道输出脚选择位
      PWMA_PS |= PWM1_3; //选择 PWM1_3 通道
      PWMA_PS |= PWM2_3; //选择 PWM2_3 通道
      PWMA_PS |= PWM3_3; //选择 PWM3_3 通道
      PWMA_PS |= PWM4_3; //选择 PWM4_3 通道
STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-11.png

    可以看下图,我们这个程序里配置的就是P6端口的输出,当然你也可以指定你要的端口,只要这表里有就可输出。

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-12.png


    5.7 PWMA_BKR = 0x80; 因为之前我们已经开启了CC1E位,我们这里遵循手册只要打开MOEn这个位就可以开启OC和OCn的输出了。

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-13.png


    5.8 PWMA_CR1 |= 0x01; 这里设置了边沿模式,并且开启了向上计数的模式

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-14.png

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-15.png

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-16.png

    至此PWM的地方就配置完成了。当然我们自己写代码可以不需要从头到尾深究每一个寄存器,我们可以参考手册的案例,参考试验箱例程,基于这个修改自己想要的模式或者功能就好了,非常的方便。

    最后,我们在定时器中断里,如果PWM1_Flag是0,就是向上计数,每隔50ms让PWM1_Duty增加10,让角度越来越大到了最大数值就把PWM1_Flag变成1,每隔50ms让PWM1_Duty减少10,角度越来越小,记得这个变量更新了以后,把我们最后的数值写入CCR的比较值寄存器,是不是初始化完了输出PWM就非常的方便了,修改这一个寄存器就可以输出我们想要的任意波形。我们这个程序最终就是4路舵机(P60,P62,P64,P66四个端口)角度在0-180度之间不断的来回运动。这里需要注意舵机不要突然给他一个瞬态变化的值,不然瞬态电流会非常大,比如你上一秒还是0度的数值,下一秒就输出180度,我们要给他缓缓的变换角度,0先到45,再到90,再到135,再到180,千万别一下就给他那啥了~另外我们这个试验用的小舵机,大功率的舵机请单独供电,不然MCU容易掉电重启。

STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-17.png

    还有这个最大值和最小值是怎么来的,因为计数的PWM_ARR是不是4799,从0-4799总共4800个数字。已知4800数值对应的是20ms的周期,我们想要高电平时间时间是10ms,那么这个CCR的数值就是2400对不对,我们想要高电平时间是5ms,那么这个CCR的数值就是1200对不对,所以这里我们最小的高电平时间是0.5ms,最大是2.5ms,然后你们也会计算了吧~


STC驱动教程系列——STC8H8K64U高级PWM驱动舵机-18.png




四、测试结果
  先用示波器观察我们的输出波形是不是我们写的这个样子,示波器的表笔接P60,地线接上,然后观察是波形。

  可以看到示波器自动测量的参数,在屏幕下方有显示,频率,周期和正脉宽的信息,频率基本稳定在50,周期约为20ms,正脉宽在500us-2.5ms之间变化,这就是我们的代码要实现的效果

      X($QPJN8@@4SPVSL$(TX6.png


  最后的最后,我们直接上机测试。





  好吧,没有这个舵机,只能到这里就全剧终了!附上代码!留给小伙伴们自行测试。

开天斧高级PWM1-PWM2-PWM3-PWM4驱动舵机.rar (39.62 KB, 下载次数: 516)



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:41
  • 最近打卡:2025-03-13 12:10:59

3

主题

20

回帖

731

积分

高级会员

积分
731
发表于 2023-2-4 15:52:59 | 显示全部楼层
不错,支持一下

点评

终于有兄弟发现好东西了哈哈  发表于 2023-2-4 17:49
  • 打卡等级:以坛为家II
  • 打卡总天数:413
  • 最近打卡:2025-05-03 00:29:17

2

主题

144

回帖

3016

积分

论坛元老

积分
3016
发表于 2023-2-6 17:11:38 | 显示全部楼层
大佬又出神作了
  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2025-04-04 21:37:37

2

主题

30

回帖

1065

积分

金牌会员

积分
1065
发表于 2023-2-17 09:30:48 | 显示全部楼层
好资料,讲解很到位,谢谢大神
已绑定手机

1

主题

16

回帖

97

积分

注册会员

积分
97
QQ
发表于 2023-2-21 00:01:55 | 显示全部楼层
感谢大佬的方案,但大佬你有什么方案可以解决因启动产生的电流。并且SG90在运动时又会产生感生电流,相当于一个小发电机,如何预防。使用在单片机IO接口与舵机之间的电路如何设计
  • 打卡等级:常住居民III
  • 打卡总天数:135
  • 最近打卡:2024-12-25 10:15:05

47

主题

1350

回帖

8058

积分

荣誉版主

冲哥视频教程和各种开源资料QQ交流群884047237,可群

积分
8058
QQ
发表于 2023-2-21 01:37:02 | 显示全部楼层
默默*** 发表于 2023-2-21 00:01
感谢大佬的方案,但大佬你有什么方案可以解决因启动产生的电流。并且SG90在运动时又会产生感生电流,相当于 ...

最常见的方案如下:
1.电源部分:舵机和MCU两部分使用独立电源供电,或者MCU部分使用隔离电源+续流电路
2.控制部分:IO输出端口可以加光耦等隔离电路保护IO,做前后隔离
3.程序控制上尽量不要做瞬间变换角度,初始位置是0,一下输出个180度的脉冲出去的话那电流还是很恐怖的。
回复 支持 1 反对 0

使用道具 举报 送花

已绑定手机

1

主题

16

回帖

97

积分

注册会员

积分
97
QQ
发表于 2023-2-21 10:18:56 | 显示全部楼层
电子DI*** 发表于 2023-2-21 01:37
最常见的方案如下:
1.电源部分:舵机和MCU两部分使用独立电源供电,或者MCU部分使用隔离电源+续流电路
2 ...

感谢大佬的回复
  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-05-02 10:07:51

73

主题

5883

回帖

1万

积分

超级版主

积分
12081
发表于 2023-3-20 14:30:49 | 显示全部楼层
本帖最后由 梁工 于 2023-3-20 14:31 编辑

舵机使用PPM信号控制的,一般比较标准的PPM信号是1.0~2.0ms,大部分应用,步进1us就足够了,有1000步。
PPM信号最好使用PWM产生,硬件产生的波形稳定,不受程序影响,不要用软件循坏输出PPM信号,占用CPU带宽,波形受中断影响。

PPM信号周期大部分是4~20ms的,响应速度快就用短周期,比如高速比例遥控车(速度30km/h以上)、四轴飞行器等等,要求操作响应非常快,则一般用5~10ms的周期。固定翼飞机模型、动作缓慢的机械手等等,则可以用到20ms周期。

1

主题

15

回帖

55

积分

注册会员

积分
55
发表于 2023-3-25 09:30:37 | 显示全部楼层

   想问下能不能做到图片上低电平时间长,高电平时间短,再到高电平时间长,低电平时间短,就是这个波形倒过来一样,就是调节范围要宽,从最低从是0到最高全是F的变化范围可以做做到吗?我现在用例程都达不到这样的效果,请问程序要怎样处理?

点评

没太懂什么意思,上电是低电平,然后占空比逐渐增大,一点点变成全都是高电平的吗?  详情 回复 发表于 2023-3-25 10:08

1

主题

15

回帖

55

积分

注册会员

积分
55
发表于 2023-3-25 09:32:06 | 显示全部楼层
我都想用定时器来写了,如果PWM能做到就太好了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-3 15:05 , Processed in 0.166610 second(s), 120 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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