嵌入式系统学习的3条路线
路线差别
单片机入门(HAL)
简单、快速,实际上工作中涉及单片机编程时,也提倡使用HAL库。对于学习来说,HAL库封装了很多技术细节,对技术成长帮助不大。
比如,可能接触不到这些知识:
重定位、代码段数据段BSS段、位置无关码、相对跳转、绝对跳转、
设置栈、中断上下文、保存/恢复中断现场、ARM架构
这些知识,是单片机的核心,学习了它,有助于在RTOS领域发展。
单片机深入(基于寄存器)
抛开HAL库,从芯片手册开始,自己写出一切代码。
注意:工作中绝对不建议这样做,但是学习时,这才能学到更深刻的知识。
这些知识,也是后续学习RTOS、学习Linux的u-boot的必备知识。
不学单片机,直接上手Linux
如果有一定的硬件基础,或者对硬件操作不敢兴起,那么可以不学习单片机。
Linux驱动 = 面向对象的编程思想 + 良好的程序框架 + 硬件操作(这就跟单片机类似)
如果你要学习Linux驱动开发,单片机虽然是基础,但是也可以在学习驱动过程中掌握。
XIP的概念
XIP:eXecute In Place,本地执行。可以不将代码拷贝到内存,而直接在代码的存储空间运行。
嵌入式系统:支持多种设备启动
系统支持SPI FLASH启动,意味着可以运行SPI FLASH上的代码。
但是SPI FLASH不是XIP设备,CPU无法直接执行里面的代码。
所以,CPU如何执行SPI FLASH上的代码?
一上电,CPU执行的第1个程序、第1条指令在哪里?
ARM板子支持多种启动方式:XIP设备启动、非XIP设备启动等等。
比如:Nor Flash、SD卡、SPI Flash, 甚至支持UART、USB、网卡启动。
这些设备中,很多都不是XIP设备。
CPU无法直接运行非XIP设备的代码,为何可以从非XIP设备启动呢?
上电后,CPU运行的第1条指令、第1个程序,位于片内ROM中,它是XIP设备。
这个程序会执行必要的初始化,比如设置时钟,设置内存;
再从“非XIP设备”中把程序读到内存
最后启动这个程序
ARM芯片内部有很多部件,这是一个片上系统(System On Chip)
比如有:
- cpu
- rom
- ram
- memory controller ---- ddr
- sd/mmc controller ---- sd card
- spi controller ----- spi flash
- usb controller ----- usb storage device
- uart controller
- interrupt controller
b. 跟PC的类比
CPU ---- 单独的芯片
启动设备 ---- BIOS芯片
DDR ---- 单独的可拔插式模块
存储设备 ---- SATA硬盘,可拔插
usb controller …
嵌入式系统启动流程概述
主芯片内部有ROM,ROM程序协助从非XIP设备启动。
以SD卡启动为例。
而CPU只能运行XIP设备中的程序,ROM程序做什么?
ROM需要把SF卡上的程序读到内存里(片内RAM或是片外的DDR)。
ROM程序要做的事情:
a. 初始化硬件
初始化时钟,提高CPU、外设速度
初始化内存:DDR需要初始化才能使用
初始化其他硬件,比如看门狗、SD卡等
b. 从外设把程序复制到内存
b.1
支持那么多的启动方式,SD卡、SPI FLASH、USB DISK,
怎么选择?
通过跳线,选择某个设备;
或
通过跳线,选择一个设备列表,按列表顺序逐个尝试
或
不让客户选择,按固定顺序逐个尝试
Cortex-M3内核 & 芯片
MCU厂商,经ARM公司授权,添加不同的外设=各种xx32芯片
F1系统架构
4个主动单元(主动发起通信)+4个被动单元
- Cortex M3内核 DCode总线(D-Bus)
- Cortex M3内核 系统总线(S-Bus)
- 通用DMA1
- 通用DMA2
- 内部FLASH
- 内部SRAM
- FSMC
- AHB到APB的桥,它连接的所有APB外设
AHB:高级高性能总线
APB:高级外围总线
ICode总线直接连接Flash接口,不需要经过总线矩阵。
总线时钟频率:
- AHB:72MHz
- APB1:36MHz
- APB2:72MHz
STM32的寻址范围
32位的单片机可以有32根地址线(每根地址线有两种状态:导通或不导通)。
单片机内存地址访问的存储单元是按字节编址的(而不是bit)。
STM32寻址大小:232=4G(字节)
0x00000000 ~ 0xFFFFFFFF
存储器映射
存储器指可以存储数据的设备,本身没有地址信息,对存储器分配地址的过程称为存储器映射。
存储器功能划分
ST将4GB地址空间分成8个块。
寄存器映射
寄存器是单片机内部一种特殊的内存,可以实现对单片机各个功能的控制。
寄存器就是单片机内部的控制机构
寄存器是特殊的存储器,给寄存器地址命名的过程,就叫寄存器映射
直接操作寄存器地址
*(unsigned int *)(0x4001 0x080C) = 0XFFFF;
定义一个名字后再操作
#define GPIOA_ODR *(unsigned int *)(0x4001 080C)
GPIOA_ODR = 0XFFFF;
为了方便编写代码及使用,将寄存器地址分为三个部分:
- 总线基地址(BUS_BASE_ADDR)
- 外设基于总线基地址的偏移量(PERIPH_OFFSET)
- 寄存器相对于外设基地址的偏移量(REG_OFFSET)
寄存器地址 = 以上三个相加
使用结构体,可以很方便的完成对寄存器的映射
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
...
}GPIO_TypeDef;
GPIOA_BASE 0X4001 0800
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
GPIOA->ODR = 0xFFFF;
STM32寄存器分类
内核寄存器
- 内核相关寄存器:包含R0~R15,xPSR、特殊功能寄存器等。
- 中断控制寄存器:包含NVIC和SCB相关寄存器
- SysTick寄存器:包含CTRL、LOAD、VAL和CALIB四个寄存器
- 内存保护寄存器:可选功能,STM32F103没有。
- 调试系统寄存器
外设寄存器
- 包含GPIO、UART、IIC等寄存器
stm32f103xe.h主要组成部分
- 中断编号定义:定义IRQn_Type枚举类型,包含STM32F103内部所有中断编号(中断号),方便后续编写代码。
- 外设寄存器结构体类型定义:以外设为单位,使用结构体类型定义每个外设所有寄存器,方便寄存器映射。
- 寄存器映射:1.定义总线地址和外设基地址。2.使用外设结构体类型定义将外设基地址强制转换成结构体指针,完成寄存器映射。
- 寄存器位定义:定义外设寄存器每个功能位的位置及掩码。
- 外设判定:判断某个外设是否合法(即是否存在该外设)。
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
...
}GPIO_TypeDef;
#define GPIOA (GPIO_TypeDef *)(GPIOA_BASE)
GPIOA->ODR = 0xFFFF;