0x0800 0000 ~ 0x0803FFFF (256KB) STM32,从字面上来理解, ST 是意法半导体, M 是 Microelectronics 的缩写, 32 表示32 位,合起来理解, STM32 就是指 ST 公司开发的 32 位微控制器。2 Z `: A- z4 ^$ `" w4 X: q3 Y ARM 公司推出了其全新的基于 ARMv7 架构的 32 位 CortexM3 微控制器内核。紧随其后, ST(意法半导体)公司就推出了基于 Cortex-M3 内核的MCU—STM32。 STM32 凭借其产品线的多样化、极高的性价比、简单易用的库开发方 式,迅速在众多 Cortex-M3 MCU 中脱颖而出,成为最闪亮的一颗新星。 STM32 一上市就 迅速占领了中低端 MCU 市场, STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、 I2C、 SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。 * e) B! _5 ^7 g( S. d% ] STM32 分类; E9 R! H) p+ L3 N2 u STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、 M3、 M4和 M7 这几种,每个内核又大概分为主流、高性能和低功耗。 单纯从学习的角度出发,可以选择 F1和 F4, F1代表了基础型,基于 Cortex-M3内核,主频为 72MHZ, F4 代表了高性能,基于 Cortex-M4 内核,主频 180M。1 ^4 S! m+ _7 L7 c& v 之于 F1, F4(429 系列以上)除了内核不同和主频的提升外,升级的明显特色就是带了 LCD 控制器和摄像头接口,支持 SDRAM,这个区别在项目选型上会被优先考虑。 但是从大学教学和用户初学来说,还是首选 F1 系列,目前在市场上资料最多,产品占有量最多的就是 F1 系列的 STM32。7 d/ K: \1 \' c# { STM8 和 STM32 分类3 _9 D: I+ d0 ]7 h. q STM32 命名方法8 E# c# L; X, S/ ~5 V3 D& }) { 2 M" i9 i( y" o/ ^ STM32F103RCT6 # ~% j$ F, K/ d8 {3 @. D* ? 寻找 IO 的功能 在使用的时候,有两个官方资料我们会经常用到,一个是参考手册(英文叫 Reference manual),另外一个是数据手册(英文叫 Data Sheet)。 STM32F103xC、 STM32F103xD和STM32F103xE增强型系列集成了FSMC模块。它具有4个片选输出,支持CF、 RAM、 PSRAM、 NOR和NAND。5 L* m4 U1 L3 I0 i; r1 V ; R5 H s) }/ ^5 `( b& B STM32 芯片架构简图: ?, ~4 Q: w3 f1 y( d0 J$ ~ - q6 q7 x t* l7 [ " y3 B+ P. @. I( z+ Q+ i! @ AHB 到 APB 的桥2 C9 M7 `- x+ G3 J, _5 B& x 从 AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特 色外设。我们经常说的 GPIO、串口、 I2C、 SPI这些外设就挂载在这两条总线上,这个是我# G- J& }& h0 m 们学习 STM32 的重点,就是要学会编程这些外设去驱动外部的各种设备。, i9 i+ U6 k6 ~. y7 S 存储器映射! @4 C) r$ d& Z( y6 O { 在图 6-4中, 被控单元的 FLASH, RAM, FSMC和 AHB到 APB的桥(即片上外设) ,/ v2 d$ @6 i4 ^, t) `4 F) ~ 这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候, 可以通过他们的地% g k0 U' r, r# d* k B9 D6 \( D 址找到他们,然后来操作他们(通过 C 语言对它们进行数据的读和写) 。+ L, D2 v6 Z+ C; D. W 在这 4GB 的地址空间中, ARM 已经粗线条的平均分成了 8 个块,每块 512MB,每个块也都规定了用途,, a: D+ u$ t; I8 H: S 存储器功能分类 9 ^/ d4 I( Z" f 在这 8个 Block里面,有 3个块非常重要,也是我们最关心的三个块。 Block0用来设计 成内部 FLASH, Block1 用来设计成内部 RAM, Block2 用来设计成片上的外设,3 Q! S" L4 U, A {5 j# ?0 n/ R' ^ + B1 r3 d( l& l 存储器 Block0 内部区域功能划分6 X8 B i% V' ?. N* t& [4 J 存储器 Block1 内部区域功能划分" t J9 Z1 m" r" l+ D+ q8 z k# ] 储存器 Block2 内部区域功能划分& z+ E5 r3 @' ?4 F/ p Block2 用于设计片内的外设,根据外设的总线速度不同, Block 被分成了 APB 和 AHB两部分,其中 APB 又被分为 APB1 和 APB2, 存储器 Block2 内部区域功能划分( }# C% k( F7 O5 E- M ) }) b6 C, _/ a# ?6 ?5 R% I 寄存器映射 存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么? 在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。 比如,找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x4001 0C0C, ODR 寄存器是 32bit,低 16bit有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB 的 16 个 IO 都输出高电平,具体见代码1。 通过绝对地址访问内存单元 2 R+ ?* T; _/ s }* T8 B
1. 总线基地址 表格 6-5 总线基地址 ' Z$ W8 v9 ]8 I& f# F 2. 外设基地址 外设 GPIO 基地址' G; T% U$ P3 e3 @ 8 M6 g9 W j2 j3 b& F/ {$ g GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以 GPIOB 端口为例,来说明 GPIO 都有哪些寄存器,具体见表格。: d4 g! i1 \0 P j0 L GPIOB 端口的 寄存器地址列表 可参考《STM32F10xx 参考手册》 startup_stm32f10x_hd.s9 V% |8 f* { L 启动文件,系统上电后第一个运行的程序,由汇编编写, C 编程用的比较少,. u; p5 O& H: Z7 I( \$ ?4 A/ ~ core_cm3.c 和 core_cm3.h 两个文件内核相关文件" g( E# ^+ m' a Y3 H6 i( N Stm32f10x.h 这个头文件实现了片上外设的所以寄存器的映射,是一个非常重要的头文件,在内核中与之想对应的头文件是 core_cm3.h。# ^% L% w1 V5 y system_stm32f10x.c6 y% M: K& A' b- M: K system_stm32f10x.c 文件实现了 STM32 的时钟配置, 操作的是片上的 RCC 这个外设。系统在上电之后,首选会执行由汇编编写的启动文件, 启动文件中的复位函数中调用的SystemInit函数就在这个文件里面定义。调用完之后,系统的时钟就被初始化成 72M。 如果后面我们需要重新配置系统时钟,我们就可以参考这个函数重写。为了维持库的完整性,我们不会直接在这个文件里面修改时钟配置函数。9 ^$ e z% ]# C; } 还有一个很特别的 misc.c 文件,这个文件提供了外设对内核中的NVIC(中断向量控制器)的访问函数,在配置中断时,我们必须把这个文件添加到工程中 7 u! w9 [" O4 }) x4 c stm32f10x_it.c、 stm32f10x_conf.h 和 system_stm32f10x.c 文件 ' D3 q! ?' E. D. k 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 这个文件还可配置是否使用“断言” 编译选项6 x) y" q9 g { 7 k5 V! d( M# P7 e0 _ 1 y+ P- N( ^+ z; G o O) d “assert_param”宏# }6 Q. Z8 L2 }" k6 b) ~. `$ h8 e * `6 j, k& F+ z9 O/ ], ^, \ 在 ST 标准库的函数中,一般会包含输入参数检查,即上述代码中的“assert_param”宏,当参数不符合要求时,会调用“assert_failed”函数,这个函数默认是空的。实际开发中使用断言时,先通过定义 USE_FULL_ASSERT 宏来使能断言,然后定义“assert_failed”函数,通常我们会让它调用 printf函数输出错误说明。 使能断言后,程序运行时会检查函数的输入参数,当软件经过测试,可发布时, 会取消 USE_FULL_ASSERT 宏来去掉断言功能, 使程序全速运行。/ D6 ]/ i) \ {" r s+ e q+ U. M; W9 I+ Y 启动文件简介 C0 b7 m8 L- P! E 启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作: 1、 初始化堆栈指针 SP=_initial_sp 2、 初始化 PC 指针=Reset_Handler 3、 初始化中断向量表 4、 配置系统时钟 5、 调用 C 库函数_main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界0 _$ k4 k& Q6 a! b + \; @7 o( W" o3 v: A0 n8 ^8 }2 `; t' ~9 K 《CM3 权威指南 CnR2》第四章:指令集。会涉及到 ARM的汇编指令和 Cortex内核的指令,0 E/ u) X9 n9 \. e 9 H S# {7 l4 `' f4 \' Q2 J 8 _% Y; ], r8 q1 \: J' \ |
【经验分享】STM32F1 GPIO工作原理
【经验分享】STM32F0xx_DMA收发USART数据配置详细过程
【经验分享】STM32F1和STM32F4 区别
【经验分享】STM32F1系列之常用外设说明
【经验分享】STM32F1x系列——Flash 模拟 EEPROM
【经验分享】STM32F1在MDK下新建标准库函数工程
【经验分享】stm32f1的存储器与复位
【经验分享】stm32F1 us延时函数
【经验分享】STM32F1之定时器
【经验分享】STM32 system_stm32f10x.c文件分析