注:本文属博主学习时所作笔记,内容源大参考于野火的《零死角玩转STM32F103》以及部分网络资料,笔记内容仅作为自己参考,免去频繁查询参考手册的麻烦,如有错误,还请指出!

高级定时器(STM32F1 系列中为 TIM1 和 TIM8)和通用定时器在基本定时器的基础上引入了外部引脚,可以实现输入捕获和输出比较功能。高级定时器比通用定时器增加了可编程死区互补输出、重复计数器、带刹车(断路)功能,这些功能都是针对电机控制方面。

高级控制定时器框图

由于TIM高级定时器结构复杂,功能众多,可以直接看野火的开发书籍,不做过多详述,文章主要侧重几个常用实验,并在代码中层层递进。

PWM 信号输出控制 SG90 舵机

该例程将通过将使用 STM32 输出 PWM 信号,控制 SG90 舵机进行转动,使用的是 TIM3 的通道1 为 PA6 口,舵机接线如下:

SG90 舵机实物图

控制原理及 PWM 配置

伺服电机通常被称为舵机,它是一种带有输出轴的小装置。当我们向伺服器发送一个控制信号时,输出轴就可以转到特定的位置。只要控制信号持续不变,伺服机构就会保持轴的角度位置不改变。如果控制信号发生变化,输出轴的位置也会相应发生变化。

舵机的内部结构如上图所示。你可以看到控制电路,马达,一组齿轮和外壳

舵机的内部结构

舵机内部的控制电路,电位计(可变电阻器)和电机均被连接到电路板上,如内部结构图的右边部分。控制电路通过电位计可监控舵机的当前角度。

如果轴的位置与控制信号相符,那么电机就会关闭。如果控制电路发现这个角度不正确,它就会控制马达转动,直到它达到指定的角度。舵机角度根据制造商的不同而有所不同。比如,一个180度的舵机,它可以在0度至180度之间运动。由于限位装置被安装在主输出装置上,超出这个范围机械结构就不能再转动了。

舵机的输出功率与它所需要转动的距离成正比。如果输出轴需要转动很长的距离,马达就会全速运转,如果它只需要短距离转动,马达就会以较慢的速度运行,这叫做速度比例控制。

舵机的控制一般需要一个 20ms 的时基脉冲,该脉冲的高电平部分一般为 0.5ms~2.5ms 范围内的角度控制脉冲部分。

舵机的运动原理

控制线用于传输角度控制信号。这个角度是由控制信号脉冲的持续时间决定的,这叫做脉冲编码调制(PCM)。舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为 0.5ms-2.5ms 范围,总间隔为 2ms。脉冲的宽度将决定马达转动的距离。例如:1.5毫秒的脉冲,电机将转向90度的位置(通常称为中立位置,对于180°舵机来说,就是90°位置)。如果脉冲宽度小于1.5毫秒,那么电机轴向朝向0度方向。如果脉冲宽度大于1.5毫秒,轴向就朝向180度方向。

伺服电机时序图

综上,设置PWM周期为20ms = (7200*200)/72000000=0.02,所以TIM_Period = 199,TIM_Prescaler = 7199。

以180度舵机 SG90 为例,对应的控制关系是这样的:

脉冲宽度转动角度设置占空比
0.5 ms175
1.0 ms45°180
1.5 ms90°185
2.0 ms135°190
2.5 ms180°195

STM32工程目录

很久不接触 STM32 工程,文件结构差点乱了,这里贴个目录树:

├─Doc │ readme.txt │ ├─Libraries │ ├─CMSIS │ │ └─startup │ │ │ └─FWlib │ ├─inc │ └─src │ ├─Listing │ ├─Output │ ├─Project │ └─RVMDK(uv5) │ └─DebugConfig │ └─User │ main.c │ stm32f10x_conf.h │ stm32f10x_it.c │ stm32f10x_it.h │
├─PWM │ bsp_pwm.c │ bsp_pwm.h │ └─SysTick bsp_SysTick.c bsp_SysTick.h

参考代码

bsp_pwm.h

#ifndef  _BSP_PWM_H_
#define  _BSP_PWM_H_

#include "stm32f10x.h"
// 这里使用 TIM3
#define            GENERAL_TIM                   TIM3
#define            GENERAL_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define            GENERAL_TIM_CLK               RCC_APB1Periph_TIM3
#define            GENERAL_TIM_Period            199
#define            GENERAL_TIM_Prescaler         7199
// TIM3 输出比较通道
#define            GENERAL_TIM_CH1_GPIO_CLK      RCC_APB2Periph_GPIOA
#define            GENERAL_TIM_CH1_PORT          GPIOA
#define            GENERAL_TIM_CH1_PIN           GPIO_Pin_6

void GENERAL_TIM_Init(void);

#endif

bsp_pwm.c

#include "bsp_pwm.h"   

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);
}


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 = 0; 
 
 // 计数器计数模式,设置为向上计数
 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
 
 // 重复计数器的值,没用到不用管
 TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
 
 // 初始化定时器
 TIM_TimeBaseInit(GENERAL_TIM, &TIM_TimeBaseStructure);

 /*--------------------输出比较结构体初始化-------------------*/ 
 // 占空比配置
 // uint16_t CCR_Val = 100;

 
 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_Low;
 
 // 输出比较通道 1
 //TIM_OCInitStructure.TIM_Pulse = CCR_Val;
 
 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

#include "stm32f10x.h"
#include "bsp_pwm.h"
#include "bsp_SysTick.h"


/**
  * @brief  主函数
  * @param  
  * @retval
  */
int main(void)
{ 
 /* 定时器初始化 */
 GENERAL_TIM_Init();
 
 /* 配置SysTick 为10us中断一次 */
 SysTick_Init();
 
 while(1)
 {
  SysTick_Delay_Ms( 1000 );
  TIM_SetCompare1(TIM3, 175);   // 0°
  SysTick_Delay_Ms( 1000 );
  TIM_SetCompare1(TIM3, 180);   // 45°
  SysTick_Delay_Ms( 1000 );
  TIM_SetCompare1(TIM3, 185);   // 90°
  SysTick_Delay_Ms( 1000 );
  TIM_SetCompare1(TIM3, 190);   // 135°
  SysTick_Delay_Ms( 1000 );
  TIM_SetCompare1(TIM3, 195);   // 180°
  SysTick_Delay_Ms( 1000 );
 }

}

舵机测试

舵机测试