舵机的工作原理
以9g舵机为例有三根线:电源正极 电源负极 信号线(输入PWM)
我么可以通过PWM来控制舵机转动,其中改变PWM的占空比可以控制舵机角度,如下图所示:

使用PWM控制舵机的要求
以9g舵机为例,要求控制PWM为50Hz 周期为20ms
代码部分
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//启动TIM GPIO
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//需要使用GPIO来输出PWM PWM来自片上外设TIM 所以设置为复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置TIM
//CK_PSC时钟频率 PSC分频 ARR自动重装
//PWM频率 = CK_PSC/(PSC+1)/(ARR+1)
//PWM占空比 = CCR/(ARR+1)
//PWM分辨率 = 1/(ARR+1)
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //CK_PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
//初始化赋值 为了代码维护性
TIM_OCStructInit(&TIM_OCInitStructure);
//选择输出模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//选择电频输出状态 高电频 低电平 这里是高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//输出状态 使能 使能 这里是使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//占空比
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);
}
//更改CCR占空比用的
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2,Compare);
}
控制舵机角度 servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init()
{
PWM_Init();
}
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 *2000 + 500);
}
按钮部分 Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
/**
* 函 数:按键初始化
* 参 数:无
* 返 回 值:无
*/
void Key_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB1和PB11引脚初始化为上拉输入
}
/**
* 函 数:按键获取键码
* 参 数:无
* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下
* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0; //定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 1; //置键码为1
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 2; //置键码为2
}
return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "servo.h"
uint8_t Key_Num;
float Angle;
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init();
Servo_Init();
OLED_ShowString(1,1,"Angle:");
while (1){
Key_Num = Key_GetNum();
if(Key_Num == 1)
{
Angle += 30;
if(Angle > 180)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1, 7, Angle, 3);
}
}
评论 (0)