- ARM Cortex-M3嵌入式開發實例詳解
- 張燕妮編著
- 930字
- 2019-01-09 16:32:22
第1章 Cortex-M3體系結構
ARM公司是全球領先的半導體知識產權(IP)提供商,32位嵌入式微處理器的行業領先提供商,已推出各種各樣基于通用架構的處理器,這些處理器具有高性能和行業領先的特點,而且其系統成本也有所降低。ARM公司擁有至少900芯片生產廠家、工具和軟件的合作伙伴,并已推出一系列(20多種)處理器,可以解決每個應用挑戰。迄今為止,ARM公司已生產出超過250億個處理器,每天的銷量超過1600萬個,是真正意義上的嵌入式系統的基礎。
本章主要介紹目前ARM公司的Cortex-M3處理器,包括發展歷史、處理器結構、各個功能特征。本章將結合LPC17XX對其功能特征進行介紹。
1.1 Cortex-M3簡介
ARM公司的Cortex-M3是一種基于ARMV7構架的最新ARM嵌入式內核,采用哈佛結構,具有低成本、低功耗特點。與ARM7TDMI相比,Cortex-M3具有各種優勢。本節將介紹ARM的歷史、Cortex-M3的特點及與ARM7TDMI之間的性能對比。
1.1.1 ARM的歷史
ARM這個詞既代表了一個公司,也代表了微處理器,還代表了一項技術。1991年,ARM公司成立于英國劍橋,主要出售芯片技術的授權(目前已經授權給多家公司)。利用這種授權關系,ARM公司很快成為許多全球性RISC標準的締造者。
1.ARM體系結構的版本歷史
ARM體系結構是構建每個ARM處理器的基礎。ARM體系結構支持跨越多個性能點的實現,并已在許多細分市場中成為主導的體系結構。ARM體系結構支持非常廣泛的性能,因而可以利用最新的微體系結構技術獲得極小的ARM處理器實現和極有效的高級設計實現。ARM體系結構能夠解決實現規模小、性能差和功耗低的問題。
ARM體系結構通常描述為精簡指令集計算機(RISC)體系結構,它包含以下典型的RISC體系結構特征。
(1)大的、統一的寄存器文件。
(2)簡單的尋址模式。
(3)統一和固定長度的指令域,3地址指令格式,簡化了指令的譯碼。采用3地址指令格式、較多寄存器和對稱的指令格式便于生成優化代碼。
(4)單周期操作。ARM指令系統中的指令只需要執行簡單的和基本的操作,因此其執行過程在一個機器周期內完成。
(5)固定的32位長度指令。指令長度固定為32位,使得指令譯碼結構簡單,效率提高。
(6)采用指令流水線技術。
ARM處理器對基本RISC體系結構進行功能擴展,實現了高性能、較小代碼大小、較低功耗和較小硅面積的良好平衡。
ARM體系結構自誕生至今,已經發生了很大的演變,至今已定義了8種不同的版本,用版本號V1~V8表示。
(1)V1版架構只在原型機ARM1中出現過,其基本指令包括基本的數據處理指令(無乘法),字節、半字和字的Load/Store指令,轉移指令,子程序調用及鏈接指令,軟件中斷指令,其尋址空間為64MB。
(2)V2版架構對V1版架構進行了擴展,如ARM2與ARM3(V2a版)架構,增加的指令包括乘法和乘加指令、支持協處理器操作指令、快速中斷模式指令、SWP/SWPB的最基本的存儲器與寄存器交換指令,其尋址空間為64MB。
(3)V3版架構對ARM體系結構做了較大的改動,把尋址空間增至32位(4GB),增加了當前程序狀態寄存器CPSR和程序狀態保存寄存器SPSR,以便于進行異常處理;增加了中止和未定義2種處理器模式。ARM6就采用了該版架構。該版架構的指令集的變化包括增加了MRS/MSR指令(以訪問新增的CPSR/SPSR寄存器)及從異常處理返回的指令。
(4)V4版架構是目前最廣泛應用的ARM體系結構,它對V3版架構進行了進一步擴充,有的還引進了16位的Thumb指令集,使得ARM的使用變得更加靈活。ARM7、ARM9和StrongARM都采用了該版結構。其指令集中增加的功能包括:增加了符號化和非符號化半字及符號化字節的存/取指令,增加了16位Thumb指令集,完善了軟件中斷SWI指令的功能,處理器系統模式引進特權方式時使用用戶寄存器操作,把一些未使用的指令空間捕捉為未定義指令。
(5)V5版架構在V4版架構基礎上增加了一些新的指令。ARM10和XScale都采用該版架構。這些新增指令有帶有鏈接和交換的轉移BLX指令、計數前導零計數CLZ指令、BRK中斷指令、信號處理指令(V5TE版)、協處理器的更多可選擇的指令。
(6)V6版架構是2001年發布的。其基本特點包括100%與以前的體系兼容;SIMD媒體擴展,使媒體處理速度快了1.75倍;改進了內存管理,使系統性能提高30%;改進了混合端(Endian)與不對齊數據支持,使得小端系統支持大端數據(如TCP/IP)。例如,許多RTOS是小端的,為實時系統縮短了中斷響應時間,將最壞情況下的35周期減小為11個周期。
(7)V7版架構是2005年發布的。它使用了能夠帶來更高性能、功耗效率和代碼密度的Thumb-2技術。它首次采用了強大的信號處理擴展集,對H.264和MP3等媒體編解碼提供了加速功能。Cortex-M3處理器采用的就是V7版架構。
(8)V8版架構開始支持64位體系結構,它包括以下部分。
① 64位通用寄存器、SP(堆棧指針)和PC(程序計數器)。
② 64位數據處理和擴展的虛擬尋址。
③ 兩種主要執行狀態。
a.AArch64-64位執行狀態,包括該狀態的異常模型、內存模型、程序員模型和指令集支持。
b.AArch32-32位執行狀態,包括該狀態的異常模型、內存模型、程序員模型和指令集支持。
④ 支持三個主要指令集。
a.A32(或ARM):32位固定長度指令集,通過不同體系結構變體增強。部分32位體系結構執行環境現在稱為AArch32。
b.T32(Thumb):以16位固定長度指令集的形式引入,隨后在引入Thumb-2技術時增強為16位和32位混合長度指令集。
c.A64:提供與ARM和Thumb指令集有類似功能的32位固定長度指令集。它隨ARMv8-A一起引入,是一種AArch64指令集。
2.處理器內核的歷史
ARM公司開發了多種處理器內核,目前廣泛使用的有ARM7系列、ARM9系列、ARM9E系列、ARM11系列、SecurCore系列及Cortex系列。不同處理器內核使用不同的體系結構版本。
如表1-1所示是ARM內核與ARM體系結構版本的對應關系。
表1-1 ARM內核與ARM體系結構版本的對應關系

下面對幾種使用廣泛的ARM內核做簡單介紹。
1)ARM7系列
ARM7系列具有三級流水、空間統一的指令與數據Cache、平均功耗為0.6mW/MHz、時鐘速度為66MHz、每條指令平均執行1.9個時鐘周期等特性。其中ARM710、ARM720和ARM740為內帶Cache的ARM內核。ARM7指令集與Thumb指令集擴展組合在一起,可以減少內存容量和系統成本。同時,它還利用嵌入式ICE調試技術來簡化系統設計,并用一個DSP增強擴展來改進性能。ARM7體系結構是小型、快速、低能耗、集成式的RISC內核結構。該產品的典型用途是數字蜂窩電話和硬盤驅動器等。目前主流的ARM7內核是ARM7TDMI、ARM7TDMI-S、ARM7EJ-S、ARM720T。現在市場上用得最多的ARM7系列有思智浦公司的LPC2000系列微控制器、Samsung公司的S3C44BOX與S3C4510處理器、Atmel公司的AT91FR40162系列處理器、Cirrus公司的EP73xx系列等。該系列包括ARM7TDMI、ARM7TDMI-S、帶有高速緩存處理器宏單元的ARM720T和擴充了Jazelle的ARM7EJ-S等。這些處理器提供Thumb 16位壓縮指令集和EmbededICE軟件調試方式,適用于更大規模的SoC設計中。
ARM7系列廣泛應用于多媒體和嵌入式設備中,包括互聯網設備、網絡和調制解調器設備,以及移動電話、PDA等無線設備。
2)ARM9E系列
ARM9系列采用了ARMV4T(哈佛)體系結構。由于這種體系結構中的程序和數據存儲器在兩個分開的物理空間中,所以取指和執行能完全重疊。ARM9采用五級流水處理及分離的Cache結構,平均功耗為0.7mW/MHz。其時鐘速度為120~200MHz,每條指令平均執行1.5個時鐘周期。與ARM7系列相似,ARM9系列中的ARM920、ARM940和ARM9E處理器均為含有Cache的CPU核,其性能(速率)為132MIPS(120MHz時鐘,3.3V供電)或220MIPS(200MHz時鐘)。ARM9系列同時也配備了Thumb指令擴展、調試,以及Harvard總線。在生產工藝相同的情況下,其性能(速率)是ARM7TDMI處理器的兩倍。它常用于無線設備、儀器儀表、聯網設備、機頂盒設備、高端打印機及數碼相機應用中。ARM9E內核在ARM9內核的基礎上增加了緊密耦合存儲器TCM及DSP部分。目前主流的ARM9內核是ARM920T、ARM922T、ARM940。相關的處理器芯片有Samsung公司的S3C2510、Cirrus公司的EP93xx系列等。主流的ARM9E內核是ARM926EJ-S、ARM946E-S、ARM966E-S等。
3)SecurCore系列
SecurCore系列提供了基于高性能的32位RISC技術的安全解決方案,該系列具有體積小、功耗低、代碼密度大和性能高等特點。另外,最為特別的是,該系列提供了安全解決方案。它采用軟內核技術,以提供最大限度的靈活性,以及防止外部對其進行掃描探測;提供面向智能卡的和低成本的存儲保護單元MPU,可以靈活地集成用戶自己的安全特性和其他的協處理器。該系列目前包括SC100、SC110、SC200、SC210共4種產品。
4)ARM11系列
ARM11系列可以在使用130nm工藝技術、芯片面積小至2.2mm2和功率低至0.24mW/MHz的前提下獲得高達500MHz的速率。ARM11系列處理器以眾多消費產品市場為目標,推出了許多新的技術,包括針對媒體處理的SIMD,用以提高安全性能的TrustZone技術,智能能源管理(IEM),以及需要非常高的、可升級的超過2600 Dhrystone 2.1 MIPS性能的系統多處理技術。主要的ARM11系列處理器有ARM1136JF-S、ARM1156T2F-S、ARM1176JZF-S、ARM11 MCORE等多種。
5)ARM Cortex系列
ARM Cortex系列基于ARMV7架構,又分為Cortex-M、Cortex-R和Cortex-A 3類。ARM Cortex系列的3款產品全都集成了Thumb?-2指令集,可滿足各種不同的日益增長的市場需求。ARM Cortex系列的3款處理器瞄準的領域如下。
(1)ARM Cortex-A系列:針對復雜操作系統及用戶應用設計的應用處理器。
(2)ARM Cortex-R系列:針對實時系統專用嵌入式處理器。
(3)ARM Cortex-M系列:針對微控制器和低成本應用、專門優化的深嵌入式處理器。
Cortex-M3是首款基于ARMV7-M架構的處理器,專門瞄準對功耗和成本敏感的嵌入式應用,是為使其實現高性能而設計的,它大大簡化了可編程的復雜性,使得ARM體系結構成為各種應用方案(即使是最簡單的方案)的上佳選擇。
注意:NXP公司的LPC17XX系列屬于Cortex-M3的核;LPC11XX系列屬于Cortex-M0的ARM核。
Cortex系列處理器并沒有開拓新的應用領域,從某種意義上講,它是對原有應用領域的主流產品的一次大升級,提供了更好的性能。Cortex系列處理器對應于原有的歷史上的各種處理核。ARM公司給出了各種產品的替代關系表,如表1-2所示。Cortex系列處理器主要是針對ARM11、ARM9、ARM7 3款經典處理器的升級。
表1-2 Cortex-M0/M3與ARM7/9/11的替代關系

1.1.2 Cortex-M3的特征
Cortex-M3是一個32位處理器內核。它基于哈佛構架,其指令和數據各使用一條總線,集成了分支預測、單周期乘法、硬件除法等特性。Cortex-M3增加了MPU,用于重要數據的保護及特權處理。它適用于高確定性的實時應用,適用于汽車車體系統、控制系統及無線網絡和傳感器等多個應用場合。
Cortex-M3的特點有以下幾個。
(1)功耗低:Cortex-M3使用了最少的ARM內核,內核的核心部分(0.18um G)的門數僅為33000個,并支持擴展時鐘門控和集成睡眠模式,使得Cortex-M3的功耗低,滿足目前的白色家電和無線網絡市場對低功耗的要求。
(2)位帶操作:Cortex-M3支持兩塊位帶存儲區域,其中一塊區域是SRAM,另一塊區域是外設區。使用位帶操作可簡化外設控制流程。另外,通過使用布爾變量操作位帶別名區可實現對位帶區的單位操作,從而降低SRAM的使用率。
(3)采用Thumb?-2指令集,能夠執行硬件除法、單周期乘法和位字段操作,從而獲取最佳的性能和代碼大小。
(4)低延遲中斷處理機制:Cortex-M3處理器中集成的NVIC可實現硬件中斷處理及低延遲,有利于減弱中斷處理對處理器性能的影響。NVIC和處理器的緊密集成加快了中斷服務程序的執行速度,并減少了進入中斷所需的周期數。
(5)Cortex-M3 NVIC在設計時是可配置的,最多可提供240個具有單獨優先級、動態重設優先級功能和集成系統時鐘的系統中斷。
(6)支持兩種工作模式(線程模式和處理器模式)及兩個等級(有特權和無特權)的代碼訪問,在不犧牲應用程序安全性的前提下執行復雜的開放式系統。
(7)豐富的連接功能和性能的組合使基于Cortex-M3的設備可以有效處理多個I/O通道和協議標準,如USB OTG(On-The-Go)。
(8)開發工具的多樣化:串行線調試端口或串行線JTAG調試端口。
(9)ARM提供標準的CMSIS標準支持。
Cortex-M0/M3是針對ARM7TDMI-S用戶群體設計的,ARM公司給出了將ARM7TDMI-S升級到Cortex-M0/3的益處,以及ARM7TDMI-S與Cortex-M0/3的性能對比,如表1-3所示。
表1-3 ARM7TDMI-S與Cortex-M0/3的對比關系表

如圖1-1所示是Cortex-M3的內部功能接口框圖。

圖1-1 Cortex-M3的內部功能接口框圖
NVIC是Cortex-M3處理器中一個完整的部分,它可以進行高度配置,為處理器提供出色的中斷處理能力。在NVIC的標準執行中,它提供了1個非屏蔽中斷(NMI)和32個通用物理中斷,這些中斷帶有8級的搶占優先權。NVIC可以通過綜合選擇配置為1~240個物理中斷中的任何一個,并帶有多達256個優先級。
MPU是Cortex-M3處理器中一個可選的部分,它通過保護用戶應用程序中操作系統所使用的重要數據,分離處理任務(禁止訪問各自的數據),禁止訪問存儲器區域,將存儲器區域定義為只讀,以及對有可能破壞系統的未知的存儲器訪問進行檢測等手段來改善嵌入式系統的可靠性。
對Cortex-M3處理器系統的調試訪問是通過調試訪問端口(Debug Access Port)來實現的。該端口可以作為串行線調試端口(SW-DP)[構成一個兩腳(時鐘和數據)接口]或串行線JTAG調試端口(SWJ-DP)(使能JTAG或SW協議)使用。SWJ-DP在上電復位時默認為JTAG模式,并且可以通過外部調試硬件所提供的控制序列進行協議的切換。
總線矩陣用來將處理器和調試接口與外部總線相連。總線矩陣與下面的外部總線相連。
(1)I-Code總線:該總線用于從代碼空間取指令和向量,是32位AHB-Lite總線。
(2)D-Code總線:該總線用于對代碼空間進行數據加載/存儲及調試訪問,是32位AHB-Lite總線。
(3)系統總線:該總線用于對系統空間執行取指令和向量,數據加載/存儲及調試訪問,是32位AHB-Lite總線。
(4)PPB:該總線用于對PPB空間進行數據加載/存儲及調試訪問,是32位APB(v2.0)總線。
總線矩陣還對以下方面進行控制。
(1)非對齊訪問:總線矩陣將非對齊的處理器訪問轉換為對齊訪問。
(2)總線矩陣將位帶別名訪問轉換為對位帶區的訪問:對位帶加載進行位域提取;對位帶存儲進行原子讀—修改—寫;寫緩沖。總線矩陣包含一個單入口寫緩沖區,該緩沖區使得處理器內核不受到總線延遲的影響。
1.2 內核寄存器
Cortex-M3處理器擁有R0~R15的寄存器組,其中R13用做堆棧指針SP(SP有兩個,但在同一時刻只能有一個可以看到),R14為寄存器,R15為程序計數寄存器。Cortex-M3有5個專用寄存器。
如表1-4所示是Cortex-M3的寄存器表,該表中給出了是否需要特權才能訪問的情況。
表1-4 Cortex-M3的寄存器表

1.2.1 通用寄存器
R0~R12都是32位通用寄存器,用于數據操作,保存數據或地址值。R0~R7被稱為低寄存器,其余的被稱為高寄存器。16位Thumb指令只能訪問R0~R7,而32位Thumb-2指令可以訪問所有寄存器。
1.2.2 連接寄存器
連接寄存器在匯編代碼中可以寫成R14或LR。它用于存儲函數調用(程序寄存器返回值)和異常的返回信息。當執行分支(branch)和鏈接(BL)指令或帶有交換的分支和鏈接指令(BLX)時,LR用于接收來自PC的返回地址。復位時,LR的數值為0xFFFFFFFF。
startup_LPC17xx.s文件中定義了__user_initial_stackheap函數,該函數由MDK自動調用,不用用戶調用。其格式如下:
__user_initial_stackheap … BX LR;返回
1.2.3 程序計數器
程序計數器(PC)指向當前的程序地址。如果修改它的值,就能改變程序的執行流。PC[0]總是0,這是因為指令的取值必須按半字對齊。復位時,處理器用復位向量的值加載PC,復位向量地址為0x00000004。
1.2.4 專用寄存器
Cortex-M3包含了一些專用寄存器,主要有3種類型,5(或者說7)個寄存器,這些寄存器只能通過MSR或MRS指令操作,無對應內存地址。
(1)程序狀態寄存器:該寄存器又分為3個子狀態寄存器,分別是應用程序狀態寄存器(APSR)、中斷號狀態寄存器(IPSR)、執行狀態寄存器(EPSR)。
(2)中斷屏蔽寄存器,有PRIMASK、FAULTMASK、BASEPRI。
(3)控制寄存器(CONTROL)。
1.程序狀態寄存器
程序狀態寄存器(如表1-5所示)可以作為一個整體訪問或通過APSR、IPSR、EPSR形式獨立訪問。程序狀態寄存器作為一個整體訪問時,寄存器的名字是“xPSR”。可以使用MRS指令讀取程序狀態寄存器的內容,使用MSR指令改變APSR的內容;EPSR和IPSR為只讀寄存器。CMSIS中的core_cm3.c給出了__get_IPSR, __get_xPSR, __get_APSR 3個函數實現讀取APSR、IPSR與xPSR,其中__get_APSR的代碼如下,該函數通過MRS指令讀取APSR。
表1-5 程序狀態寄存器

__ASM uint32_t __get_APSR(void) { mrs r0, apsr bx lr }
當需要讀取EPSR及設置APSR寄存器時,可通過以下兩條指令實現:
MRS r0,EPSR MSR APSR,r0
2.中斷屏蔽寄存器
中斷屏蔽寄存器用于實現對中斷的開放與關閉。中斷屏蔽寄存器主要有PRIMASK、FAULTMASK、BASEPRI。
(1)PRIMASK:只有一位的寄存器。該寄存器被置1后,除了NMI和硬故障(hard fault)之外的所有異常將被關閉;寫0,不影響。在特權模式下,可通過MSR、MRS、CPS指令對該寄存器進行訪問。
(2)FAULTMASK:只有一位的寄存器。該寄存器被置1后,除NMI外的所有異常將被關閉。在特權模式下,可通過MSR、MRS、CPS指令對該寄存器進行訪問。
(3)BASEPRI:定義了屏蔽異常的最低優先級。當BASEPRI設置為某數值時,所有不大于數值的異常將被屏蔽;寫0,不影響。
對時序要求嚴格的應用程序,可以通過PRIMASK與BASEPRI寄存器臨時屏蔽中斷,以達到時序控制目的。FAULTNASK主要用于在操作系統中關閉任務崩潰的各種異常。
CMSIS提供了如下函數,用于讀取或設置中斷屏蔽寄存器:
uint32_t __get_PRIMASK(void) uint32_t __get_BASEPRI(void) uint32_t __get_FAULTMASK(void) void __set_BASEPRI(uint32_t basePri) void __set_FAULTMASK(uint32_t faultMask) void __set_PRIMASK(uint32_t priMask)
3.控制寄存器
控制寄存器(CONTROL)主要用于定義特權級別,以及選擇使用哪個堆棧指針。
如表1-6所示是控制寄存器的位含義。
表1-6 控制寄存器的位含義

在處理模式下,始終使用主堆棧指針MSP。只有在異常進入和返回情況下,才會更新控制寄存器。
CONTROL[0]:僅當在特權級下操作時才允許寫該位。一旦進入了用戶級,唯一返回特權級的途徑就是觸發一個(軟)中斷(SVC指令),再由服務例程改寫該位。
CMSIS中提供了對該寄存器進行訪問及操作的函數:
__get_CONTROL();// 讀取當前CONTROL寄存器中的內容 __set_CONTROL(x);// 設置CONTROL寄存器中的內容
1.3 操作模式和特權級別
Cortex-M3有兩種操作模式,分別是線程模式和處理器模式,它們與ARM7TDM的操作模式有很大的區別。這兩個模式的訪問方式為特權和非特權方式,可以在不犧牲應用程序安全的前提下實現對復雜的開放式系統的執行。
線程模式:用于執行應用軟件。處理器在退出復位時進入線程模式。該模式是常用的工作模式。它同時支持享有特權的代碼和沒有特權的代碼。
處理器模式(handler mode):用于處理異常。處理器在完成異常處理后退回線程模式。該模式中的所有代碼都享有特權。
軟件執行的特權級別分別為非特權等級(或者稱為用戶等級)和特權等級。
非特權:對MSR和MRS指令的有限訪問權限,且不能執行CPS指令;不能訪問系統定時器、NVIC、系統控制模塊;可能限制對存儲器或外設的訪問。
特權:軟件可使用所有指令,并可訪問全部資源。
如圖1-2所示是操作模式與特權等級之間的切換過程。

圖1-2 操作模式與特權等級之間的切換過程
特權級下的代碼可以通過置位CONTROL[0]來進入用戶級。不管是由于什么原因產生了什么異常,處理器都將用特權級來運行其服務例程,異常返回后,處理器將回到產生異常時所處的級別。用戶級下的代碼不能再通過試圖修改CONTROL[0]來回到特權等級。它必須通過SVC指令實現一個異常處理器模式,由這個異常處理器模式來修改CONTROL[0],然后才能在返回線程模式后拿到特權等級。
1.4 存儲器映射
ARM7內核沒有定義存儲器映射,各芯片廠商自己定義了存儲器映射。Cortex-M3內核規定了存儲器映射,從而使得各廠商生產的基于Cortex-M3內核的微控制器芯片具有相同的存儲器映射。Cortex-M3留給了各廠商存儲器空間,廠商可在此基礎上進行擴展使用。本節將介紹Cortex-M3預定義的存儲器映射,并說明LPC17XX在遵照Cortex-M3的預定義基礎上使用的預留空間情況。
1.4.1 地址空間
Cortex-M3采用了預定義的內存映射方式。Cortex-M3的中斷控制器及調試組件可通過簡單的內存存儲指令進行訪問,從而使得軟件設計適合用C語言編程實現。預定義的內存方式使得Cortex-M3更優化與高度集成。
如圖1-3所示是Cortex-M3預定義的存儲器映射,其空間是4G(0x00000000~0xFFFFFFFF)。

圖1-3 Cortex-M3預定義的存儲器映射
程序可以在代碼區(0x00000000~0x1FFFFFFF)、內部SRAM區及外部RAM區中執行。由于指令總線與數據總線是分開的,所以最理想的是把程序放到代碼區,從而使取指和數據訪問各自使用自己的總線。
片上SRAM區的大小是512MB,用于讓芯片制造商連接片上的SRAM,這個區通過系統總線來訪問。有一個1MB的區間被稱為“位帶區”。該位帶區還有一個對應的32MB的“位帶別名(alias)區”。位帶區對應的是最低的1MB地址范圍,而位帶別名區里的每個字對應的是位帶區的一個比特。位帶操作只適用于數據訪問,不適用于取指。通過位帶的功能,可以把多個布爾型數據打包在單一的字中,但是依然可以從位帶別名區中像訪問普通內存一樣地使用它們。
片上外設區(0x40000000~0x5FFFFFFF)供片上寄存器使用。這個區中也有一個32MB的位帶別名區,以便于快捷地訪問外設寄存器,其用法與內部SRAM區中的位帶相同。例如,可以方便地訪問各種控制位和狀態位。要注意的是,外設區內不允許執行指令。
0x60000000~0x9FFFFFFF區域與0xA0000000~0xDFFFFFFF區域分別用于連接外部RAM和外部設備,它們之中沒有位帶。兩者的區別在于外部RAM區允許執行指令,而外部設備區則不允許執行指令。
0xE0000000~0xE00FFFFF是Cortex-M3獨立擁有的區域,該區域不提供給芯片制造廠家使用。如圖1-4所示是Cortex-M3的私有外設總線的內存映射圖。私有外設總線有以下兩條。

圖1-4 Cortex-M3的私有外設總線的內存映射圖
(1)AHB私有外設總線:只用于CM3內部的AHB外設,它們是NVIC,FPB,DWT和ITM。
(2)APB私有外設總線:既用于CM3內部的APB設備,也用于外部設備(指的是芯片生產廠家的APB外設)。CM3允許芯片生產廠家增添一些片上APB外設到APB私有總線上,它們通過APB接口來訪問。
下面來看一下LPC17XX是如何使用Cortex-M3規定的內存映射的。如表1-7所示是LPC17XX的內存映射。LPC17XX在Cortex-M3的預定義的代碼區中有64KB的SRAM,該部分用于執行代碼與保存數據。
表1-7 LPC17XX的內存映射

如圖1-5所示是LPC1768的存儲器映射情況。LPC177X/LPC178X與LPC1768除了AHB外設(即DMA、USB、以太網及新增加的LCD)的存儲空間不一樣外,其余的APB占用空間幾乎一樣。Cortex-M3的內核空間均符合Cortex-M3的存儲空間分配要求。

圖1-5 LPC176X的存儲器映射情況
技巧:編譯程序時,輸出map文件可以查看內存分配情況,以及程序代碼、數據占用空間情況。
1.4.2 位帶操作
1.位帶概念
位帶(bit-band)操作更像C語言中的union與struct相結合后的位操作。為了更好地理解它,下面先舉一個例子:
struct_Bitband_Alias_Bits { Uint32 Alias0:1; … Uint32 Alias31:1; }; @@@ union BitBand_Operator { Uint32 BitBand; struct _Bitband_Alias_Bits Bitband_Alias_Bits; }; BitBand_Operator myBitBand;
由上述程序可知,通過myBitband. Bitband_Alias_Bits. Alias0的訪問是通過對myBitBand. BitBand的第0位操作實現的。
Cortex-M3的位帶操作類似于上面的C語言操作方式,但區別在于BitBand與Bitband_Alias_Bits是兩塊獨立的內存地址,而且位帶操作中的Alias0代表一個32位的數據,但只有最低0位是有效數據。
Cortex-M3支持了位帶操作后,可以使用普通的加載/存儲指令來對單一的比特進行讀/寫。在Cortex-M3中,有兩個區中實現了位帶:一個是SRAM區的最低1MB范圍(0x20000000-0x200FFFFF),對應的位帶別名區是0x22000000~0x23FFFFFF;第二個則是片內外設區的最低1MB范圍(0x40000000~0x400FFFFF),對應的別名區是0x42000000~0x43FFFFFF。
映射公式顯示如何將別名區中的字與bit-band區中的對應位或目標位關聯。映射公式舉例:
bit_word_offset=(byte_offset×32)+(bit_number×4) bit_word_addr=bit_band_base+bit_word_offset
這里:
Bit_word_offset為bit-band存儲區中的目標位的位置; Bit_word_addr為別名存儲區中映射為目標位的字的地址; Bit_band_base是別名區的開始地址; Bit_offset為bit-band區中包含目標位的字節的編號; Bit_number為目標位的位置(0~7)。
如圖1-6所示為SRAM位帶區與對應的別名區之間的關系圖。圖1-6中箭頭所指映射的轉換關系如下:

圖1-6 SRAM位帶區與對應的別名區之間的關系圖
地址0x23FFFFE0的別名字映射為0x200FFFFC的位帶字節的位0: 0x23FFFFE0=0x22000000+(0xFFFFF*32)+0*4 地址 0x23FFFFEC的別名字映射為 0x200FFFFC的位帶字節的位 7: 0x23FFFFEC=0x22000000+(0xFFFFF*32)+7*4 地址 0x22000000 的別名字映射為 0x20000000 的位帶字節的位 0: 0x22000000=0x22000000+(0*32)+0*4 地址 0x220001C的別名字映射為 0x20000000 的位帶字節的位 0: 0x2200001C=0x22000000+(0*32)+7*4
2.位帶操作方法
向別名區中的一個字執行寫操作會更新位帶區中的單個位。但并不是別名區的所有位都有效,別名區的一個字的第0位是有效的,會影響對應位帶區的數值,即向別名區中的一個字寫入值的位[0]決定了寫入位帶區中目標位的值:寫入一個位[0]設為1的值,則會向位帶位寫入一個1;寫入一個位[0]設為0的值,則會向位帶位寫入一個0。
注意:別名字的位[31:1]對位帶位無影響,寫入0x01與寫入0xFF效果相同,寫入0x00與寫入0x0E效果相同。
讀取別名區中的一個字,如讀取數據為
(1)0x00000000,表示位帶區中的目標位被設為0;
(2)0x00000001,表示位帶區中的目標位被設為1。
位帶區的訪問可通過字節、半字、字的方式進行訪問。
通過C語言實現對Cortex-M3的位帶操作一般使用宏定義方式進行,并通過“1位帶概念”中的位帶與別名區映射公式進行。下列代碼給出的定義中采用了右移代替乘法運算。
/*SRAM的位帶區與別名區對應地址*/ #define BITBAND_SRAM_REF 0x20000000 #define BITBAND_SRAM_BASE 0x22000000 // 將位帶地址轉換到別名區,其中a代表位帶區地址,b代表要轉換的位帶字節的位數 #define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + ((a-BITBAND_SRAM_REF)<<5) + (b<<2))) /* 外設位帶地址宏定義 */ #define BITBAND_PERI_REF 0x40000000 #define BITBAND_PERI_BASE 0x42000000 // 將位帶地址轉換到別名區,其中a代表位帶區地址,b代表要轉換的位帶字節的位數 #define BITBAND_PERI(a,b)((BITBAND_PERI_BASE+((a-BITBAND_PERI_REF)<<5)+(b<<2)))
當使用位帶功能時,要訪問的變量必須用volatile來定義。因為C編譯器并不知道同一個比特可以有兩個地址,所以需要通過volatile使編譯器每次都如實地把新數值寫入存儲器中,而不再出于優化的考慮,在中途使用寄存器來操作數據的復本,直到最后才把復本寫回——這會導致按不同的方式訪問同一個位得到不一致的結果。
小知識:對于volatile聲明的變量,編譯器在訪問該變量的代碼時不再進行優化,從而可以提供對特殊地址的穩定訪問。
下列程序演示了通過位帶方式操作SPI控制寄存器的過程。
/* SRAM的位帶區與別名區對應地址*/ #define BITBAND_SRAM_REF 0x20000000 #define BITBAND_SRAM_BASE 0x22000000 // 將位帶地址轉換到別名區,其中a代表位帶區地址,b代表要轉換的位帶字節的位數 #define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE + ((a-BITBAND_SRAM_REF)<<5) + (b<<2))) /* 外設位帶地址宏定義 */ #define BITBAND_PERI_REF 0x40000000 #define BITBAND_PERI_BASE 0x42000000 // 將位帶地址轉換到別名區,其中a代表位帶區地址,b代表要轉換的位帶字節的位數 #define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE + ((a-BITBAND_PERI_REF)<<5) + (b<<2))) /* 位帶區操作函數 */ #define BITBAND_SRAM_ClearBit(a,b) (*(volatile uint32_t *) (BITBAND_SRAM(a,b)) = 0) #define BITBAND_SRAM_SetBit(a,b) (*(volatile uint32_t*)(BITBAND_SRAM(a,b))=1) #define BITBAND_SRAM_GetBit(a,b) (*(volatile uint32_t *) (BITBAND_SRAM(a,b))) @@@ #define BITBAND_PERI_ClearBit(a,b) (*(volatile uint32_t *) (BITBAND_PERI(a,b)) = 0) #define BITBAND_PERI_SetBit(a,b) (*(volatile uint32_t*)(BITBAND_PERI(a,b))=1) #define BITBAND_PERI_GetBit(a,b) (*(volatile uint32_t*)(BITBAND_PERI(a,b))) #define VAR_ADDRESS LPC_GPIO2_BASE//0x2007C000 #define VAR_BIT 3//bit 3 @@@ #define PERI_ADDRESS 0x40020000//SPI控制寄存器地址(S0SPCR) #define PERI_BIT 5//bit 5-Master mode select(主模式選擇) uint32_t temp; uint32_t temp1; @@@ int main(void) { @@@ //SRAM temp = (*(volatile uint32_t *)(VAR_ADDRESS)); temp1 = BITBAND_SRAM_GetBit(VAR_ADDRESS,VAR_BIT); if(temp1==(temp&1<<VAR_BIT)) { ;//可在此設置斷點,驗證結果的正確性 } @@@ //設置第3位為1 BITBAND_SRAM_SetBit(VAR_ADDRESS,VAR_BIT); temp=(*(volatile uint32_t*)(VAR_ADDRESS)); //temp的第3位為1,即temp=0x8 temp1= BITBAND_SRAM_GetBit(VAR_ADDRESS,VAR_BIT); //temp1 =1 //外設區代碼演示 LPC_SPI->SPCR=(1<<5); //直接設置0x40020000 temp1 = (*(volatile uint32_t *)(PERI_ADDRESS));//讀取0x40020000 temp=BITBAND_PERI_GetBit(PERI_ADDRESS,PERI_BIT); //讀取對應別名區,即temp=1 BITBAND_PERI_ClearBit(PERI_ADDRESS,PERI_BIT); temp=BITBAND_PERI_GetBit(PERI_ADDRESS,PERI_BIT); //temp=0 temp1=(*(volatile uint32_t*)(PERI_ADDRESS)); BITBAND_PERI_SetBit(PERI_ADDRESS,PERI_BIT); temp=BITBAND_PERI_GetBit(PERI_ADDRESS,PERI_BIT); //temp=1 temp1=(*(volatile uint32_t*)(PERI_ADDRESS)); //temp1=0x20,即temp結果右移5位 while(1); return 0; }
3.位帶操作的用途
對應于LPC17XX的外設,所有外設(除了以太網、USB、DMA、GPIO以外)都位于位帶操作區域。APB0與APB1對應的外設內存都屬于位帶區域。
注意:由于GPIO不屬于位帶區,所以如果經常需要設置引腳狀態時,不能通過位帶方式進行操作。
1.4.3 端模式
Cortex-M3支持32位字、16位半字、8位字節數據類型。Cortex-M3處理器將存儲器看做一個從0開始增長的線性集合。存儲器可理解成:
(1)地址0~3保存了第一個要保存的字。
(2)地址4~7保存了第二個要保存的字。
Cortex-M3支持大端模式和小端模式(但對應于具體芯片時,可能不遵守這個約定)。可以通過讀取AIRCR.ENDIANNESS的位判斷當前的端模式。端模式的配置通過Cortex-M3提供的BIGEND引腳進行。
注意:LPC17XX僅支持小端模式,不支持大端模式。它沒有提供配置引腳。如果程序中出現大端模式需求,需要自己手工解決這個問題(即大端模式需求)。
鑒于LPC17XX僅支持小端模式,本節僅給出小端模式的概念。
小端模式是指處理器將一個字的最低(有效)位位字節存儲在序號最小的字節中,將最高(有效)位位字節存儲在序號最大的字節中。圖1-7給出了小端模式下,在地址A到地址A+3之間如何保存一個字數據及將數據存儲在寄存器中的情況。

圖1-7 小端模式舉例
1.5 總線接口
Cortex-M3內部有若干條總線接口,使得Cortex-M3能同時取指和訪問內存。本節將介紹各總線,以及LPC17XX的總線對應的外設情況。
1.5.1 3級流水線
原有的ARM7TDMI的指令執行速度為0.9MIPS/MHz。目前采用ARMv6構建的Cortex-M0已經可以達到這個執行速度,而采用哈佛總線結構的Cortex-M3的指令執行速度可達到1.25 DMIPS/MHz。Cortex-M3處理器使用一個3級流水線。流水線的3級分別是取指、譯碼和執行,如圖1-8所示。

圖1-8 Cortex-M3的3級流水線
由于使用了指令流水線,所以讀PC時返回的值是當前指令的地址+4。例如:
0x1000: MOV R0, PC ; 讀到R0=0x1004,而不是0x1000
這個偏移量總是4,不管是執行16位指令還是32位指令,這就保證了在Thumb和Thumb2之間的一致性。
1.5.2 總線矩陣
總線矩陣用來將處理器和調試接口與外部總線相連。總線矩陣與下面的外部總線相連。
(1)I-Code總線:該總線用于從代碼空間取指令和向量,是32位AHB-Lite總線。
(2)D-Code總線:該總線用于對代碼空間進行數據加載/存儲及調試訪問,是32位AHB-Lite總線。
(3)系統總線:該總線用于對系統空間執行取指令和向量,數據加載/存儲及調試訪問,是32位AHB-Lite總線。
(4)PPB:該總線用于對PPB空間進行數據加載/存儲及調試訪問,是32位APB(v2.0)總線。
注意:對于多層AHB矩陣,只有當多個主機試圖同時訪問同一矩陣的從機端口時,才會做主機之間的仲裁。在默認情況下,Cortex-M3的D-Code總線有最高優先級,然后是I-Code總線。所有其他主機的優先級較低。
如圖1-9所示是Cortex-M3總線接口示例,該圖結合LPC17XX,說明了系統總線與各個設備之間的連接關系。在LPC17XX中,Cortex-M3內核總線通過多層AHB總線矩陣連接各個外設設備。圖1-9中給出的外設是LPC17XX的部分外設。

圖1-9 Cortex-M3總線接口示例
1.I-Code總線
I-Code總線是一條基于AHB-Lite總線協議的32位總線,負責在0x00000000~0x1FFFFFFF之間的取指操作。取指以字的長度執行,即使是對于16位指令也如此。因此,CPU內核可以一次取出兩條16位Thumb指令。
2.D-Code總線
D-Code總線也是一條基于AHB-Lite總線協議的32位總線,負責在0x00000000~0x1FFFFFFF之間的數據訪問操作。盡管Cortex-M3支持非對齊訪問,但該總線不允許這樣操作,這是因為處理器的總線接口會把非對齊的數據傳送都轉換成對齊的數據傳送。因此,連接到D-Code總線上的任何設備都只需要支持AHB-Lite的對齊訪問,而不需要支持非對齊訪問。
ARM公司推薦D-Code總線的優先級高于I-Code總線的優先級。
3.系統總線
系統總線也是一條基于AHB-Lite總線協議的32位總線,負責在0x20000000~0xDFFFFFFF和0xE0100000~0xFFFFFFFF之間的所有數據傳送,取指和數據訪問都包括在內。和D-Code總線一樣,系統總線的所有數據傳送都是對齊的。
4.私有外設總線
私有外設總線是一條基于APB總線協議的32位總線。此總線負責0xE0040000~0xE00FFFFF之間的私有外設訪問。但是由于此APB存儲空間的一部分已經被TPIU、ETM及ROM表用掉了,所以只留下0xE0042000~E00FF000這個區間用于配接附加的(私有)外設。
1.6 存儲器保護單元(MPU)
1.6.1 MPU概述
存儲器保護單元(MPU)是Cortex-M3選配的功能,用于實現對存儲器的保護,從而使軟件更加健壯和可靠。MPU將存儲器映射劃分為多個區,并定義了每個區的位置、大小、訪問權限及存儲器屬性。可定義8個單獨的存儲區及1個背景區。存儲區可重疊,此時存儲器訪問受最大的區的屬性影響(7的優先級最高,0的優先級最低)。每個區域的大小可以是32B~4GB。
背景區具有與默認存儲映射相同的存儲器訪問屬性,但只能被特權軟件訪問。
如果程序訪問被MPU禁止的存儲區,則處理器會產生一個存儲器管理異常。因此,在嵌入式操作系統中,內核可根據執行的任務動態更新MPU區設置,從而實現存儲器保護。
例如,FreeRTOS實現了具有MPU功能的FreeRTOS-MPU,用于根據任務情況實現對存儲區設置只讀屬性、將其他區設置成不可執行及利用xTaskCreateRestricted()生成一些訪問受限的任務。
1.6.2 MPU的寄存器
MPU的寄存器如表1-8所示。
表1-8 MPU的寄存器

TYPE寄存器指示是否存在MPU,以及如果存在,它支持多少個存儲區。
CTRL寄存器用于使能MPU;使能默認存儲器映射的背景區;在硬故障、不可屏蔽中斷(NMI)和FAULTMASK升級的處理程序中,使能MPU的使用。
RNR寄存器用于選擇哪個存儲區被RBAR和RASR寄存器引用。
RBAR寄存器定義了RNR所選擇MPU區的基址,并可以更新RNR的值。
RASR寄存器定義了區的大小和RNR所指定的MPU區的存儲器屬性,并使能該區,以及其子區的寄存器匯總。
1.6.3 MPU的使用
CMSIS給出了MPU寄存器的C語言訪問方式,主要有MPU->TYPE、MPU->RNR、MPU->RBAR、MPU->RASR、MPU->CTRL,與表1-8中名字相同的寄存器一一對應。MPU的設置流程如圖1-10所示。

圖1-10 MPU的設置流程
1.7 中斷和異常
Cortex-M3采用ARMV7-M體系結構中的異常(中斷)模型,取消了原來在ARM7(如LPC23XX,LPC24XX)系列中的FIQ,但采用了中斷優先級,引入了嵌套中斷模式,從而實現了嵌套中斷,即一個高優先級的中斷能夠覆蓋或搶占低優先級的中斷。Cortex-M3支持11個系統異常,最多240個外部中斷。外設產生的中斷信號,除了SysTick外,全部連接到NVIC的中斷輸入信號線上。
注意:LPC17XX的外部中斷是35個。實際上芯片制造商并不會全部使用240個外部中斷。
所有的異常處理都是在處理模式下實現的。當出現中斷時,處理器的狀態自動保存到堆棧中,并在中斷服務程序(ISR)結束時自動從堆棧中恢復。取出向量和保存狀態是同時進行的,從而提高了進入中斷的效率。Cortex-M3還支持末尾連鎖(Tail-chaining),使得處理器無須保存和恢復狀態便可執行連續的中斷。
注意:末尾連鎖(Tail-chaining)指的是當處理器響應某中斷時,又發生了其他優先級較低的中斷,則低優先級的中斷先被掛起。在當前中斷執行返回后,不再執行出棧和入棧操作,直接處理掛起的中斷,就好像后一個中斷與前一個中斷的尾連接起來了,前后只執行了一次入棧/出棧操作,從而使得兩個ISR之間的間隔大大縮短。
Cortex-M3的中斷處理方式與原有的ARM處理中斷方式相比,具有不易丟失中斷的特點。如果一個發生的異常不能被即刻響應,就稱它被“掛起”(pending)。不過,少數fault異常是不允許被掛起的。一個異常被掛起的原因,可能是系統當前正在執行一個更高優先級異常的服務例程,或者因相關掩蔽位的設置導致該異常被禁能。對于每個異常源,在被掛起的情況下,都會有一個對應的“掛起狀態寄存器”保存其異常請求。等到該異常能夠響應時,執行其服務例程,這與傳統的ARM是完全不同的。在以前,是由產生中斷的設備保持住請求信號的;在Cortex-M3中,則由NVIC的掛起狀態寄存器來保持住請求信號的。于是,哪怕設備在后來已經釋放了請求信號,曾經的中斷請求也不會錯失。
1.7.1 異常類型
Cortex-M3自帶的異常是16個,其中異常號為0與7~10的異常保留,以供將來使用。異常主要有以下幾種。
1.復位
復位的異常號為0,優先級為-3(最高優先級)。
上電或熱復位時,復位啟動。異常模型將復位當做一種特殊的異常形式。當復位使能時,處理器可能在一條指令的任何位置停止操作。當復位禁能時,由向量表中的復位表項提供重新開始執行的地址。在線程模式下,重新執行是特權執行。
2.NMI
不可屏蔽中斷(NMI)可由一個外設發出信號或由軟件觸發。這是除復位外的最高優先級的異常。它被永久性使能,具有固定的優先級-2。NMI不能因任何其他異常的激活而被屏蔽或阻止,不能被除復位外的其他任何異常搶占。
3.硬故障
硬故障是一種異常,其發生原因是異常在處理期間出錯,或異常無法被任何其他異常機制管理。硬故障具有固定優先級-1,這意味著它的優先級高于任何具有可配置優先級的異常。
4.存儲器管理故障
存儲器管理故障是一種由與存儲器保護相關的故障引發的異常。對于指令和數據存儲事務,MPU或固定的存儲器保護約束條件決定此故障。此故障用于終止對從不執行(XN)存儲區的指令的訪問,即使MPU被禁能也是如此。
5.總線故障
總線故障是一種異常,由一個指令或數據存儲器事務的存儲器的相關故障引發。這可能源于從一個存儲器系統總線上檢測出的錯誤。
6.使用故障
使用故障是與指令執行相關的故障導致的一種異常。使用故障包括:
(1)未定義的指令;
(2)非法的非對齊訪問;
(3)指令執行時的無效狀態;
(4)異常返回時的錯誤。
當內核被配置為報告使用故障時,以下情況可以導致一個使用故障:
(1)字和半字存儲器訪問時的非對齊地址;
(2)除以零。
7.SVCall
SVCall是指執行系統服務調用指令(SVC)引發的異常。一次超級用戶調用是由SVC指令觸發的異常。在OS環境中,應用程序可使用SVC指令來訪問OS內核函數和設備驅動程序。
8.PendSV
PendSV是一個對系統級服務的中斷驅動請求。在OS環境中,當無其他有效的異常時,使用PendSV進行上下文切換。
9.SysTick
SysTick是當系統定時器達到零時產生的異常。軟件也可以產生一個SysTick。在OS環境中,處理器可將此異常用做系統節拍。
10.中斷(IRQ)
中斷(IRQ)是由一個外設發出信號,或由一個軟件請求產生的異常。所有中斷都與指令執行異步。在系統中,外設使用中斷與處理器進行通信。
注意:中斷(IRQ)在LPC17XX中是指一些外設的中斷,如看門狗、UART的中斷。在LPC176X總共有35個中斷(LPC177X/LPC178X中為41個)。
1.7.2 異常優先級
Cortex-M3中的異常優先級可決定一個異常是否能被掩蔽,以及在未掩蔽的情況下何時可以響應。優先級的數值越小,優先級越高。Cortex-M3支持中斷嵌套,使得高優先級異常會搶占低優先級異常。有3個系統異常:復位,NMI及硬fault,它們有固定的優先級,并且它們的優先級號是負數,高于所有其他異常。所有其他異常的優先級都是可編程的。
異常優先級的設置分為兩步:
(1)設置組優先級有效位(LPC17XX通過設置系統控制寄存器AIRCR[10:8]實現);
(2)設置組優先級及子優先級(LPC17XX通過設置各自的IPRx或系統控制中的SHPx寄存器實現)。
注意:組優先級在有的書中也叫做搶占優先級或主優先級。有的文獻中使用group priority,有的文獻中使用preempt priority,雖然使用的詞語不一樣,但它們的含義是一樣的,均指具有搶占功能。
如表1-9所示是優先級分組。其中分組位置指的是AIRCR[10:8]中的數值。IPRx寄存器分成搶占優先級與子優先級兩部分。
表1-9 優先級分組

如果有多個掛起異常共用相同的組優先級,則需使用次優先級區來決定同組中異常的優先級,這就是同組內的次優先級。組優先級和次優先級的結合就是通常所說的優先級。如果兩個掛起異常具有相同的優先級,則掛起異常的編號越低,優先級越高。這與優先級機制是一致的。
搶占優先級(組優先級)決定了搶占行為:當系統正在響應某異常L時,如果來了搶占優先級更高的異常H,則H可以搶占L。只有組優先級才能決定中斷異常的搶占。當處理器正在執行一個中斷異常處理程序時,另一個與正在處理中的中斷具有相同組優先級的中斷不會搶占處理程序。
子優先級處理組內優先級的情況:當搶占優先級相同的異常有不止一個掛起時,就最先響應子優先級最高的異常。這種優先級分組做出了如下規定:子優先級至少是1個位。因此,搶占優先級最多是7個位,128級。
從表1-9中可以看出,Cortex-M3允許從比特7處分組,此時所有的位都表達子優先級,沒有任何位表達搶占優先級,因此所有優先級可編程的異常之間就不會發生搶占——相當于在它們之中禁用了Cortex-M3的中斷嵌套機制。復位、NMI和硬fault屬于例外情況,它們無論何時出現,都立即搶占所有中斷(異常)。
CMSIS提供設置優先級函數:
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup); //如何進行優先級分組 void NVIC_SetPriority(IRQn_Type IRQn,uint32_t priority);//設置中斷源IRQn的優先級情況(包 //含組優先級與子優先級) NVIC_EncodePriority(uint32_tPriorityGroup,uint32_tPreemptPriority,uint32_t SubPriority);;//將組優 //先級與子優先級組合起來,用于給NVIC_SetPriority函數賦值
LPC17XX中存在EINT0、EINT1、EINT2、EINT3四個外部中斷,在使用時分別給予了不同優先級。在這里設定EINT0>EINT1的優先級。可以根據需要采用兩種方式:一種是設定不同組,這樣EINT0可以搶占EINT1的優先級,并可能存在優先級嵌套方式;另外一種設定同一組,但EINT0的子優先級高于EINT1的優先級。
程序代碼如下所示。該程序使用宏定義方式來區分兩種方式,并根據需要設定了INT_MODE的數值,以及采用了NVIC_EncodePriority簡化組優先級和子優先級的組合計算問題。
#define P_Group 4 //組屬性定義 #if(INT_MODE==0) //相同組,不同子優先級 NVIC_SetPriorityGrouping(P_Group);//設定組屬性;子優先級為3 NVIC_SetPriority(EINT0_IRQn,NVIC_EncodePriority(P_Group,0,2)); //組優先級:0;子優先 //級:2 NVIC_SetPriority(EINT3_IRQn,NVIC_EncodePriority(P_Group,0,1)); //組優先級:0;子優先 //級:1 #else//不同組,搶占模式 NVIC_SetPriorityGrouping(P_Group); //sets group priorities:8-subpriorities:3 NVIC_SetPriority(EINT0_IRQn,NVIC_EncodePriority(P_Group,0,0)); //組優先級:0;子優先 //級:0 NVIC_SetPriority(EINT3_IRQn,NVIC_EncodePriority(P_Group,1,0)); //組優先級:1;子優先 //級:0 #endif
1.7.3 異常響應過程
異常響應的基本原則就是只要中斷掛起寄存器為0,而且該中斷沒有被屏蔽,就可進入中斷掛起狀態。如果在進入handler模式之前,掛起寄存器被清除掉,則進入中斷服務程序。
如果掛起寄存器被清0(不管是軟件清除,還是通過進入中斷服務程序方式清除),就可響應新的異常觸發信號。如果掛起寄存器已經為1,對于外部新的中斷請求,也僅響應一次。
異常響應的狀態主要有以下6種。
(1)當中斷輸入腳生效后,該中斷就被掛起。即使后來中斷源取消了中斷請求,已經被標記成掛起的中斷也被記錄下來。當在系統中它的優先級最高時,它就會得到響應,如圖1-11所示。

圖1-11 中斷響應
(2)如果在某個中斷得到響應之前,其掛起狀態被清除了(例如,當PRIMASK或FAULTMASK置位時,軟件清除了掛起狀態標志),則中斷被取消,如圖1-12所示。

圖1-12 掛起狀態被提前清除掉
(3)當某中斷的服務例程開始執行時,就稱此中斷進入了“活躍”狀態,并且其掛起位會被硬件自動清除。在一個中斷活躍后,直到其服務例程執行完畢,并且返回后,才能對該中斷的新請求予以響應。當然,新請求的響應也是由硬件自動清零并掛起標志位的。中斷服務例程也可以在執行過程中把自己對應的中斷重新掛起(使用時要注意避免進入“死循環”),如圖1-13所示。

圖1-13 中斷請求被清除
(4)如果中斷源上一直存在請求信號,該中斷就會在其上次服務例程返回后再次被置為掛起狀態,在這一點上,CM3和傳統的ARM7TDMI是相同的。這種情況發生在電平觸發源類型的中斷過程中,如圖1-14所示。

圖1-14 請求一直有效
(5)如果某個中斷在得到響應之前,其請求信號以若干脈沖形式呈現,則被視為只有一次中斷請求,多出的請求脈沖全部丟失,這種情況是由于中斷請求過快造成的。
假如設計程序時,將外部中斷EINT0的優先級設定得比較低,雖然觸發多次中斷,但因為其他高優先級的中斷一直占用CPU,所以最后將僅表現為響應一次中斷。因此,對中斷的優先級及中斷響應時長都需要嚴格規劃好,否則會出現各種意想不到的問題,如圖1-15所示。

圖1-15 中斷請求多次觸發
(6)如果在服務例程執行過程中,中斷請求釋放了,但是在服務例程返回前又重新被置為有效,則CM3會記住此動作,重新掛起該中斷。例如,使用SysTick時,如果中斷處理程序比較長(運行時間需要1ms),但時鐘間隔設置比較短(設置成了500μs),這種情況發生后,程序將一直占用處理器,除非具有更高優先級的中斷產生,如圖1-16所示。

圖1-16 中斷掛起狀態消失后重新觸發
從最后兩種形式可以看出,中斷的掛起狀態影響著中斷的響應次數。因為中斷掛起狀態只能是0和1,無其他狀態,所以只要該狀態寄存器被清除,就可以響應下一次中斷了。該寄存器如果已經為1,則將無法響應新的狀態。
1.8 指令系統
Cortex-M3不再支持ARM指令,而是支持ARMV6Thumb指令,同時引入了Thumb-2指令。Thumb-2指令是一種新型混合指令集,融合了16位和32位指令,用于實現密度和性能的最佳平衡。在不對性能進行折中的情況下,它節省了許多高集成度系統級設計的總體存儲成本。
Cortex-M3支持的指令可歸納為6大類:數據傳送、數據處理、分支指令、系統指令、飽和指令和雜項指令。
數據傳送指令可:
(1)在兩個寄存器之間傳送數據;
(2)在寄存器與存儲器之間傳送數據;
(3)在寄存器與特殊功能寄存器之間傳送數據;
(4)將一個立即數加載到寄存器中。
數據處理指令主要是一些四則運算指令與移位操作指令,包含16位與32位操作指令。
分支指令又分為無條件跳轉及有條件跳轉,如B、BL、CBZ指令。
系統指令主要是一些直接針對Cortex-M3寄存器的操作指令,如CPSIE和CPSID。
飽和指令主要是SSAT和USAT,用于對寄存器進行飽和操作。
由于目前Cortex-M3進行軟件開發時,更多地關注于C語言開發,所以CMSIS也提供了各種對應指令的C語言版本函數及操作寄存器的C語言函數。僅僅在Cortex-M3的啟動文件涉及部分匯編代碼。
下面將對啟動文件(Startup.s)中涉及的跳轉指令和加載指令進行解釋說明。
啟動文件中使用的跳轉指令:
B Label ;跳轉到Label處對應的地址 BX reg ;跳轉到由寄存器reg給出的地址 BLX reg ;跳轉到由寄存器reg給出的地址,并根據REG的LSB切換處理器狀態,還要 ;把轉移前的下條指令地址保存到LR
BL和BLX指令可將下一條指令的地址復制到鏈接寄存器(LR)R14中。使用BL與BLX時要小心,因為它們還帶有改變狀態的功能。寄存器reg的LSB必須是1,以確保處理器不會試圖進入ARM狀態。如果忘記置位LSB,將會出現UsageFault異常。
加載指令LDR用于把存儲器中的內容加載到寄存器中。Startup.s使用了LDR偽指令。如果匯編器發現要產生的立即數是一個程序地址,它會自動地把LSB置位,例如:
LDR r0, =address1 ;R0=0x40001 … address1 0x4000: MOV R0, R1
在這個例子中,由于匯編器會認出address1是一個程序地址,所以自動置位LSB,即R0的復制不是0x4000,而是0x4001。另外,如果匯編器發現要加載的是數據地址,則不會自動置位LSB,例如:
LDR R0, =address1 ; 會把0x4000 原封不動地加載到R0 … address1 0x4000: DCD 0x0 ;0x4000 處記錄的是一個數據
至此,我們可以看一下Startup.s里面的使用情況:
IMPORT SystemInit IMPORT __main LDR R0,=SystemInit;自動將R0的LSB置位 BLX R0;切換到Thumb狀態,并加載下一條指令 LDR R0,=__main BX R0
由于C語言不能直接訪問Cortex-M3指令,所以CMSIS提供了通過C語言訪問指令及直接訪問寄存器的方式,這些方式通過內聯匯編的方式實現。
表1-10中給出了CMSIS指令關系。
表1-10 CMSIS指令關系

1.9 小結
本章介紹了Cortex-M3的基本框圖、寄存器、存儲器的使用情況及異常的基礎理論,對比了Cortex-M3與ARM7TDMI,并結合LPC17XX,給出了一些Cortex-M3具體應用于某款芯片時的一些特點和用法。
本章中的中斷知識是以后章節中經常使用的內容。Cortex-M3對中斷的處理機制與ARM7TDMI有非常大的區別。本章中結合CMSIS給出了對中斷進行設置的方法。
本章對原有ARM7TDMI經常涉及的指令系統只做了簡單介紹。這主要是因為目前Cortex-M3的產品設計是C語言的天下(當然對于ARM公司而言,可能仍是匯編語言的天下,但對于普通用戶而言,就是C語言的天下了),在Cortex-M3的軟件設計中幾乎可以不用了解匯編。ARM公司為Cortex-M3軟件設計提供的便捷方法會在第3章的CMSIS一節中介紹到。
第2章將介紹進行LPC17XX硬件設計的基礎理論內容。
- 構建高質量的C#代碼
- 三菱FX3U/5U PLC從入門到精通
- 大數據專業英語
- Mastering VMware vSphere 6.5
- 樂高機器人EV3設計指南:創造者的搭建邏輯
- 來吧!帶你玩轉Excel VBA
- 網上生活必備
- Maya 2012從入門到精通
- 人工智能與人工生命
- Java Web整合開發全程指南
- OpenStack Cloud Computing Cookbook
- 網中之我:何明升網絡社會論稿
- Chef:Powerful Infrastructure Automation
- 基于企業網站的顧客感知服務質量評價理論模型與實證研究
- Hands-On Data Warehousing with Azure Data Factory