1.ARM公司在2009年2月发布了Cortex-M0
2.Cortex-M0的运行效率很高(0.9DMIPS/MHz),能在较少的周期里完成一项任务(甚至可以完成单周期的32位算法)。
3.Cortex-M0的另一种重要应用为专用标准电路(ASSP)和片上系统(SoC)。
4.和Cortex-M3类似,Cortex-M0和Cortex-M1包含了嵌套向量中断控制器(NVIC),使用了相同的异常/中断处理机制。
5.Cortex-M0处理器基于冯●诺伊曼架构(单总线接口),使用32位的精简指令集(RISC)。该指令集被称为Thumb(首次使用在ARM7TDMI上)。
6.Cortex-M0总共支持56个基本指令
7.嵌套向量中断控制器(NVIC)可以处理最多32个中断请求和一个不可屏蔽中断(NMI)输入。
8.NVIC需要比较正在执行中断和处于请求状态中断的优先级,然后自动执行高优先级中断。如果要处理一个中断,NVIC会和处理器进行通信,通知处理器执行正确的中断处理。
9.WIC可以在NVIC和处理器处于休眠的情况下,执行中断屏蔽功能。当WIC检测到一个中断时,会通知电源管理部分给系统上电,让NVIC和处理器内核执行剩下的中断处理。
10.当调试事件发生时,处理器内核会被置于暂停状态
11.JTAG是通用的5针通信协议,一般用于测试。
12.Cortex-M0处理器的存储器映射由架构预定义。
13.CoreSight技术,调试器可以在没有停止处理器的情况下,操作存储器和外设。
14.ARM微控制器使用32位线性地址空间,无须使用分页访问
15.Cortex-M0具有多种寻址方式,这就提高了代码密度
16.Cortex-M0处理器的门数少,这也就直接降低了芯片的动态功耗和漏电流。
17.Cortex-M0和Cortex-M1处理器都是基于ARMv6-M内核架构的
18.在Cortex-M0上设计的程序,可以在完全不用修改的情况下,在Cortex-M3处理器上运行。
19.Cortex-M0和Cortex-M1的二进制代码可以直接在Cortex-M3处理器上运行。
20.处理器在运行程序时处于Thumb状态(Thumb state),在这种状态下,处理器可以处在线程模式(Thread mode),也可以处在处理模式(Handler mode)。
21.线程模式通过配置CONTROL特殊寄存器,可以使用影子栈指针。
22.在Thumb状态或是调试状态下,调试器都可以访问系统存储器空间。
23.Cortex-M0的寄存器组中,包含了13个32位的通用目的寄存器
24.Cortex-M0在不同物理位置上存在两个栈指针。主栈指针为上电后的默认指针,用于异常处理。另外一个称作进程栈指针,只能用在线程模式。通过配置CONTROL寄存器,可以选择使用哪个栈指针
25.在ARM处理器中,由于寄存器时32位的,故PUSH和POP指令永远是32位操作,而且存取的地址须是32位字对齐的。
26.在处理器的上电流程中,中断向量表的头4字节会被取出,然后填充到MSP,作为MSP的初始值。
27.PSP一般是没有必要用的,对于许多应用,系统完全依赖MSP。
28.使用操作系统的设计通常会用到PSP,这是因为操作系统内核的栈空间和线程级的应用空间的栈空间是相互独立的。
29.R14为链接寄存器,用于存储子程序或者函数调用的返回地址。
30.子程序或函数执行完毕,存储在LR中的返回地址将被装载到程序计数器(PC)中,以便调用程序可以继续执行。
31.当发生异常中断时,LR会提供一个特定值,用于中断返回机制。
32.Cortex-M0处理器的函数返回地址始终是偶数(最低位为0,因为最小的指令都是16位,也就是半字对齐的)
33.R15为程序计数器,并且可读可写。读操作返回当前正在执行的指令地址加上4(这是由流水线的特性决定的),而写入R15会导致程序跳转执行(和函数调用不同,链接寄存器不会更新)。
34.Cortex-M0处理器的指令地址须是半字(也就是16位)对齐的,这也就意味着PC最低位必须始终为0。
35.在使用跳转指令(BX或BLX)执行程序跳转时,PC的最低位应该被置为1,以表明目标分支处于Thumb程序区域。
36.Cortex-M0处理器只支持Thumb状态
37.要改变CONTROL寄存器的值,应该在线程模式下操作,或者借助异常中断进入和返回机制。
38.私有外设,包括内置的中断控制器(NVIC)和调试部件
39.Cortex-M0的存储器系统支持各种大小的数据传输,包括字节(8位)、半字(16位)和字(32位)。
40.Cortex-M0处理器的栈操作基于“满递减”的栈模型,这就意味着栈指针始终指向栈空间的最后一个数据
41.PUSH和POP通常用在函数或子程序的开始和结尾处。
42.Cortex-M0的栈空间被设计为字对齐的(地址值必须是4的倍数,比如0x0、0x4、0x8等)
43.操作系统内核使用MSP,而应用程序进程则使用PSP。
44.MSP的初始值位于程序空间的开头部分
45.异常分为很多种,中断只是其中的一种。
46.中断一般由片上外设或者IO口的外部输入产生。
47.异常编号还指明了异常向量的地址。
48.在设备驱动库中,异常编号和中断编号是相互独立的。
49.系统异常使用负数定义,而中断则使用从0~31的正数定义。
50.数值为1的异常号在IPSR中是不可见的。(这个没懂)
51.Cortex-M0则会从存储器的向量表中,自动定位异常处理的入口。
52.BPU最多支持四个硬件断点,DWT最多支持2个监视点。
53.通常,Cortex-M0处理器的程序映像是从地址0x00000000开始的。
54.程序映像的开始处为向量表,其中包含了异常的起始地址,每个中断向量的地址都等于“异常号*4”。
55.这些向量的最低位都被置1,表明异常处理执行时使用Thumb指令。
56.向量表中还包含了主栈指针的初始值,它存储在向量表的头4个字节。
57.复位时,处理器首先读取向量表前两个字(即前8字节),第一个字为MSP的初始值;第二个字为复位向量,它表示程序执行的起始地址(复位处理)。
58.复位流程也会初始化主栈指针(MSP)
59.微控制器启动以后,在执行Flash里的用户程序前,Boot loader会首先运行。
60.在复位流程中,处理器会取出MSP的初始值和复位向量,然后开始执行复位处理,这些所需信息都放在一个叫做启动代码的程序文件中。
61.对于用C开发的应用程序,在进入主流程以前,启动代码就已经开始执行,并对应用程序用到的变量和内存等进行了初始化。
62.应用程序可能还会用C库函数,此时,C编译器/链接器会将所需的库函数纳入编译好的程序映像中。
63.外部中断源或者芯片外设都可以触发中断,并唤醒处理器。
64.通过软件变量传递,中断处理程序和主处理流程间可以进行信息交换。
65.使用RTOS,需要有定时器产生周期性的中断请求。
66.当一个时间片的时间到时,RTOS的任务调度器会由定时器中断触发,并判断是否需要执行上下文切换。
67.Cortex-M0的寄存器映射到了系统空间,并且它们还控制着外设。
68.时钟信号被分为了许多路,而且为了降低功耗,它们可以单独开关。
69.将printf打印的字符输出到串行口(或者其他接口)的技术一般被称作“重定向”
70.对于ARM开发工具,链接文件被称作分散加载文件。
71.如果使用MDK,分散加载文件由内存分布窗口自动生成。
72.ARM C编译器包含了嵌入式汇编器,这样就能很容易地在C程序代码中使用汇编代码。
73.嵌入式汇编器和内联汇编器的汇编语法是不同的。
74.向量表可以用C语言或汇编语言实现。
75.向量表的入口需要编译器和链接器生成的内容
76.栈指针的初始值被链接到链接器生成的栈空间地址,而复位向量则指向了C启动代码的地址,这些都是同编译器相关的。
77.复位向量一般指向C启动代码的开头
78.C启动代码用于设置像全局变量之类的数据,也会清零加载时未被初始化的内存区域。
79.初始化完成后,启动代码跳转到main()程序执行。
80.C启动代码由编译器/链接器自动嵌入到程序中。
81.使用Microlib就可以很好地减小代码体积
82.RAM的使用一般可以分为数据、栈和堆区域。
83.数据存储在内存的底部,包含全局变量和静态变量。
84.栈空间用于临时数据存储、局部变量的存储空间、函数调用参数传递和异常处理的寄存器备份等。
85.在Cortex-M0上编程时,变量的存放地址为其数据宽度的倍数。
86.对于ARM编程,数据长度也可以被称作字、半字和字节。
87.为了简化程序代码,可以将外设寄存器组定义为结构体,而将外设当做指向这个结构体的指针。
88.大多数情况下,外设寄存器都被定义为32位宽度,这是因为连接外设的外部总线(使用APB协议)是按照32位处理数据传输的。
89.外设访问定义指针时,需要使用volatile关键字。
90.标准化的寄存器定义就能提高软件的可移植性。
91.通用的系统初始化函数被命名位void SystemInit(void)
92.CMSIS是为了满足基本操作的兼容性而开发的
93.所有的CMSIS设备驱动库都有相同的内核函数以及相似的接口。
94.CMSIS符合(Motor Industry Software Reliability Association,汽车工业软件可靠性联会)标准。
95.使用了CMSIS开发的嵌入式操作系统和中间件则可以在多种编译器上工作,并可用于多个系列的微控制器。
96.同ARM指令相比,Thumb指令使得代码体积减小了30%
97.Cortex-M0所基于的ARMv6-M体系结构只使用了16位Thumb指令和一小部分的32位Thumb指令。
98.Cortex-M0不支持非对齐访问
99.执行字访问需要操作地址的最低两位为0,半字访问则需要操作地址的最低位为0。
100.STR指令用于单次存储器写。
101.Cortex-M0处理器支持5个跳转指令。
102.Cortex-M0处理器支持3个存储屏障指令。
103.SVC一般可作为系统服务的入口或者应用程序编程接口(API)
104.如果在中断处理中使用了SVC指令,并且该中断的优先级大于等于SVC的,这样会引起错误异常。
105.SVC不能用于硬件错误处理程序、NMI或者SVC自身处理程序中。
106.在对时间敏感的代码中,可以通过切换PRIMASK的状态来开关中断。
107.SEV(发送事件)指令常见于多处理器系统中,用于一个处理器唤醒另外一个处理器
108.Cortex-M0支持NOP指令,该指令可用作指令对齐或延时。
109.Cortex-M0处理器还具备硬件断点单元,并且支持4个硬件断点。
110.LDR为最常用的伪指令,它可以将32位立即数加载到寄存器中。
111.对于Cortex-M0处理器,可以将多个低寄存器(R0到R7)和LR中的返回函数地址压栈
112.多寄存器加载存储指令只能用于字操作
113.用于外设的存储器区域不允许程序代码执行
114.SRAM区域位于存储器映射的第二个512MB,它主要用于数据存储,这其中也包括栈,它还可用于程序代码存储。
115.内部PPB存储器空间用于处理器内部的外设,包括中断控制器NVIC和调试部件等。
116.当Cortex-M0处理器从复位启动时,它会首先访问0地址的向量表,从而取得MSP的初始值和复位向量,然后它就可以从复位向量开始执行程序。
117.Boot loader会在上电阶段通过地址别名被重映射到存储器的开头。
118.Boot loader由芯片供应商预先编程,有时它位于片上Flash存储器并且与用户程序分离的,而其他情况下Boot loader则会位于和可编程程序存储器分离的非易失性存储器中。
119.存储器映射的切换由硬件寄存器控制,Boot loader执行时会设置这些寄存器。
120.处理器从Boot loader别名里取出复位向量,并且开始执行Boot loader
121.将SRAM重映射到地址0后,程序就可以被复制到SRAM并以最快速度执行,这样会避免取向量表时出现等待,否则会增加中断等待的时间。
122.如果嵌入式应用中没有操作系统,那么它只会使用一个栈。
123.通常将栈放在存储器块的尾部,而堆存储则紧跟在普通存储的后面。
124.Cortex-M0处理器可以支持小端的存储器格式,也可以支持大端的。
125.Cortex-M0支持的大端模式被称作字节不变大端模式,或者“BE8”,这也是ARM架构的大端模式之一。
126.多数的Cortex-M0微控制器使用小端配置
127.在访问存储器时,存储器接口会根据传输大小和地址的最低两位选择数据链路。
128.多数情况下,连接到外设总线(APB)的外设应该使用字传输来访问。
129.通过APB访问的外设寄存器通常被声明为“volatile unsigned integer”。
130.通常情况下,C编译器不会产生任何非对齐访问,而如果C程序直接操作一个指针的话,非对齐访问还是会出现的。
131.取指或数据访问都可以引起总线错误。
132.Cortex-M0处理器支持多寄存器加载和存储指令
133.应该避免在外设访问时使用LDM或STM指令
134.要使不同设备的软件移植更加容易,可以对存储器映射中的每个区域设置相应的存储器属性。
135.在一个可缓冲存储器区域上执行数据写操作,写传输可能会被缓存起来,这就意味着处理器不必等待当前的写传输完成,就可以继续执行下一条指令。
136.如果系统中含有缓存设备,它可以在本地备份当前传输的数据,并可以下次在访问相同的存储器位置时重新使用,这样可以加速系统执行。
137.Cortex-M0不具有访问重排序特性。
138.Cortex-M0处理器没有单独的特权和非特权访问等级,处理器一直处于特权访问等级。
139.微控制器的中断可以由片上外设或软件产生。
140.Cortex-M0的每个异常源都有一个单独的异常编号,NMI的编号为2,而片上外设和外部中断的则为16~47。1~15的其他编号,用于处理器内部的系统异常
141.NMI可以用于掉电处理,也可以连接到看门狗单元,以便在系统停止响应时将系统复位。
142.SVC指令执行时就会产生SVC异常
143.在OS上使用PendSV可以确保高优先级任务完成后才执行系统调度。
144.中断信号可以连接到片上外设,也可以通过I/O端口连接到外部中断源上。
145.NVIC能够接受的中断请求信号可以是高逻辑电平,也可以是中断脉冲。
146.在微控制器的外部接口中,外部中断信号可以是高电平也可以是低电平
147.对于具有可编程优先级的异常,优先级配置寄存器为8位宽,而且只能使用最高两位。
148.为了使Cortex-M0/M3设备间的软件移植更为简单,处理器没有使用优先级寄存器的最低位,而是使用了最高位。
149.对NVIC的一个寄存器执行写操作可以清除异常挂起状态
150.如果两个异常同时发生,并且它们被赋予相同的优先级,异常编号较小的异常将会首先执行。
151.Cortex-M0处理器对中断嵌套的支持无需任何软件干预
152.对于Cortex-M0处理器,内置的中断控制器NVIC支持向量中断,这就意味这不同中断的异常向量是独立的,而且中断服务程序的入口自动分配,无需软件干预。
153.当Cortex-M0处理器要处理中断服务请求时,它需要首先确定异常处理的起始地址,所需的信息叫做向量表,它存储在存储器空间的开始位置。
154.向量表包含了系统中可用异常的异常向量,以及主栈指针(MSP)的初始值。
155.异常向量的地址为异常编号乘4
156.Cortex-M0处理器采用了硬件自动处理的方法来备份和恢复处理器状态
157.链接寄存器(LR/R14)则会被更新为异常返回时使用的特殊值(EXC_RETURN)
158.EXC_RETURN特殊值被加载到程序计数器(PC)中时,异常返回机制就会启动。
159.如果加载到PC的值不是EXC_RETURN,则其会被当做普通的BX或POP指令。
160.异常的末尾连锁降低了异常处理的开销,因此也提高了能耗效率。
161.Cortex-M0中EXC_RETURN的bit0保留,且必须为1
162.EXC_RETURN的bit2表示出栈恢复寄存器时使用的是主栈(MSP)还是进程栈(PSP)。
163.EXC_RETURN的bit3表示处理器要返回线程模式还是处理模式。
164.由于EXC_RETURN的值在异常入口处被自动加载到LR中,异常处理会把它当成普通的返回地址。
165.当异常发生时,8个寄存器会被自动压栈,这些寄存器包括R0到R3、R12、R14(链接寄存器)、返回地址(下一条指令的地址或程序计数器)和程序状态寄存器(xPSR)。
166.对于嵌套异常,压栈时总是使用主栈,因为处理器当前处于处理模式,这种情况下只能使用主栈。
167.压栈时保存到栈里的数据被统称为“栈帧”。
168.在Cortex-M0处理器中,一个栈帧总是双字对齐的
169.压栈完成以后,处理器会从向量表中取出异常向量,然后将向量写到PC,并且将从这个地址中开始异常处理的取指。
170.异常处理开始执行后,LR的值会被更新为相应的EXC_RETURN值,这个值将会被用作异常返回,IPSR也会被更改为当前处理异常对应的异常编号。
171.异常返回过程完成后,处理器将恢复的返回地址放到程序计数器中,并且继续执行中断之前的程序。
172.每个外部中断都可以独立地使能或禁止,并且其挂起状态也可以手动地设置和清除。
173.SCB寄存器涉及的特性包括SysTick定时器操作、系统异常管理、优先级控制和休眠模式控制。
174.使能中断时使用SETENA地址,而禁止中断时则使用CLRENA地址。
175.多个应用程序进程同时访问寄存器时,可能会导致已编程的控制信息丢失,而设置和清除的分离则能防止这种情况发生
176.中断挂起状态寄存器允许使用软件来触发中断。
177.如果一个产生中断的外设需要重新编程,就得关闭这个外设的中断,重新设置控制寄存器,并且在重新使能外设以前清除中断挂起状态。
178.每一个外部中断都有一个对应的优先级寄存器,每个优先级都是2位宽,并且使用中断优先级寄存器的最高两位,每个寄存器占1个字节(8位)。
179.PRIMASK寄存器只有1位有效,并且在复位后默认为0。该寄存器为0时,所有的中断和异常都处于允许状态;而设为1后,只有NMI和硬件错误异常处于使能。
180.NMI的挂起状态也可以由软件产生
181.通常情况下,Cortex-M0的中断等待时间为16个周期。
182.如果中断返回时产生了另外一个中断请求,处理器就会跳过出栈和压栈过程,这样就减少了中断等待时间。
183.Cortex-M0处理器具备零误差特性。
184.数据同步屏障(为了提高可移植性而使用),确保所有的存储器访问完成
185.Cortex-M0处理器上的配置和控制寄存器(CCR)为只读的,它决定了栈的双字对齐设置和非对齐访问的处理。
186.STACKALIGN为1,表示当产生异常压栈时,栈帧总是自动对齐到双字对齐的存储器位置上。
187.SysTick定时器,24位向下计数,且周期产生SysTick异常。
188.通过异常机制,应用程序可以使用SVC访问OS服务。
189.SysTick由系统控制空间的4个寄存器控制。
190.栈指针的选择由Cortex-M0处理器的当前模式和CONTROL寄存器的值决定。
191.SVC指令包含了一个8位立即数,SVC处理可以将这个数提取出来,并根据它来确定所需的OS服务。
192.受Cortex-M0处理器中断优先级规则所限,SVC只能运行在线程模式,或者比SVC自身优先级低的异常处理中,否则,硬件错误异常就会发生。
193.PendSV也是一种异常,你可以通过设置NVIC的挂起状态来激活它。
194.PendSV异常主要用作以下功能:(1)嵌入式OS的上下文切换(2)将一个中断处理过程划分为两部分
195.等待数据/事件的任务通过调用SVC服务切换到另外一个任务中。
196.实际的上下文切换过程可以发生在低优先级的PendSV处理中,并且与SysTick处理相分离。
197.OS任务调度器可以在退出异常之前设置PendSV异常的挂起状态,如果此时没有IRQ处理在运行,在SysTick异常结束后,PendSV处理会立即启动并且执行上下文切换。
198.Cortex-M0处理器的设计非常小,最低配置的设计只需12K逻辑门。
199.Cortex-M0处理器的代码密度较高,因此它对Flash存储器的要求较低,这样功耗也就随之下降。
200.两个指令用于进入休眠模式:WFE(等待事件)和WFI(等待中断)。
201.如果事件锁存置位,那么WFE指令完成,它将会被清零,并且处理器不会进入休眠
202.WFE一般用于空循环或者轮询循环,它能否让处理器进入休眠是不确定的。
203.如果事件寄存器被设为1,WFE的执行不会进入休眠,而WFI的执行总会进入休眠
204.WFE可由外部事件信号唤醒
205.在PRIMASK置位时,WFI可由使能的中断请求唤醒
206.WIC本身不包括任何的可编程寄存器,它的接口被耦合至Cortex-M0的NVIC,这样处理器的中断屏蔽信息在休眠期间就会被自动地传送到WIC,而且WIC也只能在深度休眠期间被激活(SLEEPDEEP置位)。
207.WIC使用SRPG(状态保持功率门)计数降低了Cortex-M0的待机功率。
208.在WIC模式深度休眠时,SysTick定时器将会停止
209.由于在总线传输中使用了非法地址,就产生了bus error
210.总线错误可以由很多种情况引发,例如错误的指针操作、栈空间损坏、内存溢出、非法存储器映射以及其他原因。
211.根据错误类型的不同,通常能够直接确定引起硬件错误异常的指令的位置。
212.为了给分析提供更多的信息,也可以生成程序映像的汇编代码,并且利用在栈帧中找到的PC值确定错误的位置。
213.压入栈的IPSR能够反映处理器是否在进行异常处理,EPSR则代表了处理器状态
214.栈中的LR也可能会提供一些信息,例如发生错误的函数的返回地址、错误是否发生在异常处理中,以及EXC_RETURN的值是否被异常破坏等。
215.向量表中的向量的最低位应该置1,以表示当前处于Thumb状态。
216.如果声明的函数指针的最低位为0,调用这个函数时,处理器会进入硬件错误
217.由于硬件错误可能是由栈指针错误引起的,而且C代码的运行可能需要依赖栈存储,用C语言编写的硬件错误处理可能无法正常运行。
218.由于C处理器程序需要从栈中提取调试信息,而且C编译器生成的程序代码通常也需要栈存储,所以只有栈信息合法时,处理程序才能正常执行。
219.如果在硬件错误处理期间发生了另外一个错误,或者NMI处理期间发生了一个错误,则Cortex-M0处理器就会进入锁定状态。
220.锁定状态能够防止失败程序破坏存储器或者外设中更多的数据。
221.一旦进入硬件错误异常或NMI异常处理后,总线错误响应就会引发锁定了。
222.由于xPSR无法确定,因此处理器无法获取到系统的正确优先级。
223.通过复位控制器的配置,微控制器或片上系统的设计者可以利用LOCKUP信号来复位系统。
224.如果有必要,可以用汇编实现整个硬件错误处理。这样可以避免一些栈存储的访问,从而避免由于栈指针被破坏而指向非法存储器地址引起的锁定。
225.通常需要添加断点、数据监视点,查看存储器空间以及寄存器等,这些调试体系特性目前已经是现代处理器设计的一部分。
226.程序执行到被标记为断点的地址,引起能够暂停处理器的调试事件
227.数据或外设的地址可以被标记为监视变量,对该地址的访问会产生调试事件,它会暂停程序执行
228.用户可以通过调试器或调试事件暂停程序执行
229.Cortex-M0处理器的调试特性为可配置的。
230.在设计嵌入式系统时,如果你想调试系统方便,就应该避免将调试接口引脚用作I/O
231.调试系统使能之后,调试器才能停止处理器、下载应用程序以及复位控制器,然后我们才可以测试应用程序。
232.程序执行可以通过硬件断点、软件断点、监视点或者向量捕获事件等停止。
233.当产生向量捕获时,处理器在执行复位或硬件错误异常处理的第一条指令前就会停止。
234.处理器暂停后,你才能访问处理器寄存器组中的寄存器以及特殊寄存器,才能访问存储器和外设中的数据或者执行单步操作。
235.调试系统也提供了访问Flash、SRAM及外设等系统存储器的入口,即使处理器正在运行,系统存储器也可以被访问。(认为与234条所描述的矛盾)
236.除了可以将程序下载到Flash存储器中,也可以将其下载到RAM中
237.在调试会话开始时,程序会被加载到SRAM中,程序计数器(PC)也会被自动设置为正确的程序映像起点,之后应用程序开始执行。
238.除了定义微控制器可用的存储器分布之外,分散文件还可用于定义指针地址以及预留RAM中的存储区域作为特殊用途。
239.只要SysTick异常产生并且进入了异常处理,SysTick定时器就会被禁止,这样SysTick处理只会执行一次。
240.全局中断屏蔽PRIMASK在复位后默认清零,所以就不必使能全局中断了。
241.由于各家微控制器的外设在设计上存在不同,可能需要在中断服务程序中清除中断请求,请注意中断服务程序使用的全局变量需要以“volatile”进行定义。
242.如果输入参数的数量超过了4个,则栈就会被用到了(细节可以参考AAPCS)。
243.如果一个汇编函数需要调用C函数,就应该首先确保当前使用的栈指针指向双字对齐的地址,这是由EABI标准决定的。
244.符合EABI标准的C编译器产生的程序代码的栈一般是双字对齐的。
245.在ARM/Keil开发工具中,汇编器用REQUIRE8伪指令表示函数需要双字栈对齐,PRESERVE8则表示函数要保持双字对齐。
246.FUNCTION伪指令表明了函数的开始,而ENDFUNC伪指令则代表了函数的结束
247.将输入参数转移到高寄存器后,会有更多的寄存器可以用于数据处理,这样也使得开发程序代码更加容易。
248.要为函数内的局部变量分配存储器空间,可以在该函数的开头修改SP的值
249.寄存器R0到R3、R12以及LR可能会被更改,如果这些寄存器中的数据之后还要使用,就需要将它们保存到栈上
250.SP的值应该是双字对齐的
251.返回值(假定为32位或更小)一般存在R0中
252.如果汇编代码是按照C文件中的嵌入式汇编编写的,应该使用_CPP关键字代替IMPORT关键字来引入地址符号。
253.在Keil MDK中,_cpp关键字用于访问C或C++编译时的常量表达式
254.若改变了寄存器R4到R11里的任何数值,需要将原始数值保存到栈中,并且在返回到C代码以前恢复原始值(和249条比较下)
255.若要在汇编函数中调用另外一个函数,需要将LR的值保存在栈中,并且利用它执行返回操作
256.如果汇编代码需要访问C代码中的一些变量,也可以使用IMPORT关键字。
257.通过嵌入汇编,我们可以在C文件中实现汇编函数。
258.在嵌入汇编内,同样可以使用__cpp关键字引入地址值或数据符号
259.嵌入汇编允许你在异常处理中定位栈帧,这也是嵌入汇编的一个优势。
260.应该尽量使用CMSIS内在函数,这样能够提高应用程序代码的可移植性。
261.进入休眠模式后,处理器可以由中断请求、调试请求、事件和复位唤醒。
262.Cortex-M0处理器的系统控制块中有一个可编程的寄存器,被称作系统控制寄存器
263.处理器通过执行WFE指令进入休眠后,可以由中断(包括未使能的)利用挂起发送事件特性唤醒。
264.退出休眠特性可以通过以下两点降低系统的功耗:①中断驱动的应用无需执行线程中不必要的程序,②减少了不必要的压栈和出栈操作。
265.在深度休眠期间SysTick定时器也可能会被禁止
266.在多处理器系统中,像自旋锁之类的处理器间的通信机制往往需要轮询公用内存的软件标志。
267.请求管理调用(SVC)通常用于OS环境,应用任务可以利用它获取OS提供的系统服务。
268.利用提出的栈帧地址,SVC异常处理可以定位并且读出压入栈的寄存器作为输入参数
269.利用栈帧的PC值,SVC异常处理也可以查到执行过的SVC指令中的立即数
270.返回值也应该放入栈帧中,否则,存入R0的值可能会在异常返回出栈过程中丢失。
271.对于Keil MDK或ARM RVDS,“__svc”关键字用于定义SVC函数以及SVC号。
272.由于在C语言的SVC处理中无法获知栈帧的起始位置,所以SVC处理的前半部分需要用汇编实现。
273.SVC异常的优先级是可编程的,要为SVC异常指定一个新的优先级,可以使用CMSIS的NVIC_SetPriority函数。
274.和SVC不同,PendSV异常由中断控制状态寄存器触发。
275.如果由于优先级不够,PendSV无法执行,则它将等到当前优先级下降或者屏蔽接触(如PRIMASK)后再执行。
276.每次高优先级的定时器被触发后,定时器的处理程序只会运行一小段时间,执行重要的任务以及设置PendSV的挂起状态。
277.定时器中断处理的不太紧急部分在低优先级的PendSV异常中处理
278.PendSV仅作为子程序出现,没有任何输入参数及输出返回值。
279.OS需要额外的程序以及内存空间,而且OS本身也需要一定的处理时间。
280.嵌入式OS需要利用定时器产生的中断,这样OS才可以执行任务调度以及系统管理。
281.任务优先级的安排是同中断优先级完全独立的。
282.每个任务在声明时都必须使用__task关键字
283.需要在每个任务执行前对它进行初始化,OS内核也需要初始化步骤。
284.OS初始化并启动内核,初始任务被指定为init()
285.Init()任务是OS创建的第一个任务,它还可用于创建其他的任务。
286.信号量特性同MUTEX类似,只是MUTEX仅限于一个任务访问一个共享资源,而信号量则可以限定固定数量的任务访问多个资源。
287.消息内容存储在位于固定区域的内存池中,消息内容的指针则通过信箱传递。
288.除了ARM Keil微控制器开发套件(MDK),ARM还提供了另外一种开发工具,也就是RealView开发组件(RealView Development Suite,RVDS)。
289.用RVDS进行开发时,可以使用IDE,也可以使用命令行。
290.利用“-I”选项添加了Keil MDK的包含路径,这样就能直接使用MDK的文件,不必将它们复制到本地目录。
291.要使用分散加载特性,需要在armlink中使用--scatter选项。
292.对于具有多个ROM区域的系统,可以增加存储段并且为不同的存储段分配不同的目标。
293.分散加载文件也可用于定义栈存储和堆存储。
294.在Keil MDK中,ARM微控制器的默认启动代码是用汇编实现的。
295.在Keil MDK中,可以通过设置一个编译选项,选择使用MicroLIB来减小代码体积。
296.MicroLIB为C标准库的一个版本,并且专门用于微控制器应用
297.仅仅使用一个armasm命令,就可以完成程序的汇编和程序映像的生成。
298.在生成编译映像以后,为了测试程序,通常需要将程序映像烧写到Flash存储器中。
299.要想使用Keil MDK编程程序映像,需要将文件的扩展名.elf改为.axf。
300.如果要使用在线调试器(如UNLINK2),需要设置调试选项,如果有必要还要设置Flash编程选项。Flash编程选项应该默认自动配置好了。
301.对于Cortex-M0软件开发,多数情况下,应该使用EABI版本的Sourcery G++
302.GNU工具链包括C编译器、汇编器、链接器、库、调试器以及其他工具。
303.链接阶段通常是由C编译器执行的,而不是作为一个单独的步骤,这样可以确保所需参数和库的细节能够正确地传递到链接器中。
304.在编译Cortex-M处理器的应用程序时,Sourcery G++提供了一个向量表,该向量表时CodeSourcery通用启动代码序列(CS3)特性的一部分
305.GNU C编译器支持内联汇编
306.可以在一个单独的gcc编译步骤中,混合使用C程序和汇编代码。
307.汇编文件用于SVC处理中的包装函数
308.C语言的SVC处理需要一个输入参数,以获取SVC异常栈帧的起始地址。
309.基于C的SVC处理就可以从栈帧中提取输入参数,这样它就能提取处SVC指令执行时使用的SVC编号。
310.压栈的PC值对于定位位置非常有用,通过创建编译程序映像的反汇编列表,可以利用压栈的程序计数器值来确定硬件错误产生的位置。
311.压栈的xPSR中的IPSR的值表明错误是否发生在一个异常处理中
312.几乎所有的ARM开发工具都支持DWARF文件格式
313.ARM7TDMI的寄存器组基于当前操作模式,而在Cortex-M0中,只有SP是基于操作模式的,并且多数无OS的简单程序只需使用MSP。
314.Cortex-M0不支持ARM指令集
315.ARM7TDMI上的Thumb指令还可以重用在Cortex-M0上
316.Cortex-M0具有内置的中断控制器NVIC,并且可以支持最多32个中断输入,每个中断可以设置为4个优先级之一
317.Cortex-M0的向量表存储了每个中断服务程序的起始地址
318.如果异常处理将PRIMASK置位,那么在异常退出前还需要将其清除,否则PRIMASK就会一直保持置位状态
319.根据UAL语法,更新APSR的数据处理指令需要S后缀。
320.由于Thumb指令不支持交换操作(SWP和SWPB指令),处理原子操作的代码必须得修改。
321.由于系统中只可能有一个SVC异常实例在运行(当异常处理进行时,其他同级或更低级的异常会被阻塞),可以使用SVC作为原子操作的入口。
322.Cortex-M0的异常流程将R0-R3以及R12等寄存器自动压栈
323.Cortex-M0的硬件自动处理了中断嵌套,并且NVIC中的中断优先级也是可以设置的。可以重新调整异常的优先级,使系统获得最优的性能。
324.Cortex-M1和Cortex-M0都是基于ARMv6-M架构的
325.SVC和PendSV在Cortex-M1上是可选的,而在Cortex-M0上却总是存在的。
326.在用户线程模式下,对处理器配置寄存器(如NVIC和SysTick)的访问是受限制的,可选的存储器保护单元(MPU)可以用于阻止程序访问某些存储器区域。
327.BASEPRI寄存器可以阻止某优先级或更低优先级的中断,FAULTMASK寄存器则提供了其他的错误管理特性。
328.NVIC将优先级分为两个部分:抢占优先级(用于嵌套中断)和子优先级(用于多个具有相同抢占优先级的中断同时发生),并且它们可由软件配置。
329.Cortex-M3的NVIC还支持动态修改优先级,而Cortex-M0的优先级在使能后就不应该被修改了。
330.在Cortex-M0中,所有的数据传输操作必须是对齐的,这就意味着字传输的地址必须能被4整除,半字传输则需要发生在偶数地址上。
331.Cortex-M3则允许多个存储器访问指令产生非对齐访问,Cortex-M0的非对齐数据可以通过多条指令来执行。
332.数据监视点和跟踪单元(DWT)可以跟踪监控数据及存储器区域的访问,可用于产生事件跟踪,这样就可以看到异常进入和退出时的信息。
333.不同的处理器架构具有不同的启动代码和中断向量表,移植时它们通常需要被新的替换。
334.如果数据结构需要非对齐数据处理,可以在定义该结构时加上“__packed”属性。
335.Cortex-M0需要多条指令才能完成非对齐数据的访问,所以最好修改数据结构,确保内部的所有元素都是对齐的。
336.在ARM中,向量表则包含了主栈指针的初始值和异常处理的起始地址