打印

科星 stm32F107 第四节 定时器中断的学习

[复制链接]
3218|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ren0zhe|  楼主 | 2013-11-20 09:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
第四节 定时器中断的学习


4科星F107开发板学习笔记—定时器中断.pdf (479.64 KB)

学习中我们带着带着以下几个问题来看下,效果会更好,答案会在最后面给出,问题如下:
1、  中断定时器的时钟是以什么时钟为时钟源?
2、  定时器根据定时器时钟如何设定中断发生的时间?
3、  中断向量表如何设置的?
4、  中断处理函数在哪写?
下面边写代码边讲解定时器的配置使用。
前面提到了,这里咱们是在上一节中写的代码的基础上写的,没有的朋友,请在下面的链接中,下载上一节的源码:
现在开始代码的编写:
这里咱们首先明确TIM2-TIM5的时钟不是直接来自于APB1,而是来自于APB1的一个倍频器。这个倍频器的作用是:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其他数值时(即预分频系数为2、4、8或16),这个倍频器起作用,定时器的时钟频率等于APB1的频率的2倍。如下图所示(截自芯片寄存器手册),这里APB1预分频器的时钟是72M的,但是给后面PCLK1提供的最大时钟为36M,所以这里APB1 prescaler 为2分频,则定时器前面的倍频器乘以了2。
                              
定时器的使用跟GPIO的使用一样,使用之前都需要配置一些寄存器,也就是它本身的一些参数,同样,也用一个库中提供的结构体来定义,我们这里以一个通用定时器TIM2为例来讲述定时器的应用,详细内容如下:
结构体TIM_TimeBaseInitTypeDef定义了TIM定时器中断的发生的时间等参数。
和GPIO口一样,配置前我们也需要先打开TIM时钟,如下:
  
然而,我们这里的定时器中断还需要配置一下“嵌套向量中断控制器NVIC(Nested Vectored Interrupt Controller)”,因为它是使用中断所必须的,这里专门定义一个函数专门配置NVIC。如下:
int main(void)
{
  GPIO_InitTypeDef           GPIO_InitStructure;//定义GPIO的结构体
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
  
  SystemInit();
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIOA时钟   
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//打开TIM2时钟  
  
/* NVIC Configuration */
NVIC_Configuration();  //NVIC配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 |GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
  
  GPIO_Init(GPIOA,&GPIO_InitStructure);  //初始化IO                                       
  
  while (1)
  {
/*这里我们使用中断来对LED灯做处理,所以这里全部注释掉,或者直接删掉也可以
    delay(6999999);
    GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_7);//置高IO
    delay(6999999);
    GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_5);//置高IO
    delay(6999999);
    GPIO_ResetBits(GPIOA,GPIO_Pin_4 |GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);//置低io
*/
  }
}
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);  //选择中断分组0
  /* Enable the TIM2 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn; //选择TIM2的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占式中断优先级设置为0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1; //响应式中断优先级设置为1
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;  //使能中断
  NVIC_Init(&NVIC_InitStructure);
}
因为该函数定义在main()函数后面,并且在main()函数中会调用该函数,所以需要在最前面声明该函数,以便让main()函数能够调用该函数,声明如下图:
这里配置了TIM2的中断。
STM32(Cortex-M3)中有两个优先级的概念——抢占式中断优先级和响应式中断优先级,有人把响应式优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。  
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。  
         当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
STM32把指定中断优先级的寄存器位是4位,这4个寄存器位的分组方式如下:   
0组:所有4位用于指定响应优先级  
1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级  
2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级  
3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级  
4组:所有4位用于指定抢占式优先级
我们的代码中设定的是第0组,也就是所有4个寄存器位用于指定响应优先级,所以代码中抢占中断优先级设置为0(这里只能设定为0,因为前面设定的是第0组),响应优先级设定的是1(这里选择比较多,有4位,可以设置0~15)。
假如我们的代码中设定的是第1组,也就是有1个寄存器位用于指定抢占优先级,有3个寄存器位用于指定响应优先级,所以代码中抢占中断优先级可以设置为0和1;响应优先级可以设定为0~7。
下一步在配置GPIO口结构体的后面配置TIM的结构体,如下图:
首先我们想使用定时器,就必须使能定时器的时钟,这就是函数
RCC_APB1PeriphClockCmd();,通过它开启 RCC_APB1Periph_TIM2。然后我们对自动重装载寄存器赋值,TIM_Period的大小实际上表示的是需要经过(TIM_Period+1)次计数后才会发生一次更新或中断。接下来需要设置时钟预分频数TIM_Prescaler,这里有一个公式,我们举例来说明:例如时钟频率=72MHZ/(时钟预分频+1)。说明当前设置的这个TIM_Prescaler,直接决定定时器的时钟频率。通俗点说,就是一秒钟能计数多少次。比如TIM_Prescaler为(10000-1)则算出来的时钟频率是7200,也就是一秒钟会计数7200次,而此时如果TIM_Period设置为(7200-1),即7200次计数后就会中断一次。由于时钟频率是一秒钟计数7200次,因此要1秒钟,就会中断一次。
再往后的代码,还有一个需要注意的,
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;就是我们一般采用向上计数模式,即每次计数就会加1,直到寄存器溢出发生中断为止。最后别忘了,需要使能定时器!!
发生中断时间=(TIM_Prescaler+1)* (TIM_Period+1)/FLK
用上述公式可算出:发生中断时间 7200-1+1*10000-1+1)/72000000=1
编写中断服务程序。同样需要注意的,一进入中断服务程序,第一步要做的,就是清除掉中断标志位。由于我们使用的是向上溢出模式,因此使用的函数应该是:
TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);
STM32开发板实现的中断服务程序如下:每隔一秒,发生中断时,进入此中断函数执行程序,让LED亮(或灭)1秒钟,此中断程序所在文件stm32f10x_it.c.
咱们这里用到了配置TIM的库文件的函数,所以咱们必须得添加外设库文件,在“StdPeriph_Driver”中添加“stm32f10x_tim.c”,添加路径:
\Libraries\STM32F10x_StdPeriph_Driver\src             如下图:
下面我们开始写中断处理函数
中断处理函数是在“stm32f10x_it.c”中,添加处理函数“void TIM2_IRQHandler(void),如下图所示,
      
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
这个语句就是用“1”减去当前GPIO口的状态,如果当前状态为“1”,则将其置为“0”;如果当前状态为“0”,则置为“1”。其实这个语句就是对GPIO取反。
最后我们给出最开始提出的四个问题的答案:
1、  中断定时器的时钟是以什么时钟为时钟源?
APB1的一个倍频器为定时器时钟提供内部时钟,详见芯片手册中的时钟树。
2、  定时器根据定时器时钟如何设定中断发生的时间?
由公式“发生中断时间=(TIM_Prescaler+1)*(TIM_Period+1)/FLK”计算出来的
3、  中断向量表如何设置的?
详见“void NVIC_Configuration(void)”函数的解释
4、  中断处理函数在哪写?
中断处理函数是在“stm32f10x_it.c”中,添加处理函数“void TIM2_IRQHandler(void)”,详细的介绍请看上面的内容。


沙发
ren0zhe|  楼主 | 2013-11-20 09:43 | 只看该作者
4科星F107开发板学习笔记源码—定时器中断.rar (2.78 MB)

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:科星F107&F407开发板官网  http://corxstm32.taobao.com/      QQ交流群: 144728423

12

主题

255

帖子

5

粉丝