目录
STM32 分类
STM8 和 STM32 分类
STM32 命名方法
STM32F103RCT6
寻找 IO 的功能
存储器映射
存储器 Block0 内部区域功能划分
存储器 Block1 内部区域功能划分
存储器 Block2 内部区域功能划分
寄存器映射
stm32f10x_it.c、 stm32f10x_conf.h 和 system_stm32f10x.c 文件
“assert_param”宏
启动文件简介
FLASH:我们的程序就放在这里。 |
0x0800 0000 ~ 0x0807 FFFF (512KB) |
0x0800 0000 ~ 0x0803FFFF (256KB)
STM32,从字面上来理解, ST 是意法半导体, M 是 Microelectronics 的缩写, 32 表示32 位,合起来理解, STM32 就是指 ST 公司开发的 32 位微控制器。
ARM 公司推出了其全新的基于 ARMv7 架构的 32 位 CortexM3 微控制器内核。紧随其后, ST(意法半导体)公司就推出了基于 Cortex-M3 内核的MCU—STM32。 STM32 凭借其产品线的多样化、极高的性价比、简单易用的库开发方 式,迅速在众多 Cortex-M3 MCU 中脱颖而出,成为最闪亮的一颗新星。 STM32 一上市就 迅速占领了中低端 MCU 市场,
STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、 I2C、 SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。
STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、 M3、 M4和 M7 这几种,每个内核又大概分为主流、高性能和低功耗。
单纯从学习的角度出发,可以选择 F1和 F4, F1代表了基础型,基于 Cortex-M3内核,主频为 72MHZ, F4 代表了高性能,基于 Cortex-M4 内核,主频 180M。
之于 F1, F4(429 系列以上)除了内核不同和主频的提升外,升级的明显特色就是带了 LCD 控制器和摄像头接口,支持 SDRAM,这个区别在项目选型上会被优先考虑。 但是从大学教学和用户初学来说,还是首选 F1 系列,目前在市场上资料最多,产品占有量最多的就是 F1 系列的 STM32。
CPU 位数 | 内核 | 系列 | 描述 |
32 | Cortex-M0 | STM32-F0 | 入门级 |
STM32-L0 | 低功耗 | ||
Cortex-M3 | STM32-F1 | 基础型,主频 72M | |
STM32-F2 | 高性能 | ||
STM32-L1 | 低功耗 | ||
Cortex-M4 | STM32-F3 | 混和信号 | |
STM32-F4 | 高性能,主频 180M | ||
STM32-L4 | 低功耗 | ||
Cortex-M7 | STM32-F7 | 高性能 | |
8 |
超级版 6502 |
STM8S | 标准系列 |
STM8AF | 标准系列的汽车应用 | ||
STM8AL | 低功耗的汽车应用 | ||
STM8L | 低功耗 |
STM32F103ZET6/STM32F103RCT6 | |
家族 | STM32 表示 32bit 的 MCU |
产品类型 | F 表示基础型 |
具体特性 | 基础型 |
引脚数目 | Z 表示 144pin,其他常用的为 C 表示 48, R 表示 64, V 表示 100, Z 表示 144, B 表示 208, N 表示 216 |
FLASH 大小 | E 表示 512KB,其他常用的为 C 表示 256, E 表示 512, I 表示 2048 |
封装 | T 表示 QFP 封装,这个是最常用的封装 |
温度 | 6 表示温度等级为 A : -40~85° |
32bit 的 MCU。64引脚,256kb flash ,QFP 封装
引脚分类 | 引脚说明说明 |
电源 | (VBAT)、 (VDD VSS)、 (VDDA VSSA)、 (VREF+ VREF-)等 |
晶振 IO | 主晶振 IO, RTC 晶振 IO |
下载 IO | 用于 JTAG 下载的 IO: JTMS、 JTCK、 JTDI、 JTDO、 NJTRST |
BOOT IO | BOOT0、 BOOT1,用于设置系统的启动方式 |
复位 IO | NRST,用于外部复位 |
上面 5 部分 IO 组成的系统我们也叫做最小系统 | |
GPIO | 专用器件接到专用的总线,比如 I2C, SPI, SDIO, FSMC, DCMI 这些总线的器件需要接到专用的普通的元器件接到 GPIO,比如蜂鸣器, LED,按键等元器件用普通的GPIO |
在使用的时候,有两个官方资料我们会经常用到,一个是参考手册(英文叫 Reference manual),另外一个是数据手册(英文叫 Data Sheet)。
手册 | 主要内容 | 说明 |
参考手册 | 片上外设的功能说 明和寄存器描述 |
对片上每一个外设的功能和使用做了详细的说明,包 含寄存器的详细描述。编程的时候需要反复查询这个 手册。 |
数据手册 | 功能概览 | 主要讲这个芯片有哪些功能,属于概括性的介绍。芯 片选型的时候首先看这个部分。 |
引脚说明 | 详细描述每一个引脚的功能,设计原理图的时候和写 程序的时候需要参考这部分。 |
|
内存映射 | 讲解该芯片的内存映射,列举每个总线的地址和包含 有哪些外设。 |
|
封装特性 | 讲解芯片的封装,包含每个引脚的长度宽度等,我们 画 PCB 封装的时候需要参考这部分的参数。 |
STM32F103xC、 STM32F103xD和STM32F103xE增强型系列集成了FSMC模块。它具有4个片选输出,支持CF、 RAM、 PSRAM、 NOR和NAND。
STM32 芯片架构简图
AHB 到 APB 的桥
从 AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特
色外设。我们经常说的 GPIO、串口、 I2C、 SPI这些外设就挂载在这两条总线上,这个是我
们学习 STM32 的重点,就是要学会编程这些外设去驱动外部的各种设备。
在图 6-4中, 被控单元的 FLASH, RAM, FSMC和 AHB到 APB的桥(即片上外设) ,
这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候, 可以通过他们的地
址找到他们,然后来操作他们(通过 C 语言对它们进行数据的读和写) 。
在这 4GB 的地址空间中, ARM 已经粗线条的平均分成了 8 个块,每块 512MB,每个块也都规定了用途,
存储器功能分类
序号 | 用途 | 地址范围 |
Block 0 | Code | 0x0000 0000 ~ 0x1FFF FFFF(512MB) |
Block 1 | SRAM | 0x2000 0000 ~ 0x3FFF FFFF(512MB) |
Block 2 | 片上外设 | 0x4000 0000 ~ 0x5FFF FFFF(512MB) |
Block 3 | FSMC 的 bank1 ~ bank2 | 0x6000 0000 ~ 0x7FFF FFFF(512MB) |
Block 4 | FSMC 的 bank3 ~ bank4 | 0x8000 0000 ~ 0x9FFF FFFF(512MB) |
Block 5 | FSMC 寄存器 | 0xA000 0000 ~ 0xCFFF FFFF(512MB) |
Block 6 | 没有使用 | 0xD000 0000 ~ 0xDFFF FFFF(512MB) |
Block 7 | Cortex-M3 内部外设 | 0xE000 0000 ~ 0xFFFF FFFF(512MB) |
在这 8个 Block里面,有 3个块非常重要,也是我们最关心的三个块。 Block0用来设计
成内部 FLASH, Block1 用来设计成内部 RAM, Block2 用来设计成片上的外设,
块 | 用途说明 | 地址范围 |
Block0 | 预留 | 0x1FFE C008 ~ 0x1FFF FFFF |
选项字节: 用于配置读写保护、BOR 级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。当芯片不小心被锁住之后,我们可以从RAM 里面启动来修改这部分相应的寄存器位。 | 0x1FFF F800 - 0x1FFF F80F | |
系统存储器:里面存的是 ST出厂时烧 写 好 的 isp 自 举 程 序 ( 即Bootloader),用户无法改动。串口下载的时候需要用到这部分程序。 | 0x1FFF F000- 0x1FFF F7FF | |
预留 | 0x0808 0000 ~ 0x1FFF EFFF | |
FLASH:我们的程序就放在这里。 |
0x0800 0000 ~ 0x0807 FFFF (512KB) | |
预留 | 0x0008 0000 ~ 0x07FF FFFF | |
取决于 BOOT引脚,为 FLASH、系统存储器、 SRAM 的别名。 | 0x0000 0000 ~ 0x0007 FFFF |
块 | 用途说明 | 地址范围 |
Block1 | 预留 | 0x2001 0000 ~ 0x3FFF FFFF |
SRAM 64KB | 0x2000 0000 ~0x2000 FFFF |
储存器 Block2 内部区域功能划分
Block2 用于设计片内的外设,根据外设的总线速度不同, Block 被分成了 APB 和 AHB两部分,其中 APB 又被分为 APB1 和 APB2,
块 | 用途说明 | 地址范围 |
Block2 | APB1 总线外设 | 0x4000 0000 ~ 0x4000 77FF |
APB2 总线外设 | 0x4001 0000 ~ 0x4001 3FFF | |
AHB 总线外设 | 0x4001 8000 ~ 0x5003 FFFF |
存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
比如,找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x4001 0C0C, ODR 寄存器是 32bit,低 16bit有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB 的 16 个 IO 都输出高电平,具体见代码1。 通过绝对地址访问内存单元
1 // GPIOB 端口全部输出 高电平
2 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;
/*
0x4001 0C0C在我们看来是 GPIOB端口 ODR的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针,即(unsigned int *)0x4001 0C0C,然后再对这个指针进行 * 操作。
*/
1. 总线基地址
表格 6-5 总线基地址
总线名称 | 总线基地址 | 相对外设基地址的偏移 |
APB1 | 0x4000 0000 | 0x0 |
APB2 | 0x4001 0000 | 0x0001 0000 |
AHB | 0x4001 8000 | 0x0001 8000 |
2. 外设基地址
外设 GPIO 基地址
外设名称 | 外设基地址 | 相对 APB2 总线的地址偏移 |
GPIOA | 0x4001 0800 | 0x0000 0800 |
GPIOB | 0x4001 0C00 | 0x0000 0C00 |
GPIOC | 0x4001 1000 | 0x0000 1000 |
GPIOD | 0x4001 1400 | 0x0000 1400 |
GPIOE | 0x4001 1800 | 0x0000 1800 |
GPIOF | 0x4001 1C00 | 0x0000 1C00 |
GPIOG | 0x4001 2000 | 0x0000 2000 |
GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以 GPIOB 端口为例,来说明 GPIO 都有哪些寄存器,具体见表格。
GPIOB 端口的 寄存器地址列表
寄存器名称 | 寄存器地址 | 相对 GPIOB 基址的偏移 |
GPIOB_CRL | 0x4001 0C00 | 0x00 |
GPIOB_CRH | 0x4001 0C04 | 0x04 |
GPIOB_IDR | 0x4001 0C08 | 0x08 |
GPIOB_ODR | 0x4001 0C0C | 0x0C |
GPIOH_BSRR | 0x4001 0C10 | 0x10 |
GPIOH_BRR | 0x4001 0C14 | 0x14 |
GPIOH_LCKR | 0x4001 0C18 | 0x18 |
可参考《STM32F10xx 参考手册》
startup_stm32f10x_hd.s
启动文件,系统上电后第一个运行的程序,由汇编编写, C 编程用的比较少,
core_cm3.c 和 core_cm3.h 两个文件内核相关文件
启动文件 | 区别 |
startup_stm32f10x_ld.s | ld: low-density 小容量, FLASH 容量在 16-32K 之间 |
startup_stm32f10x_md.s | md: medium-density 中容量, FLASH 容量在 64-128K 之间 |
startup_stm32f10x_hd.s | hd: high-density 中容量, FLASH 容量在 256-512K 之间 |
startup_stm32f10x_xl.s | xl: 超大容量, FLASH 容量在 512-1024K 之间 |
以上四种都属于基本型,包括 STM32F101xx、 STM32F102xx、 STM32F103xx 系列 | |
startup_stm32f10x_cl.s | cl:connectivity line devices 互联型,特指 STM32F105xx 和 STM32F107xx 系列 |
startup_stm32f10x_ld_vl.s | vl:value line devices 超值型系列,特指 STM32F100xx 系列 |
startup_stm32f10x_md_vl.s | |
startup_stm32f10x_hd_vl.s |
Stm32f10x.h
这个头文件实现了片上外设的所以寄存器的映射,是一个非常重要的头文件,在内核中与之想对应的头文件是 core_cm3.h。
system_stm32f10x.c
system_stm32f10x.c 文件实现了 STM32 的时钟配置, 操作的是片上的 RCC 这个外设。系统在上电之后,首选会执行由汇编编写的启动文件, 启动文件中的复位函数中调用的SystemInit函数就在这个文件里面定义。调用完之后,系统的时钟就被初始化成 72M。 如果后面我们需要重新配置系统时钟,我们就可以参考这个函数重写。为了维持库的完整性,我们不会直接在这个文件里面修改时钟配置函数。
还有一个很特别的 misc.c 文件,这个文件提供了外设对内核中的NVIC(中断向量控制器)的访问函数,在配置中断时,我们必须把这个文件添加到工程中
stm32f10x_it.c:这个文件是专门用来编写中断服务函数的,在修改前,这个文件已经定义了一些系统异常(特殊中断)的接口,其它普通中断服务函数由自己添加。但是我们怎么知道这些中断服务函数的接口如何写?是不是可以自定义呢?答案当然不是,这些都可以在汇编启动文件中找到.
system_stm32f10x.c:这个文件包含了STM32芯片上电后初始化系统时钟、扩展外部存储器用的函数,例如我们前两章提到供启动文件调用的“SystemInit”函数,用于上电后初始化时钟,该函数的定义就存储在 system_stm32f10x.c 文件。 STM32F103 系列的芯片,调用库的这个 SystemInit 函数后,系统时钟被初始化为 72MHz,如有需要可以修改这个文件的内容,设置成自己所需的时钟频率,但鉴于保持库的完整性,我们在做系统时钟配置的时候会另外重写时钟配置函数。
stm32f10x_conf.h: 这个文件被包含进 stm32f10x.h 文件。 当我们使用固件库编程的时候,如果需要某个外设的驱动库,就需要包含该外设的头文件: stm32f10x_ppp.h,包含一个还好,如果是用了多外设,就需要包含多个头文件,这不仅影响代码美观也不好管理,现我们用一个头文件 stm32f10x_conf.h 把这些外设的头文件都包含在里面,让这个配置头文件统一管理这些外设的头文件,我们在应用程序中只需要包含这个配置头文件即可,我们又知道这个头文件在 stm32f10x.h 的最后被包含,所以最终我们只需要包含 stm32f10x.h这个头文件即可,非常方便。 Stm32f10x_conf.h见代码清单 10-2。 默认情况下是所以头文件都被包含,没有被注释掉。我们也可以把不要的都注释掉,只留下需要使用的即可。
stm32f10x_conf.h 这个文件还可配置是否使用“断言” 编译选项
在 ST 标准库的函数中,一般会包含输入参数检查,即上述代码中的“assert_param”宏,当参数不符合要求时,会调用“assert_failed”函数,这个函数默认是空的。实际开发中使用断言时,先通过定义 USE_FULL_ASSERT 宏来使能断言,然后定义“assert_failed”函数,通常我们会让它调用 printf函数输出错误说明。 使能断言后,程序运行时会检查函数的输入参数,当软件经过测试,可发布时, 会取消 USE_FULL_ASSERT 宏来去掉断言功能, 使程序全速运行。
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
1、 初始化堆栈指针 SP=_initial_sp
2、 初始化 PC 指针=Reset_Handler
3、 初始化中断向量表
4、 配置系统时钟
5、 调用 C 库函数_main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界
《CM3 权威指南 CnR2》第四章:指令集。会涉及到 ARM的汇编指令和 Cortex内核的指令,