请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
  • TA的每日心情
    开心
    5 小时前
  • 签到天数: 69 天

    [LV.6]常住居民II

    31

    主题

    885

    回帖

    6276

    积分

    荣誉版主

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

    积分
    6276
    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, 下载次数: 252)



    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-13 08:41
  • 签到天数: 24 天

    [LV.4]偶尔看看III

    2

    主题

    15

    回帖

    326

    积分

    中级会员

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

    点评

    终于有兄弟发现好东西了哈哈  发表于 2023-2-4 17:49
  • TA的每日心情
    奋斗
    12 小时前
  • 签到天数: 94 天

    [LV.6]常住居民II

    1

    主题

    103

    回帖

    1715

    积分

    金牌会员

    积分
    1715
    发表于 2023-2-6 17:11:38 | 显示全部楼层
    大佬又出神作了

    该用户从未签到

    2

    主题

    30

    回帖

    1047

    积分

    金牌会员

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

    该用户从未签到

    1

    主题

    16

    回帖

    65

    积分

    注册会员

    积分
    65
    QQ
    发表于 2023-2-21 00:01:55 | 显示全部楼层
    感谢大佬的方案,但大佬你有什么方案可以解决因启动产生的电流。并且SG90在运动时又会产生感生电流,相当于一个小发电机,如何预防。使用在单片机IO接口与舵机之间的电路如何设计
  • TA的每日心情
    开心
    5 小时前
  • 签到天数: 69 天

    [LV.6]常住居民II

    31

    主题

    885

    回帖

    6276

    积分

    荣誉版主

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

    积分
    6276
    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

    回帖

    65

    积分

    注册会员

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

    感谢大佬的回复

    该用户从未签到

    45

    主题

    2744

    回帖

    6134

    积分

    超级版主

    积分
    6134
    发表于 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, 2024-3-29 13:58 , Processed in 0.082204 second(s), 72 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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