舵机简介

舵机控制PWM - 图1
舵机最早用于船舶上实现转向功能,由于可以通过程序连续控制其转角,因而被广泛应用于智能小车转向以及机器人各类关节中.
工作电压:3V-7.2V(取决于具体型号)
工作电流:100mA(取决于具体型号)
舵机控制PWM - 图2
舵机由直流电机、减速齿轮组、传感器和控制电路组成的一套自动控制系统.通过发送信号,指定输出轴旋转角度.舵机一般而言都有最大旋转角度(比如180度)
与普通直流电机的区别:

  • 直流电机是一圈圈转动的
  • 舵机智能在一定角度内转动,不能一圈圈转动
  • 普通的直流电机无法反馈转动的角度信息,而舵机可以

用途也不相同:

  • 普通直流电机一般是整圈转动做动力用
  • 舵机是控制某物体转动一定角度用(比如机器人的关节)

舵机控制PWM - 图3

舵机信号线

舵机控制PWM - 图4
棕色接地 中间红线5v 橙色信号线

舵机的主要组成部分为伺服电机,所谓伺服就是服从信号的要求而动作。在信号来之前,转子停止不动;信号来到之后,转子立即运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。 舵机接收的是PWM信号,当信号进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。简单来说就是给舵机一个特定的PWM信号,舵机就可以旋转到指定的位置。 舵机上有三根线,分别是GND(棕线)、VCC(红线)和SIG(橙线),也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。
一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。如下图。
舵机控制PWM - 图5
舵机控制PWM - 图6

代码示例

P01对应的PWM6为例
配置PWM环境,这里需要使用修改后的,支持分频系数的PWM依赖库:
PWM.CPWM.h

初始化PWM

  1. #include "config.h"
  2. #include "delay.h"
  3. #include "GPIO.h"
  4. #include "PWM.h"
  5. #include "UART.h"
  6. #include "Exti.h"
  7. #include <stdio.h>
  8. char putchar(char dat)
  9. {
  10. TX1_write2buff(dat);
  11. return dat;
  12. }
  13. #define MOTOR P01
  14. // 分频系数:可以是1~65535中的任意值
  15. #define Prescaler 10
  16. // 频率
  17. #define PREQ 50
  18. // 保证分母 >= 367
  19. #define PERIOD (MAIN_Fosc / (PREQ * Prescaler))
  20. void GPIO_config(void)
  21. {
  22. GPIO_InitTypeDef GPIO_InitStructure; // 结构定义
  23. GPIO_InitStructure.Pin = GPIO_Pin_1; // 指定要初始化的IO,
  24. GPIO_InitStructure.Mode = GPIO_PullUp; // 指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
  25. GPIO_Inilize(GPIO_P0, &GPIO_InitStructure); // 初始化
  26. GPIO_InitStructure.Pin = GPIO_Pin_2; // 指定要初始化的IO,
  27. GPIO_InitStructure.Mode = GPIO_PullUp; // 指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
  28. GPIO_Inilize(GPIO_P3, &GPIO_InitStructure); // 初始化
  29. }
  30. void PWM_config(void)
  31. {
  32. PWMx_InitDefine PWMx_InitStructure;
  33. // 总配置
  34. // (MAIN_Fosc / 1000) - 1 周期计数值
  35. // 配置分频系数,必须使用改进后的PWM.h和PWM.C文件
  36. PWMx_InitStructure.PWM_Prescaler = Prescaler - 1;
  37. PWMx_InitStructure.PWM_Period = PERIOD - 1; // 周期时间, 0~65535
  38. PWMx_InitStructure.PWM_DeadTime = 0; // 死区发生器设置, 0~255
  39. PWMx_InitStructure.PWM_EnoSelect = ENO6P; // 输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
  40. PWMx_InitStructure.PWM_PS_SW = PWM6_SW_P01; // 切换端口
  41. // 具体PWM端口配置
  42. // pwm6
  43. PWMx_InitStructure.PWM6_Mode = CCMRn_PWM_MODE1; // 模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
  44. PWMx_InitStructure.PWM6_Duty = 0; // PWM4占空比时间, 0~Period
  45. // pwm6
  46. PWMx_InitStructure.PWM_CC6Enable = ENABLE; // 开启PWM6P输入捕获/比较输出, ENABLE,DISABLE
  47. // PWM启动配置
  48. PWMx_InitStructure.PWM_MainOutEnable = ENABLE; // 主输出使能, ENABLE,DISABLE
  49. PWMx_InitStructure.PWM_CEN_Enable = ENABLE; // 使能计数器, ENABLE,DISABLE
  50. PWM_Configuration(PWMB, &PWMx_InitStructure); // 初始化PWM, PWMA,PWMB
  51. }

初始化UART打印日志

  1. void UART_config(void)
  2. {
  3. COMx_InitDefine COMx_InitStructure; // 结构定义
  4. COMx_InitStructure.UART_Mode = UART_8bit_BRTx; // 模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
  5. COMx_InitStructure.UART_BRT_Use = BRT_Timer1; // 选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
  6. COMx_InitStructure.UART_BaudRate = 115200ul; // 波特率, 一般 110 ~ 115200
  7. COMx_InitStructure.UART_RxEnable = ENABLE; // 接收允许, ENABLE或DISABLE
  8. COMx_InitStructure.BaudRateDouble = DISABLE; // 波特率加倍, ENABLE或DISABLE
  9. COMx_InitStructure.UART_Interrupt = ENABLE; // 中断允许, ENABLE或DISABLE
  10. COMx_InitStructure.UART_Priority = Priority_0; // 指定中断优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
  11. COMx_InitStructure.UART_P_SW = UART1_SW_P30_P31; // 切换端口, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
  12. UART_Configuration(UART1, &COMx_InitStructure); // 初始化串口1 UART1,UART2,UART3,UART4
  13. }

初始化外部中断Exti

用于监听按钮P32的按下事件

  1. void Exti_config(void)
  2. {
  3. EXTI_InitTypeDef Exti_InitStructure; // 结构定义
  4. Exti_InitStructure.EXTI_Interrupt = ENABLE; // 中断使能, ENABLE或DISABLE
  5. Exti_InitStructure.EXTI_Mode = EXT_MODE_Fall; // 中断模式, EXT_MODE_RiseFall,EXT_MODE_Fall
  6. Exti_InitStructure.EXTI_Priority = Priority_0; // 中断使能, ENABLE或DISABLE
  7. Ext_Inilize(EXT_INT0, &Exti_InitStructure); // 初始化
  8. }

替换Exti.c文件:Exti.hExti.c
处理按钮按下事件

  1. u16 duty_percent = 500; // 500us -> 2500us
  2. void ext_int0_call()
  3. {
  4. static int step = 500;
  5. float angle = 0;
  6. PWMx_Duty duty;
  7. // 500 -> 0°
  8. // 1000 -> 45°
  9. // 1500 -> 90°
  10. // 2000 -> 135°
  11. // 2500 -> 180°
  12. delay_ms(10);
  13. if (P32)
  14. {
  15. // 延时10ms后,依然低电平,才执行。消除抖动
  16. return;
  17. }
  18. // 修改占空比 0 -> 100
  19. duty_percent += step;
  20. if (duty_percent >= 2500)
  21. {
  22. step = -500;
  23. }
  24. else if (duty_percent <= 500)
  25. {
  26. step = 500;
  27. }
  28. // 计算不同的占空比对应的角度值,用于打印
  29. angle = (duty_percent - 500) * 180.0f / 2000;
  30. printf("duty_percent: %d angle: %.1f \r\n", (int)duty_percent, angle);
  31. // 设置占空比
  32. duty.PWM6_Duty = PERIOD * duty_percent / 20000;
  33. UpdatePwm(PWMB, &duty);
  34. }

main函数

  1. void main()
  2. {
  3. PWMx_Duty duty;
  4. GPIO_config();
  5. UART_config();
  6. PWM_config();
  7. Exti_config();
  8. EA = 1;
  9. P32 = 1;
  10. duty.PWM6_Duty = PERIOD * duty_percent / 20000;
  11. UpdatePwm(PWMB, &duty);
  12. while (1)
  13. {
  14. delay_ms(10);
  15. }
  16. }

完整工程代码:
舵机_PWM.zip