cortex-m3 是一款非常强大的单片机,和arm A系列(应用处理器)的确有很大的区别
但是还是很多相似之处,毕竟都是arm的东西
下面是阅读CM3权威指南的一些摘要:
1. P25 两个级别(user,svc),2个运行模式(handler,thread),3种组合(user没有handler mode) handler ,thread 的区别可能就是(4)中的自动压栈
特权 | 用户 ——————————————————————— 异常(exp,irq)处理 handler | 错误 ——————————————————————— 主应用的代码 线程 | 用户
注意mode 切换图 [特权handler mode] / ^ \ ^ / / \ \ / / (异常返回) (触发异常) (异常返回) (触发异常) \ \ / / \ \________________ / / \_____________ | / / | | V / V | 复位---> [特权级thread mode]----(修改CONTROL REG)--->(用户级thread mode) CM3: handler mode (注意总是 MSP!) thread mode, 可以使用 MSP thread mode, 也可以使用PSP,通过control reg 设置 注意和ARM9 ARM A系统等比较 它们在各异常(Abort,Undefine,IRQ,FIQ),User,Supervisor 都有自己的SP(R13) 移值时注意这个巨大差异!!!
2. P27 内存的分布
3. P30 异常名称介绍,[-1]----[-3] 3个固定优先级,NMI[-2] 的作用不错 P43 11-SVCCall, 15-SysTick 可以关注下
4. P32 进入异常服务例程,自动压栈 R0-R3,R12,LR(R14),PSR,PC(R15)
5. P36 栈指针分MSP(SP_main), PSP(SP_process) ,push,pop 指令根据CONTROL[1](P40],自动判断当前那个SP 如果有特殊要求可以通过MRS/MSR 来指定访问
6. P38 注意读PC 时,当前支流+4 ,和 arm9上 (PC+8)有区别, pc 奇数这句表示必须在thumb2 下
7. P39 特殊功能寄存器 MRS/MSR 读/写 PSR 分: APSR 应用程序PSR (N,Z,C,V,Q)[31-27] IPSR 中断号PSR (Exception Number) [8-0] (如果是thread,那么值为0,因为没有0号异常) EPSR 执行PSR (ICI/IT)[26:25] ,(T)[34],(ICI/IT)[15:10]
8. P39. 异常(中断) 控制register PRIMASK 开关中断 FAULTMASK 开关异常 BASEPRI 优先级
9. P40 快速开关中断异常 CPSID I ;PRIMASK=1 关 CPSIE I ; =0 开
CPSID F ;FAULTMASK 关 CPSIE F
10. P40 CONTROL register CONTROL[1] 0---MSP (reset default) (handler mode 必须为0) CONTROL[1] 1---PSP
CONTROL[0] 1---svc mode CONTROL[0] 0---user mode
11. P41 复位后为 thread mode + svc mode thread mode + svc mode 对系统空间阻止访问,MRS/MSR 也禁用,否则fault 注意user ,svc mode 的切换 (control[0] + user 发生异常(+SVCall)
12. P43 异常相量表 P109 ,16 个异常,240 IRQ
13. P46 使用满递减栈,双栈(svc,user),用于带kernel的 user ,kernel 切换很好
14. P47 奇特的复位过程,可以看下 1. 从0x0地址取MSP 2. 从0x4地址取PC的值,这个值是复位向量,LSB 必须为1,然后从这个地址所 对应的地址处取值 [reset] -->[0x0(MSP)][0x4(复位向量)]----->[Reset Vector] ! (0x0--开始是MSP,然后接下拉是向量表!!!) (向量表中都是32bit 地址,比如0x4放的就是reset 执行指令位置) P148 图3.18
15. P50 指令后缀使用: S 更新APSR中标志 EQ,NE,LT,GT 等等 等只有B(跳转)指令才能任意用,其他指令引入IF-THEN 块,在这个块中才可以加后缀
16. P51 注意thumb 语法和thumb 2 以及arm 的区别 ,在UAL 下thumb2 和arm 基本相似 但也可以用thumb 语法明确 16bit 指令 或者通过 .N(16bit) .W(32bit) 来指定 16bit ,32bit
17. P59 自增自减,根据指令宽度, LDMIA Rd! ,{register list} 读后Rd 自增16bit LDMIA.W RD! ,{register list} 读后Rd 自增32bit
18. P69 BX reg, 指令中reg 中最低位必须为1 (LSB=1) ,因为CM3 只在Thumb 中允许,否则fault 而BLX reg arm ,thumb 都支持,根据LSB=1 thumb LSB=0 arm
19. P71 大多数16bit 算术逻辑指令,直接更新标志位 ,除 ADD.N Rd,Rn,Rm 32bit 需要S 后缀来控制
20. P72 barrier 指令 DMB --- 数据访问指令等前面数据访问指令完成才执行 DSB --- 任何指令等前面数据访问指令完成才执行 (比如建立完所有向量后追加1条DSB P168) ISB --- 任何指令等前面指令完成才执行 依次往下表现barrier 更严格
21. P73 带符号 饱和运算指令 SSAT.W Rd #imm5,Rn,{,shift} 不带符号饱和运算指令
22. P75 MRS/MSR 除APSR 可以在user级访问,其他必须特权级
23. P76 IF-THEN(IT)里最多有4条支流 IT <cond> ; 围起1条 IT<x> <cond> ; 围起2条 IT<x><y> <cond> ; 围起3条 IT<x><y><z> <cond> ; 围起4条 <x><y><z> =>为 T(then),E(else) IT指令的意义: 当执行到跳转指令时,需要清洗流水线,处理器必须从跳转地址重新取指, IT可以改善一些小跳转!!!
24. P76 比较跳转CBZ <Rn> , <lable> CBNZ <Rn> , <lable> ,但范围很小只有0-126 并且不会更新标志位
25. P80 跳转表指令TBB ,TBH
26. P84 CM3 存储映射 0x0-------------------------------------------------------------------------4G |512M(代码)|512M(片上SRAM)|512M(片上设备)|512M(片外SRAM)|512M(片外设备)|512M| / | ---------------------------------- | |内部私有外设总线|外部私有外设总线|其他| 0xE0000000 0xE0040000 0xE0100000
27. P85 CM3 存储器访问属性 Bufferable, Cacheable,Executable,Sharable
28. P88 位带操作, 0bit --- 0x0(位带别名区偏移) 1bit ---- 0x4 操作 位带区 bit2 无别名 使用别名 LDR R0,=0x20000000 LDR R0,=0x22000008 LDR R1,[R0] MOV R1,#1 ORR.W R1, #0x4 STR R1,[R0] STR R1,[R0] 注意位带别名地址必须对齐到字, 位带别名操作原子的
29. P94 对齐的地址传输: LDR/STR, LDM/STM, PUSH/POP, LDREX/STREX,位带别名操作
30. P96 LDREX/STREX 实现mutex 的原理 LDREX后,处理器会在内部标记出一段地址(范围可定义),后续执行到STR/STREX, 只要其存储地址落在标记范围内,就会清除此标记, 下一个STREX 执行先检查有没有做过标记和范围,否则驳回STREX
31. P113 子优先级至少1bit,所以抢占优先级最多7bit,这就造成了128级抢占情况 P112 AIRCR(中断及复位控制寄存器) 0xE000_ED00 其中[10:8]PRIGROUP 设置优先分组 比如写5,表示从第5bit 处分,前面2bit表示抢占优先级(P171) 一般3级: 3bit =>0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0 这8个值有效
32. P116 向量表偏移寄存器(VTOR:0xE000_ED08) 向量数对齐到2的整次幂,比如 32irq+16except=48 =>64 个 然后64*4 =256,那么按0x100 对起的偏移量 (P170 向量表移动的sample!)
33. P117 当某中断的服务例程开始执行时,就称此中断进入了active 状态,并且其悬起位 会被硬件自动清除 ,一般ISR 执行完成,才能对新请求响应,所以软件在ISR中 清除中断请求
34. P121 总线fault 状态寄存器(BFSR) 0xE000ED29
35. P122 MemManage fault 的原因: NVIC 中寄存器: MFSR(指出导致MemManage fault 的原因) DACCVIOL bit --- 数据访问违例 DACCVIOL bit --- 取指访问违例 MMAR---可读出引发此fault 的地址 在不带mmu 的操作系统中,这个东西具体发挥什么先进功能???
36. P122 用法fault必须被使能才能正常响应,使能位是NVIC的系统handler控制及状态register 的USGFAULTENA, 总线fault 和 MemManage fault 则不需要enable 用法fault 的原因可以查 UFSR(用法fault状态寄存器) 0xE000_ED2A
37. P124 硬fault状态寄存器 0xE000_ED2C 发生的三种情况: 调试, 其他fault 上访,取向量(异常处理时对向量表读取)
注意各fault服务例程在处理后要清FSRs状态寄存器
38. P125 如果分kernel user,那么user svc 进入kernel,如果user 使用PSP ,那么 kernel 访问PSP (MRS Rn,PSP) 找到user 的栈指针,然后分析LR
39. P127 关于PSVC的应用!!! OS在某中断活跃时尝试切入,线程模式,将触发use fault
40. P129 除软件中断寄存器外,所有中断控制及状态寄存器只能在特权级下访问
41. P130 enable disable 中断:(写1有效) SETENA0---SETENA7 0xE000_E100---E11C CLRENA0-7 0xE000_E180---E19C 可以参考P168的sample! (REG addr offset = (N/32)*4)
42. P131 中断pending : SETPEND0-7 (写1悬起) 0xE000_E200---E21C CLRPEND0-7 (写1解悬) 0xE000_E280---E29C
43. P131 中断优先级寄存器 (8bit可分为高底两段,分别为抢占优先级和亚优先级) PRI_0---239 (每个8bit)(0xE000_E400-E4EF) 及系统异常优先级寄存器(0xE000_ED18-ED23)
44. P132 中断活动状态 只读,处理器执行了ISR第1条指令后set1, 直到ISR返回时才硬件清0 ACTIVE0---7 (0xE000_E300---E31C)
45. P133 PRIMASK disable NMI,硬fault 之外所有异常(将当前优先级改为0) 关中断 MOV R0,#1 MSR PRIMASK,R0 或者 CPSID i
开中断 MOV R0,#0 MSR PRIMASK,R0 或者 CPSIE i
FAULTMASK 连硬fault 也disable (将当前优先级改为-1) 但注意异常退出时自动清0!
BASEPRI 寄存器 用于mask 低于某优先级的中断 BASEPRI_MAX 只能越设越高(只能扩大mask 范围) 用户态不能访问BASEPRI 根据优先级位数判断mask值是否有效 3bit =>0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0 这8个值有效 通过动态计算的方法在P167
46. P134 系统handler控制及状态寄存器SHCSR(0xE000_ED24) 使用这部分寄存器请小心 一般在: 系统调用过程中执行上下文切换 使用软件模拟为定义指令功能
47. P135 中断控制及状态寄存器ICSR (0xE000_ED04) VCETPENDING 悬起的最高优先级的ISR的编号 RETTOBAE ??? VECTACTIVE 当前活动的ISR编号 P137 中断系统设置过程示例
48. P137 软件触发中断寄存器STIR(0xE000_EF00) 写入8,则悬起IRQ #8 (P170) 软件中断的使用说明 (P137通过软件触发某 IRQ n) 1. 设置NVIC USERSETMPEND位 2. 写0xE000EF000后必须执行DSB(数据同步隔离) 3. 该IRQ n必须使能 4. 如果只是为了系统调用尽量用SVC 指令
49. P138 SysTick 控制及状态寄存器(0xE000_E010) SysTick reload寄存器(0xE000_E014) SysTick 当前数值寄存器(0xE000_E010) SysTick 校准数值寄存器(0xE000_E010) 注意当处理器在调试时被halt,SytTick也暂停运行!
50. P141 响应异常的3个动作(!!!): 1.入栈 xPSR,PC,LR,R12,R3-R0 当前上PSP 就压PSP,否则压MSP,进入服务例程一直使用MSP P142 如果栈有双字对齐,如果不对齐,先空4字节,然后压栈
2.取向量 3.更新寄存器
51. P143 异常返回(!!!)及指令 BX LR (EXC_RETURN) POP {...,PC} LDR/LDM =>PC
1.出栈, 2.更新NVIC (同级异常不可重入)
52. P144 咬尾中断(硬件处理) 当处理器响应异常时,有发生异常,前次异常push的,仍旧对下次异常有效 P145 晚到中断的方式基本同上, 省去一个pop,push
53. P147 异常返回值 LR= 0xFFFF_FFF1 返回handler mode (注意总是MSP!) 0xFFFF_FFF9 返回thread mode,并使用MSP 0xFFFF_FFFD 返回thread mode,并使用PSP 正真的LR ,已经在主程被打断前自动入栈了,(LR=>EXC_RETURN,只是指出在那个SP!!!) 0xFFFF_FFF1所指主程其实就是被抢占的服务例程
54. P148 质量LDM/STM 的中止和继续(xPSR开出若干ICI 位)
55. P148 异常响应时的faults 1.入栈 => 总线fault BFSR bit4 STKERR 存储fault MFSR bit4 MSTKERR
2.出栈 => 总线fault BFSR bit3 UNSTKERR 存储fault MFSR bit3 UNMSTKERR 3.取向量 => 硬fault HFSR bit VECTTBL
4. 无效返回 =>use fault => UFSR bit2 INVPC ,bit1 INVSTATE (if disabled then hard fault)
56. P152. 在使用C开发时建议开启CM3的双字对齐 #define NVIC_CCR ((volatile unsigned long *)(0xE000ED14)) *NVIC_CCR = *NVIC_CCR |0x200; //set STKALIGN
57. P155 一个串口输出程序 P160 使用mutex 实现信号量访问 LDREX R2,[R1] ... STREX R2,R0, [R1] CMP R2,#0 => R2 == 0 那么就表示成功 P161 使用位带实现互斥锁操作 (P88=> 位带别名操作原子的) P163 使用位段提取与查表跳转
58. P165 主堆栈安全容量计算方法 每个中断都可以嵌套,对于每1级至少需要8字(32字节),附加考虑ISR局部变量的大小量 进程堆栈处了满足本进程所有函数调用的最大需求量外,好要保留8字,用于容纳第1级 中断时被保护的寄存器(然后进入主栈)
59. P167 RBIT R2,R1, 反转R1,并LSB对齐 R1=0xE0 =>R2=0x07 (P55表) CLZ R1,R2 计算前导0个数 R1=0x07 =>R2=0x5
60. P169 一个ISR 汇编的主结构 (注意R4-R11要自己保护, 其他8个自动保护P141) PUSH {R4-R11,LR} ... POP {R4-R11,PC} 浪费了1字LR再压栈1次 PUSH {R4-R11} ... POP {R4-R11},BX LR 更节约
61. P171 一个比较完整的中断处理例程 其中SetupIrqHandler 负责建立向量表中断服务例程入口地址,所以向量表 必须移动到RAM中,否则无法动态设置!!!
62. P173 SVC使用: 1.SVC 提供1个8位立即数 2.SVC框架: svc_handler TST LR, #4 ;测试EXC_RETURN的bit2 ITE EQ ; 如果为0,下面包括2条指令 (P76) MRSEQ R0,MSP ; 如果==0 则使用MSP MRSNE R0,PSP ; 否则使用PSP LDR R1,[R0,#24] ;从栈中读取PC值 (+24见P142表9.1) ;注意自动压栈8个reg, ;总是8字对齐的所以不用考虑 LDRB R0,[R1,#-2] ;从立既数取系统调用号 ; P54表4.5 SVC 是16bit 指令 ;准备系统调用func,这需要适当调整入栈的PC及LR(EXC_RETRUN), 来进入OS内部 BX LR ;借异常返回形式,进入OS 内部,call sysfunc ;可使用TBB/TBH查表跳转加速 ;返回user的情况如何处理 ???
63. P173 一个具体的SVC 例程 (也可以放在Rx,linuxEABI-R7) 但注意咬尾+晚到情况(P176)!!! P178 一个gcc 内联汇编的svc 代码
64. P180 一个MPU系统的布局 __________________________________________________________ |用户数据 | 用户栈 | 特权数据 | 特权栈 | ---------------------------------------------------------- (user 级访问) ^ (supervisor级访问) ^ PSP MSP
65. P180 一个简单OS的init 和返回user级的过程 (这里叫user级,不叫user空间,因为没有MMU) 1. OS init 2. 建立PSP,并创建任务的堆栈(PC,xPSR) 3. 把EXC_RETURN 改为0xFFFFFFFD 返回 (P147)
66. P181 一个使用Systick及PendSV(优先级最低异常)来实现轮转调度和上下文切换的例子 1. user程序#1 ===> 响应 Systick 异常后进入OS 2. OS 执行系统管理 3. OS 悬起PendSV以便从systick 异常返回后执行调度和switch context 4. 从systick返回如果无其他异常或中断则进入 PendSV ,这时通过PSP 保存user#1程序 堆栈帧,再把PSP 指向user#2程序的堆栈帧 修改EXC_RETURN,(#2堆栈帧pop2psp)并且返回
67. P182 非基级thread mode的使用 可以在服务例程中切换到thread mode,这样如果中断服务例程是用户程序的1部分 ,可能需要让它在thread mode 下执行,以现在访问特权级下的资源 比如: redirect_handler PUSH {LR} SVC #0) ;通过系统服务,由特权级进入user级 (BL User_IRQ_Handler ) (SVC #1 ;执行完中断处理后,回特权级 POS {PC} ;本次中断返回 上面带括号中的指令,包括SVC #0 返回部分,和SVC #1的进入部分 ,都是在PSP+thread mode 下运行,理解这部分有助手动调栈的了解 * 所以第1次调整PSP,是为了SVC0返回用,否则svc0不知道返回那里 因为SVC0是在PSP上返回,所以将SVC0的MSP上内容cpy过来
* 第2次调整为了SVC1的返回,因为SVC1压栈在PSP,返回在MSP 所以,先将PSP上+0x18的内容读出(pop pc指令地址),然后 修改MSP上原来SVC0压入的LR(+0x18),然后PSP东西抛弃 然后Bx LR,返回到SVC1下面那条指令
* 一般情况下异常是在Handler+MSP下,但是通过 1. 对PSP中已经压入的IPSR清0 2. 然后NVIC中配置和控制寄存器CCR的NONBASETHRDENA位设置 3. 最后修改EXC_RETURN,并BX LR返回,就可以返回到PSP+thread 特殊的一半在handler+MSP一半在thread+PSP 需要手动调栈具体见P183-184: 主要就是 SVC #0 时特权级IRQ栈(MSP)上的内容到PSP上, 然后进入user thread,注意进入user thread要清0,IPSR 而这步必须在特权级下做!!!
68. P185 1. 消灭存储器等待周期的方法 ??? 2. 中断向量尽量放代码区,防止取向量与入栈抢总线 3. 限制非对齐访问
一些汇编的技巧: 1.使用带偏移的LDR/STR,可以省去地址增减等计算 2. 上下文相关的变量放到一起,这样就可以创造使用LDM/STM 连续地址的数据传送 3. 当遇到很小if then ,可以使用(ITxxx),最多包括4条 4. 尽量使用thumb-2 指令 5. 多用CM3新指令
69. P186 锁定图12.5
70. P191 消除systick 异常两次悬起,通过在异常处理中除能systick,并清除 NVIC的Systick 悬起位(0xE000ED04)
71. P192 系统控制寄存器(0xE000_ED10) bit 4 SEVONPEND bit 2 SLEEPDEEP 当进入睡眠mode时,使能外部的SLEEPDEEP 信号,以允许停止系统时钟 bit 1 SLEEPONEXT
图13.2进入睡眠模式的序列 WFI(WaitForInterrupt) WFE(WaitForEvent) | | | | | (清除事件锁存器) 事件锁存器!=1 | <-------------------------<------| | 事件锁存器==1 SLEEPDEEP!=1) | (SLEEPDEEP==1) | |---------------| V | | (清除事件锁存器) V V (然后执行下条指令) 进入普通睡眠(idle) 进入深度睡眠(suspend) SLEEPING信号为高 SLEEPING和SLEEPDEEP都为高 SLEEPDEEP为低!
72. P193 唤醒小节: P194 SleepOnExit 自动睡眠功能演示
73. P194 多机通信功能 (有机会再看)
74. P202 CM3共支持8个regions,允许把每个region进1步划分为更小的 子region 次外还允许启用1个背景region(即没有MPU时的全部地址空间)
75. P204 PRIVDEFENA =1 允许特权级访问背景region (图14.1) 注意有个列外: 不管MPU如何限制,响应异常是的取向量操作,以及对系统分区(E000_xxxx) 的访问不受影响
76. P205 1. MPURNR (MPU region 号) 0xE000ED98 2. MPURBAR (基地址) 0xE000ED9c [31:N] N >=16 ADDR [4] valid [3:0] 那个region 3. MPURASR (属性及容量) 0xE000EDA0
当region 不够用,如果是特权级就放到背景region中 总之这款arm 单片机就其单片机的角色,的确强大,特别是MPU,user级特权级等概念一看就是跑RTOS的好材料,但是感觉象ucos这样简单的RTOS 不是很合适,但比较强大的也不行,比如linux,主要是因为MMU,虽然都有支持linux 的cortex m3的patch ,但没有了MMU,也没发挥出强大os的特性,不过也没见过那款单片机带MMU的,所以个人认为cortex m3 应该有一款自己特定的os ,才能最大发挥他的特点
|