作者 | 将狼才鲸 |
---|---|
创建日期 | 2022-11-05 |
Gitee工程和源码地址:才鲸嵌入式 / ARM-Cortex-M3从汇编到C_从Boot到应用教程
CSDN文章阅读地址:ARM Cortex-M3从汇编到C,从Boot到应用的教程
Bilibili视频讲解地址(待完成):才鲸嵌入式
工程名称 | 作用 |
---|---|
01_Hello_world | 使用Keil的模拟器在虚拟终端输出Hello world |
02_Keil_boot_comments | Keil自带汇编boot的注释 |
03_Self_assembler_boot | 自行实现汇编boot |
04_Uart_loopback | 串口收发回环 |
… | … |
本仓库计划实现的内容:
本仓库面向的目标读者:
不适用本仓库的读者:
M3由ARM公司于2004年推出,至今仍是很多单片机芯片使用的内核,如STM32F10x系列。
ST公司于2007年首次使用ARM公司的内核,产品是F1,随后凭借其简洁易用的软件开发包逐渐发展出著名的STM32系列,累计出货量近百亿颗。
M3属于ARMv7架构,ARMv7是ARM11之后的版本。
M3属于Cortex系列,该系列有三类:A、R和M,比如熟悉的Cortex-A9。
M3内核仅33000门。
M3不能使用ARM指令集,而是使用Thumb或Thumb-2指令集。
参考资料:
MCU缺货涨价后的国产化浪潮(三):全球 MCU 市场高度集中,多因素共振加速国产替代 文章里也列出了全球和国内的MCU厂商和所有嵌入式的行业。
ARM CORTEX-M3简介
ARM Cortex-M3
ARM发布适于高性能、低成本应用的Cortex-M3处理器
芯片(IP核)手册 ARM Cortex-M3 Processor Technical Reference Manual
ARMv7-M指令集手册 ARMv7-M Architecture Reference Manual
以上两个文档描述了M3内核的通用寄存器和指令集,其中:
不使用官方自带的boot,自己用汇编写boot并引导C语言main(),成功打印输出,并编写注释,看到系统启动过程中的每一步。
工程和源码在本文档同级目录\src\03_Self_assembler_boot\下
参考网址:
实际工程中将汇编分成了好几个文件,下面列出的源码先将汇编部分都放在一起,便于观看,实际使用的文件在工程的源码中列出:
; 使用Keil自动生成时,也可用纯C写Boot相关的配置
INCLUDE YOUR_CONFIG.INC ; #include "YOUR_NAME.INC" 包含头文件
; 用户自定义宏定义
YOUR_CONFIG1 EQU 0x01 ; 类似于#define宏定义,用不同的配置选项配置程序
YOUR_CONFIG2 EQU 0x01
PRESERVE8 ; 指定当前文件要求堆栈八字节对齐
THUMB ; 使用THUMB指令集,不使用ARM指令集
; 定义堆,堆是malloc主动分配内存的位置
Heap_Size EQU 0 ; Heap_Size是MDK指定的堆空间长度名称;不用malloc分配的堆没什么用,所以长度设置为0
IF Heap_Size != 0 ; IF ELSE ENDIF和同名宏定义的含义类似
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ; 申明开辟名为HEAP的内存,不写入值初始化,可读可写,2^3 8字节对齐
EXPORT __heap_base ; MDK指定的名称,堆起始地址位置
EXPORT __heap_limit ; MDK指定的名称,堆结束地址位置
__heap_base
Heap_Mem SPACE Heap_Size ; 开始分配指定长度的内存
__heap_limit
ENDIF
; 定义栈,栈是为全局变量自动分配空间的位置
Stack_Size EQU (4096) ; Stack_Size是MDK指定的栈空间长度名称
AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 申明开辟名为STACK的内存,不写入值初始化,可读可写,2^3 8字节对齐
EXPORT __stack_limit ; MDK指定的名称,栈起始地址位置
EXPORT __initial_sp ; MDK指定的名称,栈结束地址位置
__stack_limit
Stack_Mem SPACE Stack_Size ; 开始分配指定长度的一片连续的内存
__initial_sp
AREA RESET, DATA, READONLY ; 定义数据段,名字为RESET;上电后首先运行的函数地址
EXPORT __Vectors ; 输出ARM CMSIS中需要用到的一些标号,__Vectors函数在后续定义
EXPORT __Vectors_End
EXPORT __Vectors_Size
EXPORT Default_Interrupt_Handler ; 中断入口
IMPORT __initial_sp
; 申明异常和中断入口
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; -14 NMI Handler
DCD HardFault_Handler ; -13 Hard Fault Handler
DCD MemManage_Handler ; -12 MPU Fault Handler
DCD BusFault_Handler ; -11 Bus Fault Handler
DCD UsageFault_Handler ; -10 Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; -5 SVCall Handler
DCD DebugMon_Handler ; -4 Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; -2 PendSV Handler
DCD SysTick_Handler ; -1 SysTick Handler
; Interrupts
DCD Interrupt_Handler_0
; …… 省略其它中断 ……
DCD Interrupt_Handler_45 ; BCD:分配存储单元
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
; 类似于宏定义函数
MACRO ; 宏定义函数的开始
Set_Default_Handler $Handler_Name ; 前一个时宏定义函数的名字,后面是要操作的对象
$Handler_Name PROC ; 定义子程序
EXPORT $Handler_Name [WEAK] ; 输出函数名;[WEAK]虚函数,可定义可不定义
B . ; 死循环
ENDP ; 子程序定义结束
MEND ; 宏定义函数结束
AREA |.text|, CODE, READONLY ; 定义.text代码段,只读
; 申明默认的异常和中断处理函数
Set_Default_Handler Reset_Handler
Set_Default_Handler NMI_Handler
Set_Default_Handler HardFault_Handler
Set_Default_Handler MemManage_Handler
Set_Default_Handler BusFault_Handler
Set_Default_Handler UsageFault_Handler
Set_Default_Handler SVC_Handler
Set_Default_Handler DebugMon_Handler
Set_Default_Handler PendSV_Handler
Set_Default_Handler SysTick_Handler
Set_Default_Handler Default_Interrupt_Handler
Set_Default_Handler Interrupt_Handler_0
; …… 省略其它中断 ……
Set_Default_Handler Interrupt_Handler_45
; 各种程序
; Reset_Handler是板子上电后首先执行的位置,它由异常中断的跳转来实现
Reset_Handler PROC ; 程序名 PROC 程序内容 ENDP 程序结束
EXPORT Reset_Handler
IMPORT __hardwareInit ; 自己编写的C程序,在里面初始化各种硬件配置
IMPORT __main ; main()函数入口
BL __hardwareInit ; 调用初始化硬件的汇编函数
BL __main ; 跳转到main()函数
BL cpuStall ; 程序退出后一直死循环
ALIGN
ENDP
cpuStall PROC
EXPORT cpuStall
B . ; 死循环
ENDP
END ; 通知编译器已经到了该源文件的结尾了
__hardwareInit PROC
EXPORT __hwInitialize
PUSH {R0,R1,R2,R3,LR} ; 压栈
; 配置GPIO输出
; 配置PLL系统主频,将主频从晶振原有的频率提高到实际的工作频率
; 初始化串口
; 初始化其它外设
POP {R0,R1,R2,R3,LR} ; 弹栈
BX LR ; 跳转到LR寄存器里的地址执行,也就是跳转回被调用的地方
ALIGN
ENDP
; 其它的.inc汇编头文件中要做的事
; 定义各个硬件模块的地址
; 定义所有中断号