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

STM32学习笔记:外部中断的使用

[复制链接]
pwdzsj 发布时间:2015-10-18 17:08
3 Q$ P- a" m# }0 }0 V! @

1 y9 T; I* P! b2 W+ p8 b" _  ]4 }
( |; S& A! D  M
STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组位一个单位的,同组间的外部中断同一时间只能使用一个。比如说,PA0,PB0,PC0,PD0,PE0,PF0,PG0这些为1组,如果我们使用PA0作为外部中断源,那么别的就不能够再使用了,在此情况下,我们智能使用类似于PB1,PC2这种末端序号不同的外部中断源。每一组使用一个中断标志EXTIx。EXTI0 – EXTI4这5个外部中断有着自己的单独的中断响应函数,EXTI5-9共用一个中断响应函数,EXTI10-15共用一个中断响应函数。
7 {# W3 s6 u6 o% F
对于中断的控制,STM32有一个专用的管理机构:NVIC。对于NVIC的详细解释,可以参考《ARM Cortex-M3权威指南》,Joseph Yiu著,宋岩译,北京航空航天大学出版社出版,第8章NVIC与中断控制。中断的使能,挂起,优先级,活动等等部都是NVIC在管理的。因为我学习STM32重点在于如何开发程序,所以内部的一些东西,在此我就不详细说明了,有感兴趣的可以参看上面提到的那本数。

$ Y8 K+ S1 P' r5 N
程序开发
其实上面那些基本概念和知识只是对STM32的中断系统有一个大概的认识,用程序说话将会更能够加深如何使用中断。使用外部中断的基本步骤如下:
1.设置好相应的时钟;
2.设置相应的中断;
3.IO口初始化;
4.把相应的IO口设置为中断线路(要在设置外部中断之前)并初始化;
5.在选择的中断通道的响应函数中中断函数。

% g3 q# X3 x  |  F/ Q4 Y
由于我用的奋斗开发板没有引出相应的芯片引脚,所以只能用按键来触发相应的中断。根据原理图,K1/K2/K3连接的是PC5/PC2/PC3,因此我将用EXTI5/EXTI2/EXTI3三个外部中断。PB5/PD6/PD3分别连接了三个LED灯。中断的效果是按下按键,相应的LED灯将会被点亮。
1.设置相应的时钟
首先需要打开GPIOB、GPIOC和GPIOE(因为按键另外一端连接的是PE口)。然后由于是要用于触发中断,所以还需要打开GPIO复用的时钟。相应的函数在GPIO的学习笔记中有了详细了解释。详细代码如下:
( e) l% _7 Z7 U6 f# G# X; P! A, _5 E' \
  1. void RCC_cfg()
    % v0 z2 @6 T2 \& e/ q% F% g/ ?
  2. {
    8 k4 t6 |6 p- @
  3.        //打开PE PD PC PB端口时钟,并且打开复用时钟
    ) n# l$ P" V* ]9 o# g' V
  4.        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);& _8 A5 @, K% e; V" K2 \* T' X
  5. }
复制代码
3 Z, ~& K! y$ P
    设置相应的时钟所需要的RCC函数在stm32f10x_rcc.c中,所以要在工程中添加此文件。
' n9 M1 t3 D" x
2.设置好相应的中断
设置相应的中断实际上就是设置NVIC,在STM32的固件库中有一个结构体NVIC_InitTypeDef,里面有相应的标志位设置,然后再用NVIC_Init()函数进行初始化。详细代码如下:
  1. void NVIC_cfg()
    # S; B3 y& i0 P4 _' v. k7 @
  2. {
    ) ~: j6 J! c2 K- U* o& V( E% b$ V
  3.         NVIC_InitTypeDef NVIC_InitStructure;
    + l" Y: `) g+ A/ _+ G& e
  4.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                          //选择中断分组2/ D7 S5 I& A$ W% }! k
  5.         ! q7 g' D* ^: B; `% C! q9 B+ U
  6.         
    0 M* v% ?$ x0 a1 V' Y; U
  7. NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQChannel;     //选择中断通道2
    ( D8 ~3 F8 ?! L) y- G( z5 h; T
  8. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占式中断优先级设置为04 B4 K: P( a# Q* b8 X
  9.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //响应式中断优先级设置为0; [; Q7 m" N- R2 m. \, y9 c
  10.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                   //使能中断
    / f- r  G0 D2 l& b
  11.         NVIC_Init(&NVIC_InitStructure);8 i- Y+ k5 n  e$ ?
  12.         
    $ _9 d& F1 J  Z/ ?( M
  13.         NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQChannel;            //选择中断通道3
    / ~  V7 m# Q9 l% R
  14.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占式中断优先级设置为1) P! H6 `$ h, s: _$ f: [$ d/ n
  15.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //响应式中断优先级设置为1# K- A" N& x8 R2 ^* `" m1 P' s
  16.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                   //使能中断
    & c8 f; Z7 G( q' C$ q2 v: b
  17.         NVIC_Init(&NVIC_InitStructure);. ?3 P6 V- E% {$ f2 h
  18.         ! N( }; X9 \3 @& w  h! M
  19.         NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;  //选择中断通道5
    7 b+ {# D) _% X7 Q
  20.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占式中断优先级设置为20 L# e8 C. D% N
  21.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //响应式中断优先级设置为2
    5 O$ c& V7 C' W. j; l5 F0 Z
  22.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                   //使能中断1 c3 F! ]; _2 r  o* s$ Z& c5 d
  23.         NVIC_Init(&NVIC_InitStructure);, @. H! ~/ ^5 A3 d  ]
  24. }
复制代码

9 f9 \) k  t5 V; s4 D/ i0 _6 n* Y
由于有3个中断,因此根据前文所述,需要有3个bit来指定抢占优先级,所以选择第2组。又由于EXTI5-9共用一个中断响应函数,所以EXTI5选择的中断通道是EXTI9_5_IRQChannel,详细信息可以在头文件中查询得到。用到的NVIC相关的库函数在stm32f10x_nivc.c中,需要将此文件复制并添加到工程中。具体位置可以查看关于GPIO的笔记。这段代码编译起来没有任何问题,但是在链接的时候就会报错,需要把STM32F10xR.LIB加入工程中,具体位置在…KeilARMRV31LIBSTSTM32F10xR.LIB。
$ D: [7 V5 l5 z: |6 C
3.IO口初始化
  1. void IO_cfg()1 w* R6 Y# {$ D- m' A
  2. {; T2 Q( u% c( i- J
  3.        GPIO_InitTypeDef GPIO_InitStructure;
    : H, ^( `' a1 @7 J6 k4 d6 V
  4.       
    + |' L1 H  D7 t# y: |/ F( ]
  5.        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                                             //选择引脚2# |! k- g1 S) ~6 t
  6.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           //输出频率最大50MHz
    5 S1 f1 Y% X3 X7 O
  7.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //带上拉电阻输出
    # h% P5 l8 c4 c) i) r
  8.        GPIO_Init(GPIOE,&GPIO_InitStructure);) Z) o4 @4 z) h/ |9 Q* t! ^" p
  9.        GPIO_ResetBits(GPIOE,GPIO_Pin_2);                              //将PE.2引脚设置为低电平输出, s7 z5 X% O, P0 a+ V4 a* c9 E
  10.       
    # x0 P! O7 \& g! s+ O- ]# M% F
  11.        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_5; //选择引脚2 3 5
    7 i2 x6 M0 Q3 X& r6 A+ q7 E
  12.        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //选择输入模式为浮空输入7 H$ H9 g# j8 B1 d& I8 E/ r# P
  13.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;          //输出频率最大50MHz! }0 ^/ J& R* G
  14.        GPIO_Init(GPIOC,&GPIO_InitStructure);                                 //设置PC.2/PC.3/PC.5# a% J! H3 Z( w, _. ~$ N2 r4 K& I6 P
  15.       5 t9 C$ d  k0 n/ ^! ]% l
  16.       
    9 j; w: D% c! ~" E2 n7 h# \% w8 Q
  17.        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_6;                  //选择引脚3 66 O! ^& u* a& E( n. C& O/ u
  18.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           //输出频率最大50MHz3 n7 W4 a6 ~% [! Z. U
  19.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //带上拉电阻输出& p  o# L# c. D/ m' \3 j
  20.        GPIO_Init(GPIOD,&GPIO_InitStructure);
    3 d/ G3 s" F5 B, r7 N. ~7 {$ [
  21.       , i& |3 N4 }; s6 C+ w1 p
  22.       
    7 R" u5 p- o; m$ S" q) h
  23.        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                                         //选择引脚5" ?# M& C$ f" U$ ^
  24.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;          //输出频率最大50MHz
    - J- T5 P# P. {
  25.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //带上拉电阻输出
    ' ^/ Q+ Z' u& E8 k' f
  26.        GPIO_Init(GPIOB,&GPIO_InitStructure);        
    ' _. d( h; \! j
  27. }
复制代码

# E4 L! u( U2 K& k8 t. G
其中连接外部中断的引脚需要设置为输入状态,而连接LED的引脚需要设置为输出状态,初始化PE.2是为了使得按键的另外一端输出低电平。GPIO中的函数在stm32f10x_gpio.c中。
' @/ Y1 c) X7 W
4.把相应的IO口设置为中断线路
由于GPIO并不是专用的中断引脚,因此在用GPIO来触发外部中断的时候需要设置将GPIO相应的引脚和中断线连接起来,具体代码如下:
  1. void EXTI_cfg()( o3 s9 t3 C" J' R0 o# L6 a
  2. {
    & J3 b  N& `0 X
  3.        EXTI_InitTypeDef EXTI_InitStructure;
    ' D& e9 G3 A9 U
  4.        //清空中断标志
    - a: t% k0 v3 e) O
  5.        EXTI_ClearITPendingBit(EXTI_Line2);
    . ^1 [7 K- a1 l$ Z
  6.        EXTI_ClearITPendingBit(EXTI_Line3);& y2 a8 v+ b. s' i
  7.        EXTI_ClearITPendingBit(EXTI_Line5);
    " d- O$ R2 c+ k) a* |' z
  8.        //选择中断管脚PC.2 PC.3 PC.5& U* Q8 i( L! ?& w& ]- v1 q
  9.        GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2);
    $ l6 j2 T: m6 X3 I3 V# d, F
  10.        GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource3);7 d% }; `& c/ f  h: F2 U
  11.        GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource5);
    ! b4 a4 v3 v( \  }" g+ `
  12.        EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line5; //选择中断线路2 3 5# [, n! [" o: ^" _% z# U
  13.        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置为中断请求,非事件请求
    # {6 a5 {; t$ I$ r0 x+ y7 Q# u
  14.        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //设置中断触发方式为上下降沿触发
    / I$ G2 f: e' t' H
  15.        EXTI_InitStructure.EXTI_LineCmd = ENABLE;                           //外部中断使能- _- k0 i0 a# n! l& \
  16.       EXTI_Init(&EXTI_InitStructure);' L- E4 L# @' i- B- g5 Z
  17. }
复制代码
8 K& }& P: y1 {0 l
3 J" `( w: p8 j) q/ [* ?  q
EXTI_cfg中需要调用到的函数都在stm32f10x_exti.c。
% y6 D, z9 P1 |0 R6 x' ?
5.写中断响应函数
STM32不像C51单片机那样,可以用过interrupt关键字来定义中断响应函数,STM32的中断响应函数接口存在中断向量表中,是由启动代码给出的。默认的中断响应函数在stm32f10x_it.c中。因此我们需要把这个文件加入到工程中来。
在这个文件中,我们发现,很多函数都是只有一个函数名,并没有函数体。我们找到EXTI2_IRQHandler()这个函数,这就是EXTI2中断响应的函数。我的目标是将LED灯点亮,所以函数体其实很简单:
  1. void EXTI2_IRQHandler(void)
    & I  _+ {. {3 q. G8 ]6 q7 W
  2. {
    7 x/ {7 U+ D1 j8 o
  3.        //点亮LED灯6 n# L: o2 ]: U* I  G
  4.        GPIO_SetBits(GPIOD,GPIO_Pin_6);
    8 E- p* W; b! z6 n3 y
  5.        //清空中断标志位,防止持续进入中断- s! c* a% _- I* N9 K* l
  6.        EXTI_ClearITPendingBit(EXTI_Line2);# q8 k3 S, o( L9 a, Y
  7. }1 H% o1 v) d! \# ?( l
  8. void EXTI3_IRQHandler(void)
    ; r7 {9 l* _, U; ]- a* {
  9. {
    - w2 ^% F- l& C0 d0 m
  10.        GPIO_SetBits(GPIOD,GPIO_Pin_3);
    & w& l7 ~# G1 P4 s
  11.        EXTI_ClearITPendingBit(EXTI_Line3);3 T. Z6 R: c3 W6 m# O
  12. }6 G1 C/ U* f5 e: ]+ R, K. B
  13. void EXTI9_5_IRQHandler(void)
    9 l* `; G1 n! Y. P/ i3 ?+ s; f
  14. {
    5 H" b6 T0 T  \$ A* h
  15.        GPIO_SetBits(GPIOB,GPIO_Pin_5);% e' ]0 z8 X" N! [/ J! B
  16.        EXTI_ClearITPendingBit(EXTI_Line5);
    1 @- q3 ?& y5 G) r% C
  17. }
复制代码

2 s* I4 ^9 M: Z8 ]  x
由于EXTI5-9是共用一个中断响应函数,因此所有的EXTI5 – EXTI9的响应函数都写在这个里面。

1 S1 B: n) K; c. q% ?+ ?
6.写主函数
  1. #include "stm32f10x_lib.h", a- v: P  _2 ^8 a& n% C: ]
  2. void RCC_cfg();
    8 S9 }& w% s2 I0 |! |2 x' n9 ^
  3. void IO_cfg();2 z, {2 |& W. B9 E
  4. void EXTI_cfg();2 _: `% e& E$ w4 O/ P
  5. void NVIC_cfg();
    8 H: b& C1 F% y
  6. int main()
    * s5 v$ d& @, F" V, n
  7. {
    % Q/ _( J) i0 _1 H! j& S) D
  8.           RCC_cfg();
    " L& t0 _  M: y' k2 C$ L# R* B
  9.           IO_cfg();
    ; w% Y4 F# N4 \, ?3 f) z
  10.           NVIC_cfg();' t; s$ e7 m# r& |
  11.           EXTI_cfg();
    ' _2 H7 n5 i" P+ o2 S, P' s* Z
  12.           while(1);              j% [4 a8 T4 d/ P: J+ d
  13. }
复制代码
5 J" o+ V% y# m# V$ p5 {5 s' k
main函数前是函数声明,main函数函数体中都是调用初始化配置函数,然后进入死循环,等待中断响应。
9 J% [* p* y; ]0 O; Y
赞 2 收藏 10 评论12 发布时间:2015-10-18 17:08

举报

12个回答
与非社区小虫 回答时间:2015-10-18 17:18:01
支持一个,学习 blank0.png
netlhx 回答时间:2015-10-18 17:58:56
赞,思路很清晰。2 i6 f- W+ P3 I! Q

( s, k, _% I1 B9 R6 M3 S& ^另贴代码的时候用专用的代码格式可能看起来更好!
你好我好大家好! 回答时间:2015-10-18 18:18:47
学校学校
lkl0305 回答时间:2015-10-18 20:52:49
学习了
disheng4688 回答时间:2015-10-19 08:28:19
学习                                 
peter001 回答时间:2015-10-19 09:22:42
学习学习
黑驴蹄子 回答时间:2015-10-19 14:48:13
好帖
ruilimeng 回答时间:2015-10-20 23:48:05
谢谢分享!
1 c* H/ x7 e6 T: t7 ?2 Z9 O* S2 I! H* t: I' a
futu.png / {7 M7 v8 _3 q
钊钊 回答时间:2015-10-21 00:18:16
先顶再看,
djnz 回答时间:2017-7-30 08:49:26
写得很好,非常容易理解,学习了。
jay469879154 回答时间:2017-7-30 13:02:28
非常棒的帖子
观后无感 回答时间:2018-6-19 15:14:19
很好 学习!!

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版