物联网|探索cortex-M系列CPU的内核|存储系统|中断和异常处理|复位及复位时序|物联网开发系列课程之零基础玩转Cortex-M系列CPU-学习笔记(4)

第二阶段-理论基础-探索cortex-M系列CPU的内核

参考:Cortex-M3权威指南中文版

Cortex-M架构CPU内核的存储系统

存储单元是1个字节,1byte=8bit

存储系统的特性

1存储系统的主要特性

4GB的线性寻址空间
B=byte b比特:位 1byte=8bit
预定义好的存储器映射 ARM公司给出,芯片厂家进行细化
支持大端模式和小端模式(均可配置,默认模式为: 需要看数据手册)
支持位带访问(可选) 有区域限制,直接与或操作
支持存储器保护单元(可选)CPU厂家规定是否包含,防止非法读写操作
支持非对齐的传输

Tip1:4GB寻址空间的由来

32位存储系统,32个0或32个1,
计算器先二进制输入32个1,然后转为十进制显示,再加1(因为寻址空间是从0地址开始)
在这里插入图片描述
在这里插入图片描述
表示总共可以存储的字节大小,先/1024,变为KB,再除1024,变为4096MB,再除1024,变为4GB.
或者2的32次方,除3次1024.在这里插入图片描述

Tip2:关于大端模式和小端模式

大端模式:高字节存储在内存的低地址处,低字节放在内存高地址处;当成字符串,地址由小向大增加
小端模式:与大端相反。地址的高低位与数据的高低位匹配。与人的逻辑思维方式接近。
如:0X1234
高 低
大端 0x12 0x34
小端 0x34 0x12在这里插入图片描述
参考原文链接
一般的arm芯片都是默认使用小端存储模式,对于CM3,可以使用REV/REVH等相关指令反转字节序,来完成端模式的转换。
注意:在CM3中,存储模式是在复位时确定的,且运行时不可更改。并且指令预取永远使用小端模式,在配置控制存储空间的访问也永远使用小端模式(包括NVIC, FPB等)。另外,外部私有总线地址区0xE0000000至0xE00FFFFF也永远使用小端模式。

测试STM32的大小端

int a = 0x12345678;
int i;
char b[4];
int main(void)
{
	for (i = 0; i < 4; i++){
		b[i] = *((char*)&a + i);
	}
}

▲ 利用指针验证STM32是小端存储
因此,STM32默认使用小端存储。

当然,也可以用联合体来测试:

union B_t{
	char b[4];
	int a;
};
union B_t B;
int main(void)
{
	B.a = 0x12345678;
}

Tip1: Cortex-M架构CPU的中断和ARM7架构中断的区别

FIQ 快速中断 Cortex-M 12个tick
IRQ 普通中断 ARM72种都有

Tip2:对齐传输和非对齐传输

ARM9,10必须对齐传输,
coretx_m3,m4支持非对齐传输,限制为单数据,多数据,位带,堆栈等还是要求对齐
非对齐操作转化为小的对齐操作。非对齐代码效率低。
尽量放在对齐的位置,方便后期使用。

存储系统的映射图

存储系统映射图简析

在这里插入图片描述
在这里插入图片描述
Private peripherals including 内部私有外设总线
0xFFFFFFFF
built-in interrupt controller
(NVIC) and debug
components
OxEO000000
OXDFFFFFFF
Mainly used for external 片外外设
peripherals.
OxA0000000OxSFFFFFFF
Mainly used for external 外部存储器
memory.
Ox60000000
Mainly used for peripherals. 片内外设
Ox5FFFFFFF0x40000000
Mainly used for data memory
0x3FFFFFFF
(e.g. static RAM.)
Ox20000000
Mainly used for program
0x1FFFFFFF
code.Also used for exception
vector table 中断向量表
Ox00000000

堆栈存储模型

1 何谓堆栈:LIFO模式(后进先出)
堆栈在编程模型中的功能
(1)函数调用
(2)中断和异常的处理
(3)对本地变量的保存
2 Cortex-M架构中堆栈的实现
向下生长的满栈,系统启动时堆栈指针在系统栈的末尾,每次入段都执行SP内容-4(栈对齐),出栈操作都是+4.所有位置都能用到。
3 MDK-ARM5.13的仿真演示

Cortex-M架构CPU内核的中断和异常处理

☆中断和异常的概念
1何谓异常,何谓中断?
使得当前正常运行的程序流程发生改变的事件,处理器挂起当前任务,执行异常处理程序,
中断时一种特殊的异常,软件也可以触发中断,执行ISR(中断服务例程)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2 中断类型
3 常见的中断源
I/O中断,定时器中断,软件中断…
☆异常的处理流程
判断异常是否符合处理条件
MCU运行状态,
异常EN(使能)NMI,HardFault始终使能
PR高
Non-mask
异常的响应处理序列
入栈操作:PSR,PC,LR,L12,R0-R13
取向量(异常向量表)
更新寄存器SP,PSR,PC,LR
异常的处理Handle:进行处理
异常的返回处理序列:出栈操作,NVIC

Tip1:中断向量表

数组,存储了各异常处理程序的起始地址。每次使用后,NVIC更新该值为0.

典型的中断向量表:
在这里插入图片描述
在这里插入图片描述
观察最后位都是1,表示处于sub状态,所以最后位都置为1(最低有效位LSB).
初始地址存放的是MSP(主堆栈指针初值)
☆NVIC简介(中断控制器)
1何谓NVIC
嵌套的中断向量控制器:
功能:设置优先级,使能,屏蔽,挂起
嵌套的:中断中条件满足可出发另外中断
向量: 中断/异常向里表入口加载支持
2 NVIC的功能
灵活的中断和异常管理
嵌套的中断/异常支持
中断/异常向里表入口加载支持

Tip1:可以屏蔽中断的内核特殊寄存器组

PRIMASK 1位,置1,关闭所有可屏蔽的异常,只剩下NMI,HardFault不能关闭
FAULTMASK 1位,置1,除了NMI其他所有中断都会被屏蔽
BASEPRI 9位,置值,优先级号大于当前值的都会被关闭,数越大,级别越低。

Cortex-M架构CPU的复位及复位时序

☆三种复位方式
复位有延时,主要是等待时钟源复位稳定的时间,不同厂家及CPU不一样
1、上电复位
2、系统复位(不复位调试组件)
3、处理器复位(调试组件及外设不复位)
☆复位时序描述
在这里插入图片描述

在离开复位状态后,CM3做的第一件事就是读取下列两个32位整数的值:从地址ox0000,0000处取出MSP的初始值。
从地址 ox0000,0004处取出PC的初始值——这个值是复位向量,LSB必须是1。然后从这个值所对应的地址处取值。

Tip1:异常向里表起始位置(即0地址处)存放主堆栈指针的原因

请注意,这与传统的ARM架构不同——其实也和绝大多数的其它单片机不同。传统的ARM架构总是从0地址开始执行第一条指令。它们的0地址处总是一条跳转指令。在CM3中,在0地址处提供MSP的初始值,然后紧跟着就是向量表(向量表在以后还可以被移至其它位置——译注)。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。
因为CM3使用的是向下生长的满栈,所以MSP的初始值必须是堆栈内存的末地址加1。举例来说,如果你的堆栈区域在0x20007C00-0x20007FFF之间,那么MSP的初始值就必须是0x20008000。
向量表跟随在MSP的初始值之后——也就是第2个表目。要注意因为CM3是在Thumb态下执行,所以向量表中的每个数值都必须把LSB置1(也就是奇数)。正是因为这个原因,图3.18中使用0x101来表达地址0x100。当0x100处的指令得到执行后,就正式开始了程序的执行。在此之前初始化MSP是必需的,因为可能第1条指令还没来得及执行,就发生了NMI或是其它fault。MSP初始化好后就已经为它们的服务例程准备好了堆栈。
对于不同的开发工具,需要使用不同的格式来设置MSP初值和复位向量——有些则由开发工具自行计算并生成。如果想要获知细节,最快的办法就是参考开发工具提供的一个示例工程。本书的第10章和第20章介绍ARM提供的开发工具,第19章则介绍GCC工具链。
☆启动代码讲解
1、启动代码的主要功能
堆栈空间定义
存放中断向量表
复位中断函数《Reset_Handler) ;
其它中断异常服务函数,以及弱[WEAK]声明;弱声明,如果用户已定义该名称,则进入用户定义,预防程序崩溃。
将堆栈地址传递给库函数,利用库函数初始化堆栈,和库函数自身初始化。
Tip1:为什么使用以前的8位,16位MCU,不用编写启动代码?
2、ST官方提供的Cortex-M4启动代码简单分析
打开之前的工程,打开:F:\IOT\PRJ\first_prj\RTE\Device\STM32F407ZGTx\startup_stm32f407xx.s
首行有效代码为:Heap_Size EQU 0x00000200
光标移动至EQU,按F1,打开注释:
在这里插入图片描述
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;这里的3是2的3次方,8字节对齐,

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3   ;这里的3是2的3次方,8字节对齐,
Stack_Mem       SPACE   Stack_Size ;定义我们的0x00000400大小的存储块,值均为0
__initial_sp  ;标号,指示当前堆栈的初始位置
具体地址见工程下的map文件:F:\IOT\PRJ\first_prj\Listings\first_prj.map
    __initial_sp                             0x20000670   Data           0  startup_stm32f407xx.o(STACK)
这个是最高地址,向下压栈。
; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3  ;定义堆空间
__heap_base
Heap_Mem        SPACE   Heap_Size  ;定义堆空间大小为 0x00000200
__heap_limit

                PRESERVE8 ;指示当前堆栈是8字节对齐
                THUMB ;告诉汇编器以下均为32位的THUMB指令


; Vector Table Mapped to Address 0 at Reset   向量表映射
                AREA    RESET, DATA, READONLY  ;定义复位向量段
                EXPORT  __Vectors  ;导出后,其他部位也能使用此全局标号
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack 分配4字节32位地址
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler

; Reset handler

Reset_Handler    PROC ;标记函数的开始
                 EXPORT  Reset_Handler             [WEAK] ;弱声明
        IMPORT  SystemInit	;从其他文件导入.\first_prj\RTE\Device\STM32F407ZGTx\system_stm32f4xx.c
        IMPORT  __main ;从其他文件导入

                 LDR     R0, =SystemInit ; 函数入口地址加入到R0, =把入口地址传给R0
				 ;MOV	 R1,PC
                 BLX     R0  ; 地址0x080002D1后面的1代表是sub状态
                 LDR     R0, =__main
                 BX      R0
                 ENDP ;函数结束

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .  ;死循环
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP

;*******************************************************************************
; User Stack and Heap initialization 用户堆栈初始化
;*******************************************************************************
在这里插入图片描述

                 IF      :DEF:__MICROLIB   ;如果勾选了,就会执行

                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit

                 ELSE

                 IMPORT  __use_two_region_memory   ;未勾选执行此部分,使用双堆栈
                 EXPORT  __user_initial_stackheap

__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

演示代码

reset以后,首先把堆栈指针sp指向为:0x20000670,在这里插入图片描述

在这里插入图片描述
真正执行时,已进入了Reset handler,之前的都是CPU自动完成的:
在这里插入图片描述
单步执行,得到入口地址,跳转进入SystemInit:在这里插入图片描述

在这里插入图片描述

跳出,进入main函数:
在这里插入图片描述

参考书籍:

Cortex M3与M4权威指南.pdf 2014
Protel Schematic.pdfls 2009
STM32F4xx英文参考手册.pdf 2014
STM32F4xx中文参考手册.pdf 2014
STM32F407ZGT6.pdf 2014

作业:STM32F031C8内核原理试验及启动代码分析

猜你喜欢

转载自blog.csdn.net/Medlar_CN/article/details/130530133