超乎你想象的Stm32中的 TIM 定时器(提高篇)


注:本文属博主学习时所作笔记,内容源大参考于野火的《零死角玩转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 ms 175
1.0 ms 45° 180
1.5 ms 90° 185
2.0 ms 135° 190
2.5 ms 180° 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 );
    }

}

舵机测试

舵机测试

文章作者: Mahoo Huang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Mahoo Huang !
评论
 上一篇
STM32F1XX驱动温度检测模块DS18B20 STM32F1XX驱动温度检测模块DS18B20
DS18B20 单线总线的工作方式1. DS18B20 简介DS18B20单线数字温度传感器,即“一线器件”,其具有如下独特的优点。 采用单线总线的接口方式。与微处理器连接时,仅需要1条口线即可实现微处理器与 DS18B20 的双向通信。
2020-10-13
下一篇 
Google Drive 调用 IDM 下载大文件 Google Drive 调用 IDM 下载大文件
最近在用 Google Drive 收集一些好东西,但是下载的时候由于文件过大的限制还是其他原因,遇到了一些问题: 在网上找到了解决方法,在这里记录一下: 右键点击文件,选择添加星标 在页面左栏的已加星标中,找到文件;右键点击文件复制,
2020-08-22
  目录