[ M3 PN ] STM32F10XXX(Cortex-M3) MDK-RAM TIMx中断

1定时器基础

(1) 概念

概念还是需要读参考手册获取。

可编程通用定时器TIMx[x=2,3,4,5]的主要部分是一个16位计数器(CNT)和与其相关的自动装载(APP)寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。计数器由预分频器的时钟输出CK_CNT驱动,计数器的时钟CK_CNT由 预分频器TIMx_PSC分频得到。


计数器从计数开始到计数溢出的时间被称为定时器的时基。以下3个单元决定时基

  • 计数器寄存器 (TIMx_CNT)。
  • 预分频器寄存器 (TIMx_PSC)。[ 发生更新事件更新TIMx_PSC的值 ]
  • 自动装载寄存器 (TIMx_ARR)。[ 发生更新事件更新TIMx_ARR的值 ]

更新(UEV)事件是指在允许更新TIMx_UDIS=0的情况下,某个事件(如计数器溢出)发生时影子寄存器(ARR、PSC、CCRx)的值会发生改变,用于产生下一个新的时基。


(2) 定时器中断涉及的寄存器

  • TIMx_CR1:       提供“APP自动重载”、“计数方式”、“中断更新源”、“更新事件”、“计数器使能”等配置。
  • TIMx_DIER:     中断允许设置(UIE).
  •  TIMx_PSC:        时钟分频配置,更新事件发生时其值为程序赋予的新值。
  • TIMx_ARR:       在CR1.APRE=1时更新事件发生时其值为程序赋予的新值。
  • TIMx_CNT:       可不用对其设置,则按照计数方式单周期内计数TIMx_ARR次。如在向上计数模式中,CNT从0计数到TIMx_ARR。     
  • TIMx_SR:       在中断函数内软件清0。
  • NVIC->ISERx:   开启定时器模块的中断。
  • RCC-> APBxENR:      开启定时器时钟。
STM32F10XXX外设时钟在默认下都是关闭的,而要使某模块工作必须要有时钟,所以必须开启定时器的时钟。另外,NVIC所管理的每个模块的中断也是关闭的,在使用对应模块的中断时也需开启中断。

(3) 定时器更新事件与中断请求

定时器经初始化后,计数器根据时钟信号(CK_CNT)开始按照设定的模式计数,计数器计数溢出/下溢时可能发生两件事情。

  • 在允许更新事件TIMx_CR1.UDIS=0的状态下可由“计数器溢出/下溢”产生更新事件,此时TIMx_PSC的值被更新,在TIMx_CR1.ARPE=1时TIMx_ARR的值被更新。
  • 允许更新中断(TIMx_DIER.UIE = 1) 的状态下,TIMx_CR1.URS=1产生中断请求,如果在NVIC中打开了TIMx定时器模块中断,中断就会发生。

定时器的中断和更新事件都还可以通过其它的方式(“设置TIMx_EGR.UG位”、“从模式控制”)实现。

(4) NVIC中断发生的条件

  • 中断开启。
  • 中断请求。
  • 中断执行。
  • 一般还需要清除中断标记位。

模块如定时器(TIMx)发生中断申请,并且在NVIC中TIMx中断被开启就可以发生中断,从而调用对应的中断函数,在中断函数内一般需要将中断标记清0.

(5) 定时器中断实现步骤

根据定时器工作的基本原理得到定时器中断实现的基本步骤如下:

  • 开启定时器模块时钟 (RCC->APB1ENR)。
  • 开启定时器中断(NVIC->ISERx, NVIC->ISPRx, NVIC->IPRx)。
  • 初始化定时器参数(TIMx_PSC, TIMx_CR1, TIMx_APP)。
  • 允许定时器中断更新(TIMx_DIER)。
  • (中断优先级设置)。
  • 编写定时器中断服务函数。

2 定时器中断编程流程

(1) 初始化

[1] NVIC初始化

默认情况下,外设的中断都是关闭的。在使用中断前需要配置NVIC使能对应的中断。关于NVIC寄存器的讲解需要看《PM5006 Cortex-M3技术参考手册》的” Core Peripherals ”部分。NVIC相关寄存器的“位” (《PM5006 Cortex-M3技术参考手册》 Table4.1)与中断向量表的“位置号” (《RM008 stm32f10xx参考手册》表54~55,Page 132)相对应,这些中断和中断号(向量表)被定义在MDK的<stm32f10x.h>中。如NVIC->ISER[0]|= 1 << 28表示使能TIM2中断。
对于TIM2的NVIC配置如下:
  • TIM2中断使能:NVIC->ISER[0]|= 1 << 28。
  • 默认TIM2中断优先级。

[2] 定时器初始化

时钟使能(RCC->APBxENR)

TIMx[x=2, 3, 4,5]定时器都挂在APB1总线上,语句RCC->APB1ENR         |= 0x01 << (x-2)对应TIMx时钟使能。


设置预分频和自动重载值(TIMx_PSC, TIMx_APP)

分频得CK_CNT

APB1 总线上的时钟信号在进入定时器之后第一件事情是被预分频器TIMx_PSC分频输出CK_CNT来驱动计数器计数。程序中随时都可以更改TIMx_PSC的值,但是TIMx_PSC是有缓冲的,只有发生了更新事件时新的预分频值才会生效。


计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。 fCK_PSC为进入到定时器的时钟频率。如时钟频率fCK_PSC为72MHz时,当给TIMx->PSC = (7200 - 1)时,CK_CNT = 72000KHz / 7200 = 10KHz,这就表示计数器计数一次花的时间值为0.1ms。

重载值
自动装载寄存器TIMx_ARR是预先装载的。根据在TIMX_CR1 寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被永久地或在每次的更新事件UEV 时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMX_CR1 寄存器中的UDIS 位等于0 时,产生更新事件。

若现在要产生一个5Hz(0.2s)的定时周期,则需要语句TIMx->APP= 2000 – 1 (TIMx_CR1的CMS[1:0] = 00)。


综上:预分频寄存器TIMx->PSC配置得到计数一次的时间周期,再联合自动装载寄存器TIMx->APP配置得到定时周期

控制寄存器配置(TIMx_CR1)

  • Bit 9:8,  CKD[1:0]配置数字滤波器采样频率基于定时器时钟CK_CNT的分频倍数。
  • Bit 7,     设置TIMx_ARR自动载入,TIMx_ARPE       = 1.
  • Bit 6:5,  选择边沿模式CMS[1:0]                                   = 00.
  • Bit 4,     计数器从0开始向上计数DIR                         =0.
  • Bit 3,     在发生更新事件时计数器不停止OPM        = 0.
  • Bit 2,     在更新中断允许的情况下,计数溢出产生中断URS = 1.
  • Bit 1,     允许更新UDIS        = 0.
  • Bit 0,     允许计数器计数CEN     = 1.

综上配置CR1寄存器的语句为:TIMx->CR1       =0x0085.


使能更新中断

DMA/中断使能寄存器(TIMx_DIER) 有一位为UIE,只有这一位为1才能产生更新中断。对应的语句为:TIMx_DIER     |=0x0001;。

(2) 中断服务函数

[1] 中断服务函数名

中断服务函数的名字是在 MDK 中事先有定义的。在以Cortex-M3为内核的STM32F10XXX芯片编程中,这些中断函数都被MDK定义在“startup_stm32f10x_hd.s”中。其中通用定时器的中断服务函数名为TIMx_IRQHandler。

[2] 中断服务函数格式

void BlockName_IRQHandler( void )

{

       //contents

      TIMx->SR= 0xfffe;

}
无参数,无返回值。 BlockName 为每个中断的名字,具体可以到“ startup_stm32f10x_hd.s ”查找。

3 TIM2定时中断流水灯闪烁代码

以通用定时器TIM2为例,实现一个定时中断,代替使流水灯闪烁的延迟函数。

(0) NVIC初始化

/* 
 * @function:	    通过配置NVIC->ISER[x]寄存器开启IRQChannel中断
 * @arg: 	IRQChannel为中断号,x是ISER寄存器下标*/
void
NVIC_Init(unsigned char IRQChannel, unsigned char x)
{
	NVIC->ISER[x]	|= 1 << IRQChannel;
}

(1) 定时器中断初始化

/* 
 * @function	定时器TIMx初始化
 * @arg:	TIMx可以是TIM2,…,TIM4,x为APB1ENR寄存器使能对应TIMx的位值*/
void 
TIMx_Init(TIM_TypeDef *TIMx, unsigned char x)
{
	switch(x){
		case 0:
			//开启TIM2时钟
			RCC->APB1ENR	|= 0x01;
		
			//配置TIM2参数
			TIM2->PSC	= 7200 - 1;    //CK_CNT=72Mhz / 7200 = 10KHz,0.1ms
			TIM2->ARR	= 2000 - 1;    //时基=0.1ms * 2000 = 0.2s
			TIM2->CR1	= 0x0085;     //APP重载,计数溢出时产生中断,计数器使能
			TIMx->DIER	= 0x0001;     //允许中断更新
			
		break;
		
		case 1:
			RCC->APB1ENR	|= 0x01 << 1;
		break;
		
		case 2:
		break;
		
		default:
		break;
			
	}
}

(2) TIM2中断服务函数内容

//TIM2中断服务函数
void TIM2_IRQHandler(void)
{
	//在TIM2->APRE = 1时下列语句有效
	//TIM2->ARR	= 10000 - 1;
	
//软件清TIM2中断更新标记
	TIM2->SR	= 0;
	i++;
}

当TIM2->APRE = 1时,可以每次进入中断函数后更改APP的值以产生新的时基。

(3) 主函数

int main(void)
{
	NVIC_Init(0x1c, 0);
	TIMx_Init(TIM2, 0);
	LedInit();
	
	while(1) {
		if(5 == i){
			LedLight();
			
		}else if(10 == i){
			i	= 0;
			LedDieOut();
		}
	}
}

下载程序到开发板子中,重新给开发板上电后。Led以1s的周期闪烁。如果去掉中断服务函数中的TIM2->ARR =10000 – 1语句的注释,则定时器的时基为1s,led以5s的周期闪烁。令人开心的是,这次的程序没有经过调试就运行成功了。MEIZU_X2在拍照模式下按住开机键和音量减还可以截图。
[ M3 PN ] STM32F10XXX(Cortex-M3) MDK-RAM TIMx中断_第1张图片   [ M3 PN ] STM32F10XXX(Cortex-M3) MDK-RAM TIMx中断_第2张图片
电脑已老配定时器中断led闪烁

4 附加

决定定时器是否要循环计数的控制器为TIMx->CR1.OPM位,此位为1时,在发生下次更新事件时,计数器停止计数。可以在中断函数中用TIMx->CNT = 0(或者=m);TIMx->CR1 = 0x0001来重新使能定时器,让定时器按照设定方式重新计数。
Practical Note Over.

你可能感兴趣的:([ M3 PN ] STM32F10XXX(Cortex-M3) MDK-RAM TIMx中断)