定时器(TIM)
本文来自于《STM32——江科大》的笔记整理。
7. TIM
7.1 TIM定时中断
7.1.1 TIM简介
- TIM(Timer)定时器
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
- 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
7.1.2 定时器类型
类型 | 编号 | 总线 | 功能 |
---|---|---|---|
高级定时器 | TIM1、TIM8 | APB2 | 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 | TIM6、TIM7 | APB1 | 拥有定时中断、主模式触发DAC的功能 |
- STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
7.1.2.1 基本定时器
计数模式:只支持向上计数这一种模式
我们一般把 自动重装寄存器的值等于CNT计数器的值 会产生
一个更新事件
,一个更新中断
更新中断过后,就会通往NVIC,此时我们需要配置好NVIC的定时器通道,这样定时器的中断就可以得到CPU的响应了
简单介绍主模式DAC的功能
- 它可以让内部的硬件在不受程序的控制下实现自动运行
- 这样定时器的更新就不需要通过中断来触发了DAC的转换了
7.1.2.2 通用定时器
计数模式
- 向上计数模式
- 向下计数模式
- 中央对齐模式(向上/向下计数)
内外时钟源的选择
对于基本定时器来言只能选择内部时钟源(72MHz)
对于通用定时器而言还可以选择外部时钟
第一个外部时钟可以选择——》TIMx_ETR0引脚(PA0)的外部时钟
如果想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当做计数器来用的话
配置
外部时钟模式2
当这个TRG当做外部时钟来使用时,这一路叫做“外部时钟模式1”
实现定时器的级联功能
TIM1&TIM8工作在从模式时内部触发时钟可选项
TIM2&TIM3&TIM4&TIM5工作在从模式时内部触发时钟可选项
TIM9&TIM12工作在从模式时内部触发时钟可选项
ITRx由TS位确定,TIM1&TIM8工作在从模式下内部触发时钟的可选项,如果所选器件对应的定时器不存在则该选项也不存在,如TIM1可选择TIM5_TRGO/TIM2_TRGO/TIM3_TRGO/TIM4_TRGO。
然后在选择外部时钟模式1,就实现了定时器的级联
总结:
7.1.2.3 高级定时器
简单看一下,相对于变化的是
7.1.3 定时器定时中断(使用的内部时钟)
定时中断基本结构
时序图
预分频器时序
计数器时序
计数器无预装时序
计数器有预装时序
RCC时钟树
所有定时器的时钟都是72MHz
代码:
OLED代码 -->见STM32——OLED显示屏
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
//开启TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//选择内部时钟驱动(定时器上电后默认就是使用内部时钟)
TIM_InternalClockConfig(TIM2);
//配置结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//设置时钟分频
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//向上计数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//1s定时
//重复计数器的值(高级定时器的功能不需要用)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
//初始化时基单元
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//TIM_TimeBaseInit函数里面,手动生成的一次更新时间(与更新中断同时发生)
//然后更新中断会置中断标志位
//手动清除中断标志位,解决通电立刻进中断的问题
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//设置使能中断(选择更新中断)
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//配置NVIC
//配置分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//启动定时器
TIM_Cmd(TIM2, ENABLE);
}
//放到了主函数中
//void TIM2_IRQHandler()
//{
// //检测对应标志位
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
// {
// //清除标志位
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
OLED_ShowNum(1, 5, Num, 5);
}
}
void TIM2_IRQHandler(void)
{
//查询中断标准位
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
//清除中断标准位
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
7.1.4 定时器外部时钟
注意:我选择的是 定时器2
引脚选择:PA0,因为PA0是TIM2的ERT引脚
代码
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
//选择定时器2(PA0是TIM2的ERT引脚)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//通过ETR引脚的外部时钟模式2配置
//第二个参数 外部触发预分频器
//第三个参数 外部触发的极性
//第三个参数 外部触发滤波器(0x00-0x0F)之间的值
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
//因为手动触发没那么快,所以时基不分频
//触发一次CNT+1,如果设置分频了那可以需要触发几次,CNT才能+1
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
//查看CNT的值
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
/*
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
uint16_t Timer_GetCounter(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Num:");
OLED_ShowString(2, 1, "CNT:");
while (1)
{
OLED_ShowNum(1, 5, Num, 5);
OLED_ShowNum(2, 5, Timer_GetCounter(), 5);
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
7.2 TIM输出比较
7.2.1 输出比较简介
-
OC(Output Compare)输出比较
-
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
-
每个高级定时器和通用定时器都拥有4个输出比较通道(有各自的CCR寄存器,共有CNT计数器)
-
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
7.2.2 PWM简介
-
PWM(Pulse Width Modulation)脉冲宽度调制
-
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
-
PWM参数:
-
频率 = 1 / TS
-
占空比 = TON / TS (下图占空比是50%)
-
- 分辨率 = 占空比变化步距(1%,2%,3% … 分辨率1%)
7.2.3 输出比较通道(通用定时器)
PWM的工作过程
1、CCR1寄存器:捕获/比较值寄存器:设置比较值;
计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
- OC1REF=0 无效电平
- OC1REF=1 无效电平
2、TIMx_CCMR1寄存器:OC1M[2:0]位:用于设置PWM模式
- 110:PWM模式1
- 111:PWM模式2
3、CCER寄存器:CC1P位:输入/捕获1输出极性。
- 0:高电平为有效电平
- 1:低电平为有效电平
4、CCER寄存器:CC1E位:输入/捕获1输出使能。
- 0:关闭使能
- 1:打开使能
5、输出电平信号
7.2.4 输出比较模式
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
7.2.5 PWM基本结构
参数计算
PWM的频率就等于计数器的更新频率。
原理讲解:
下图为向上计数模式:
- 在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值)。
- 当CNT小于CCRx时,TIMx_CHx通道输出低电平;
- 当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。
PWM的一个周期
- 定时器从0开始向上计数
- 当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
- t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
- 当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数…循环此过程
- 至此一个PWM周期完成
总结:
每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,
将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)
TIMx_ARR寄存器确定PWM频率,
TIMx_CCRx寄存器确定占空比
7.2.6 输出比较通道(高级定时器)
现在了解即可
7.2.7 舵机简介
- 舵机是一种根据输入PWM信号占空比来控制输出角度的装置
- 输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
硬件电路
注意:如果单独给舵机单独供电,供电的负极要和STM32共地
7.2.8 直流电机及驱动简介
- 直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
- 直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
- TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
硬件电路
H:高电平
L:低电平
7.3 PWM驱动硬件
关注收藏不迷路
给那些看完的朋友,奖励一个 赤赤博客-后端+前端,觉得不错的话可以推荐给身边的朋友哟!