操作系统有两种,用MMU的和不用MMU的:
CPU有两种,带MMU的和不带MMU的:
在意法半导体微控制器页面可以看到ST公司有很多种芯片,涉及各种用途。
出货量最多的STM32系列是STM32F1系列,这个系列不带MMU,所以STM32不能运行Linux,但是可以运行RTOS。
所以学习方向有两个:
STM32F1系列芯片是一种单片微型计算机,芯片内部不光有CPU,很多外设也被封装到里面了。以下是芯片内的资源:
STM32F1C6的片内Flash/RAM大小为32k/10k,STM32F1C8的片内Flash/RAM大小为64k/20k。
STM32F103系列芯片采用Cortex-M3内核的CPU,而Cortex-M3内核是采用的ARMv7架构。
ARMv7架构是指用的什么指令集、寄存器多少个、地址总线多少位、冯诺依曼结构还是哈佛结构。Cortex-M3内核是指用什么样的电路来实现ARMv7架构,几级流水线、有没有分支预测。
注:冯诺依曼结构程序和数据都放在内存中。哈佛结构的程序和数据分开放,拥有独立的指令总线和数据总线。x86的CPU都采用冯诺依曼结构。大多数嵌入式芯片都是哈佛结构的。
附一张ARMv7架构与ARMv8架构的比较图:
编译命令可以查看GCC手册。
STM32有三种启动方式,可由跳线帽设置:
我们一般从Flash启动,当设置从Flash启动后,存储器会将0x00000000开始的一小段地址映射到Flash。换句话说,从0x00000000获取到的内容和从0x08000000获取到的内容是一样的。
CPU上电后会做下面两件事:
因为0x00000000开始的一小段地址已经被映射到了0x08000000,这里正是我们程序烧写的地址。也就是说,只有我们正确设置程序开头几个字节的内容,就可以初始化STM32的栈寄存器和PC寄存器。
链接脚本:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 5K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K
}
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
} >FLASH
.text :
{
. = ALIGN(4);
*(.text)
} >FLASH
.data :
{
. = ALIGN(4);
*(.data)
} >RAM
.bss :
{
. = ALIGN(4);
*(.bss)
} >RAM
}
在链接脚本里isr_vector段在最前面,也就是说isr_vector段的内容会被烧录到0x08000000。
看一下汇编文件:
.section .text
.type Reset_Handler, %function
Reset_Handler:
bl main
bx lr
Default_Handler:
b Default_Handler
.section .isr_vector,"a"
.type vectors, %object
vectors:
.word 0x20002800
.word Reset_Handler
isr_vector段开始的是两个值,第一个值0x20002800会被作为栈寄存器的值,Reset_Handler会被作为PC寄存器的初始值。换句话说,CPU会从Reset_Handler开始执行。Reset_Handler里面是一条跳转指令,跳转到了C语言的main函数中了。
STM32支持JTAG和SWD两种烧录/调试协议,JTAG协议需要20根引脚,SWD协议只需要4根引脚。因为SWD引脚少,所以我们常用SWD协议。
PC需要一个USB转SWD的转换器(烧录器、调试器),常见的有JLink、ST-Link、uLink、OpenJTAG。
PC上还需要一个烧录/调试软件,很多IDE中集成了烧录/调试功能,命令行烧录软件有:
这是ST官方提供了很多开发工具。
安装的时候会出现权限问题,以STM32CubeMX为例:
下载:stm32cubemx
解压后按照Readme.html中执行
sudo xattr -cr ./SetupSTM32CubeMX-6.4.0.app
双击执行文件,在系统偏好与设置->安全与隐私中会出现陌生java程序,点击运行。再次双击运行即可。
我们经常用OpenOCD烧录程序,但它不仅仅是个烧录软件,而是一个调试软件。烧录程序(写flash)仅是调试命令之一。
OpenOCD后会监听4444端口,我们用telnet链接4444端口,就可以执行调试命令。
调试的原理也很简单,设置个断点,然后打印寄存器或者内存的值查找问题。
常用调试命令就这几条:
halt | 停止运行 |
bp 0x8000a68 2 hw | 设置硬件断点 |
reg sp | 查看寄存器 |
mdb 0x200027d0 48 | 查看从0x200027d0开始的内存,大小48个字节 |
step | 单步运行 |
reset | 复位,重新运行 |
对了,OpenOCD只能在汇编层面调试,设置断点和单步运行都是指汇编指令。如果读者想在C语言层面调试,在C语言上打断点和单步,可以用gdb,OpenOCD也是支持gdb的。
CPU只认识指令集里的指令,伪指令最终都会转化成指令集里的指令。这说明伪指令是编译器的行为,与CPU无关。编译器定义这些伪操作、伪指令只是为了方便编程而已。
不同的编译器有自己的伪指令,我们常用GNU的汇编器,文档Using as里面详细介绍了GNU的汇编器所支持的伪指令。
在st官网能找到各种处理器内核使用的架构:Arm Cortex-M4概述
我们常用的Cortex-M3和Cortex-M4内核是Armv7-M和Armv7E-M架构的,在ARM官网关于该架构的手册《Armv7-M Architecture Reference Manual》详细介绍了该架构的指令集。