STM32F103定时器配置
一、定时器
定时器,顾名思义,就是用于定时或计数,它其实就是一个加1计数器。
1.1 定时器类型
STM32f103有三类定时器:
| 类型 | 编号 | 总线 | 功能 |
|---|---|---|---|
| 高级定时器 | TIM1、TIME8 | APB2 | 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
| 通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
| 基本定时器 | TIM6、TIM7 | APB1 | 拥有定时中断、主模式触发DAC的功能 |
每个通用定时器TIMx功能:
- 位于低速的
APB1总线上; 16位向上、向下、向上/向下自动装载计数器(TIMx_CNT);- 16位可编程预分频器(
TIMx_PSC),计数器的时钟频率的分频系数为1~65535之间的任意数值; - 4个独立通道(
TIMx_CH1~4),这些通道可以用来作为:- 输入捕获;
- 输出比较;
PWM生成;- 单脉冲模式输出;
- 可使用外部信号(
TIMx_ETR)控制定时器和定时器互联的同步电路; - 如下事件发生时产生中断/
DMA(6个独立的IRQ/DMA请求生成器):- 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或内部外部触发);
- 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);
- 输入捕获;
- 输出比较;
- 支持针对定位的增量(正交)编码器和霍尔传感器电路;
- 触发输入作为外部时钟或者按周期的电流管理;
STM32的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32的每个通用定时器都是完全独立的,没有互相共享的任何资源。
1.1.1 高级定时器框图
1.1.2 通用定时器框图
1.1.3 基本定时器框图
1.2 计数器模式
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
- 向上计数模式:计数器从
0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件; - 向下计数模式:计数器从自动装入的值(
TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件; - 中央对齐模式(向上/向下计数):计数器从
0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
具体如下图所示:
1.3 功能介绍
1.3.1 定时器中断
1.3.2 输出比较
输出比较可以通过比较CNT与CCRx寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形。
输出比较模式:
| 模式 | 描述 |
|---|---|
| 冻结 | 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≤CRR时,REF置有效电平 |
| PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置有效电平,CNT≤CRR时,REF置无效电平 |
1.3.3 PWM基本结构
PWM频率 :\(F_{req} = T_{clk} / (PSC + 1) / (ARR + 1)\);
PWM占空比:\(Duty = CCR / (ARR + 1)\);
PWM分辨率:\(Peso = 1 / (ARR + 1)\)。
二、定时器相关寄存器
2.1 控制寄存器(TIMx_CR1)
想要使用定时器功能,首先要使能定时器。使能定时器主要用到控制寄存器1(TIMx_CR1),此处重点关注第一位,这一位是计数器使能位,给此位设1,使能计数器;
此外我们也需要关注位7 ARPE、位4 DIR。
2.2 DMA/中断使能寄存器(TIMx_DIER)
定时器的使用,很多情况下都伴随着中断,因此下面要重点关注的是DMA/中断使能寄存器(TIMx_DIER)。同样的,只用关注第一位。该位为更新中断允许位,当该位设置为1时,将允许由于更新事件所产生的中断;
2.3 预分频寄存器(TIMx_PSC)
接下来就要确定定时器的时间,定时器的定时是通过频率计算出来的。这里要用到预分频寄存器(TIMx_PSC)。该寄存器用于设置对时钟进行分频,然后提供给计数器,作为计数器的时钟;
2.3.1 计数器计数频率
定时器的时钟频率是\(T_{clk}\),TIMx_PSC即为PSC的值。时钟频率被分频了PSC+1,那么定时器的时钟频率:
故可知定时器计数值加1所需的时间为:$ \frac{PSC + 1}{T_{clk}}$。
2.3.2 计数器溢出频率
自动重装载值ARR,即TIM_ARR。定时器从0计数到ARR时清零。由第一步已经计算出了被分频了PSC+1的最终定时器的时钟频率为$ \frac{T_{clk}}{PSC + 1}$,这是计数一次的频率,故计数器溢出频率 (或者说定时器溢出频率):
2.3.3 计数器溢出时间
计数器溢出时间(或者说定时器溢出时间):
其中
- \(T_{clk}\)为定时器的输入时钟频率(单位
Mhz),通常为系统时钟频率或者定时器外部时钟频率; ARR(TIM_ARR):自动重装载值,是定时器溢出前的计数值;PSC(TIMx_PSC):预分频值,是用来降低定时器时钟频率的参数;- T_{out}:定时器溢出时间(单位
us),一定要注意这个单位是us。
2.4 从模式控制寄存器(TIMx_SMCR)
从框图中可以看到统通用定时器时钟来源有以下几个:
- 内部时钟(
CK_INT)默认; - 外部时钟模式
1:外部输入脚(TIx); - 外部时钟模式
2:外部触发输入(ETR); - 内部触发输入(
ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
时钟源的选择,可以通过从模式寄存器(TIMx_SMCR)的第3位来选择;
2.5 自动重装载寄存器(TIMx_ARR)
下面是自动重载寄存器。自动装载寄存器(TIMx_ARR)是预先装载的,写或读自动重装载寄存器将访问预装载寄存器;
2.6 状态寄存器(TIMx_SR)
如果发生了更新中断,UIF位会被置1;
三、定时中断源码
3.1 定时中断初始化步骤
定时中断配置流程如下:
(1) TIMx时钟使能:通过配置RCC_APB1ENR/RCC_APB2ENR寄存器使能TIMx时钟;
(2) 配置TIMx时基单元;
-
配置
TIMx_ARR寄存器自动重装载的值; -
配置
TIMx_PSC频寄存器预分频系数;
(3) 配置TIMx_DIER寄存器允许更新中断(UIE=1);
(4) 设置NVIC;
- 参考《
STM32F103嵌套向量中断控制器》:设置中断优先级分组、设置响应优先级和抢断优先级、使能相应中断位;
(5) 中断处理函数;
- 设置中断服务函数(包括清除中断标志,清
SR寄存器状态标志位(UIF=0))。
(6) 允许TIMx工作:配置TIMx_CR1位0。
3.2 源码实现
3.2.1 TIM_TypeDef
TIMx寄存器结构TIM_TypeDef,在文件stm32f10x_map.h中定义如下:
/*------------------------ TIM ----------------------------*/
typedef struct
{
vu16 CR1; // 控制寄存器1 ;
u16 RESERVED0;
vu16 CR2; // 控制寄存器2 ;
u16 RESERVED1;
vu16 SMCR; // 从模式控制寄存器 ;
u16 RESERVED2;
vu16 DIER; // DMA/中断使能寄存器 ;
u16 RESERVED3;
vu16 SR; // 状态寄存器 ;
u16 RESERVED4;
vu16 EGR; // 事件产生寄存器 ;
u16 RESERVED5;
vu16 CCMR1; // 捕获/比较模式寄存器1 ;
u16 RESERVED6;
vu16 CCMR2; // 捕获/比较模式寄存器2 ;
u16 RESERVED7;
vu16 CCER; // 捕获/比较使能寄存器 ;
u16 RESERVED8;
vu16 CNT; // 计数器寄存器 ;
u16 RESERVED9;
vu16 PSC; // 预分频寄存器 ;
u16 RESERVED10;
vu16 ARR; // 自动重装载寄存器 ;
u16 RESERVED11;
vu16 RCR; // 周期计数寄存器 ;
u16 RESERVED12;
vu16 CCR1; // 捕获/比较寄存器1 ;
u16 RESERVED13;
vu16 CCR2; // 捕获/比较寄存器2 ;
u16 RESERVED14;
vu16 CCR3; // 捕获/比较寄存器3 ;
u16 RESERVED15;
vu16 CCR4; // 捕获/比较寄存器4 ;
u16 RESERVED16;
vu16 BDTR; // 刹车和死区寄存器 ;
u16 RESERVED17;
vu16 DCR; // DMA控制寄存器 ;
u16 RESERVED18;
vu16 DMAR; // 连续模式的DMA地址寄存器 ;
u16 RESERVED19;
} TIM_TypeDef;
在前面我们已经对TIM_TypeDef结构体中定义的大部分寄存器进行了详细的介绍,那么我们如何编码去初始化这些寄存器呢?
3.2.2 TIMx初始化
定时器初始化函数TIM_Int_Init定义如下:
volatile TIM_TypeDef *TIMx[8] = {TIM1,TIM2,TIM3,TIM4,TIM5,TIM6,TIM7,TIM8}; //定义8个指针数组保存 TIMx 的地址
/**************************************************************************************************************
*
* Description: 高级定时器1和8 APB2预分频系数=1 则计数器的时钟频率为 APB2 否则APB2*2
* 通用定时器2~7 APB1预分频系数=1 则计数器的时钟频率为 APB1 否则APB1*2
* Parameter : timx TIMER1~TIMER8
DEFAULT_PSC 默认预分频系数
计数器的时钟频率 = Fclk/(PSC[15:0]+1)
time 中断时间 = (arr+1)/计数器的时钟频率/1000 单位ms
*
**************************************************************************************************************/
void TIM_Int_Init(TIMn timn,u32 time)
{
u16 arr; //存放自动重装载的值
u8 fclk; //存放定时器时钟频率 MHZ
u16 DEFAULT_PSC=DEFAULT_PSC_MS;
//u16 DEFAULT_PSC=DEFAULT_PSC_US; 定时器中断us秒
if(timn==0||timn==7) //定时器1或8
{
fclk = 72; //默认APB2 1倍频
}
else
{
fclk =72; //默认APB1 2倍频
}
arr = time*fclk*1000/(DEFAULT_PSC+1) -1; //存放自动装载的值 定时器中断ms 自增1 1ms
//arr = time*fclk/(DEFAULT_PSC+1)-1; //存放自动装载的值 定时器中断us 自增1 1us
if(timn==0) //定时器1
{
RCC->APB2ENR |=1<<11; //高级定时器1时钟使能
}
else if(timn==1) //定时器2
{
RCC->APB1ENR |=1<<0; //定时器2时钟使能
}
else if(timn==2) //定时器3
{
RCC->APB1ENR |=1<<1; //定时器3时钟使能
}
else if(timn==3) //定时器4
{
RCC->APB1ENR |=1<<2; //定时器4时钟使能
}
else if(timn==4) //定时器5
{
RCC->APB1ENR |=1<<3; //定时器5时钟使能
}
else if(timn==5) //定时器6
{
RCC->APB1ENR |=1<<4; //定时器6时钟使能
}
else if(timn==6) //定时器7
{
RCC->APB1ENR |=1<<5; //定时器7时钟使能
}
else
{
RCC->APB2ENR |=1<<13; //定时器8时钟使能
}
TIMx[timn]->PSC = DEFAULT_PSC; //预分频值
TIMx[timn]->ARR = arr; //重新装载的值
TIMx[timn]->DIER = 1<<0; //允许更新中断 UIE=1;
TIMx[timn]->CR1 = 1<<0; //使能计数器 开始计数
}
3.2.3 设置NVIC
在《STM32F103嵌套向量中断控制器》我们介绍了定时器中断包括:
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
TIM5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
比如我们使能中断TIM1_UP_IRQn(TIM1更新中断),设置中断优先级分组为2,占优先级为0,响应优先级为0;
STM32_NVIC_Init(2, TIM1_UP_IRQn ,0 ,0); // 中端使能
3.2.4 中断处理函数
这里定义了定时器相关的部分中断的处理函数,如下:
/*******************************************************************************
* Function Name : TIM1_UP_IRQHandler
* Description : This function handles TIM1 overflow and update interrupt
* request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM1_UP_IRQHandler(void) //定时器1溢出中断
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM1->SR &=~(1<<0); //清中断标志 必须 (置0清 非写1)
}
/*******************************************************************************
* Function Name : TIM2_IRQHandler
* Description : This function handles TIM2 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM2_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM2->SR &=~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM3_IRQHandler
* Description : This function handles TIM3 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM3_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM3->SR &= ~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM4_IRQHandler
* Description : This function handles TIM4 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM4_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM4->SR &= ~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM5_IRQHandler
* Description : This function handles TIM5 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM5_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM5->SR &= ~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM6_IRQHandler
* Description : This function handles TIM6 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM6_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM6->SR &= ~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM7_IRQHandler
* Description : This function handles TIM7 global interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM7_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM7->SR &= ~(1<<0); //清中断标志
}
/*******************************************************************************
* Function Name : TIM8_UP_IRQHandler
* Description : This function handles TIM8 overflow and update interrupt
* request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM8_UP_IRQHandler(void)
{
//**********************自定义用户任务****************************//
//*****************************************************************//
TIM8->SR &=~(1<<0); //清中断标志 必须 (置0清 非写1)
}
3.3 实现功能
这里我们实现一个很简单的功能,每隔1s,串口发送一条数据。
3.3.1 main函数实现
int main()
{
u16 i=0;
STM32_Clock_Init(9); //系统时钟初始化
STM32_NVIC_Init(2,USART1_IRQn,0,1); //串口中断优先级初始化,其中包括中断使能
usart_init(USART_1,115200); //串口1初始化,波特率115200 映射到PA9 PA10
// LED1初始化
gpio_init(PA8,GPO_SpeedMax_50,HIGH); //PA8接入LED1
STM32_NVIC_Init(2, TIM1_UP_IRQn ,0,0); // TIM1更新中端使能
TIM_Int_Init(TIMER1,1000); //TIM1计数到1000ms发生中断
while(1)
{
delay_ms(1000);
}
}
3.3.2 TIM1_UP_IRQHandler函数实现
修改TIM1_UP_IRQHandler中断处理函数,添加串口输出:
/*******************************************************************************
* Function Name : TIM1_UP_IRQHandler
* Description : This function handles TIM1 overflow and update interrupt
* request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM1_UP_IRQHandler(void) //定时器1溢出中断
{
//**********************自定义用户任务****************************//
usart_sendStr(USART_1, "Time1 test");
//*****************************************************************//
TIM1->SR &=~(1<<0); //清中断标志 必须 (置0清 非写1)
}
3.3.3 测试
编译程序并下载测试:
可以看到1s串口输出一条数据。
四、源码下载
源码下载路径:stm32f103。
参考文章
[1] STM32F103定时器配置

浙公网安备 33010602011771号