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

STM32F1系列之STM32的异常(基于Keil-MDK)

[复制链接]
STMCU小助手 发布时间:2022-8-27 14:22
一、STM32的异常处理机制
8 ^/ _0 M+ O' e对于cortex M3/M4来说,CPU每执行完一条指令都会检查有无异常产生,当CPU发现有异常产生时,它就会进行如下处理:
, O+ M/ X1 M7 Z保存现场
* N; I: c- N, L8 \8 ~, g分辨异常/中断,调用对应的异常/中断处理函数* A; E, L# w0 D2 b6 a
恢复现场: j4 |% j3 W( g& ^! ~' K# \* G# m- q
每个异常/中断对应着一个异常向量,所有的异常向量组成一个异常向量表,对于cortex M3/M4来说,这个异常向量表中放置的就是具体异常/中断的处理函数的地址,当发生异常时,CPU就会从向量表里找到对应的项,从而得到处理函数的地址,跳转去执行。另外,对于cortex M3/M4来说,保存/恢复现场都是是硬件实现的。
+ |  E; y5 w& s3 ^我们可以打开一个STM32库的汇编启动文件,例如startup_stm32f10x_hd.s,我们可以看到,前面的时异常,后面的就是中断了(其实中断也是一种异常)。
% H+ [' b( D+ b# A4 e/ s5 s' B% j$ Q& l5 @7 t4 B; _
20210129215047591.png $ L$ k4 s: K0 C4 T5 P) K

. E. h' t: X3 {! ~二、未定义指令异常1 b& v. z+ C  c# s$ s) F
未定义指令,即使"还没有定义的指令",也就是CPU不认识的指令。8 h) E: ?, N) R9 ^% h, B4 ?
修改汇编文件,如下所示,添加各种异常的向量表项,另外在调用mymain()函数前调用串口初始化函数,并添加一个未定义的指令异常。
% s) ]) C' Q- Q3 J; _
- G0 b4 O! |6 B' c8 y" \3 g
  1. Stack_Size      EQU     0x00000500                                  ;定义堆栈大小为1024byte* n& s& e8 m2 b$ A4 G$ n
  2.                                 AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为0,8字节对齐: h/ b6 S* g$ ]; H# X4 J: i* @. N% S
  3. Stack_Mem                SPACE        Stack_Size                                    ;保留Stack_Size大小的栈空间
    . K6 h4 e. u( {7 T
  4. __initial_sp                                                                                  ;标号,代表堆栈顶部地址,后面有用3 W6 Q3 T, ~  C/ Q$ Z* j- {; V6 Y. @7 s
  5. / \, ~. D0 {# h' `: h& c! s
  6.                 PRESERVE8                                                        ;指示编译器8字节对齐; O* o* E: q& z( c& h
  7.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                                1 y4 Z# N6 y3 I. V7 F7 C, n6 m* d
  8. . E7 I( b$ `( N, ]  |7 G: o
  9. 3 |/ x$ u( ?1 c. b  I) V; I
  10. ; Vector Table Mapped to Address 0 at Reset5 x' J. F5 F8 v4 L" N$ U  @2 P
  11.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
    , ~" e8 A6 G3 t
  12.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用2 b; Y( {! K, R0 a# V$ O9 ?
  13.                                 IMPORT         NMI_Handler                ; NMI Handler
    1 U$ P0 C# z' y: b6 L+ c
  14.                                 IMPORT         HardFault_Handler          ; Hard Fault Handler
    0 \+ \4 Y" O' u6 t: b: X
  15.                                 IMPORT         MemManage_Handler          ; MPU Fault Handler
    8 m& x6 y. f+ }7 H9 j+ D; H6 _
  16.                                 IMPORT         BusFault_Handler           ; Bus Fault Handler
    5 ~7 Y) C6 W( X! \" ^- w  {0 a. [: C( Y
  17.                                 IMPORT         UsageFault_Handler         ; Usage Fault Handler' c! d) a6 r& {7 [7 S6 n
  18.                                 IMPORT         SVC_Handler                ; SVCall Handler2 q1 ~/ H- X; X4 k1 D: m5 _0 S
  19.                                 IMPORT         DebugMon_Handler           ; Debug Monitor Handler
    % S( i1 f$ z) M- Y. {' o& s
  20.                                 IMPORT         PendSV_Handler             ; PendSV Handler2 f$ s5 d9 Q( F1 j7 G, l
  21.                                 IMPORT         SysTick_Handler            ; SysTick Handler        ! l* H. Y5 [. L' D' s2 |& a
  22.                                         . N$ a- V+ V5 D% x
  23. __Vectors       DCD     __initial_sp                                ;当前地址写入一个字(32bit)数据,值应该为栈顶地址
    $ e2 G' s: o+ Z0 {' F
  24.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址
    6 E8 b0 `  R7 I- U8 q
  25.                                 DCD     NMI_Handler                ; NMI Handler
    0 n0 j- T6 Q4 U; r
  26.                 DCD     HardFault_Handler          ; Hard Fault Handler5 K, z. g& W$ I# Y5 b$ j
  27.                 DCD     MemManage_Handler          ; MPU Fault Handler% B- g2 Y" a, x% l0 E/ J' H1 r. s
  28.                 DCD     BusFault_Handler           ; Bus Fault Handler" S5 ^' V7 y+ @4 F
  29.                 DCD     UsageFault_Handler         ; Usage Fault Handler7 z. b8 L; x& n
  30.                 DCD     0                          ; Reserved
    1 ^+ u! B5 ~. X  M6 v; m3 q
  31.                 DCD     0                          ; Reserved
    ) u, C, R' G" i9 }: e
  32.                 DCD     0                          ; Reserved. y3 \+ Q6 F8 A+ F& e, P( i
  33.                 DCD     0                          ; Reserved* t9 r7 ~; y  e2 C. X9 C, J+ J
  34.                 DCD     SVC_Handler                ; SVCall Handler; U/ u0 @. S/ m2 ~, w2 a
  35.                 DCD     DebugMon_Handler           ; Debug Monitor Handler' p" U6 O" X) F$ p0 i8 R3 D
  36.                 DCD     0                          ; Reserved) [( y+ o; g: P- ?1 E' G; P
  37.                 DCD     PendSV_Handler             ; PendSV Handler
    & b1 {8 L3 g- W- u
  38.                 DCD     SysTick_Handler            ; SysTick Handler
    7 F) o6 z$ V4 d: ]5 Z' P: G
  39. 0 ?1 ^) j1 Z) Q: g: \
  40.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text
    9 r- @& u9 I: Q7 h1 S# r
  41. & H) F4 }" t9 n: j8 A1 T
  42. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰4 l  A$ }' N8 P* O8 A. `( L! @+ j
  43. Reset_Handler   PROC                                                                ;过程的开始
    $ P( S/ J* Q; F& s, ]' k1 h
  44.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。! {; H  m- P3 F$ ^& B- j/ l% `
  45.                                         $ C( e, y  l4 o
  46.                                 IMPORT |Image$RW_IRAM1$Base|                ;从别处导入data段的链接地址
    5 y+ R6 ?: \. \4 y7 D
  47.                                 IMPORT |Image$RW_IRAM1$Length|        ;从别处导入data段的长度
    8 m5 J3 h5 f7 M
  48.                                 IMPORT |Load$RW_IRAM1$Base|                ;从别处导入data段的加载地址
    ; L; f$ F; T: t5 K
  49.                                 IMPORT |Image$RW_IRAM1$ZI$Base|        ;从别处导入ZI段的链接地址8 w0 _# v0 N* d
  50.                                 IMPORT |Image$RW_IRAM1$ZI$Length|;从别处导入ZI段的长度+ G" E" h7 z9 j6 l  n

  51. ; A' \6 B: u# y1 b
  52. ; 复制数据段
    ; v0 \4 _  ~" m$ F$ o5 L2 A
  53.                                 LDR R0, = |Load$RW_IRAM1$Base|           ;将data段的加载地址存入R0寄存器. i9 Q6 F- |" g9 B6 {
  54.                                 LDR R1, = |Image$RW_IRAM1$Base|   ;将data段的链接地址存入R1寄存器4 E9 w/ P  J* l! L3 c; N9 s
  55.                                 LDR R2, = |Image$RW_IRAM1$Length| ;将data段的长度存入R2寄存器
    6 p0 V. w  k. I9 j0 Q& }9 r
  56. CopyData                # q4 C: h' S, ^) x. z& @- {
  57.                                 SUB R2, R2, #4                                                ;每次复制4个字节的data段数据7 V5 t- H/ o3 B# I0 ^6 u1 S
  58.                                 LDR R3, [R0, R2]                                        ;把加载地址处的值取出到R3寄存器# X. V! d7 }' C6 v0 C
  59.                                 STR R3, [R1, R2]                                        ;把取出的值从R3寄存器存入到链接地址                                       
    7 T2 _$ q2 t- O% o. u* q
  60.                                 CMP R2, #0                                                        ;将计数和0相比较
    & ^! M8 h6 n2 w* E
  61.                                 BNE CopyData                                                ;如果不相等,跳转到CopyData标签处,相等则往下执行2 K/ H/ \  P- W. T4 U/ ~9 N
  62. # J  }) f" E% A0 l/ G# Z, ~; i
  63. ; 清除BSS段, Z4 f/ {2 Y) a1 M
  64.                                 LDR R0, = |Image$RW_IRAM1$ZI$Base|   ;将bss段的链接地址存入R1寄存器
    6 S6 y4 M, f, Z- E# |) X5 d% }
  65.                                 LDR R1, = |Image$RW_IRAM1$ZI$Length| ;将bss段的长度存入R2寄存器! H# w$ Z9 v( j* V. I$ z4 W
  66. CleanBss        
    7 p' l* m" Z* l7 T4 Q& v( _
  67.                                 SUB R1, R1, #4                                                ;每次清除4个字节的bss段数据
    + t' s, P% k) {
  68.                                 MOV R3, #0                                                        ;将0存入r3寄存器; h' {/ S0 y2 e2 h4 _1 [. z& m
  69.                                 STR R3, [R0, R1]                                        ;把R3寄存器存入到链接地址                                        $ g* l. v' ^( n: j- Q9 G& J
  70.                                 CMP R1, #0                                                        ;将计数和0相比较
    , Y" W# V9 @& V# R
  71.                                 BNE CleanBss                                                ;如果不相等,跳转到CleanBss标签处,相等则往下执行
      S/ Y4 Z7 j* M1 ^
  72.                                 
    , R9 g# ]+ ]- L5 k1 [
  73.                                 . _# U4 Y7 \+ _9 D2 l
  74.                                 IMPORT  mymain                                                ;通知编译器要使用的标号在其他文件
    $ T# @3 v' I% f% K+ v
  75.                                 IMPORT        uart_init
    : S0 c$ w2 I' r; [; U& p, Y
  76.                                 
    3 s' n4 v( d' i+ o& A
  77.                                 BL                uart_init                                         ;跳转去执行uart_init函数
    ! ^+ `. ^9 B6 O" Z$ c( R. q
  78.                                 " ~1 B6 x; C4 A* |/ f
  79.                                 DCD                0XFFFFFFFF                                        ;一个未定义的指令* T$ j! e: L% o, j6 {3 a1 z- r! Y4 Q
  80.                                 1 M5 v9 w0 P6 J* k( m
  81.                                 BL                mymain                                                 ;跳转去执行main函数$ Y7 L: R, N0 S' W
  82.                                 B                .                                                        ;原地跳转,即处于循环状态
    * Q$ Q8 |$ t/ R& \$ E* i1 A
  83.                                 ENDP; w" d$ M2 c' A7 C) W

  84.   d+ j; H) N9 u& p
  85.                 ALIGN                                                                 ;填充字节使地址对齐2 J: A+ R8 w) C9 |/ w9 ?
  86.                 END                                                                        ;整个汇编文件结束
复制代码
8 o5 t% \# S5 @+ ~, d! e  T
然后新建exception.c文件,并添加各种异常处理函数) K8 ?. a. n' g* ^+ y6 O2 B

4 e. n6 K% x$ Y
  1. #include "uart.h"
    1 F1 D; _! h& g0 f

  2. ; U5 a# t+ l$ O1 P8 {3 O
  3. void NMI_Handler(void)8 w9 i! p( v2 O% E3 A
  4. {' U% O: X+ ]9 x$ j* F( |
  5.         putstring("Exception: NMI.\r\n");: Z$ G  G% S# I7 J" d+ w5 r
  6. }1 {6 s+ E  N) C: J' J9 m2 g0 D
  7. void HardFault_Handler(void)! q2 F  F4 P% W0 `
  8. {
    - f9 v: K$ m5 D1 }
  9.         putstring("Exception: Hard Fault.\r\n");
    ( a6 j2 }7 }1 \4 q  k3 |
  10. }, D' g" }9 v3 p+ ^2 J
  11. void MemManage_Handler(void); }4 g4 E$ H; y6 t2 |6 b( e' e+ q
  12. {' x+ c& l; I1 ?: R' N. f
  13.         putstring("Exception: Mem Manage Fault.\r\n");
    4 b" H2 U5 w( B' U  z- B  k
  14. }/ {& E3 f8 s) W  D8 T0 p% ?) o
  15. void BusFault_Handler(void)
    0 c; i( R1 [+ H/ u
  16. {
    4 G# g) K# d- ^6 x9 ?4 l8 X3 @
  17.         putstring("Exception: Bus Fault.\r\n");
    , z; ?+ c6 g/ ~1 ^1 P* d4 e5 X/ u
  18. }. b+ b# i( P, y
  19. void UsageFault_Handler(void)) Q) \8 n5 `7 F5 Y, V8 G3 B' s
  20. {
      M, }2 u# A& J3 ^" g
  21.         putstring("Exception: Usage Fault.\r\n");2 H* t4 y8 s, m  Q& ?# k  C
  22. }' Z8 X  s, q* O* K' I
  23. void SVC_Handler(void)
    6 @" X: i! P" b7 E6 j+ N# f
  24. {
    7 \6 H" @' j+ _" k1 h' n- ], x
  25.         putstring("Exception: SVCall.\r\n");: r0 S0 c% T- W& o2 l
  26. }% j: F+ E* `& |& A
  27. void DebugMon_Handler(void)
    ) z2 L# B0 D4 C
  28. {
    9 k+ f- U$ |5 F9 x$ z
  29.         putstring("Exception: Debug Monitor.\r\n");
    % v3 p/ \: U) y! w) r/ n: G7 m. b" A
  30. }' S7 s- i9 p" R
  31. void PendSV_Handler(void)
    ! J, a  c2 |( X# z+ p) I2 Y; Z2 v
  32. {
    8 t! i; F* Z5 r2 L" {, |" g! `
  33.         putstring("Exception: PendSV.\r\n");
    8 m9 I8 [" G* l( `( u1 b, h
  34. }
      ~) s- p! a5 O- ^2 Y3 Q% S3 o! }+ C
  35. void SysTick_Handler(void)- v5 n: B2 ~& @8 @" Y8 [- ]
  36. {! x( o, `  t2 U6 H
  37.         putstring("Exception: SysTick.\r\n");
    - W. l& F) k5 q/ n) J( m
  38. }
复制代码

2 E6 w) u6 G# V- p; j8 ]然后编译烧录运行,可以看到,串口一种打印硬件错误异常,这是因为没有将异常标志位清除的结果% A/ r( i7 Q. n) R3 M3 v

6 C3 N: S( ]' E- l- U' a- o$ x 20210129221446998.png 2 b+ y$ N1 ?. C
. N% @' K: [# V- R4 w
另外,进入了硬件错误异常而不是未定义指令异常,是因为cortex M3/M4的如下机制,因为未定义指令属于"处理器操作相关的错误",如果没有使能"Usage Fault",发就会触发"Hard Fault"。2 r; U' G1 I' O& B) T
* W2 ]4 p7 a5 R$ i1 E* |, u! C2 j
20210129221743384.png ' {' F" f# Y% o5 }. F' X/ ?6 X

7 o" K4 T+ P, f1 R# f, L; K+ O0 @那么要怎么使能"Usage Fault",我们要对SCB->SHCSR寄存器的第十八位写1来使能。即System Control Block的System Handler Control and State Register。7 B) y+ Y, L- a, l$ |
我们找到STM32库里的core_cm3.h文件可以看到这个寄存器的结构体定义,将这个文件加入我们的工程中! p/ g# C: v' |! A" m( L% r# g4 m
" [4 U7 B6 N  O5 e+ b2 ^
20210129223305415.png
* V; h# s3 g  A& f% \. P0 E
* \2 E3 v- E0 ~* R. y然后在exception.c文件中添加exception_init()函数使能"Usage Fault",
1 A# N7 S% D. N# k' w  H/ w0 Q; s6 O* ]
  1. void exception_init(void)
    $ y9 B/ \# k6 D( E, {
  2. {+ `" i) \( `: i; ^
  3.         SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk);% q% k4 Q3 F) O$ @& V& i, {
  4. }
复制代码
- V8 c! K& V- G. J* {3 D; L
然后在汇编文件中调用
' V. z) ?. j5 j
5 S0 ]  q, G% X* s# h4 o 2021012922430136.png
, n* e2 N, _) o% h5 d* F6 {  g  h4 h/ n* v) v
编译发现core_cm3.h有许多未定义的错误,
+ q# e& H  H' f, t# l+ p& Z
0 r! C: _; `5 D$ M' r" V 20210129224837391.png ( s9 x. t9 Q+ l/ z0 B8 y1 ^
$ S! p/ V8 r! z, {5 d0 O
我们不使用这些将其屏蔽掉即可,然后重新编译链接烧录运行,可以看到进入了使用错误异常, t2 ]! [$ H4 |& c; Q, ]; m
6 U# M' |, O/ P4 J' T% `) @

- e. ?1 b. T$ {0 u+ n: h5 N& B1 i8 ?8 G1 O( o$ F
————————————————' Y- r5 ~  l$ f; G' l8 O
转载:Willliam_william! I( p( x6 m6 x: p! N

" K1 ~3 \) M! P+ G$ z$ m/ s5 p* B0 x1 S( e1 w1 _! t! v
20210129224946141.png
赞 收藏 评论0 发布时间:2022-8-27 14:22

举报

0个回答

所属标签

相似技术帖

官网相关资源

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