stm32c8t6多路舵机PWM输出
对于舵机的简单使用大家都不陌生,在实验中使用的SG90舵机最大转动角度为180度,而对舵机控制的原理是我们每一个使用者都应该了解的,具体的可以从下面这张角度和占空比的关系看看出来。(本次实验基于前一篇多路串口使用改写)通过串口中断接收数据来控制不同的舵机转动,对串口使用不太会的小伙伴前看前面一篇文章
舵机转动原理:
舵机的控制一般需要一个20ms的时基脉冲,该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度舵机为例,那么对应的控制关系是这样的:
可以发现一个规律:角度每次增加45度,高电平时间相应增加0.5ms
总周期 - 高电平脉冲 = 低电平脉冲
舵机角度的转动就是通过高、低脉冲的变化实现的
硬件接线引脚对应关系
了解完舵机的基本原理后,下面我们就进行pwm的配置。在stm32c8t6最小系统中使用pwm的时候我们需要进行定时器相应的重映射,每个定时器有四路PWM输出,大家可以根据自己情况配置不同的定时器进行多路PWM的输出,下面是重映射对应关系。
本次实验中使用的是定时器3的4个通道
TIM3_CH1---------------------->PB4
TIM3_CH2---------------------->PB3
TIM3_CH3---------------------->PB0
TIM3_CH4---------------------->PB1
自己在实验的时候搞了很久,配置没问题一开始就是不能控制多路Pwm,在配置的四路中有时候只有两路,甚至一路可以,然后就改成其他定时器重新进行映射,结果还是一样不能同时控制,折腾了很久时间,后面突然又可以了,就挺离谱的,代码一模一样的有时候一下就成功,有时候要好久才成功。
所以各位小伙伴不管做什么都一定要有耐心哦
实验源码
/*--------------------servo.h-------------------*/
#ifndef __SERVO_H
#define __SERVO_H
#include "stm32f10x.h"
#define SERVO_GPIO_PORT GPIOB
#define SERVO_GPIO_CLK RCC_APB2Periph_GPIOB
#define SERVO_GPIO_PIN (GPIO_Pin_5 |GPIO_Pin_0 |GPIO_Pin_1| GPIO_Pin_4)
void servo_init(void);
void Servo_Run2(uint16_t angle);
void Servo_Run(uint16_t angle);
void Servo_Run3(uint16_t angle);
void Servo_Run4(uint16_t angle);
#endif /* __SERVO_H */
/*-----------------------servo.c---------------------*/
#include "./servo/servo.h"
/**
* 功能:舵机初始化函数
* 参数:None
* 返回值:None
*/
void servo_init(void)
{
GPIO_InitTypeDef GPIO_Init_Structure; //定义GPIO初始化结构体
TIM_TimeBaseInitTypeDef Timer_Init_Structure; //定义定时器初始化结构体
TIM_OCInitTypeDef Timer_OC_Init_Structure; //定义定时器输出PWM结构体
RCC_APB2PeriphClockCmd(SERVO_GPIO_CLK, ENABLE); //开Timer3通道2GPIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开复用时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //选择部分重映射
//配置GPIO初始化结构体
GPIO_Init_Structure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init_Structure.GPIO_Pin = SERVO_GPIO_PIN;
GPIO_Init_Structure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SERVO_GPIO_PORT, &GPIO_Init_Structure);
//配置通用定时器初始化结构体
Timer_Init_Structure.TIM_ClockDivision = TIM_CKD_DIV1; //设置分频系数,通常采用默认的分频
Timer_Init_Structure.TIM_CounterMode = TIM_CounterMode_Up; //设置捕获模式为向上计数(基本定时器仅有向上计数)
Timer_Init_Structure.TIM_Period = 200-1; //设置重装载值(在预分频值为72000下,10000为一秒)
Timer_Init_Structure.TIM_Prescaler = 7200-1; //设置初始化预分频值为7200
// Timer_Init_Structure.TIM_RepetitionCounter = 0 //重复计数器,仅高级定时器需要设置
TIM_TimeBaseInit(TIM3, &Timer_Init_Structure);
//配置定时器输出PWM结构体
Timer_OC_Init_Structure.TIM_OCMode = TIM_OCMode_PWM1; //定时器模式选择Timer脉冲宽度调制模式 1
Timer_OC_Init_Structure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
Timer_OC_Init_Structure.TIM_OCPolarity = TIM_OCPolarity_Low; //选择有效输出为低电平
TIM_OC1Init(TIM3, &Timer_OC_Init_Structure); //PB4
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC2Init(TIM3, &Timer_OC_Init_Structure); //PB5
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC3Init(TIM3, &Timer_OC_Init_Structure); //PB0
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC4Init(TIM3, &Timer_OC_Init_Structure); //PB1
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_Cmd(TIM3, ENABLE);
}
/**
* 功能:舵机驱动(可从0~180,每45度旋转一次)
* 参数:angle ;舵机旋转度数(相对角度)
* 返回值:None
*/
void Servo_Run(uint16_t angle)
{
switch(angle)
{
case 180 :TIM_SetCompare2(TIM3, 175); break;//对应180度
case 135 :TIM_SetCompare2(TIM3, 180); break;//对应135度
case 90 :TIM_SetCompare2(TIM3, 185); break;//对应90度
case 45 :TIM_SetCompare2(TIM3, 190); break;//对应45度
case 0 :TIM_SetCompare2(TIM3, 195); break;//对应0度
}
}
void Servo_Run2(uint16_t angle)
{
switch(angle)
{
case 180 :TIM_SetCompare3(TIM3, 175); break;//对应180度
case 135 :TIM_SetCompare3(TIM3, 180); break;//对应135度
case 90 :TIM_SetCompare3(TIM3, 185); break;//对应90度
case 45 :TIM_SetCompare3(TIM3, 190); break;//对应45度
case 0 :TIM_SetCompare3(TIM3, 195); break;//对应0度
}
}
void Servo_Run3(uint16_t angle)
{
switch(angle)
{
case 180 :TIM_SetCompare4(TIM3, 175); break;//对应180度
case 135 :TIM_SetCompare4(TIM3, 180); break;//对应135度
case 90 :TIM_SetCompare4(TIM3, 185); break;//对应90度
case 45 :TIM_SetCompare4(TIM3, 190); break;//对应45度
case 0 :TIM_SetCompare4(TIM3, 195); break;//对应0度
}
}
void Servo_Run4(uint16_t angle)
{
switch(angle)
{
case 180 :TIM_SetCompare1(TIM3, 175); break;//对应180度
case 135 :TIM_SetCompare1(TIM3, 180); break;//对应135度
case 90 :TIM_SetCompare1(TIM3, 185); break;//对应90度
case 45 :TIM_SetCompare1(TIM3, 190); break;//对应45度
case 0 :TIM_SetCompare1(TIM3, 195); break;//对应0度
}
}
如果只有单路PWM输出(通道1)
TIM_OC1Init(TIM3, &Timer_OC_Init_Structure); //PB4
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
在上面初始化中写上述配置的任意一个都可以
一定要进行我们选择的通道要和pwm比较所对应
这里如果我们选择的通道1 则下面也选择1的比较
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
串口代码在前一篇已经分享了这里就不重复了
/*---------------------main.c---------------------*/
#include "stm32f10x.h"
#include "usart.h"
#include "servo.h"
#include "delay.h"
int main()
{
Init_Usart();
servo_init();
led_init();
Servo_Run(45);
Servo_Run2(45);
Servo_Run3(45);
Servo_Run4(45);
printf("====================串口测试=====================\n");
USART_Send_String(USART1,"usart1 test\n");
//USART_Send_String(USART2,"usart1 test\n");
//USART_Send_String(USART3,"usart1 test\n");
while(1)
{
}
}
//串口1中断
void USART1_IRQHandler(void)
{
char temp;
//USART_Send_String(USART1,"\nUsart1 interrupt test\n");
if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET)
{
temp = USART_ReceiveData(USART1);
if(temp == '1')
{
//GPIO_ResetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo1 open\n");
Servo_Run(0);
//delay_ms(500);
USART_Send_String(USART1,"servo1 over\n");
}
else if(temp == '2')
{
//GPIO_ResetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo1 close\n");
Servo_Run(45);
//delay_ms(500);
USART_Send_String(USART1,"servo1 over\n");
printf("------------servo1-----------\n");
}
else if(temp == '3')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo2 open\n");
Servo_Run2(0);
//delay_ms(500);
USART_Send_String(USART1,"servo2 over\n");
}
else if(temp == '4')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo2 close\n");
Servo_Run2(45);
//delay_ms(500);
USART_Send_String(USART1,"servo2 over\n");
printf("------------servo2-----------\n");
}
else if(temp == '5')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo3 open\n");
Servo_Run3(0);
//delay_ms(500);
USART_Send_String(USART1,"servo3 over\n");
}
else if(temp == '6')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo3 close\n");
Servo_Run3(45);
//delay_ms(500);
USART_Send_String(USART1,"servo3 over\n");
printf("------------servo3-----------\n");
}
else if(temp == '7')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo4 open\n");
Servo_Run4(0);
//delay_ms(500);
USART_Send_String(USART1,"servo4 over\n");
}
else if(temp == '8')
{
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
USART_Send_String(USART1,"servo4 open\n");
Servo_Run4(45);
//delay_ms(500);
USART_Send_String(USART1,"servo4 over\n");
printf("------------servo4-----------\n");
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断
}
}
实验结果
注意点
上述串口中断接收时我最开始在每接收完数据了进行短暂延时在打印相关信息,不过在实验的现象是,只能接收一次就卡在了延时的上面,最后就把延时去掉了,大家可以看看,自己哪里有没有这种现象,有错还望大佬指正。
工程源码链接需要自取
链接:https://pan.baidu.com/s/1AMKLH_A98tMibWFsJsN7sQ
提取码:52x6
--来自百度网盘超级会员V4的分享