找回密码
 立即注册

QQ登录

只需一步,快速开始

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

STC单片机关于舵机pwm控制源程序(单个与多个速度控制都有)

  [复制链接]
跳转到指定楼层
楼主
具体内容见附件


单片机源程序如下:
  1. /*****************************************************************************
  2. ** 文件                        : main.c
  3. ** 作者                        : Zheng23
  4. ** 日期                        :                        
  5. ** 功能                        :   产生8路PWM输出控制8路舵机,方法:将20毫秒分成8个2500微秒,
  6.                                                 即8个单元,先计算出第一个单元的舵机PWM控制线的脉冲宽度,因
  7.                                                 为控制舵机的脉冲宽度最大为2500微秒,剩余的(20000 - 2500)
  8.                                                 毫秒就是剩下的7个单元时间,第一个单元的舵机PWM控制线全部为
  9.                                                 低电平了,这时就可以安心的处理剩下的7个单元的事情了,同理
  10.                                                 第二个单元也是一样,这样循环下去就可以控制8路PWM输出了。

  11. /*****************************************************************************/
  12. //#include <STC15.h>
  13. //#include"STC89C5xRC.H"


  14. //一定要用12M的晶振
  15. //一定要用12M的晶振
  16. //一定要用12M的晶振
  17. //一定要用12M的晶振
  18. //一定要用12M的晶振
  19. //一定要用12M的晶振


  20. #include "reg51.h"

  21. #define bool        bit
  22. #define uint8        unsigned char   
  23. #define uint16        unsigned short int
  24. #define uint32        unsigned long

  25. sbit SERVO0=P0^0;                        //控制舵机的PWM输出口
  26. sbit SERVO1=P0^1;                       
  27. sbit SERVO2=P0^2;               
  28. sbit SERVO3=P0^3;
  29. sbit SERVO4=P0^4;               
  30. sbit SERVO5=P0^5;               
  31. sbit SERVO6=P0^6;                       
  32. sbit SERVO7=P0^7;

  33. sbit LED=P2^7;                //调试用

  34. uint16 ServoPwmDuty[8] = {1500,1500,1500,1500,1500,1500,1500,1500};        //PWM脉冲宽度
  35. uint16 ServoPwmDutySet[8] = {1500,1500,1500,1500,1500,1500,1500,1500};        //PWM脉冲宽度
  36. double ServoPwmDutyInc[8];                //为了速度控制,当PWM脉宽发生变化时,每2.5ms或20ms递增的PWM脉宽

  37. bool Flag_20ms = 0;                //20毫秒标识位,在定时中断里面置位
  38. bool Flag_2_5ms = 0;        //2.5毫秒标识位,在定时中断里面置位
  39. bool ServoPwmDutyHaveChange = 0;        //脉宽有变化标志位
  40. uint16 ServoTime = 2000;                        //舵机从当前角度运动到指定角度的时间,也就是控制速度
  41. /***********************************************************
  42. * 名    称: DelayMs(uint16 ms)
  43. * 功    能: 延时ms毫秒
  44. * 入口参数: ms        毫秒
  45. * 出口参数: 无
  46. * 说    明:                                          
  47. /**********************************************************/
  48. void DelayMs(uint16 ms)
  49. {
  50.         uint16 i,j;
  51.         for(i=0;i<800;i++)                //89单片机用85,12系列单片机用800
  52.                 for(j=0;j<ms;j++);
  53. }

  54. /***********************************************************
  55. * 名    称:InitTimer0()
  56. * 功    能:时钟0初始化
  57. * 入口参数:无
  58. * 出口参数:无
  59. * 说    明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
  60.                         因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
  61.                         x毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-x)        从这个值开始计数产生定时中断                                         
  62. /**********************************************************/
  63. void InitTimer0(void)
  64. {
  65. //        AUXR &= 0x7F;                //定时器时钟12T模式
  66. //        AUXR |= 0x00;                //T0,T1工作在12T
  67.         TMOD &= 0xF0;                //设置定时器模式
  68.         TMOD |= 0x01;                //设置定时器模式
  69.         TL0 = 0x00;                        //设置定时初值
  70.         TH0 = 0x00;                        //设置定时初值
  71.         TF0 = 0;                        //清除TF0标志
  72.         TR0 = 1;                        //定时器0开始计时
  73.         ET0 = 1;                        //开定时器0中断
  74. }

  75. /***********************************************************
  76. * 名    称:Timer0Value(uint16 pwm)  
  77. * 功    能:给定时器0计数器赋值产生定时中断
  78. * 入口参数:pwm                控制舵机的PWM脉冲宽度值(范围:500~2500)
  79. * 出口参数:无
  80. * 说    明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
  81.                         因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
  82.                         pwm毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-pwm)        从这个值开始计数产生定时中断                                          
  83. /**********************************************************/
  84. void Timer0Value(uint16 pwm)
  85. {
  86.         uint16 value;
  87.         value=0xffff-pwm;               
  88.         TR0 = 0;
  89.         TL0=value;                        //16位数据给8位数据赋值默认将16位数据的低八位直接赋给八位数据
  90.     TH0=value>>8;                //将16位数据右移8位,也就是将高8位移到低八位,再赋值给8位数据       
  91.         TR0 = 1;
  92. }

  93. /***********************************************************
  94. * 名    称: ServoPwmDutyCompare()
  95. * 功    能: 脉宽变化比较及速度控制
  96. * 入口参数: 无
  97. * 出口参数: 无
  98. * 说    明:                                          
  99. /**********************************************************/
  100. void ServoPwmDutyCompare()
  101. {
  102.         uint8 i;
  103.        
  104.         static uint16 ServoPwmDutyIncTimes;        //需要递增的次数
  105.         static bool ServoRunning = 0;        //舵机正在以指定速度运动到指定的脉宽对应的位置
  106.         if(ServoRunning == 0 && ServoPwmDutyHaveChange)//停止运动并且脉宽发生变化时才进行计算
  107.         {
  108.                 ServoPwmDutyHaveChange = 0;
  109. //                ServoPwmDutyIncTimes = ServoTime*2/5;        //ServoTime/(20/8)        //当每2.5ms调用一次ServoPwmDutyCompare()函数时用此句
  110.                 ServoPwmDutyIncTimes = ServoTime/20;        //当每20ms调用一次ServoPwmDutyCompare()函数时用此句
  111.                 for(i=0;i<8;i++)
  112.                 {
  113.                         //if(ServoPwmDuty[i] != ServoPwmDutySet[i])
  114.                         {
  115.                                 if(ServoPwmDutySet[i] > ServoPwmDuty[i])
  116.                                 {
  117.                                         ServoPwmDutyInc[i] = ServoPwmDutySet[i] - ServoPwmDuty[i];
  118.                                         ServoPwmDutyInc[i] = -ServoPwmDutyInc[i];
  119.                                 }
  120.                                 else
  121.                                 {
  122.                                         ServoPwmDutyInc[i] = ServoPwmDuty[i] - ServoPwmDutySet[i];
  123.                                        
  124.                                 }
  125.                                 ServoPwmDutyInc[i] /= ServoPwmDutyIncTimes;//每次递增的脉宽
  126.                         }
  127.                 }
  128.                 ServoRunning = 1;        //舵机开始动作
  129.         }
  130.         if(ServoRunning)
  131.         {
  132.                 ServoPwmDutyIncTimes--;
  133.                 for(i=0;i<8;i++)
  134.                 {
  135.                         if(ServoPwmDutyIncTimes == 0)
  136.                         {                //最后一次递增就直接将设定值赋给当前值
  137.                                 ServoPwmDuty[i] = ServoPwmDutySet[i];
  138.                                 ServoRunning = 0;        //到达设定位置,舵机停止运动
  139.                         }
  140.                         else
  141.                         {
  142.                                 ServoPwmDuty[i] = ServoPwmDutySet[i] +
  143.                                         (signed short int)(ServoPwmDutyInc[i] * ServoPwmDutyIncTimes);
  144.                         }
  145.                 }
  146.                
  147.         }
  148. }

  149. /***********************************************************
  150. * 名    称: main()
  151. * 功    能: 入口函数
  152. * 入口参数: 无
  153. * 出口参数: 无
  154. * 说    明:                                          
  155. /**********************************************************/
  156. void main(void)
  157. {
  158.         uint8 i;
  159.         uint16 Time;
  160.         InitTimer0();        //定时器0初始化
  161.         EA = 1;                        //开总中断
  162.         while(1)                //大循环
  163.         {       
  164.                 if(Flag_20ms)        // Flag_2_5ms        2.5毫秒调用一次ServoPwmDutyCompare()或20毫秒调用一次        
  165.                 {       
  166.                         Flag_20ms = 0;
  167.                         Flag_2_5ms = 0;
  168.                         Time++;
  169.                         ServoPwmDutyCompare();
  170.                 }
  171.                 if(Time > 110)
  172.                 {
  173.                         Time = 0;
  174.                         LED = ~LED;
  175.                         if(ServoPwmDutySet[0] == 500)
  176.                         {
  177.                                 ServoPwmDutySet[0] = 2500;                        //脉冲宽度在2500微秒,对应90°
  178.                                 for(i = 1;i<8;i++)
  179.                                         ServoPwmDutySet[i] = 500;                //脉冲宽度在500微秒,对应-90°
  180.                         }
  181.                         else
  182.                         {
  183.                                 ServoPwmDutySet[0] = 500;                        //脉冲宽度在500微秒,对应-90°
  184.                                 for(i = 1;i<8;i++)
  185.                                         ServoPwmDutySet[i] = 1500;                //脉冲宽度在1500微秒,对应0°
  186.                         }
  187.                         ServoPwmDutyHaveChange = 1;
  188.                 }
  189.         }
  190. }

  191. /***********************************************************
  192. * 名    称: Timer0_isr() interrupt 1 using 1
  193. * 功    能: 时钟0中断处理
  194. * 入口参数: 无
  195. * 出口参数: 无
  196. * 说    明:                                          
  197. /**********************************************************/
  198. void Timer0_isr(void) interrupt 1 using 1
  199. {
  200.         static uint16 i = 1;        //静态变量:每次调用函数时保持上一次所赋的值,
  201.                                                         //跟全局变量类似,不同是它只能用于此函数内部
  202.         switch(i)
  203.         {
  204.                 case 1:
  205.                         SERVO0 = 1;        //PWM控制脚高电平
  206.                         //给定时器0赋值,计数Pwm0Duty个脉冲后产生中断,下次中断会进入下一个case语句
  207.                         Timer0Value(ServoPwmDuty[0]);
  208.                         Flag_20ms = 1;
  209.                         Flag_2_5ms = 1;
  210.                         break;
  211.                 case 2:
  212.                         SERVO0 = 0;        //PWM控制脚低电平
  213.                         //此计数器赋值产生的中断表示下一个单元要进行任务的开始
  214.                         Timer0Value(2500-ServoPwmDuty[0]);       
  215.                         break;
  216.                 case 3:
  217.                         SERVO1 = 1;       
  218.                         Timer0Value(ServoPwmDuty[1]);
  219.                         Flag_2_5ms = 1;
  220.                         break;
  221.                 case 4:
  222.                         SERVO1 = 0;        //PWM控制脚低电平
  223.                         Timer0Value(2500-ServoPwmDuty[1]);       
  224.                         break;
  225.                 case 5:
  226.                         SERVO2 = 1;       
  227.                         Timer0Value(ServoPwmDuty[2]);
  228.                         Flag_2_5ms = 1;
  229.                         break;
  230.                 case 6:
  231.                         SERVO2 = 0;        //PWM控制脚低电平
  232.                         Timer0Value(2500-ServoPwmDuty[2]);       
  233.                         break;       
  234.                 case 7:
  235.                         SERVO3 = 1;       
  236.                         Timer0Value(ServoPwmDuty[3]);
  237.                         Flag_2_5ms = 1;
  238.                         break;
  239.                 case 8:
  240.                         SERVO3 = 0;        //PWM控制脚低电平
  241.                         Timer0Value(2500-ServoPwmDuty[3]);       
  242.                         break;       
  243.                 case 9:
  244.                         SERVO4 = 1;       
  245.                         Timer0Value(ServoPwmDuty[4]);
  246.                         Flag_2_5ms = 1;
  247.                         break;
  248.                 case 10:
  249.                         SERVO4 = 0;        //PWM控制脚低电平
  250.                         Timer0Value(2500-ServoPwmDuty[4]);       
  251.                         break;       
  252.                 case 11:
  253.                         SERVO5 = 1;       
  254.                         Timer0Value(ServoPwmDuty[5]);
  255.                         Flag_2_5ms = 1;
  256.                         break;
  257.                 case 12:
  258.                         SERVO5 = 0;        //PWM控制脚低电平
  259.                         Timer0Value(2500-ServoPwmDuty[5]);       
  260.                         break;
  261.                 case 13:
  262.                         SERVO6 = 1;       
  263.                         Timer0Value(ServoPwmDuty[6]);
  264.                         Flag_2_5ms = 1;
  265.                         break;
  266.                 case 14:
  267.                         SERVO6 = 0;        //PWM控制脚低电平
  268.                         Timer0Value(2500-ServoPwmDuty[6]);       
  269.                         break;
  270.                 case 15:
  271.                         SERVO7 = 1;       
  272.                         Timer0Value(ServoPwmDuty[7]);
  273.                         Flag_2_5ms = 1;
  274.                         break;
  275.                 case 16:
  276.                         SERVO7 = 0;        //PWM控制脚低电平
  277.                         Timer0Value(2500-ServoPwmDuty[7]);
  278.                         i = 0;       
  279.                         break;                                 
  280.         }
  281.         i++;
  282. }
复制代码
  1. /*****************************************************************************
  2. ** 文件                        : main.c
  3. ** 作者                        : Zheng23
  4. ** 日期                        :                        
  5. ** 功能                        :
  6. /*****************************************************************************/
  7. //#include <STC15.h>
  8. //#include"STC89C5xRC.H"


  9. //一定要用12M的晶振
  10. //一定要用12M的晶振
  11. //一定要用12M的晶振
  12. //一定要用12M的晶振
  13. //一定要用12M的晶振
  14. //一定要用12M的晶振


  15. #include "reg51.h"

  16. #define bool        bit
  17. #define uint8        unsigned char   
  18. #define uint16        unsigned short int
  19. #define uint32        unsigned long

  20. sbit SERVO0=P0^0;                        //控制舵机的PWM输出口

  21. sbit LED=P2^7;                //调试用

  22. uint16 ServoPwmDuty=1500;        //PWM脉冲宽度
  23. uint16 ServoPwmDutySet=1500;
  24. double ServoPwmDutyInc;

  25. bool Flag_20ms = 0;        //20毫秒标识位,在定时中断里面置位
  26. bool ServoPwmDutyHaveChange = 0;        //脉宽有变化标志位

  27. uint16 ServoTime = 2000;
  28. /***********************************************************
  29. * 名    称: DelayMs(uint16 ms)
  30. * 功    能: 延时ms毫秒
  31. * 入口参数: ms        毫秒
  32. * 出口参数: 无
  33. * 说    明:                                          
  34. /**********************************************************/
  35. void DelayMs(uint16 ms)
  36. {
  37.         uint16 i,j;
  38.         for(i=0;i<85;i++)        //89单片机用85,12系列单片机用800
  39.                 for(j=0;j<ms;j++);
  40. }
  41. /***********************************************************
  42. * 名    称:InitTimer0()
  43. * 功    能:时钟0初始化
  44. * 入口参数:无
  45. * 出口参数:无
  46. * 说    明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
  47.                         因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
  48.                         x毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-x)        从这个值开始计数产生定时中断                                         
  49. /**********************************************************/
  50. void InitTimer0(void)
  51. {
  52. //        AUXR &= 0x7F;                //定时器时钟12T模式
  53. //        AUXR |= 0x00;                //T0,T1工作在12T
  54.         TMOD &= 0xF0;                //设置定时器模式
  55.         TMOD |= 0x01;                //设置定时器模式
  56.         TL0 = 0x00;                        //设置定时初值
  57.         TH0 = 0x00;                        //设置定时初值
  58.         TF0 = 0;                        //清除TF0标志
  59.         TR0 = 1;                        //定时器0开始计时
  60.         ET0 = 1;                        //开定时器0中断
  61. }
  62. /***********************************************************
  63. * 名    称:Timer0Value(uint16 pwm)  
  64. * 功    能:给定时器0计数器赋值产生定时中断
  65. * 入口参数:pwm                控制舵机的PWM脉冲宽度值(范围:500~2500)
  66. * 出口参数:无
  67. * 说    明:12M晶振,12分频,所以计数器每递增一个数就是1微秒,完全满足舵机控制的精度要求
  68.                         因为定时器是TH0,TL0都要全部计数到0xFF后在计1个数就会产生中断,所以要想产生
  69.                         pwm毫秒的中断,那么TH0,TL0就应该赋值(0xFFFF-pwm)        从这个值开始计数产生定时中断                                          
  70. /**********************************************************/
  71. void Timer0Value(uint16 pwm)
  72. {
  73.         uint16 value;
  74.         value=0xffff-pwm;
  75.         TR0 = 0;
  76.         TL0=value;                        //16位数据给8位数据赋值默认将16位数据的低八位直接赋给八位数据
  77.     TH0=value>>8;                //将16位数据右移8位,也就是将高8位移到低八位,再赋值给8位数据       
  78.         TR0 = 1;
  79. }
  80. /***********************************************************
  81. * 名    称: ServoPwmDutyCompare()
  82. * 功    能: 脉宽变化比较及速度控制
  83. * 入口参数: 无
  84. * 出口参数: 无
  85. * 说    明:                                          
  86. /**********************************************************/
  87. void ServoPwmDutyCompare()
  88. {

  89.         static uint16 ServoPwmDutyIncTimes;        //需要递增的次数
  90.         static bool ServoRunning = 0;        //舵机正在以指定速度运动到指定的脉宽对应的位置
  91.         if(ServoRunning == 0 && ServoPwmDutyHaveChange)//停止运动并且脉宽发生变化时才进行计算
  92.         {
  93.                 ServoPwmDutyHaveChange = 0;
  94.                 ServoPwmDutyIncTimes = ServoTime/20;       
  95.                 if(ServoPwmDutySet > ServoPwmDuty)
  96.                 {
  97.                         ServoPwmDutyInc = ServoPwmDutySet - ServoPwmDuty;
  98.                         ServoPwmDutyInc = -ServoPwmDutyInc;
  99.                 }
  100.                 else
  101.                 {
  102.                         ServoPwmDutyInc = ServoPwmDuty - ServoPwmDutySet;
  103.                        
  104.                 }
  105.                 ServoPwmDutyInc /= ServoPwmDutyIncTimes;//每次递增的脉宽
  106.                 ServoRunning = 1;        //舵机开始动作
  107.         }
  108.         if(ServoRunning)
  109.         {
  110.                 ServoPwmDutyIncTimes--;
  111.                 if(ServoPwmDutyIncTimes == 0)
  112.                 {                //最后一次递增就直接将设定值赋给当前值
  113.                         ServoPwmDuty = ServoPwmDutySet;
  114.                         ServoRunning = 0;        //到达设定位置,舵机停止运动
  115.                 }
  116.                 else
  117.                 {
  118.                         ServoPwmDuty = ServoPwmDutySet +
  119.                                 (signed short int)(ServoPwmDutyInc * ServoPwmDutyIncTimes);
  120.                 }
  121.         }
  122. }
  123. /***********************************************************
  124. * 名    称: main()
  125. * 功    能: 入口函数
  126. * 入口参数: 无
  127. * 出口参数: 无
  128. * 说    明:                                          
  129. /**********************************************************/
  130. void main(void)
  131. {
  132.         uint16 Time;
  133.         InitTimer0();        //定时器0初始化
  134.         EA = 1;                        //开总中断
  135.         while(1)                //大循环
  136.         {       
  137.                 if(Flag_20ms)
  138.                 {
  139.                         Time++;
  140.                         Flag_20ms = 0;
  141.                         ServoPwmDutyCompare();
  142.                 }
  143.                 if(Time > 110)
  144.                 {
  145.                         Time = 0;
  146.                         LED = ~LED;
  147.                         if(ServoPwmDutySet == 500)
  148.                         {
  149.                                 ServoPwmDutySet = 2500;                        //脉冲宽度在2500微秒,对应90°
  150.                         }
  151.                         else
  152.                         {
  153.                                 ServoPwmDutySet = 500;                        //脉冲宽度在500微秒,对应-90°
  154.                         }
  155.                         ServoPwmDutyHaveChange = 1;
  156.                 }
  157.         }
  158. }

  159. /***********************************************************
  160. * 名    称: Timer0_isr() interrupt 1 using 1
  161. * 功    能: 时钟0中断处理
  162. * 入口参数: 无
  163. * 出口参数: 无
  164. * 说    明:                                          
  165. /**********************************************************/
  166. void Timer0_isr(void) interrupt 1 using 1
  167. {
  168.         static uint16 i = 1;        //静态变量:每次调用函数时保持上一次所赋的值,
  169.                                                         //跟全局变量类似,不同是它只能用于此函数内部
  170.         switch(i)
  171.         {
  172.                 case 1:
  173.                         SERVO0 = 1;        //PWM控制脚高电平
  174.                         //给定时器0赋值,计数Pwm0Duty个脉冲后产生中断,下次中断会进入下一个case语句
  175.                         Timer0Value(ServoPwmDuty);
  176.                         Flag_20ms = 1;       
  177.                         break;
  178.                 case 2:
  179.                         SERVO0 = 0;        //PWM控制脚低电平
  180.                         //高脉冲结束后剩下的时间(20000-Pwm0Duty)全是低电平了,Pwm0Duty + (20000-Pwm0Duty) = 20000个脉冲正好为一个周期20毫秒
  181.                         Timer0Value(20000-ServoPwmDuty);
  182.                         i = 0;       
  183.                         break;         
  184.         }
  185.         i++;
  186. }
复制代码


所有资料51hei提供下载:
STC单片机舵机PWM控制教程.zip (284.19 KB, 下载次数: 113)


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

使用道具 举报

沙发
ID:639290 发表于 2019-12-21 22:05 | 只看该作者
求详细控制多舵机程序
回复

使用道具 举报

板凳
ID:225956 发表于 2020-2-24 15:47 | 只看该作者
看看,多谢分享!!!!
回复

使用道具 举报

地板
ID:225956 发表于 2020-2-24 15:48 | 只看该作者
多谢分享!!!!!!!!!!!
回复

使用道具 举报

5#
ID:282869 发表于 2020-2-26 19:28 | 只看该作者
謝謝大大無私分享.....
回复

使用道具 举报

6#
ID:399179 发表于 2020-2-27 07:44 来自手机 | 只看该作者
正在学习,谢谢楼主无私奉献!
回复

使用道具 举报

7#
ID:689120 发表于 2020-2-27 10:54 | 只看该作者
要是能用在机器狗上面多好啊!谢谢楼主分享。
回复

使用道具 举报

8#
ID:639290 发表于 2020-3-8 08:13 | 只看该作者
感谢楼主分享
回复

使用道具 举报

9#
ID:165117 发表于 2020-6-3 16:21 | 只看该作者
看看,多谢分享!!!
回复

使用道具 举报

10#
ID:105698 发表于 2020-6-5 15:33 | 只看该作者
学习中,感谢楼主无私奉献!
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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