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

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

[复制链接]
STMCU小助手 发布时间:2022-8-27 14:22
一、STM32的异常处理机制
对于cortex M3/M4来说,CPU每执行完一条指令都会检查有无异常产生,当CPU发现有异常产生时,它就会进行如下处理:
保存现场
分辨异常/中断,调用对应的异常/中断处理函数
恢复现场
每个异常/中断对应着一个异常向量,所有的异常向量组成一个异常向量表,对于cortex M3/M4来说,这个异常向量表中放置的就是具体异常/中断的处理函数的地址,当发生异常时,CPU就会从向量表里找到对应的项,从而得到处理函数的地址,跳转去执行。另外,对于cortex M3/M4来说,保存/恢复现场都是是硬件实现的。
我们可以打开一个STM32库的汇编启动文件,例如startup_stm32f10x_hd.s,我们可以看到,前面的时异常,后面的就是中断了(其实中断也是一种异常)。

20210129215047591.png

二、未定义指令异常
未定义指令,即使"还没有定义的指令",也就是CPU不认识的指令。
修改汇编文件,如下所示,添加各种异常的向量表项,另外在调用mymain()函数前调用串口初始化函数,并添加一个未定义的指令异常。

  1. Stack_Size      EQU     0x00000500                                  ;定义堆栈大小为1024byte
  2.                                 AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为0,8字节对齐
  3. Stack_Mem                SPACE        Stack_Size                                    ;保留Stack_Size大小的栈空间
  4. __initial_sp                                                                                  ;标号,代表堆栈顶部地址,后面有用

  5.                 PRESERVE8                                                        ;指示编译器8字节对齐
  6.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                               


  7. ; Vector Table Mapped to Address 0 at Reset
  8.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
  9.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
  10.                                 IMPORT         NMI_Handler                ; NMI Handler
  11.                                 IMPORT         HardFault_Handler          ; Hard Fault Handler
  12.                                 IMPORT         MemManage_Handler          ; MPU Fault Handler
  13.                                 IMPORT         BusFault_Handler           ; Bus Fault Handler
  14.                                 IMPORT         UsageFault_Handler         ; Usage Fault Handler
  15.                                 IMPORT         SVC_Handler                ; SVCall Handler
  16.                                 IMPORT         DebugMon_Handler           ; Debug Monitor Handler
  17.                                 IMPORT         PendSV_Handler             ; PendSV Handler
  18.                                 IMPORT         SysTick_Handler            ; SysTick Handler        
  19.                                        
  20. __Vectors       DCD     __initial_sp                                ;当前地址写入一个字(32bit)数据,值应该为栈顶地址
  21.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址
  22.                                 DCD     NMI_Handler                ; NMI Handler
  23.                 DCD     HardFault_Handler          ; Hard Fault Handler
  24.                 DCD     MemManage_Handler          ; MPU Fault Handler
  25.                 DCD     BusFault_Handler           ; Bus Fault Handler
  26.                 DCD     UsageFault_Handler         ; Usage Fault Handler
  27.                 DCD     0                          ; Reserved
  28.                 DCD     0                          ; Reserved
  29.                 DCD     0                          ; Reserved
  30.                 DCD     0                          ; Reserved
  31.                 DCD     SVC_Handler                ; SVCall Handler
  32.                 DCD     DebugMon_Handler           ; Debug Monitor Handler
  33.                 DCD     0                          ; Reserved
  34.                 DCD     PendSV_Handler             ; PendSV Handler
  35.                 DCD     SysTick_Handler            ; SysTick Handler

  36.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text

  37. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
  38. Reset_Handler   PROC                                                                ;过程的开始
  39.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
  40.                                        
  41.                                 IMPORT |Image$RW_IRAM1$Base|                ;从别处导入data段的链接地址
  42.                                 IMPORT |Image$RW_IRAM1$Length|        ;从别处导入data段的长度
  43.                                 IMPORT |Load$RW_IRAM1$Base|                ;从别处导入data段的加载地址
  44.                                 IMPORT |Image$RW_IRAM1$ZI$Base|        ;从别处导入ZI段的链接地址
  45.                                 IMPORT |Image$RW_IRAM1$ZI$Length|;从别处导入ZI段的长度

  46. ; 复制数据段
  47.                                 LDR R0, = |Load$RW_IRAM1$Base|           ;将data段的加载地址存入R0寄存器
  48.                                 LDR R1, = |Image$RW_IRAM1$Base|   ;将data段的链接地址存入R1寄存器
  49.                                 LDR R2, = |Image$RW_IRAM1$Length| ;将data段的长度存入R2寄存器
  50. CopyData               
  51.                                 SUB R2, R2, #4                                                ;每次复制4个字节的data段数据
  52.                                 LDR R3, [R0, R2]                                        ;把加载地址处的值取出到R3寄存器
  53.                                 STR R3, [R1, R2]                                        ;把取出的值从R3寄存器存入到链接地址                                       
  54.                                 CMP R2, #0                                                        ;将计数和0相比较
  55.                                 BNE CopyData                                                ;如果不相等,跳转到CopyData标签处,相等则往下执行

  56. ; 清除BSS段
  57.                                 LDR R0, = |Image$RW_IRAM1$ZI$Base|   ;将bss段的链接地址存入R1寄存器
  58.                                 LDR R1, = |Image$RW_IRAM1$ZI$Length| ;将bss段的长度存入R2寄存器
  59. CleanBss        
  60.                                 SUB R1, R1, #4                                                ;每次清除4个字节的bss段数据
  61.                                 MOV R3, #0                                                        ;将0存入r3寄存器
  62.                                 STR R3, [R0, R1]                                        ;把R3寄存器存入到链接地址                                       
  63.                                 CMP R1, #0                                                        ;将计数和0相比较
  64.                                 BNE CleanBss                                                ;如果不相等,跳转到CleanBss标签处,相等则往下执行
  65.                                 
  66.                                 
  67.                                 IMPORT  mymain                                                ;通知编译器要使用的标号在其他文件
  68.                                 IMPORT        uart_init
  69.                                 
  70.                                 BL                uart_init                                         ;跳转去执行uart_init函数
  71.                                 
  72.                                 DCD                0XFFFFFFFF                                        ;一个未定义的指令
  73.                                 
  74.                                 BL                mymain                                                 ;跳转去执行main函数
  75.                                 B                .                                                        ;原地跳转,即处于循环状态
  76.                                 ENDP

  77.                 ALIGN                                                                 ;填充字节使地址对齐
  78.                 END                                                                        ;整个汇编文件结束
复制代码

然后新建exception.c文件,并添加各种异常处理函数

  1. #include "uart.h"

  2. void NMI_Handler(void)
  3. {
  4.         putstring("Exception: NMI.\r\n");
  5. }
  6. void HardFault_Handler(void)
  7. {
  8.         putstring("Exception: Hard Fault.\r\n");
  9. }
  10. void MemManage_Handler(void)
  11. {
  12.         putstring("Exception: Mem Manage Fault.\r\n");
  13. }
  14. void BusFault_Handler(void)
  15. {
  16.         putstring("Exception: Bus Fault.\r\n");
  17. }
  18. void UsageFault_Handler(void)
  19. {
  20.         putstring("Exception: Usage Fault.\r\n");
  21. }
  22. void SVC_Handler(void)
  23. {
  24.         putstring("Exception: SVCall.\r\n");
  25. }
  26. void DebugMon_Handler(void)
  27. {
  28.         putstring("Exception: Debug Monitor.\r\n");
  29. }
  30. void PendSV_Handler(void)
  31. {
  32.         putstring("Exception: PendSV.\r\n");
  33. }
  34. void SysTick_Handler(void)
  35. {
  36.         putstring("Exception: SysTick.\r\n");
  37. }
复制代码

然后编译烧录运行,可以看到,串口一种打印硬件错误异常,这是因为没有将异常标志位清除的结果

20210129221446998.png

另外,进入了硬件错误异常而不是未定义指令异常,是因为cortex M3/M4的如下机制,因为未定义指令属于"处理器操作相关的错误",如果没有使能"Usage Fault",发就会触发"Hard Fault"。

20210129221743384.png

那么要怎么使能"Usage Fault",我们要对SCB->SHCSR寄存器的第十八位写1来使能。即System Control Block的System Handler Control and State Register。
我们找到STM32库里的core_cm3.h文件可以看到这个寄存器的结构体定义,将这个文件加入我们的工程中

20210129223305415.png

然后在exception.c文件中添加exception_init()函数使能"Usage Fault",

  1. void exception_init(void)
  2. {
  3.         SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk);
  4. }
复制代码

然后在汇编文件中调用

2021012922430136.png

编译发现core_cm3.h有许多未定义的错误,

20210129224837391.png

我们不使用这些将其屏蔽掉即可,然后重新编译链接烧录运行,可以看到进入了使用错误异常



————————————————
转载:Willliam_william


20210129224946141.png
收藏 评论0 发布时间:2022-8-27 14:22

举报

0个回答

所属标签

相似分享

官网相关资源

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