你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

嵌入式学习—STM32 定时器(一)定时器中断

[复制链接]
STMCU小助手 发布时间:2022-12-14 14:12
本文主要介绍常规定时器中的TIM3,实现定时器中断的功能。STM32定时器的分类在其中一篇文章中已经介绍过,本文主要内容主要介绍定时器的基础功能-定时器中断,对于STM32定时器分类简单复习一下。

9 T  f0 U+ X: j8 I+ W
一、STM32定时器的分类
1.1 按照内核、外核、特定、常规分为4大类:
1)内核定时器:Systick
2)外设定时器:特定应用定时器+常规定时器; R! [* P6 r7 B5 q3 Q% e( s% N
3)特定应用定时器:LPTIM,RTC,WTD,HRTIM/ ~" Y' h. l" c6 C& v1 p* y; b0 Q
4) 常规定时器:基本定时器TIM6&TIM7)、通用定时器(TIM2~TIM5,TIM9~TIM14)、高级定时器(TIM1&TIM8)
3 F9 H# P4 P. y8 }4 m
1.2 CPU时序  
此处我们提一下学习单片机原理的课程时,提到的几个CPU时序。
振荡周期:为单片机提供定时信号的振荡源的周期。
状态周期:1个状态周期=2个振荡周期
机器周期:1个机器周期=6个状态周期=12个振荡周期
指令周期:完成1条指令所占用的全部时间,以机器周期为单位。
以12MHz外接晶振为例
振荡周期=1/12us,相当于1/12*10^6,所以单位为us;
状态周期=1/6us
机器周期=1us
指令周期=1~4us
STM32共有14组常规定时器,其实也可以称为计数器,定时器/计数器的工作过程是自动完成的,不需要CPU的参与,互相独立,执行不同的任务,可以增加单片机的效率。
: F1 j+ D/ j7 ^) d) h
二、定时器中断原理
2.1 何为定时器中断:定时器中断是由单片机中的定时器溢出而申请的中断。" v$ q, }( s6 m2 {) r" A% ?8 Z
提到中断,必须满足几个要素:中断源中断请求中断优先级。 使CPU发生中断的事件称为中断源,中断源向CPU发出中断请求,CPU暂时中断原来执行的事件A转去执行事件B,事件B处理完成后继续返回原先中断的位置(该过程称为中断返回,原先中断的地方称为断点),继续执行原先的事件。

7 r6 s) i' V" t: n
2.2 中断流程可以用下图表示:

0 I# M# ?7 a; |+ ~$ F! s1 z
640.jpg
640 (9).png

+ x  x) i3 w+ R. D8 ~
2.3 中断优先级
《嵌入式学习(八)—STM32中断优先级分组与抢占优先级和响应优先级的关系》这篇文章里,介绍了STM32中的中断优先级分组、中断优先级(抢占优先级&响应优先级)嵌套向量中断控制器NVIC等概念,那么我们定时器中断也必须满足这个规则---定时器中断也要用NVIC来设置其中断组别、抢占优先级、响应优先级。
STM32中断分组有5种

- E* p" R* l. O& n
640 (8).png
0 m$ h) _1 m% H/ U
#define NVIC_PriorityGroup_0         ((uint32_t)0x700)   
/*!< 0 bits for pre-emption priority  4 bits for subpriority */
' t8 z4 {7 v9 u0 Z6 z9 a#define NVIC_PriorityGroup_1         ((uint32_t)0x600)   
/*!< 1 bits for pre-emption priority 3 bits for subpriority */9 K) s( g& c& D' T
#define NVIC_PriorityGroup_2         ((uint32_t)0x500)   
/*!< 2 bits for pre-emption priority 2 bits for subpriority */; T4 n* K& S. d7 `% K
#define NVIC_PriorityGroup_3         ((uint32_t)0x400)   
/*!< 3 bits for pre-emption priority1 bits for subpriority */
( d6 D/ V( Q2 W: e, j! G$ Q! g#define NVIC_PriorityGroup_4         ((uint32_t)0x300)   
/*!< 4 bits for pre-emption priority 0 bits for subpriority */
在函数中要调用
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)
实现对某一个中断的中断分组和优先级配置。
与定时器配置紧密相关的就是自动重装载计数器(CNT)和预分频器(PSC),初始化定时器就是对定时器的CNT、PSC进行设置。下面介绍一下与本文密切相关的几个通用定时器的寄存器8 P+ K! d3 t. q. [' M+ Q! p

  `  f9 b) V, F, H" W& Z% e, y, c: Q
三、定时器相关寄存器及中断编程
3.1定时器相关寄存器
3.1.1 控制寄存器TIMx_CR1
640 (7).png
$ n+ V& l  r9 t9 C
位0 CEN:计数器使能,0:禁止计数器,1,使能计数器
注意:只有事先通过软件将CEN位置去,才可以使用外部时钟、门控模式、编码器模式,而触发模式可以通过硬件自动将CEN置1;在单脉冲模式下,当发生更新事件时会自动将CEN位清零。
本实验中,我们只用到了TIMx_CR1的最低位,也就是计数器使能位,该位必须置1,才能让定时器开始计数。

% S7 g' |! r5 d
3.1.2 DMA中断使能寄存器 TIMx_DIER. B" Y$ L) R" l# K8 K4 _" T
640 (6).png
位0  UIE:更新中断使能,0:禁止更新中断,1:使能更新中断
TIMx_DIER是一个16bit的寄存器,对于要实现的中断试验,我们仅关心第0bit,因为定时器中断实验要用到定时器的更新中断,所以将该位置为1,表示允许更新时间所产生的中断。9 `) W3 f- q6 Y1 C
- {' [8 R5 |" X
3.1.3预分频寄存器TIMx_PSC
640 (5).png
位0:15 PSC:预分频器值。(范围是0~65535)
表示计数器时钟频率CK_INT 等于Fck_psc/(PSC[15:0]+1).PSC包含在每次发生更新事件时要装载到实际预分频器寄存器的值。(84MHz的CK_INT,那计数器的时钟频率为84/(PSC[15:0]+1)MHz,计数器时钟的取值范围为(0.00128~84)MHz,那么计数器时钟周期为0.012us(84MHz)~781us(0.001MHz);
这个地方要注意:预分频值=实际分频值-1,如果要设定实际分频值为8400(定时器的工作频率为10kHz),那我们设定预分频值为8399
也再复习一下定时器的时钟知识:, I8 b7 u7 |# L+ r* M# S- c- s: m
------------------------------------------------------------------------------
1.STM32总的有3种时钟源,分为内部时钟、外部时钟、锁相环倍频输出时钟,包含LSI,LSE,HSI,HSE等;
2.系统时钟为168MHz,其他时钟都是通过分频(系统时钟除以一个分频系数)给系统的各板块使用;
3.看下图三个红色框的部分,系统时钟(以F407系列为例)是168MHZ,通过设置不同的分频值给AHB总线,看第一个红框,可以设置为1.2...512,然后AHB总线再分频给APB分线,看第二个红框,再次分频的值可以为1.2.4.8.16,上面的是直接分频过后给APBx外设时钟使用,我们重点看第二根线,注意第三个红框,如果APBx的分频值设置为1,那么APBx的定时器时钟的时钟频率设置为与APB一样,如果是其他的数字,那么设置为APB的时钟频率的两倍。通过查手册知道两个基本定时器的时钟频率都归属于APB线上的,且APB1和APB2的分频系数都不为1(可以通过<system.stm324fxx.c>中找到配置),因此基本定时器的时钟频率已经确定。
640 (4).png
看下面这张图,在文件system.stm324fxx.c中可以找到,
第一行表示系统时钟来源是HSE,之前提过,它是高速外部时钟,由外部晶振产生,第二三行表示系统时钟设置为168MHZ(由外部时钟HSE倍频实现,具体这里不深究),第四五六行,分别表示AHB,APB1,APB2的分频系数,即分别设置为168MHZ,42MHZ,84MHZ。
注意,如前所述APB1的分频值为4,不为1,故其包含的基本定时器模块的时钟频率需乘2,即42×2为84MHZ。由此我们得知基本定时器的时钟源为84MHZ。
640 (3).png
1)内部时钟(CK_INT)
0 l2 m$ s0 T  w  G3 m. @
2)外部时钟模式1:外部输入引脚(TIx)3 Y6 x# K7 \# c( M+ ~+ B
3)外部时钟模式2:外部触发输入(ETR)用于TIM2.TIM3.TIM4
( Y' N% }( }+ s8 U1 x" T
4)  内部触发输入(ITRx),使用定时器A作为B定时器的预分频(A为B提供时钟)
这些时钟,具体选择哪个可以通过TIMx_SMCR寄存器的相关位来设置,CK_INT时钟是从APB1倍频来的,除非APB1的时钟分频数设置为1,否则通用定时器TIMx的时钟是APB1时钟的2倍,当APB1时钟不分频时,通用定时器的时钟就等于APB1的时钟,这里还要注意的就是高级定时器以及TIM9~TIM11的时钟是来自APB2。% {  P$ g$ u# \9 c+ n
" h  s0 a& e0 J2 P; p
3.1.4 TIMx_CNT计数器

- ?4 y) T0 Q: S. d1 S+ B
640 (2).png
0 K' U3 {" r! b0 U2 ]: I- ~9 k
位15:0  CNT[15:0]:计数器值,该寄存器存储了当前寄存器的计数值。范围为0~65535,可以计时的范围是0~51s(假定是分频PSC设为65535,计数器时钟频率是84/65536MHz,每个时钟脉冲周期为781us)

* o4 X; Z. U  A' ^8 O  ~& w
3.1.5自动重载寄存器(TIMx_ARR)
7 C2 }% O' F+ z& Z, e1 U2 s" j

- O  F" D( y7 M- Z8 T8 R
640 (1).png

$ g! G2 ~) X) j. ]. W
位15:0  ARR[15:0]:自动重载值。
ARR是要装载到实际自动重载寄存器的值。需要注意,该寄存器在物理上实际对应着2个寄存器,一个是程序员可以直接配置的,另外一个是程序员看不到的,这个看不到的寄存器叫影子寄存器,在《STM32F4xx中文参考手册》里面有提到,事实上真正起作用的是影子寄存器,根据TIMx_CR1寄存器中的APRE位的设置:APRE=0,预装载寄存器的内容可以随时传送到影子寄存器,此时两者是连通的;而APRE=1时,每一次更新事件(UEV)时,才能把预装载寄存器ARR的内容传送到影子寄存器。

" F& J0 e  P- p0 `0 j/ |
3.1.6状态寄存器(TIMx_SR)

+ f+ o, Z' w5 }, E3 i: Y8 `# H/ J
640.png

' ?- e' i; ]+ g9 E: z
位0 UIF:更新中断标志。
  • 该位在发生更新事件时通过硬件置1,但需要通过软件清零。0:未发生更新,1:更新中断挂起
  • 上溢或者下溢(对于TIM2~TIM5)以及当TIMx_CR1寄存器UDIS=0时,8 J1 i# {- {/ H4 H: o5 N/ Y, i: l
  • TIMx_CR1中的寄存器中的URS=0且UDIS=0,并且由软件使用TIMx_EGR寄存器中的UG位重新初始化CNT时。TIMx_CR1寄存器中的URS=0&UDIS=0,并且由CNT由触发事件重新初始化。& c: m8 r: I0 l( k+ b/ s+ @
    2 G* N$ o+ x* g; y* K. ~
3.2定时器中断编程
3.2.1编程步骤
* t$ ]# c6 C& [4 f1)TIM3时钟使能,通过APB1ENR的第1位来设置TIM3的时钟,APB1的分频系数是4,那么APB1为168/4=42MHz,TIM3时钟是APB1时钟的2倍,等于84MHz.
2)设置TIM3_ARR和TIM3_PSC的值,通过这两个寄存器,设置自动重装值和分频系数,这两个参数加上时钟频率决定了定时器的溢出事件。3)设置TIM3_DIER允许更新中断。因为我们要使用TIM3的更新中断,所以设置DIER的UIE位为1,使能更新中断7 @8 p5 k! k5 F% ^# h0 W4 l
4)允许TIM3工作。设置好定时器参数后,还需要开启定时器,通过TIM3_CR1的CEN位来设置
8 z! s# k# W! |9 M0 H  f5)TIM3中断分组设置。配置完定时器后,因为要产生中断,必须要设置NVIC相关寄存器,以使能TIM3中断。
, Y' {" q" A  S0 L$ C/ @6)编写中断服务函数。在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型,然后执行相关的操作,这里采用的是更新(溢出)中断,所以要关注状态寄存器的SR的最低位,在处理完成之后,将TIM3_SR的最低位写0,来清除该中断标志。' m) T+ G' }; I% t! b
以下是定时器3的中断测试代码
  1. //通用定时器3中断初始化
    6 G- c1 r% V3 o* Y) o
  2. //时钟选择为APB1的2倍,APB1=42MHz% U7 e7 I6 s8 Y) t5 i4 _
  3. //arr:自动重载值
    9 ~, m, \- \6 Z1 c. i8 _
  4. //psc:时钟预分频数
    , U" F& t& Q$ s! o, \0 n. x' ?
  5. //定时器溢出时间:Tout=((arr+1)*(psc+1))/ft
    3 J& V: |7 H- [1 Q  K
  6. //ft=定时器的工作频率,MHz) q" B# h$ Y( Q; h; v# W
  7. void TIM3_Int_Init(u16 arr,u16 psc)
    9 _( e2 |. L4 J( ]' H5 ?
  8. {+ q8 y8 y$ M6 W( N4 i! d& r
  9. RCC->APB1ENR|=1<<1;//TIM3时钟使能    # p$ x/ x% q, u5 l# k; m: ^
  10. TIM3->ARR=arr;  //设定计数器自动重装值 ! e+ ]0 M; L4 P
  11. TIM3->PSC=psc;  //预分频器  * p2 f3 @* I! v, Q" @
  12. TIM3->DIER|=1<<0;   //允许更新中断  
    ) U3 [% t; q9 X" q! n, O8 F. o' _
  13. TIM3->CR1|=0x01;    //使能定时器3* l( [; z" @- W. i' ^
  14. MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2 }
复制代码
  1. //定时器3中断服务程序   
    1 e/ x* p; d0 }/ z5 V& m
  2. void TIM3_IRQHandler(void)
    & B$ i& e5 p9 n
  3. {                         & }, k. c# n5 p& f' k  t2 Q
  4.   if(TIM3->SR&0X0001)//溢出中断! d7 h: {. n( `3 \+ C& P
  5.   {
    6 V* p* J- b* n# V6 ]* B' h
  6.     LED1=!LED1;                                          - s6 k3 W; m% P
  7.   }           
    0 _0 _$ z- o3 U# d9 M7 M7 n4 z% U
  8.   TIM3->SR&=~(1<<0);//清除中断标志位      
    ( f& T+ i9 U% B6 R* c1 O. k
  9. }
复制代码

% K, y: a$ g* A# A5 i" ]* E' T& [8 C' m$ ^, E5 e5 G
转载自: diandianmo
) Q5 C3 v6 ?1 C' w) n7 j" B5 b7 j3 x

$ n; G, s0 {( \' g8 h# c                          
  }             TIM3->SR&=~(1<<0);//清除中断标志位       }
1 D  r3 g( F& @$ G, r# ]9 |, n- J" Z8 l/ j/ k( D9 K

- s% ?7 ~- J9 u7 z
收藏 评论0 发布时间:2022-12-14 14:12

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版