【Cortex-M】異常中斷時的程序運行指針SP獲取,及SCB寄存器錯誤類型獲取

【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…