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

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

[复制链接]
STMCU小助手 发布时间:2022-8-27 14:22
一、STM32的异常处理机制
# L! m9 t, V  e4 i) ]对于cortex M3/M4来说,CPU每执行完一条指令都会检查有无异常产生,当CPU发现有异常产生时,它就会进行如下处理:
( u- M2 d2 X, N1 t保存现场
% M$ C% j1 z% n, ?$ p; ]& c: ]分辨异常/中断,调用对应的异常/中断处理函数
9 x+ u0 d" E: z& G  ^, ?2 J2 r% D恢复现场
: o, u5 I$ _1 z$ k每个异常/中断对应着一个异常向量,所有的异常向量组成一个异常向量表,对于cortex M3/M4来说,这个异常向量表中放置的就是具体异常/中断的处理函数的地址,当发生异常时,CPU就会从向量表里找到对应的项,从而得到处理函数的地址,跳转去执行。另外,对于cortex M3/M4来说,保存/恢复现场都是是硬件实现的。
8 C: w8 u0 G% N& O3 o. \我们可以打开一个STM32库的汇编启动文件,例如startup_stm32f10x_hd.s,我们可以看到,前面的时异常,后面的就是中断了(其实中断也是一种异常)。
  ~$ a. z/ N6 p2 m0 P3 F8 _% u8 I# Q, Q+ x
20210129215047591.png
7 p9 Z2 G% B3 Z
" x: V3 ^% K9 O. K: Y& q7 u二、未定义指令异常
& K6 R; n+ r8 y' Y( R未定义指令,即使"还没有定义的指令",也就是CPU不认识的指令。
) s: ^. T" |9 @% H) g) f修改汇编文件,如下所示,添加各种异常的向量表项,另外在调用mymain()函数前调用串口初始化函数,并添加一个未定义的指令异常。
# O# D3 W2 `4 |+ o! J4 i, S5 q0 H+ x3 g
  1. Stack_Size      EQU     0x00000500                                  ;定义堆栈大小为1024byte/ i. W6 D' O: B8 ^! w5 t) [
  2.                                 AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为0,8字节对齐* b' I" b0 t4 B+ R
  3. Stack_Mem                SPACE        Stack_Size                                    ;保留Stack_Size大小的栈空间8 r! H( V* H' i
  4. __initial_sp                                                                                  ;标号,代表堆栈顶部地址,后面有用8 I8 G! f- ~1 r
  5. # ]9 ], Z$ v, Z& L! ]. T& Z* V
  6.                 PRESERVE8                                                        ;指示编译器8字节对齐# X0 b, u; J) r: g# R2 Z5 f6 S
  7.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                                ) C& ^. C- _' t

  8. ) ~( z1 M4 A: H

  9. , o* Z+ M, v  ?0 V9 d( n+ J& p. L
  10. ; Vector Table Mapped to Address 0 at Reset
    ' l0 E$ l: J/ A7 M% D# ]/ {
  11.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址; d% b; |# G. ]& o8 v8 R
  12.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用# N3 X6 y. x& A" n
  13.                                 IMPORT         NMI_Handler                ; NMI Handler7 v  ?* k) E0 J+ k
  14.                                 IMPORT         HardFault_Handler          ; Hard Fault Handler1 y4 H- r2 Z, U/ d9 R6 P$ _
  15.                                 IMPORT         MemManage_Handler          ; MPU Fault Handler) F* |2 G; A. s) a! C* \! R
  16.                                 IMPORT         BusFault_Handler           ; Bus Fault Handler' x, q. X+ I* Q6 _, f9 D' U
  17.                                 IMPORT         UsageFault_Handler         ; Usage Fault Handler
    2 l6 G* A* ]6 Z6 U2 J/ C
  18.                                 IMPORT         SVC_Handler                ; SVCall Handler) P& X2 O/ N& Q/ _: x9 {* o
  19.                                 IMPORT         DebugMon_Handler           ; Debug Monitor Handler6 j/ g4 ]: e0 q/ J! e/ Y, ]& _: j
  20.                                 IMPORT         PendSV_Handler             ; PendSV Handler
    , M7 n5 U1 g! H6 y$ V# E- j
  21.                                 IMPORT         SysTick_Handler            ; SysTick Handler        : g& w  \' T0 X+ n& m5 w. ~
  22.                                         : p0 h' I" v+ r5 m& ~8 L, }/ D) w, u
  23. __Vectors       DCD     __initial_sp                                ;当前地址写入一个字(32bit)数据,值应该为栈顶地址5 ^# H6 K1 c7 M! N+ f: k
  24.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址5 H0 |' g/ S9 o1 ?  h$ T
  25.                                 DCD     NMI_Handler                ; NMI Handler. Y0 M2 F: k" ^( C: u' N
  26.                 DCD     HardFault_Handler          ; Hard Fault Handler6 a& n0 N+ q- M
  27.                 DCD     MemManage_Handler          ; MPU Fault Handler
    ( ]  t# D* U, F* p$ e2 m5 I$ ]
  28.                 DCD     BusFault_Handler           ; Bus Fault Handler! o4 `" Q0 f/ B. K  P5 @
  29.                 DCD     UsageFault_Handler         ; Usage Fault Handler
    * W' U4 T9 }* F, W
  30.                 DCD     0                          ; Reserved
    3 ~7 t- u9 F* b7 K7 v2 E1 W0 b0 i4 h
  31.                 DCD     0                          ; Reserved
    ! q: s. M6 H- }5 z8 _% J- f
  32.                 DCD     0                          ; Reserved% w% f0 H8 T9 ?2 b9 \& b
  33.                 DCD     0                          ; Reserved3 b9 d) e" a6 j/ x6 {/ b; g
  34.                 DCD     SVC_Handler                ; SVCall Handler- h' ]/ K# `. _# W  W+ L# b
  35.                 DCD     DebugMon_Handler           ; Debug Monitor Handler0 R# K4 r. p4 u
  36.                 DCD     0                          ; Reserved, z/ |4 G7 d9 M) k8 U/ g/ c
  37.                 DCD     PendSV_Handler             ; PendSV Handler0 a/ A; O% a! S& d+ F$ R9 I3 a
  38.                 DCD     SysTick_Handler            ; SysTick Handler" }9 r" Y+ U! \# \
  39. ' l# [1 i) K' u/ V, Y6 ]
  40.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text
    2 M' b# @) W" C$ t
  41. " \- {0 o5 [. o, E
  42. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
    & m# \3 z! u+ s
  43. Reset_Handler   PROC                                                                ;过程的开始
      U# Y: g' M6 q/ h* J- ]8 x, Z
  44.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。! M5 }5 F0 U2 i: Q  |
  45.                                         ) z1 Y# y( @# G% j7 k2 }
  46.                                 IMPORT |Image$RW_IRAM1$Base|                ;从别处导入data段的链接地址
    ! E  j6 i  g/ ]' m6 V" B$ V* t' K) l- @
  47.                                 IMPORT |Image$RW_IRAM1$Length|        ;从别处导入data段的长度) J8 G( Z6 I3 d2 S$ m; F9 e
  48.                                 IMPORT |Load$RW_IRAM1$Base|                ;从别处导入data段的加载地址
    3 o# L# d' j% d* g& ^5 t
  49.                                 IMPORT |Image$RW_IRAM1$ZI$Base|        ;从别处导入ZI段的链接地址, w* s* o5 T- X# |5 I* E
  50.                                 IMPORT |Image$RW_IRAM1$ZI$Length|;从别处导入ZI段的长度  ]6 l; g5 i6 S0 R, F; V+ m! X
  51. : t0 f. C6 Z1 R+ w/ L6 i& ~
  52. ; 复制数据段4 y- j7 H) X6 w' r+ z  R
  53.                                 LDR R0, = |Load$RW_IRAM1$Base|           ;将data段的加载地址存入R0寄存器
    / j* A4 G9 Q& G8 c5 ^$ R
  54.                                 LDR R1, = |Image$RW_IRAM1$Base|   ;将data段的链接地址存入R1寄存器$ W& R2 A. |3 A3 d& K, T( J* b
  55.                                 LDR R2, = |Image$RW_IRAM1$Length| ;将data段的长度存入R2寄存器
    1 N( o$ J4 y. o# I& b; _
  56. CopyData                * H6 @% P4 V' P. ]' {! {
  57.                                 SUB R2, R2, #4                                                ;每次复制4个字节的data段数据9 f( I! O. a4 s& W
  58.                                 LDR R3, [R0, R2]                                        ;把加载地址处的值取出到R3寄存器: ^5 t2 I1 S1 E  [
  59.                                 STR R3, [R1, R2]                                        ;把取出的值从R3寄存器存入到链接地址                                       
    9 C& G6 R" a# ~
  60.                                 CMP R2, #0                                                        ;将计数和0相比较" v6 p5 G: C( y1 f* A' k
  61.                                 BNE CopyData                                                ;如果不相等,跳转到CopyData标签处,相等则往下执行
    3 b; O  {0 }; o# v6 A5 Y8 j

  62. . D0 O* ^. W) z% D# ~+ K
  63. ; 清除BSS段
    / X+ o5 L& G$ _1 c' P' y
  64.                                 LDR R0, = |Image$RW_IRAM1$ZI$Base|   ;将bss段的链接地址存入R1寄存器
    7 m0 ]9 }% u7 \, R1 c7 n
  65.                                 LDR R1, = |Image$RW_IRAM1$ZI$Length| ;将bss段的长度存入R2寄存器: g) {! H/ E4 R6 Z" u- Q0 L
  66. CleanBss        
    + b+ {  l  |5 s. X- Q9 H0 x6 D
  67.                                 SUB R1, R1, #4                                                ;每次清除4个字节的bss段数据
    ' R+ q, g+ U, y) u( P; W7 Z. w
  68.                                 MOV R3, #0                                                        ;将0存入r3寄存器% ~- B8 l- ~' W/ S
  69.                                 STR R3, [R0, R1]                                        ;把R3寄存器存入到链接地址                                        ! U0 T7 }# e3 {9 }: ]; ~4 w
  70.                                 CMP R1, #0                                                        ;将计数和0相比较0 z$ s) L' Q3 \* i6 n* o) c8 y* n$ [
  71.                                 BNE CleanBss                                                ;如果不相等,跳转到CleanBss标签处,相等则往下执行
    & e- h# \4 |6 B  d
  72.                                 
    2 D( C6 H2 w$ y
  73.                                 * |4 @- Q1 o1 V( K
  74.                                 IMPORT  mymain                                                ;通知编译器要使用的标号在其他文件' A1 `, R9 h' n
  75.                                 IMPORT        uart_init0 p5 Z4 L# G# C/ W' ~7 G
  76.                                 2 N& P( M& e% t% ~# ?# r1 j( D
  77.                                 BL                uart_init                                         ;跳转去执行uart_init函数
    + T3 p% Y% [8 b0 d* A
  78.                                 # {; q, u% q; \, A$ r! z# B4 M2 C
  79.                                 DCD                0XFFFFFFFF                                        ;一个未定义的指令
    ; v4 g# T- Q# N# E
  80.                                 9 M' W4 s6 U* X! f. V/ }) C
  81.                                 BL                mymain                                                 ;跳转去执行main函数
    ) H1 ^3 S1 V) L- I& L- q2 D
  82.                                 B                .                                                        ;原地跳转,即处于循环状态
    $ D& u* b! v, u! q/ ~
  83.                                 ENDP
    % `2 E  ]$ H% z/ S
  84. " |1 I. s  H* T; ?+ F! R4 g
  85.                 ALIGN                                                                 ;填充字节使地址对齐$ i# ~" G& C6 W2 \
  86.                 END                                                                        ;整个汇编文件结束
复制代码

+ g- I1 [7 c5 x4 t; I然后新建exception.c文件,并添加各种异常处理函数
$ c& B$ n. i0 {/ g1 I+ q9 `
- p+ W4 G' P. x. k3 Q  |
  1. #include "uart.h"
    ! `; o8 U$ ^. e7 g

  2. 5 [* I/ {" r" r
  3. void NMI_Handler(void)1 V6 p$ h  W9 W% y  a$ {
  4. {, \9 v* B3 n( A4 @
  5.         putstring("Exception: NMI.\r\n");6 Y) w- M1 \  e0 W; A) U' E
  6. }! U' s# P; c  h, P9 ]8 R3 I
  7. void HardFault_Handler(void)# K& ~8 ?; `) y0 E4 n! Z, ^  `0 {1 ?# f
  8. {
    6 v$ J7 r) E8 W& i
  9.         putstring("Exception: Hard Fault.\r\n");
      Q% _8 J) `: A; g6 b
  10. }3 M2 I! Q: {5 S% S1 ^7 G4 z
  11. void MemManage_Handler(void)
    % p  n2 F+ n( u: R
  12. {1 y4 N' z. e7 B7 ]' a  i/ K
  13.         putstring("Exception: Mem Manage Fault.\r\n");
    0 D; A+ v  k, b" Q0 x5 m1 ?+ r% o! Z$ [
  14. }
    0 U8 \& O" ?# d3 P( Z
  15. void BusFault_Handler(void)
    % b* f" |! |: S
  16. {. {& w" j( @: R* D
  17.         putstring("Exception: Bus Fault.\r\n");
    % g, A# _) F6 C- R+ U
  18. }' K" t- U6 u0 Z8 Y1 Q) I9 a
  19. void UsageFault_Handler(void)& S" M3 x, N  R" C
  20. {* c( w, M# L+ \. G
  21.         putstring("Exception: Usage Fault.\r\n");
    ) I; C7 C; S- l7 H, ~  w1 w9 u
  22. }. u5 p! d% e8 p/ U& o
  23. void SVC_Handler(void)
    2 A4 o8 y6 J+ k! a
  24. {% K0 v7 M) z: c8 g
  25.         putstring("Exception: SVCall.\r\n");, E' t' N7 h7 G7 h2 g
  26. }
    . M$ Y' O* E* F* T/ F# q
  27. void DebugMon_Handler(void)% r" k/ X$ }5 S6 _$ V1 U6 l
  28. {, a, m, s+ b$ p& v9 A9 Q! ^
  29.         putstring("Exception: Debug Monitor.\r\n");+ s. n: c* i9 Q$ y" B/ y/ Z- A$ p
  30. }
    . }% g0 w9 M# o& ~9 a: U/ X( i& C! Z
  31. void PendSV_Handler(void)
    7 O7 w4 s' q) r( s
  32. {
    3 ~# M; Y7 p/ @( P: i) m2 H. G
  33.         putstring("Exception: PendSV.\r\n");/ l) R) S8 C; }
  34. }. s3 O+ C; h  d% u& t! Z8 }) l' y" j
  35. void SysTick_Handler(void)$ r3 V6 g4 J8 ~, z
  36. {9 k* g% Z. P7 u% f* h
  37.         putstring("Exception: SysTick.\r\n");
    ) }6 Z$ `/ D* l
  38. }
复制代码
1 L- T# j2 R/ l" j" X0 ]
然后编译烧录运行,可以看到,串口一种打印硬件错误异常,这是因为没有将异常标志位清除的结果8 v2 H% _  Q3 T8 {0 t3 D

$ v2 z" X  \0 } 20210129221446998.png
9 N! p9 f$ c2 Q8 {% Y3 e6 X9 g( I! x, v# t( T* H+ V+ Q
另外,进入了硬件错误异常而不是未定义指令异常,是因为cortex M3/M4的如下机制,因为未定义指令属于"处理器操作相关的错误",如果没有使能"Usage Fault",发就会触发"Hard Fault"。
$ o. G4 C, @* ?7 B# k& [0 y3 U, ^  {7 ?
20210129221743384.png
$ Y! ^$ Y; c) S( w( b
2 Z8 k" W6 P8 L2 Y" V那么要怎么使能"Usage Fault",我们要对SCB->SHCSR寄存器的第十八位写1来使能。即System Control Block的System Handler Control and State Register。4 A  R% |7 y! n0 @; U- G
我们找到STM32库里的core_cm3.h文件可以看到这个寄存器的结构体定义,将这个文件加入我们的工程中
- m: P1 N( x8 ~; x5 e/ N9 e( ~
8 Q) N; D4 w& w9 [6 j3 k- W 20210129223305415.png
# K% S# k$ W2 F/ A
$ u7 A" h- q% Y4 B然后在exception.c文件中添加exception_init()函数使能"Usage Fault",
' b7 b: M9 G- T; w% z$ n$ j
' c* V3 n! \4 `* N3 X* E- |
  1. void exception_init(void)
    8 I9 p5 N/ U; Q  q
  2. {
    . q+ o* J7 z1 W* B: [
  3.         SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk);
    " v  @/ ]. b9 e1 X+ d! U- w  U+ r" s
  4. }
复制代码

0 P$ f3 Q; D5 n# ^' y2 T然后在汇编文件中调用
$ G1 o+ I- O0 K/ J' s. }8 u% F) [& s
2021012922430136.png
( G0 \; b% i9 n. Q5 s+ x) K+ ~  w; V3 S3 Y
编译发现core_cm3.h有许多未定义的错误,
: H2 \! x% Z1 w# V0 `  h" D, q% F, [" s
20210129224837391.png
9 A! J; T# x" h" g$ V7 N0 q. \, @5 w* l- y/ w7 Y
我们不使用这些将其屏蔽掉即可,然后重新编译链接烧录运行,可以看到进入了使用错误异常
4 D( G' R$ q8 d9 C9 n# c
5 X3 v& j8 m) O  f$ ~* |3 e3 e
" h9 G# _! W* R4 O0 i/ v5 |9 f2 @& Q* ^& S* K. m5 v7 H: |
————————————————) w% r8 s1 M4 _2 W: o
转载:Willliam_william
5 J! A1 O4 H1 ~8 X) s; B! V
# n1 W6 ]4 H4 x/ t/ s# A9 p3 }6 K5 w
20210129224946141.png
收藏 评论0 发布时间:2022-8-27 14:22

举报

0个回答

所属标签

相似分享

官网相关资源

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