【Cortex-M】异常中断时的程序运行指针SP获取,及SCB寄存器错误类型获取

更新以gitee为准:
gitee

文章目录

  • 异常中断
  • 异常的程序运行指针SP获取
  • SCB寄存器错误类型获取
    • 硬件错误异常 Hard fault status register (SCB->HFSR)
    • 存储器管理错误异常 SCB->CFSR中MMFSR位
    • 总线错误异常 SCB->CFSR中BFSR位
    • 使用错误异常 SCB->CFSR中UFSR位
  • 附录:压缩字符串、大小端格式转换
    • 压缩字符串
      • 浮点数
      • 压缩Packed-ASCII字符串
    • 大小端转换
      • 什么是大端和小端
      • 数据传输中的大小端
      • 总结
      • 大小端转换函数

异常中断

当MCU因堆栈溢出等造成异常会 会进入异常中断
可以在startup.s中查看
在这里插入图片描述
通常引起的时HardFault中断(包括内存溢出、非法赋值、寄存器操作错误、堆栈溢出等)
譬如手动引起异常:

*(uint32_t *)0x45678912=1;

异常的程序运行指针SP获取

打个断点在异常代码处 可以看到地址为0x08003792
在这里插入图片描述
在HardFault_Handler中也打个断点 就能在运行到其中时停止
在这里插入图片描述
此时 LR寄存器为特殊标记值
0xFFFFFFE9对应的是要看MSP寄存器
0xFFFFFFFD对应的是要看PSP寄存器
具体根据芯片手册而定(cortex-M权威指南)
不过也有个更方便的方式是直接读取SP指针
通过汇编即可实现

__asm uint32_t MZ_CM_ReadReg13(void){MOV     R0, spbx lr
}

那么 就可以通过此方式获取到0x200101B0
通过内存查看该地址:
在这里插入图片描述
第5和6个uint32值即为上一次异常程序地址 和如果正常运行的话 下一个程序地址
注意为小端格式 所以解析出来就是0x08003793和0x0800379A
在这里插入图片描述
但程序地址的最后一位是标志位 用于区分是否为thumb指令或ARM指令(cortex权威指南)

由于历史原因,最初的16位单片机用的指令是Thumb指令,为16位指令。后来,ARM推出了32位的ARM指令。16位的指令更适合空间有限的嵌入式处理器,而32位的指令则更加灵活、功能丰富。因此,有些单片机在运行时可以根据需求切换指令状态。如下图:
在这里插入图片描述

后来诞生了Thumb-2指令集,它实现了16位指令和32位指令的共存。而Cortex-M3架构则就是采用Thumb-2指令集。由于16位指令长度是2字节,32位指令长度是4字节,因此其PC指针地址是按照2字节(16位)对齐的。换言之其最低位一定是0。

另外 需要注意的是 如果在代码层面进行获取 不能包装子函数(子函数会导致LR和SP被重写) 所以直接用一个宏替代即可

#define MZ_CM_SP_UpdateStat() MZ_CM_SP_Stat.sp = MZ_CM_ReadReg13();MZ_CM_SP_Stat.spAddr = (uint32_t *)MZ_CM_SP_Stat.sp;MZ_CM_SP_Stat.lastFlashAddr = MZ_CM_SP_Stat.spAddr[5];MZ_CM_SP_Stat.nextFlashAddr = MZ_CM_SP_Stat.spAddr[6];

调用后 如下:
在这里插入图片描述
后一个地址正是如果正常运行的下一个程序语句地址
在这里插入图片描述

SCB寄存器错误类型获取

关键寄存器:

SCB->CFSR(可配置故障状态寄存器):判断故障类型(如内存访问错误、未对齐访问、指令执行错误)。

SCB->HFSR(硬件故障状态寄存器):确认是否为HardFault的直接触发原因。

SCB->MMFAR/SCB->BFAR:记录导致内存管理或总线故障的具体地址。

直接通过memcpy获取即可

硬件错误异常 Hard fault status register (SCB->HFSR)

在这里插入图片描述

存储器管理错误异常 SCB->CFSR中MMFSR位

在这里插入图片描述

总线错误异常 SCB->CFSR中BFSR位

在这里插入图片描述

使用错误异常 SCB->CFSR中UFSR位

在这里插入图片描述

附录:压缩字符串、大小端格式转换

压缩字符串

首先HART数据格式如下:
在这里插入图片描述
在这里插入图片描述
重点就是浮点数和字符串类型
Latin-1就不说了 基本用不到

浮点数

浮点数里面 如 0x40 80 00 00表示4.0f

在HART协议里面 浮点数是按大端格式发送的 就是高位先发送 低位后发送

发送出来的数组为:40,80,00,00

但在C语言对浮点数的存储中 是按小端格式来存储的 也就是40在高位 00在低位
浮点数:4.0f
地址0x1000对应00
地址0x1001对应00
地址0x1002对应80
地址0x1003对应40

若直接使用memcpy函数 则需要进行大小端转换 否则会存储为:
地址0x1000对应40
地址0x1001对应80
地址0x1002对应00
地址0x1003对应00

大小端转换:

void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}

压缩Packed-ASCII字符串

本质上是将原本的ASCII的最高2位去掉 然后拼接起来 比如空格(0x20)
四个空格拼接后就成了
1000 0010 0000 1000 0010 0000
十六进制:82 08 20
对了一下表 0x20之前的识别不了
也就是只能识别0x20-0x5F的ASCII表
在这里插入图片描述

压缩/解压函数后面再写:

//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_ASCII_to_Pack(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(buf,0,str_len/4*3);	  for(i=0;i<str_len;i++){if(str[i]==0x00){str[i]=0x20;}}for(i=0;i<str_len/4;i++){buf[3*i]=(str[4*i]<<2)|((str[4*i+1]>>4)&0x03);buf[3*i+1]=(str[4*i+1]<<4)|((str[4*i+2]>>2)&0x0F);buf[3*i+2]=(str[4*i+2]<<6)|(str[4*i+3]&0x3F);}return 1;
}//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_Pack_to_ASCII(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(str,0,str_len);for(i=0;i<str_len/4;i++){str[4*i]=(buf[3*i]>>2)&0x3F;str[4*i+1]=((buf[3*i]<<4)&0x30)|(buf[3*i+1]>>4);str[4*i+2]=((buf[3*i+1]<<2)&0x3C)|(buf[3*i+2]>>6);str[4*i+3]=buf[3*i+2]&0x3F;}return 1;
}

大小端转换

在串口等数据解析中 难免遇到大小端格式问题

什么是大端和小端

所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

简单来说:大端——高尾端,小端——低尾端

举个例子,比如数字 0x12 34 56 78在内存中的表示形式为:

1)大端模式:

低地址 -----------------> 高地址

0x12 | 0x34 | 0x56 | 0x78

2)小端模式:

低地址 ------------------> 高地址

0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

数据传输中的大小端

比如地址位、起止位一般都是大端格式
如:
起始位:0x520A
则发送的buf应为{0x52,0x0A}

而数据位一般是小端格式(单字节无大小端之分)
如:
一个16位的数据发送出来为{0x52,0x0A}
则对应的uint16_t类型数为: 0x0A52

而对于浮点数4.0f 转为32位应是:
40 80 00 00

以大端存储来说 发送出来的buf就是依次发送 40 80 00 00

以小端存储来说 则发送 00 00 80 40

由于memcpy等函数 是按字节地址进行复制 其复制的格式为小端格式 所以当数据为小端存储时 不用进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x00,0x00,0x80,0x40};memcpy(&dat,buf,4);float f=0.0f;f=*((float*)&dat); //地址强转printf("%f",f);

或更优解:

   uint8_t buf[]={0x00,0x00,0x80,0x40};   float f=0.0f;memcpy(&f,buf,4);

而对于大端存储的数据(如HART协议数据 全为大端格式) 其复制的格式仍然为小端格式 所以当数据为小端存储时 要进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&dat); //大小端转换f=*((float*)&dat); //地址强转printf("%f",f);

或:

uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&f); //大小端转换printf("%f",f);

或更优解:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};float f=0.0f;dat=(buf[0]<<24)|(buf[0]<<16)|(buf[0]<<8)|(buf[0]<<0)f=*((float*)&dat);

总结

固 若数据为小端格式 则可以直接用memcpy函数进行转换 否则通过移位的方式再进行地址强转

对于多位数据 比如同时传两个浮点数 则可以定义结构体之后进行memcpy复制(数据为小端格式)

对于小端数据 直接用memcpy写入即可 若是浮点数 也不用再进行强转

对于大端数据 如果不嫌麻烦 或想使代码更加简洁(但执行效率会降低) 也可以先用memcpy写入结构体之后再调用大小端转换函数 但这里需要注意的是 结构体必须全为无符号整型 浮点型只能在大小端转换写入之后再次强转 若结构体内采用浮点型 则需要强转两次

所以对于大端数据 推荐通过移位的方式来进行赋值 然后再进行个别数的强转 再往通用结构体进行写入

多个不同变量大小的结构体 要主要字节对齐的问题
可以用#pragma pack(1) 使其对齐为1
但会影响效率

大小端转换函数

直接通过对地址的操作来实现 传入的变量为32位的变量
中间变量ptr是传入变量的地址

void swap16(void * p)
{uint16_t *ptr=p;uint16_t x = *ptr;x = (x << 8) | (x >> 8);*ptr=x;
}void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}void swap64(void * p)
{uint64_t *ptr=p;uint64_t x = *ptr;x = (x << 32) | (x >> 32);x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);*ptr=x;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89230.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89230.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/89230.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目流程管理系统使用建议:推荐13款

本文分享了13款主流的项目流程管理系统&#xff0c;包括&#xff1a;1.PingCode&#xff1b;2.Worktile&#xff1b;3.泛微 E-Office&#xff1b;4.Microsoft Project&#xff1b;5.简道云&#xff1b;6.Zoho Projects&#xff1b;7.Tita 项目管理&#xff1b;8.Oracle Primave…

neovim的文件结构

在 Linux 系统中&#xff0c;Neovim 的配置文件主要存放在以下目录结构中&#xff1a; &#x1f4c1; 核心配置目录路径内容描述~/.config/nvim/主配置目录 (Neovim 的标准配置位置)~/.local/share/nvim/Neovim 运行时数据&#xff08;插件、会话等&#xff09; &#x1f5c2;️…

【网易云-header】

网易云静态页面&#xff08;1&#xff09;效果htmlcss效果 html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Android开发知识点总结合集

初级安卓开发需要掌握的知识点主要包括安卓四大组件、Context、Intent、Handler、Fragment、HandlerThread、AsyncTask、IntentService、Binder、AIDL、SharedPreferences、Activity、Window、DecorView以及ViewRoot层级关系、触摸事件分发机制、View绘制流程、自定义View。 1…

如何通过域名白名单​OVP防盗链加密视频?

文章目录前言一、什么是域名白名单​OVP防盗链二、域名白名单​OVP防盗链的实现原理三、如何实现域名白名单​OVP防盗链加密视频总结前言 用户原创视频资源面临被非法盗链、恶意嵌入的严峻挑战&#xff0c;盗用行为不仅侵蚀创作者收益&#xff0c;更扰乱平台生态秩序。域名白名…

密码学系列文(2)--流密码

一、流密码的基本概念RC4&#xff08;Rivest Cipher 4&#xff09;是由密码学家 Ron Rivest&#xff08;RSA 算法发明者之一&#xff09;于 1987 年设计的对称流加密算法。它以简单、高效著称&#xff0c;曾广泛应用于网络安全协议&#xff08;如 SSL/TLS、WEP/WPA&#xff09;…

Drools‌业务引擎

drools引擎使用 官网介绍 一、底层原理 ReteOO 网络 • 本质是一张“有向无环图”&#xff0c;节点类型&#xff1a; – Root / ObjectTypeNode&#xff1a;按 Java 类型分发事实 – AlphaNode&#xff1a;单对象约束&#xff08;age > 18&#xff09; – BetaNode&#xf…

linux的磁盘满了清理办法

今天测试系统的某个磁盘满了&#xff0c;需要看一下&#xff0c;可以看到的是&#xff0c;已经被占用百分之百了&#xff0c;某些服务运行不了了&#xff0c;需要清一下&#xff0c;这个我熟看哪个目录占用空间大cd / du -sh * ##找到占用最大&#xff0c;比如cd /home cd /hom…

阿里开源项目 XRender:全面解析与核心工具分类介绍

阿里开源项目 XRender&#xff1a;全面解析与核心工具分类介绍 在开源技术飞速发展的浪潮中&#xff0c;阿里巴巴推出的 XRender 作为专注于表单与数据可视化的开源框架&#xff0c;凭借独特的设计理念和强大功能&#xff0c;已在开发者群体中崭露头角。XRender 以 “协议驱动…

网络安全初级--搭建

一、Docker搭建apt-get install docker.io docker-compose 下载docker 配置docker代理 a.创建对应的以及对应的文件mkdir /etc/systemd/system/docker.service.dvim /etc/systemd/system/docker.service.d/http-proxy.confb.写入以下内容[Service]Environment"HTTP_PROXYh…

文心一言4.5深度评测:国产大模型的崛起之路

在⼤语⾔模型竞争⽇益激烈的今天&#xff0c;百度推出的文⼼⼀⾔4.5凭借其在中文处理上的独特优势&#xff0c;正在成为越来越 多开发者的选择。经过为期⼀周的深度测试和数据分析&#xff0c;我将从技术参数、性能表现、成本效益等多个维度&#xff0c; 为⼤家呈现这款国产⼤模…

科技的成就(六十九)

631、摄影术的先驱 1801年&#xff0c;德国物理学家约翰威廉里特&#xff08;Johann Wilhelm Ritter&#xff09;发现了紫外线。他注意到&#xff0c;太阳光谱中紫色一侧光谱之外的位置的不可见射线比紫光更快地使氯化银试剂变暗&#xff0c;他将其称为“化学射线”。后来这种射…

用Golang gRPC异步处理:释放并发性能的秘密武器

目录 章节一:为什么gRPC异步处理是并发性能的“加速器” 异步的本质:解放Goroutine的潜能 异步gRPC的适用场景 章节二:从零开始:搭建一个异步gRPC服务 准备工作:定义Protobuf 实现同步gRPC服务 迈向异步:初步改造 章节三:用Worker Pool模式榨干并发性能 Worker …

MCP终极篇!MCP Web Chat项目实战分享

目录 前言 MCP Web Chat 功能概要说明 MCP Web Chat代码调用结构说明 api动态生成MCP Server 方法一&#xff08;之前的方法&#xff09; 方法二&#xff08;现在的方法&#xff09; 做个比较 相关代码 相关问题解决说明 稳定性 由此引申而来的异步任务问题 MCP周…

破解VMware迁移难题

理解VMware迁移的常见挑战 VMware迁移过程中可能遇到的难题包括兼容性问题、性能瓶颈、数据完整性风险以及网络配置复杂性。识别这些问题是制定有效迁移策略的基础。 评估当前环境与目标环境 详细分析源VMware环境的配置、虚拟机数量、存储类型和网络拓扑。对比目标环境的硬件和…

15-STM32F103RCT6的FLASH写入

STM32F103RCT6的FLASH写入 1.//*******************************固件升级地址信息******************************// #define STM32_FLASH_BASE 0x08000000 //固件起始地址 #define FLASH_APP_ADDR 0x08005000 //APP开始地址 #define FLASH_PARA_ADDR 0x0803C000 //固件关…

PPO:近端策略优化算法

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" PPO&#xff1a;近端策略优化算法 摘要 我们提出了一类新的用于强化学习的 policy gradient 方法&#xff0c;该方法在与环境交互以采样数据和使用随机梯度上升优化一个“代理”目标函数之间交替进行。与标准的…

数据结构的算法分析与线性表<1>

一、算法分析&#xff1a; 由于语句执行一次的实际所需时间与机器的软硬件有关&#xff0c;则算法分析是针对语句执行次数&#xff0c;而非执行时间。 时间复杂度 计算时间复杂度&#xff1a; 常量阶 如果算法中的n是固定的&#xff0c;或者说n是常数&#xff0c;或者时间复杂…

esp32使用ESP-IDF在Linux下的升级步骤,和遇到的坑Traceback (most recent call last):,及解决

因为之前使用的是ESP-IDF5.3版本。而5.3版本又不支持ESP32P4。而V5.4版本开始正式对P4的支持。所以我把ESP-IDF 升级到V5.4.2的release版本。 一、升级版本&#xff1a;【根据乐鑫官方的方式升级】ESP-IDF 版本简介 - ESP32-P4 - — ESP-IDF 编程指南 v5.4.2 文档 更新至一个稳…

【算法】贪心算法:最大数C++

文章目录前言题目解析算法原理字典序代码示例策略证明前言 题目的链接&#xff0c;大家可以先试着去做一下再来看一下思路。179. 最大数 - 力扣&#xff08;LeetCode&#xff09; 题目解析 还是老样子&#xff0c;把题目读懂&#xff0c;画出有用信息。 认真看示例&#xff0…