舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。使用stm32控制机器时,经常要用到舵机,如使某个部位转到特定的角度,或者在行进过程中的方向控制,这篇文章将以stm32F103C8T6为例,从分析舵机的原理出发,到介绍使用stm的TIM功能输出PWM波,掌握理论后进行实战,先控制一个舵机上手,然后控制多个舵机。
一、舵机的原理
一个舵机由变速齿轮箱,电位器,威廉希尔官方网站
板与直流电机组成。电机的高速、短周期运动由齿轮箱转换为慢速、长周期的运动,最终到最外端的齿轮,齿轮的转动带动电位器转动,电位器的电位与信号线进行比较,从而实现转动到特定角度的功能。
伺服电机由信号线输入的PWM信号控制。信号的频率应为50Hz,周期为20ms,PWM的占空比决定了舵机旋转到的角度。
二、如何控制一个舵机
舵机的控制由一个脉冲宽度调制信号(PWM波)来实现,该信号在这个实验里使用stm32来发出。
通常来说,1ms的脉宽对应0度位置,1.5ms对应90度,2ms对应180度。这一数据会由于舵机型号不同而略有差异。
三、使用stm32TIM功能输出PWM波来控制舵机
根据STM32F10xxx参考手册,
通用TIM 定时器与高级TIM功能包括:
● 16位向上、向下、向上/向下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步威廉希尔官方网站
● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器威廉希尔官方网站
● 触发输入作为外部时钟或者按周期的电流管理
在这里使用4个独立通道:PWM生成,要注意的是,基本定时器并不具备该功能。
不同型号的芯片包含的TIM模块可能不同,使用时建议先查询数据手册。
配置时基单元
要使用该功能,首先要配置时基单元,时基单元可以理解为定时器的心脏,它主要包含:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)
之间的关系是:
驱动定时器的时钟:CK_CNT=CK_INT/(PSC+1),CNT开始计数,由ARR决定产生更新事件。
预分频器
预分频器可以将计数器的时钟频率按1到65536之间的任意值进行分频,选中的时钟由分频器分频后进行计数,在这里使用内部时钟INT,经过分频后CK_CNT=CK_INT/(PSC+1)
那么如何得知内部时钟的频率呢?参考时钟树,可以发现:
在STM32F10xxx中,TIM1和TIM8挂载在APB2总线上,其他都挂载到APB1总线上
定时器时钟频率分配由硬件按以下2种情况自动设置: 1. 如果相应的APB预分频系数是1,定时器的时钟频率与所在APB总线频率保持一致2. 否则,定时器的时钟频率被设为与其相连的APB总线频率的2倍。
在标准库中,APB1与APB2预分频系数是2,所以定时器时钟TIMxCLK=36MHz*2=72MHz
计数器模式
在向上计数模式中,计数器CNT从0计数到自动加载值(ARR中的内容),然后重新从0开始计数并且产生一个计数器溢出事件。
这里还有一个自动加载影子寄存器,简单来说就是当写入新数据至影子计数器时,若ARPE=0,则ARR寄存器值立即装入,若ARPE=1,要等到下一个更新事件才会写入。
在向下计数模式中,计数器从自动加载值开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出的事件。
在这里使用向上或者向下计数模式
**
在PWM模式(模式1或模式2)下,TIMx_CNT和TIMx_CCRx始终在进行比较。
PWM模式1与PWM模式2的描述如下:(见STM32F10xxx参考手册)
PWM模式1——在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
PWM模式2- 在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。
综上所述,产生PWM波的步骤如下:
1.配置预分频寄存器PSC的值为(7200-1),内部时钟为72MHz,则计数一次的时间为(7200/72000000)s=0.1ms;
2.配置自动装载寄存器ARR的值为(200-1),那么产生一次计数中断的周期为20ms,与驱动舵机需要的周期相符
3.使用输出比较功能的PWM模式,更改CCR寄存器的值即可改变占空比,如前面所说的,脉宽为1ms对应0°,2ms对应180°
四、实战——先从控制一个舵机入手
在这里,将以十分常见的SG90舵机为例(下图),使用stm32C8T6驱动
长这个样子
首先,我们新建bsp_servo.h与bsp_servo.c文件
本实验使用TIM3的输出通道1,在bsp_servo.h文件中定义宏
/*先使用TIM1的通道1*/
#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
//配置预分频寄存器PSC的值为(7200-1),内部时钟为72MHz,则计数一次的时间为(7200/72000000)s=0.1ms;
#define GENERAL_TIM_Prescaler (72000-1)
//配置自动装载寄存器ARR的值为(200-1),那么产生一次计数中断的周期为20ms,与驱动舵机需要的周期相符
#define GENERAL_TIM_Period (20-1)
// TIM3 输出比较通道1
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_6
之后来到bsp_servo.c文件,配置GPIO端口为复用,输出端口为PA6,具体使用哪个端口可以参考对应型号的data sheet。
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
}
使用TIM功能的PWM模式
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
定义一个初始化函数,同时初始化时基与输出比较
void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}
这样初始化配置就完成了,接下来来到main.c使用TIM_SetComparex(TIMx, Compare);来配置占空比。
首先定义一个延时函数,使舵机有时间转动特定的角度
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
为了方便使用定义一个宏
#define SOFT_DELAY Delay(0x0FFFFF);
前面将TIM3配置为PWM模式1,即为向上计数,CNT
int main(void)
{
GENERAL_TIM_Init();
while(1)
{
for(int i = 5;i < 26;i++)//结果测试,设置CCR的值为5-6对应舵机旋转到0°-180°
{
TIM_SetCompare1(GENERAL_TIM , i);
SOFT_DELAY;
}
}
}
此时将舵机上电,信号线连接到PA6,可以看到舵机旋转
五、控制多个舵机
控制一个舵机成功后,进一步控制多个舵机,先以TIM3增加输出通道为例,这里增加Channel2,输出端口配置为PA7
// TIM3 输出比较通道2
#define GENERAL_TIM_CH2_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH2_PORT GPIOA
#define GENERAL_TIM_CH2_PIN GPIO_Pin_7
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
}
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 输出比较通道 2
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
然后来到main.c,这里是使用TIM_SetCompare2(GENERAL_TIM ,Compare);对于函数
TIM_SetComparex(TIMy,Compare);设置的是TIMy模块CHx的脉宽。
int main(void)
{
GENERAL_TIM_Init();
while(1)
{
for(int i = 5;i<26;i++)
{
TIM_SetCompare1(GENERAL_TIM ,i);
SOFT_DELAY;
}
for(int i = 5;i<26;i++)
{
TIM_SetCompare2(GENERAL_TIM ,i);
SOFT_DELAY;
}
}
}
可以看到两个舵机按照程序转动起来。
之后要增加控制舵机的数据,可以先增加TIMx的通道数,一个通用或者高级定时器有四个输出通道,还可以增加新的TIM,达到控制多个舵机的目的。为了避免篇幅过于冗长,这里不挂出具体代码。
最后再来一个挂载四个的。
舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。使用stm32控制机器时,经常要用到舵机,如使某个部位转到特定的角度,或者在行进过程中的方向控制,这篇文章将以stm32F103C8T6为例,从分析舵机的原理出发,到介绍使用stm的TIM功能输出PWM波,掌握理论后进行实战,先控制一个舵机上手,然后控制多个舵机。
一、舵机的原理
一个舵机由变速齿轮箱,电位器,威廉希尔官方网站
板与直流电机组成。电机的高速、短周期运动由齿轮箱转换为慢速、长周期的运动,最终到最外端的齿轮,齿轮的转动带动电位器转动,电位器的电位与信号线进行比较,从而实现转动到特定角度的功能。
伺服电机由信号线输入的PWM信号控制。信号的频率应为50Hz,周期为20ms,PWM的占空比决定了舵机旋转到的角度。
二、如何控制一个舵机
舵机的控制由一个脉冲宽度调制信号(PWM波)来实现,该信号在这个实验里使用stm32来发出。
通常来说,1ms的脉宽对应0度位置,1.5ms对应90度,2ms对应180度。这一数据会由于舵机型号不同而略有差异。
三、使用stm32TIM功能输出PWM波来控制舵机
根据STM32F10xxx参考手册,
通用TIM 定时器与高级TIM功能包括:
● 16位向上、向下、向上/向下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步威廉希尔官方网站
● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器威廉希尔官方网站
● 触发输入作为外部时钟或者按周期的电流管理
在这里使用4个独立通道:PWM生成,要注意的是,基本定时器并不具备该功能。
不同型号的芯片包含的TIM模块可能不同,使用时建议先查询数据手册。
配置时基单元
要使用该功能,首先要配置时基单元,时基单元可以理解为定时器的心脏,它主要包含:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)
之间的关系是:
驱动定时器的时钟:CK_CNT=CK_INT/(PSC+1),CNT开始计数,由ARR决定产生更新事件。
预分频器
预分频器可以将计数器的时钟频率按1到65536之间的任意值进行分频,选中的时钟由分频器分频后进行计数,在这里使用内部时钟INT,经过分频后CK_CNT=CK_INT/(PSC+1)
那么如何得知内部时钟的频率呢?参考时钟树,可以发现:
在STM32F10xxx中,TIM1和TIM8挂载在APB2总线上,其他都挂载到APB1总线上
定时器时钟频率分配由硬件按以下2种情况自动设置: 1. 如果相应的APB预分频系数是1,定时器的时钟频率与所在APB总线频率保持一致2. 否则,定时器的时钟频率被设为与其相连的APB总线频率的2倍。
在标准库中,APB1与APB2预分频系数是2,所以定时器时钟TIMxCLK=36MHz*2=72MHz
计数器模式
在向上计数模式中,计数器CNT从0计数到自动加载值(ARR中的内容),然后重新从0开始计数并且产生一个计数器溢出事件。
这里还有一个自动加载影子寄存器,简单来说就是当写入新数据至影子计数器时,若ARPE=0,则ARR寄存器值立即装入,若ARPE=1,要等到下一个更新事件才会写入。
在向下计数模式中,计数器从自动加载值开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出的事件。
在这里使用向上或者向下计数模式
**
在PWM模式(模式1或模式2)下,TIMx_CNT和TIMx_CCRx始终在进行比较。
PWM模式1与PWM模式2的描述如下:(见STM32F10xxx参考手册)
PWM模式1——在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
PWM模式2- 在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。
综上所述,产生PWM波的步骤如下:
1.配置预分频寄存器PSC的值为(7200-1),内部时钟为72MHz,则计数一次的时间为(7200/72000000)s=0.1ms;
2.配置自动装载寄存器ARR的值为(200-1),那么产生一次计数中断的周期为20ms,与驱动舵机需要的周期相符
3.使用输出比较功能的PWM模式,更改CCR寄存器的值即可改变占空比,如前面所说的,脉宽为1ms对应0°,2ms对应180°
四、实战——先从控制一个舵机入手
在这里,将以十分常见的SG90舵机为例(下图),使用stm32C8T6驱动
长这个样子
首先,我们新建bsp_servo.h与bsp_servo.c文件
本实验使用TIM3的输出通道1,在bsp_servo.h文件中定义宏
/*先使用TIM1的通道1*/
#define GENERAL_TIM TIM3
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
//配置预分频寄存器PSC的值为(7200-1),内部时钟为72MHz,则计数一次的时间为(7200/72000000)s=0.1ms;
#define GENERAL_TIM_Prescaler (72000-1)
//配置自动装载寄存器ARR的值为(200-1),那么产生一次计数中断的周期为20ms,与驱动舵机需要的周期相符
#define GENERAL_TIM_Period (20-1)
// TIM3 输出比较通道1
#define GENERAL_TIM_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH1_PORT GPIOA
#define GENERAL_TIM_CH1_PIN GPIO_Pin_6
之后来到bsp_servo.c文件,配置GPIO端口为复用,输出端口为PA6,具体使用哪个端口可以参考对应型号的data sheet。
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
}
使用TIM功能的PWM模式
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
定义一个初始化函数,同时初始化时基与输出比较
void GENERAL_TIM_Init(void)
{
GENERAL_TIM_GPIO_Config();
GENERAL_TIM_Mode_Config();
}
这样初始化配置就完成了,接下来来到main.c使用TIM_SetComparex(TIMx, Compare);来配置占空比。
首先定义一个延时函数,使舵机有时间转动特定的角度
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
为了方便使用定义一个宏
#define SOFT_DELAY Delay(0x0FFFFF);
前面将TIM3配置为PWM模式1,即为向上计数,CNT
int main(void)
{
GENERAL_TIM_Init();
while(1)
{
for(int i = 5;i < 26;i++)//结果测试,设置CCR的值为5-6对应舵机旋转到0°-180°
{
TIM_SetCompare1(GENERAL_TIM , i);
SOFT_DELAY;
}
}
}
此时将舵机上电,信号线连接到PA6,可以看到舵机旋转
五、控制多个舵机
控制一个舵机成功后,进一步控制多个舵机,先以TIM3增加输出通道为例,这里增加Channel2,输出端口配置为PA7
// TIM3 输出比较通道2
#define GENERAL_TIM_CH2_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM_CH2_PORT GPIOA
#define GENERAL_TIM_CH2_PIN GPIO_Pin_7
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH1_PORT, &GPIO_InitStructure);
// 输出比较通道1 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM_CH2_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_CH2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GENERAL_TIM_CH2_PORT, &GPIO_InitStructure);
}
static void GENERAL_TIM_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM_Period;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM_Prescaler;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);
/*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置为PWM模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 输出通道电平极性配置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 输出比较通道 1
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 输出比较通道 2
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(GENERAL_TIM, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(GENERAL_TIM, TIM_OCPreload_Enable);
// 使能计数器
TIM_Cmd(GENERAL_TIM, ENABLE);
}
然后来到main.c,这里是使用TIM_SetCompare2(GENERAL_TIM ,Compare);对于函数
TIM_SetComparex(TIMy,Compare);设置的是TIMy模块CHx的脉宽。
int main(void)
{
GENERAL_TIM_Init();
while(1)
{
for(int i = 5;i<26;i++)
{
TIM_SetCompare1(GENERAL_TIM ,i);
SOFT_DELAY;
}
for(int i = 5;i<26;i++)
{
TIM_SetCompare2(GENERAL_TIM ,i);
SOFT_DELAY;
}
}
}
可以看到两个舵机按照程序转动起来。
之后要增加控制舵机的数据,可以先增加TIMx的通道数,一个通用或者高级定时器有四个输出通道,还可以增加新的TIM,达到控制多个舵机的目的。为了避免篇幅过于冗长,这里不挂出具体代码。
最后再来一个挂载四个的。
举报