前言
在单片机中对于PWM波的运用可谓是非常广泛,控制舵机,电机啥的都是采用配置相关引脚的PWM波对相关外设进行控制,这节我们来实现对于舵机的控制。
工作原理说明:
1、工作原理:
STM32控制舵机的工作原理是利用控制电路接收信号源的控制脉冲,并驱动电机转动。在齿轮组的作用下,电机的速度会被成大倍数缩小,并将电机的输出扭矩放大相应倍数,然后输出。电位器和齿轮组的末级一起转动,同时测量舵机轴的转动角度。电路板检测并根据电位器判断舵机转动角度,从而控制舵机转动到目标角度或保持在该角度。
2、注意事项:在使用STM32控制舵机时,需要根据实际应用场景选择合适的舵机型号和规格,以满足所需的角度控制、速度控制等要求。
在连接舵机到STM32的电路板时,需要注意接线方式和正负极性,避免短路和反向连接。
在控制舵机时,需要根据实际需求设置PWM信号的频率、占空比等参数,以满足舵机的速度和位置控制要求。
在使用STM32控制舵机时,需要注意安全问题,避免在恶劣环境下使用,以免造成设备损坏或安全隐患。
因为STM32定时器不同引脚之间存在无重定向、部分重定向、完全重定向的不同,所以在配置时也大不相同。我们来讲完全重定向和一般情况下的控制方式。(此处是一般控制方式)
代码部分
初始化配置函数和定时器中断函数都是在同一个C文件当中,在这C文件里调用后面的控制角度的bsp_servo.h文件
初始化配置函数
void TIM1_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能时钟
//定时器1初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期值
TIM_TimeBaseStructure.TIM_Prescaler = (psc-1); //设置时钟的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim //36Mhz
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //定时器向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //关闭重复计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //初始化定时器1寄存器
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE ); //使能定时器1通道
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; //TIM1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //此优先级为3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能IRQ通道
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIMx
}
定时器中断函数
int num = 0;
void TIM1_UP_IRQHandler(void) //TIM1中断
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) //判断TIM1中断是在发生
{
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除TIM1中断标志
num++;
#ifdef USE_SERVO_J1
if(num <= (Angle_J1 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J1_PORT, Servo_J1_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J1_PORT, Servo_J1_PIN ); //置低舵机引脚电平
}
#endif
#ifdef USE_SERVO_J2
if(num <= (Angle_J2 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J2_PORT, Servo_J2_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J2_PORT, Servo_J2_PIN ); //置低舵机引脚电平
}
#endif
#ifdef USE_SERVO_J3
if(num <= (Angle_J3 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J3_PORT, Servo_J3_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J3_PORT, Servo_J3_PIN ); //置低舵机引脚电平
}
#endif
#ifdef USE_SERVO_J4
if(num <= (Angle_J4 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J4_PORT, Servo_J4_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J4_PORT, Servo_J4_PIN ); //置低舵机引脚电平
}
#endif
#ifdef USE_SERVO_J5
if(num <= (Angle_J5 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J5_PORT, Servo_J5_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J5_PORT, Servo_J5_PIN ); //置低舵机引脚电平
}
#endif
#ifdef USE_SERVO_J6
if(num <= (Angle_J6 * 11 + 500)/10)
{
GPIO_SetBits(Servo_J6_PORT, Servo_J6_PIN ); //置高舵机引脚电平
}
else
{
GPIO_ResetBits(Servo_J6_PORT, Servo_J6_PIN ); //置低舵机引脚电平
}
#endif
if(num == 2000) //2000*10=20ms 20ms为一个周期,这儿num的值可以根据自己需求进行改变
{
num = 0;
}
}
}
控制角度函数 (bsp_servo.c)
为避免在主函数里面进行复杂的代码编写,我们在这对舵机角度的控制进行了编写函数的过程以便于在主函数里面进行调用就可以对舵机角度进行控制,减少代码编写量
bsp_servo.h文件
#ifndef __BSP_SERVO_H__
#define __BSP_SERVO_H__
#include "stm32f10x.h"
/*对舵机相关引脚进行宏定义*/
#define USE_SERVO_J1
#define USE_SERVO_J2
#define USE_SERVO_J3
#define USE_SERVO_J4
#define USE_SERVO_J5
#define USE_SERVO_J6
extern int Angle_J1;
extern int Angle_J2;
extern int Angle_J3;
extern int Angle_J4;
extern int Angle_J5;
extern int Angle_J6;
#define Servo_J1_PIN GPIO_Pin_11
#define Servo_J2_PIN GPIO_Pin_13
#define Servo_J3_PIN GPIO_Pin_14
#define Servo_J4_PIN GPIO_Pin_15
#define Servo_J5_PIN GPIO_Pin_8
#define Servo_J6_PIN GPIO_Pin_1
#define Servo_J1_PORT GPIOA
#define Servo_J2_PORT GPIOB
#define Servo_J3_PORT GPIOB
#define Servo_J4_PORT GPIOB
#define Servo_J5_PORT GPIOA
#define Servo_J6_PORT GPIOA
#define Servo_J1_RCC RCC_APB2Periph_GPIOA
#define Servo_J2_RCC RCC_APB2Periph_GPIOB
#define Servo_J3_RCC RCC_APB2Periph_GPIOB
#define Servo_J4_RCC RCC_APB2Periph_GPIOB
#define Servo_J5_RCC RCC_APB2Periph_GPIOA
#define Servo_J6_RCC RCC_APB2Periph_GPIOA
void Servo_J1(int v_iAngle);/*控制通道一舵机角度函数*/
void Servo_J2(int v_iAngle);/*控制通道二舵机角度函数*/
void Servo_J3(int v_iAngle);/*控制通道三舵机角度函数*/
void Servo_J4(int v_iAngle);/*控制通道四舵机角度函数*/
void Servo_J5(int v_iAngle);/*控制通道五舵机角度函数*/
void Servo_J6(int v_iAngle);/*控制通道六舵机角度函数*/
#endif
bsp_servo.c文件
#include "bsp_servo.h"
#include "delay.h"
/*初始角度*/
int Angle_J1 = 0;
int Angle_J2 = 0;
int Angle_J3 = 0;
int Angle_J4 = 0;
int Angle_J5 = 0;
int Angle_J6 = 0;
void Servo_J1(int v_iAngle)/*控制通道一舵机角度函数*/
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500; //PWM波模拟值
GPIO_SetBits(Servo_J1_PORT, Servo_J1_PIN ); //将通道一引脚置高
delay_us(pulsewidth); //根据脉宽值进行延时
GPIO_ResetBits(Servo_J1_PORT, Servo_J1_PIN ); //将通道一引脚置低
delay_ms(20 - pulsewidth/1000); //根据前面的num值确定的周期减去脉宽值进行延时
}
/****后面的Servo_J2、3、4、5、6(int v_iAngle)和Servo_J1(int v_iAngle)一模一样,都是舵机角度控制函数***/
void Servo_J2(int v_iAngle)/
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500;
GPIO_SetBits(Servo_J2_PORT, Servo_J2_PIN );
delay_us(pulsewidth);
GPIO_ResetBits(Servo_J2_PORT, Servo_J2_PIN );
delay_ms(20 - pulsewidth/1000);
}
void Servo_J3(int v_iAngle)
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500;
GPIO_SetBits(Servo_J3_PORT, Servo_J3_PIN );
delay_us(pulsewidth);
GPIO_ResetBits(Servo_J3_PORT, Servo_J3_PIN );
delay_ms(20 - pulsewidth/1000);
}
void Servo_J4(int v_iAngle)
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500;
GPIO_SetBits(Servo_J4_PORT, Servo_J4_PIN );
delay_us(pulsewidth);
GPIO_ResetBits(Servo_J4_PORT, Servo_J4_PIN );
delay_ms(20 - pulsewidth/1000);
}
void Servo_J5(int v_iAngle)
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500;
GPIO_SetBits(Servo_J5_PORT, Servo_J5_PIN );
delay_us(pulsewidth);
GPIO_ResetBits(Servo_J5_PORT, Servo_J5_PIN );
delay_ms(20 - pulsewidth/1000);
}
void Servo_J6(int v_iAngle)
{
int pulsewidth;
pulsewidth = (v_iAngle * 11) + 500;
GPIO_SetBits(Servo_J6_PORT, Servo_J6_PIN );
delay_us(pulsewidth);
GPIO_ResetBits(Servo_J6_PORT, Servo_J6_PIN );
delay_ms(20 - pulsewidth/1000);
}
主函数调用
因为这我们一次性吧TIM1的无重定向引脚全配置完了,所在下面给的是可以同时控制6个舵机的函数 ,用户可以根据自己的需要对控制函数等进行不同的调用
int main(void)
{
TIM1_Int_Init(9, 72); /*计数到10为10us Tout = (9+1)*(71+1)/72M = 10us ,这儿根据需求自己修改*/
while (1)
{
Servo_J1(90);
Servo_J2(50);
Servo_J3(50);
Servo_J4(50);
Servo_J5(50);
Servo_J6(50);
Angle_J1 = 0;
Angle_J2 = 0;
Angle_J3 = 0;
Angle_J4 = 0;
Angle_J5 = 0;
Angle_J6 = 0;
delay_ms(1000);
delay_ms(1000);
}
}
不出意外只需要这些代码就能够正常运行,但因为不是完整的工程,所以在每个人在运用的过程中有些人可能因为微弱的不同机会出现错误啥的,所在需要完整工程的我这也提供了。
https://pan.quark.cn/s/b1666b82f5c0,需要的小伙伴自行下载