一、铺垫:
自己一路走来,关于STM32最小系统驱动舵机遇到了太多技术壁垒,自己攻克这个问题之后成功实现了将STM32F103C8T6四个定时器全部用来驱动舵机且 0错误0警告,在我实际测试过程中完全可以达到预期效果。现在由我整理上传,不需要投币、不需要加微型加群或者关注什么的,完全免费的分享出来,只期望后来者在学习这个技术的时候,可以有所参考和借鉴。
实物连接如下
基础原理截图
二、配套代码如下:
/******************************************************************************************************************************************
2022/04/18 成功实现四个按键控制舵机转向不同角度
2022/04/19 成功实现四个舵机按键触发,写代码没注意舵机极限角度,貌似烧了一个舵机!
一定要及时备份!!!!
2022/04/20 1、将LED流水灯集成为函数,
2、之所以采用低电平触发是因为所有单片机刚上电IO都会有一个瞬间的高电平脉冲,此处为了防止误触发
?
2022.05.14 让舵机初始在相对0度,左右45度执行,防止超行程损坏;依照此来改定时器1PWM输出
2022.05.16 写好了8路舵机代码,大家可以放心使用。如果有帮到你的话,有时间请给我点个赞,让我更有动力去推进这件事情。拜托啦!!!
2022.07.30 基础知识储备:
TIM1_ETR PA12
TIM1_CH1 PA8 TIM2_CH1_ETR PA0 TIM3_CH1 PA6 TIM4_CH1 PB6
TIM1_CH2 PA9 TIM2_CH2 PA1 TIM3_CH2 PA7 TIM4_CH2 PB7
TIM1_CH3 PA10 TIM2_CH3 PA2 TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 TIM2_CH4 PA3 TIM3_CH4 PB1 TIM4_CH4 PB9
SG90舵机控制方法:
--高电平时间为T1-- 总周期20ms:
—— —— —— ——
| | 高电平时间T1为 0.5ms 舵机转动0°
| | 高电平时间T1为 1ms 舵机转动45°
| | 高电平时间T1为 1.5ms 舵机转动90°
| | 高电平时间T1为 2ms 舵机转动135°
—— —— —— —— —— —— 高电平时间T1为 2.5ms 舵机转动180°
--------总周期 T2为20ms--------
代码思路:1、Ctrl + c 然后 Ctrl + v 哈哈哈哈!
以180度舵机为例定义:高电平时间T1为 1.5ms 舵机转动90° 相对位置为 0° 参数为1850
相对而言: 高电平时间T1为 0.5ms 舵机转动0° 相对位置为 -90° 参数为1945
高电平时间T1为 1ms 舵机转动45° 相对位置为 -45° 参数为1900
高电平时间T1为 1.5ms 舵机转动90° 相对位置为 0° 参数为1850
高电平时间T1为 2ms 舵机转动135° 相对位置为 +45° 参数为1800
高电平时间T1为 2.5ms 舵机转动180° 相对位置为 +90° 参数为1750
//-90°
//TIM_SetCompare1(TIM4,1945);//占空比(2000-1945)/2000*20ms=0.5ms
//-45°
//TIM_SetCompare1(TIM4,1900);//占空比(2000-1900)/2000*20ms=1ms
//0°
//TIM_SetCompare1(TIM4,1850);//占空比(2000-1850)/2000*20ms=1.5ms
//+45°
//TIM_SetCompare1(TIM4,1800);//占空比(2000-1800)/2000*20ms=2ms
//+90°
//TIM_SetCompare1(TIM4,1750);//占空比(2000-1750)/2000*20ms=2.5ms
2022.07.31 1、机械狗项目需要用到的引脚有:
***************** TIM2_CH1_ETR PA0 ***************** *****************
***************** TIM2_CH2 PA1 TIM3_CH2 PA7 *****************
***************** ***************** TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 ***************** TIM3_CH4 PB1 TIM4_CH4 PB9
2、测试按键IO为:
KEY0 PAin(4)
KEY1 PAin(5)
KEY2 PCin(15)
KEY3 PCin(14)
实现思路: 1、按键触发检测,依次从定时器1编写到定时器4 成功实现预期目标,继续推进到所需引脚。
2022.07.31 顺利完成舵机驱动代码的编写,验证成功,继续推进和MPU6050融合。
2022.07.31 写好了16路舵机代码,大家可以放心使用。如果有帮到你的话,有时间请给我点个赞,让我更有动力去推进这件事情。拜托啦!!!
我是B站“简单快乐的123”UP主, 主要推进语音识别和机器人技术,感兴趣的话期待你的关注。
*****************************************************************************************************************************************************************************/
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "key.h"
#include "pwm.h"
#include "pwm1.h"
#include "pwm2.h"
#include "pwm3.h"
int main(void)
{
u8 t=0;
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //初始化与按键连接的硬件接口
TIM1_PWM_Init(1999,719);
TIM2_PWM_Init(1999,719);
TIM3_PWM_Init(1999,719);
TIM4_PWM_Init(1999,719); //PWM频率=72000000/(719+1)/(1999+1)=50hz=20ms
LED0=0; //点亮LED
while(1)
{
t=KEY_Scan(0); //得到键值
switch(t)
{
case KEY0_PRES: //此处将四个通道分别放在四个按键内,在自己设计时,只需要将相应的引脚和通道注释了即可。
TIM_SetCompare1(TIM1,1800);delay_ms(500);LED_Blink_1(); //TIM1_CH1 PA8
TIM_SetCompare1(TIM1,1900);delay_ms(500);LED_Blink_1();
TIM_SetCompare1(TIM2,1900);delay_ms(500);LED_Blink_1(); // TIM2_CH1_ETR PA0
TIM_SetCompare1(TIM2,1850);delay_ms(500);LED_Blink_1();
TIM_SetCompare1(TIM3,1800);delay_ms(500);LED_Blink_1(); // TIM3_CH1 PA6
TIM_SetCompare1(TIM3,1900);delay_ms(500);LED_Blink_1();
TIM_SetCompare1(TIM4,1800);delay_ms(500);LED_Blink_1(); // TIM4_CH1 PB6
TIM_SetCompare1(TIM4,1900);delay_ms(500);LED_Blink_1();
break;
case KEY1_PRES:
TIM_SetCompare2(TIM1,1800);delay_ms(500);LED_Blink_2(); //TIM1_CH2 PA9
TIM_SetCompare2(TIM1,1900);delay_ms(500);LED_Blink_2();
TIM_SetCompare2(TIM2,1900);delay_ms(500);LED_Blink_2(); // TIM2_CH2 PA1
TIM_SetCompare2(TIM2,1850);delay_ms(500);LED_Blink_2();
TIM_SetCompare2(TIM3,1900);delay_ms(500);LED_Blink_2(); //TIM3_CH2 PA7
TIM_SetCompare2(TIM3,1850);delay_ms(500);LED_Blink_2();
TIM_SetCompare2(TIM4,1800);delay_ms(500);LED_Blink_2(); // TIM4_CH2 PB7
TIM_SetCompare2(TIM4,1900);delay_ms(500);LED_Blink_2();
break;
case KEY2_PRES:
TIM_SetCompare3(TIM1,1800);delay_ms(500);LED_Blink_3(); //TIM1_CH3 PA10
TIM_SetCompare3(TIM1,1900);delay_ms(500);LED_Blink_3();
TIM_SetCompare3(TIM2,1800);delay_ms(500);LED_Blink_3(); //TIM2_CH3 PA2
TIM_SetCompare3(TIM2,1900);delay_ms(500);LED_Blink_3();
TIM_SetCompare3(TIM3,1900);delay_ms(500);LED_Blink_3(); //TIM3_CH3 PB0
TIM_SetCompare3(TIM3,1850);delay_ms(500);LED_Blink_3();
TIM_SetCompare3(TIM4,1900);delay_ms(5000);LED_Blink_3(); //TIM4_CH3 PB8
TIM_SetCompare3(TIM4,1850);delay_ms(5000);LED_Blink_3();
break;
case KEY3_PRES:
TIM_SetCompare4(TIM1,1900);delay_ms(500);LED_Blink_4(); //TIM1_CH4 PA11
TIM_SetCompare4(TIM1,1850);delay_ms(500);LED_Blink_4();
TIM_SetCompare4(TIM2,1800);delay_ms(500);LED_Blink_4(); //TIM2_CH4 PA3
TIM_SetCompare4(TIM2,1900);delay_ms(500);LED_Blink_4();
TIM_SetCompare4(TIM3,1900);delay_ms(500);LED_Blink_4(); //TIM3_CH4 PB1
TIM_SetCompare4(TIM3,1850);delay_ms(500);LED_Blink_4();
TIM_SetCompare4(TIM4,1900);delay_ms(500);LED_Blink_4(); //TIM4_CH4 PB9
TIM_SetCompare4(TIM4,1850);delay_ms(500);LED_Blink_4();
break;
default:delay_ms(10);
}
}
}
/*****************************************************************************************************************************************************
基础知识储备:
TIM1_ETR PA12
TIM1_CH1 PA8 TIM2_CH1_ETR PA0 TIM3_CH1 PA6 TIM4_CH1 PB6
TIM1_CH2 PA9 TIM2_CH2 PA1 TIM3_CH2 PA7 TIM4_CH2 PB7
TIM1_CH3 PA10 TIM2_CH3 PA2 TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 TIM2_CH4 PA3 TIM3_CH4 PB1 TIM4_CH4 PB9
SG90舵机控制方法:
--高电平时间为T1-- 总周期20ms:
———————
| | 高电平时间T1为 0.5ms 舵机转动0°
| | 高电平时间T1为 1ms 舵机转动45°
| | 高电平时间T1为 1.5ms 舵机转动90°
| | 高电平时间T1为 2ms 舵机转动135°
————————— 高电平时间T1为 2.5ms 舵机转动180°
--------总周期 T2为20ms--------
代码思路:1、Ctrl + c 然后 Ctrl + v 哈哈哈哈!
|
|
|
--------------
以180度舵机为例定义:高电平时间T1为 1.5ms 舵机转动90° 相对位置为 0° 参数为1850
相对而言:高电平时间T1为 0.5ms 舵机转动0° 相对位置为 -90° 参数为1945
高电平时间T1为 1ms 舵机转动45° 相对位置为 -45° 参数为1900
高电平时间T1为 1.5ms 舵机转动90° 相对位置为 0° 参数为1850
高电平时间T1为 2ms 舵机转动135° 相对位置为 +45° 参数为1800
高电平时间T1为 2.5ms 舵机转动180° 相对位置为 +90° 参数为1750
//-90°
//TIM_SetCompare1(TIM4,1945);//占空比(2000-1945)/2000*20ms=0.5ms
//-45°
//TIM_SetCompare1(TIM4,1900);//占空比(2000-1900)/2000*20ms=1ms
//0°
//TIM_SetCompare1(TIM4,1850);//占空比(2000-1850)/2000*20ms=1.5ms
//+45°
//TIM_SetCompare1(TIM4,1800);//占空比(2000-1800)/2000*20ms=2ms
//+90°
//TIM_SetCompare1(TIM4,1750);//占空比(2000-1750)/2000*20ms=2.5ms
A8 A9 A10 A11
*******************************************************************************/
#include "pwm1.h"
//TIM4 PWM部分初始?
//PWM输出初始化
//arr:自动重装载值 psc:时钟预分频数
/******************************************************************************
* 模块描述
* 项目代号或名称 : 8路PWM输出
* 创建日期 : 2022/05/14
* 创建人 : 志城
* 模块功能 :
* 修改日期 :
* 参考文档 : 精通STM32F4库函数版
* 项目运行平台 : STM32F103C8T6
* 其它 : PWM的一般步骤
实例要求:TIM4来产生PWM输出,并使能TIM4的通道1、2、3、4,逐个重映射到PB6\PB7\PB8\PB9,产生PWM来控制舵机
1、使能定时器和相关IO时钟,调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
2、初始化IO为复用功能输出,调用函数:GPIO_Init();这里我们把PB6\PB7\PB8\PB9用作定时器的PWM输出引脚,要重映射配置即GPIO_Mode_AF_PP;
复用推挽输出;所以需要开启AFIO时钟。
3、初始化定时器,调用函数:TIM_TimeBaseInit();
4、初始化输出比较参数,调用函数:TIM_OCInitStructure();
5、使能预装载寄存器,调用函数:TIM_OC1PreloadConfig();
6、使能定时器,调用函数:TIM_Cmd();
7、设置舵机初始角度,调用函数:TIM_SetCompare1(TIM4,1945);
高级定时器1: TIM_CtrlPWMOutputs(TIM1,ENABLE);//TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
设置定时器的PWM输出时需配置 TIM_OCInitTypeDef结构体参数,输入捕获是用TIM_ICInitTypeDef结构体
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState; 输出状态
uint16_t TIM_OutputNState; 互补通道的输出状态
uint16_t TIM_Pulse; 占空比
uint16_t TIM_OCPolarity; 输出极性
uint16_t TIM_OCNPolarity; 互补通道的输出极性
uint16_t TIM_OCIdleState; 空闲状态
uint16_t TIM_OCNIdleState; 互补通道的输出状态
} TIM_OCInitTypeDef;
*******************************************************************************/
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; //初始化TIM1设置ARR,PSC控制输出PWM的周期
TIM_OCInitTypeDef TIM_OCInitStructure; //PWM通道设置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //使能定时器4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO复用功能时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE); //GPIOA使能
//设置该引脚为复用输出功能 GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11; //此处只用PA11引脚,即TIM1_CH4 通道
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //初始化GPIO
//初始化TIM1,设置TIM4的ARR和PSC
TIM_TimeBaseStruct.TIM_Period = arr; //设置自动重装载周期值 //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStruct.TIM_Prescaler = psc; //设置预分频值 //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0; //设置时钟分割TDTS = Tck_tim
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStruct); //根据指定参数初始化TIMx //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化出输出比较参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM1,&TIM_OCInitStructure); //根据TIMX的参数设定初始化外设 TIM1 ch1 ch2 ch3 ch4
TIM_OC2Init(TIM1,&TIM_OCInitStructure);
TIM_OC3Init(TIM1,&TIM_OCInitStructure);
TIM_OC4Init(TIM1,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM1,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM1,ENABLE); //TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
TIM_Cmd(TIM1,ENABLE); //使能TIM1
//参数设置函数
//作用:在四个通道中选择一个,设置比较值。通常在初始化函数中已经设置了比较值,此函数用于除初始化之外的修改
//TIM_SetCompare1(TIM1,1850);
//TIM_SetCompare2(TIM1,1850);
//TIM_SetCompare3(TIM1,1850);
//TIM_SetCompare4(TIM1,1850);
}
#include "key.h"
#include "delay.h"
//按键初始化函数
//PA15和PC5 设置成输入
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入,平时为高,按下时低电平拉低
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PA5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //PC15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入,平时为高,按下时低电平拉低
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC15
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //PC14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC14
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//KEY2_PRES,KEY2按下
//KEY3_PRES,KEY3按下
//注意此函数有响应优先级,KEY0>KEY1>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==0))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(KEY2==0)return KEY2_PRES;
else if(KEY3==0)return KEY3_PRES;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==1)key_up=1;
return 0;// 无按键按下
}
#include "led.h"
#include "delay.h"
//使能时钟
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PC端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //PC13 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure); //根据设定参数初始化GPIOPC13
GPIO_SetBits(GPIOC,GPIO_Pin_13); //PC13输出高
}
/**************************************************************************
功能描述:等间隔亮灭1次 亮 -- 灭
入口参数:无
返回值:无
*************************************************************************/
void LED_Blink_1()
{
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(100);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
}
/**************************************************************************
功能描述:等间隔亮灭2次 亮 -- 灭 -- 亮 --灭
入口参数:无
返回值:无
*************************************************************************/
void LED_Blink_2()
{
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(200);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(200);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
}
/**************************************************************************
功能描述:等间隔亮灭3次 亮 -- 灭 -- 亮 --灭
入口参数:无
返回值:无
*************************************************************************/
void LED_Blink_3()
{
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(300);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(300);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
GPIO_SetBits(GPIOC,GPIO_Pin_13); //LED输出高
delay_ms(300);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //LED输出低
delay_ms(100);
}
/**************************************************************************
功能描述:等间隔亮灭3次 亮 -- 灭 -- 亮 --灭-- 亮 --灭
入口参数:无
返回值:无
*************************************************************************/
void LED_Blink_4()
{
LED_Blink_2();
LED_Blink_2();
}
void LED_Blink_5()
{
LED_Blink_2();
LED_Blink_3();
}
/*********************************END FILE********************************************/
#include "pwm2.h"
//TIM4 PWM部分初始?
//PWM输出初始化
//arr:自动重装载值 psc:时钟预分频数
/******************************************************************************
* 模块描述
* 项目代号或名称 : 8路PWM输出
* 创建日期 : 2022/05/14
* 创建人 : 志城
* 模块功能 :
* 修改日期 :
* 参考文档 : 精通STM32F4库函数版
* 项目运行平台 : STM32F103C8T6
* 其它 : PWM的一般步骤
实例要求:TIM4来产生PWM输出,并使能TIM4的通道1、2、3、4,逐个重映射到PB6\PB7\PB8\PB9,产生PWM来控制舵机
1、使能定时器和相关IO时钟,调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
2、初始化IO为复用功能输出,调用函数:GPIO_Init();这里我们把PB6\PB7\PB8\PB9用作定时器的PWM输出引脚,要重映射配置即GPIO_Mode_AF_PP;
复用推挽输出;所以需要开启AFIO时钟。
3、初始化定时器,调用函数:TIM_TimeBaseInit();
4、初始化输出比较参数,调用函数:TIM_OCInitStructure();
5、使能预装载寄存器,调用函数:TIM_OC1PreloadConfig();
6、使能定时器,调用函数:TIM_Cmd();
7、设置舵机初始角度,调用函数:TIM_SetCompare1(TIM4,1945);
高级定时器1: TIM_CtrlPWMOutputs(TIM1,ENABLE);//TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
设置定时器的PWM输出时需配置 TIM_OCInitTypeDef结构体参数,输入捕获是用TIM_ICInitTypeDef结构体
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState; 输出状态
uint16_t TIM_OutputNState; 互补通道的输出状态
uint16_t TIM_Pulse; 占空比
uint16_t TIM_OCPolarity; 输出极性
uint16_t TIM_OCNPolarity; 互补通道的输出极性
uint16_t TIM_OCIdleState; 空闲状态
uint16_t TIM_OCNIdleState; 互补通道的输出状态
} TIM_OCInitTypeDef;
基础知识储备:
TIM1_ETR PA12
TIM1_CH1 PA8 TIM2_CH1_ETR PA0 TIM3_CH1 PA6 TIM4_CH1 PB6
TIM1_CH2 PA9 TIM2_CH2 PA1 TIM3_CH2 PA7 TIM4_CH2 PB7
TIM1_CH3 PA10 TIM2_CH3 PA2 TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 TIM2_CH4 PA3 TIM3_CH4 PB1 TIM4_CH4 PB9
*******************************************************************************/
void TIM2_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; //初始化TIM1设置ARR,PSC控制输出PWM的周期
TIM_OCInitTypeDef TIM_OCInitStructure; //PWM通道设置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能定时器4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO复用功能时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE); //GPIOA使能
//设置该引脚为复用输出功能
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //TIM2_CH1/CH2/CH3/CH4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //初始化GPIO
//初始化TIM1,设置TIM4的ARR和PSC
TIM_TimeBaseStruct.TIM_Period = arr; //设置自动重装载周期值 //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStruct.TIM_Prescaler = psc; //设置预分频值 //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0; //设置时钟分割TDTS = Tck_tim
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStruct); //根据指定参数初始化TIMx //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化出输出比较参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM2,&TIM_OCInitStructure); //根据TIMX的参数设定初始化外设 TIM2 ch1 ch2 ch3 ch4
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
TIM_OC3Init(TIM2,&TIM_OCInitStructure);
TIM_OC4Init(TIM2,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM2,ENABLE); //TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
TIM_Cmd(TIM2,ENABLE); //使能TIM2
//参数设置函数
//作用:在四个通道中选择一个,设置比较值。通常在初始化函数中已经设置了比较值,此函数用于除初始化之外的修改
//TIM_SetCompare1(TIM2,1850);
//TIM_SetCompare2(TIM2,1850);
//TIM_SetCompare3(TIM2,1850);
//TIM_SetCompare4(TIM2,1850);
}
#include "pwm3.h"
//TIM4 PWM部分初始?
//PWM输出初始化
//arr:自动重装载值 psc:时钟预分频数
/******************************************************************************
* 模块描述
* 项目代号或名称 : 8路PWM输出
* 创建日期 : 2022/05/14
* 创建人 : 志城
* 模块功能 :
* 修改日期 :
* 参考文档 : 精通STM32F4库函数版
* 项目运行平台 : STM32F103C8T6
* 其它 : PWM的一般步骤
实例要求:TIM4来产生PWM输出,并使能TIM4的通道1、2、3、4,逐个重映射到PB6\PB7\PB8\PB9,产生PWM来控制舵机
1、使能定时器和相关IO时钟,调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
2、初始化IO为复用功能输出,调用函数:GPIO_Init();这里我们把PB6\PB7\PB8\PB9用作定时器的PWM输出引脚,要重映射配置即GPIO_Mode_AF_PP;
复用推挽输出;所以需要开启AFIO时钟。
3、初始化定时器,调用函数:TIM_TimeBaseInit();
4、初始化输出比较参数,调用函数:TIM_OCInitStructure();
5、使能预装载寄存器,调用函数:TIM_OC1PreloadConfig();
6、使能定时器,调用函数:TIM_Cmd();
7、设置舵机初始角度,调用函数:TIM_SetCompare1(TIM4,1945);
高级定时器1: TIM_CtrlPWMOutputs(TIM1,ENABLE);//TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
设置定时器的PWM输出时需配置 TIM_OCInitTypeDef结构体参数,输入捕获是用TIM_ICInitTypeDef结构体
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState; 输出状态
uint16_t TIM_OutputNState; 互补通道的输出状态
uint16_t TIM_Pulse; 占空比
uint16_t TIM_OCPolarity; 输出极性
uint16_t TIM_OCNPolarity; 互补通道的输出极性
uint16_t TIM_OCIdleState; 空闲状态
uint16_t TIM_OCNIdleState; 互补通道的输出状态
} TIM_OCInitTypeDef;
基础知识储备:
TIM1_ETR PA12
TIM1_CH1 PA8 TIM2_CH1_ETR PA0 TIM3_CH1 PA6 TIM4_CH1 PB6
TIM1_CH2 PA9 TIM2_CH2 PA1 TIM3_CH2 PA7 TIM4_CH2 PB7
TIM1_CH3 PA10 TIM2_CH3 PA2 TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 TIM2_CH4 PA3 TIM3_CH4 PB1 TIM4_CH4 PB9
*******************************************************************************/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; //初始化TIM1设置ARR,PSC控制输出PWM的周期
TIM_OCInitTypeDef TIM_OCInitStructure; //PWM通道设置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能定时器4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO复用功能时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOB ,ENABLE); //GPIOA、B使能
//设置该引脚为复用输出功能
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM1_CH1/CH2
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //初始化GPIO
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //TIM3_CH3/CH4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct); //初始化GPIO
//初始化TIM1,设置TIM4的ARR和PSC
TIM_TimeBaseStruct.TIM_Period = arr; //设置自动重装载周期值 //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStruct.TIM_Prescaler = psc; //设置预分频值 //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0; //设置时钟分割TDTS = Tck_tim
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStruct); //根据指定参数初始化TIMx //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化出输出比较参数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM3,&TIM_OCInitStructure); //根据TIMX的参数设定初始化外设 TIM1 ch1 ch2 ch3 ch4
TIM_OC2Init(TIM3,&TIM_OCInitStructure);
TIM_OC3Init(TIM3,&TIM_OCInitStructure);
TIM_OC4Init(TIM3,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM3,ENABLE);//TIM1输出使能 高级定时器绝对得加上因为这句话我搞了一个晚上,后人戒之慎之
TIM_Cmd(TIM3,ENABLE); //使能TIM1
//参数设置函数
//作用:在四个通道中选择一个,设置比较值。通常在初始化函数中已经设置了比较值,此函数用于除初始化之外的修改
//TIM_SetCompare1(TIM3,1850);
//TIM_SetCompare2(TIM3,1850);
//TIM_SetCompare3(TIM3,1850);
//TIM_SetCompare4(TIM3,1850);
}
#include "pwm.h"
//TIM4 PWM部分初始?
//PWM输出初始化
//arr:自动重装载值 psc:时钟预分频数
/******************************************************************************
* 模块描述
* 项目代号或名称 : 8路PWM输出
* 创建日期 : 2022/05/14
* 创建人 : 志城
* 模块功能 :
* 修改日期 :
* 参考文档 : 精通STM32F4库函数版
* 项目运行平台 : STM32F103C8T6
* 其它 : PWM的一般步骤
实例要求:TIM4来产生PWM输出,并使能TIM4的通道1、2、3、4,逐个重映射到PB6\PB7\PB8\PB9,产生PWM来控制舵机
1、使能定时器和相关IO时钟,调用函数:RCC_APB1PeriphClockCmd();RCC_APB2PeriphClockCmd();
2、初始化IO为复用功能输出,调用函数:GPIO_Init();这里我们把PB6\PB7\PB8\PB9用作定时器的PWM输出引脚,要重映射配置即GPIO_Mode_AF_PP;
复用推挽输出;所以需要开启AFIO时钟。
3、初始化定时器,调用函数:TIM_TimeBaseInit();
4、初始化输出比较参数,调用函数:TIM_OCInitStructure();
5、使能预装载寄存器,调用函数:TIM_OC1PreloadConfig();
6、使能定时器,调用函数:TIM_Cmd();
7、设置舵机初始角度,调用函数:TIM_SetCompare1(TIM4,1945);
基础知识储备:
TIM1_ETR PA12
TIM1_CH1 PA8 TIM2_CH1_ETR PA0 TIM3_CH1 PA6 TIM4_CH1 PB6
TIM1_CH2 PA9 TIM2_CH2 PA1 TIM3_CH2 PA7 TIM4_CH2 PB7
TIM1_CH3 PA10 TIM2_CH3 PA2 TIM3_CH3 PB0 TIM4_CH3 PB8
TIM1_CH4 PA11 TIM2_CH4 PA3 TIM3_CH4 PB1 TIM4_CH4 PB9
//TIM3部分重映射
*查看数据手册,引脚的定时器通道是完全映射,还是部分映射
*二者调用参数不相同
*完全映射 :GPIO_FullRemap_TIM4
*部分映射 :GPIO_PartialRemap_TIM4
*
//设置TIM4_CH1重映射到PB6上
//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM4,ENABLE);
*******************************************************************************/
void TIM4_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; //初始化TIM4设置ARR,PSC控制输出PWM的周期
TIM_OCInitTypeDef TIM_OCInitStructure; //PWM通道设置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //使能定时器4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO复用功能时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE); //GPIOB使能
//设置该引脚为复用输出功能
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //TIM4_CH1/CH2/CH3/CH4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct); //初始化GPIO
//初始化TIM4,设置TIM4的ARR和PSC
TIM_TimeBaseStruct.TIM_Period = arr; //设置自动重装载周期值 //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStruct.TIM_Prescaler = psc; //设置预分频值 //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0; //设置时钟分割TDTS = Tck_tim
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStruct); //根据指定参数初始化TIMx //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//输出初始化函数,选择四个通道,初始化PWM输出模式、比较输出极性、 比较输出使能、 比较值CCRx的值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM4,&TIM_OCInitStructure); //根据TIMX的参数设定初始化外设 TIM1 ch1 ch2 ch3 ch4
TIM_OC2Init(TIM4,&TIM_OCInitStructure);
TIM_OC3Init(TIM4,&TIM_OCInitStructure);
TIM_OC4Init(TIM4,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_OC2PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM4, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM4,ENABLE); //使能TIM4
//参数设置函数
//作用:在四个通道中选择一个,设置比较值。通常在初始化函数中已经设置了比较值,此函数用于除初始化之外的修改
//TIM_SetCompare1(TIM4,1850);
//TIM_SetCompare2(TIM4,1850);
//TIM_SetCompare3(TIM4,1850);
//TIM_SetCompare4(TIM4,1850);
}
三、常见问题:
1、舵机没反应:
1. 电压不是5V直流稳压。
2. 按键引脚选择不对,注意是低电平触发。
3. 舵机接线引脚和代码输出引脚不匹配,或舵机坏了。
2、舵机抽搐或发抖:
1.电源不稳。
2. 多个舵机同时工作导致电压不稳。
3、舵机只动了一下就没反应了:
- 超行程损毁,
四、下载链接:
1、使用寄语:
1. 凭良心发布,如果对你有所帮助的话,也希望你能点个赞,我们各取所需,你的鼓励是我持续推进的动力。
2.自己需要几路PWM输出,只需要注释相关引脚和通道即可,在使用过程中注意看好按键低电平触发引脚是哪四个;另注意舵机的供电得是平稳的5V直流电压,期待你的丝滑移植。
2、CSDN免费下载:
免费分享、良心发布、赞后启用
3、Gitee免费下载:
https://gitee.com/ha-city/STM32F103C8T6.git
五、更新日志:
1、2022/07/31 完成基础框架搭建。