1、本书配套的仿真器为 Fire-Debugger,遵循 ARM 公司的 CMSIS-DAP 标准,支持所有基于 Cortex-M
内核的单片机,常见的 M3、M4 和 M7 都可以完美支持。Fire-Debugger 支持下载和在线仿真程序,支持 XP/WIN7/WIN8/WIN10 这四个操作系统,免驱,不需要安装驱动即可使用,支持 KEIL 和 IAR 直接下载,非常方便。
2、野火 STM32 开发板的配置是:F1 选 512K,F4 选 1M。
3、现在的市场产品竞争越来越激烈,对成本极其敏感,相应地对 MCU 的性能要求也更苛刻:更多功能,更低功耗,易用界面和多任务。
4、基于这样的市场需求,ARM 公司推出了其全新的基于 ARMv7 架构的 32 位 Cortex-M3 微控制器内核。紧随其后,ST(意法半导体)公司就推出了基于 Cortex-M3 内核的 MCU——STM32。
5、STM32 属于一个微控制器,自带了各种常用通信接口,比如 USART、I2C、SPI 等,可接非常多的传感器,可以控制很多的设备。现实生活中,我们接触到的很多电器产品都有 STM32 的身影,比如智能手环,微型四轴飞行器,平衡车、移动 POST 机,智能电饭锅,3D 打印机等等。
6、STM32 有很多系列,可以满足市场的各种需求,从内核上分有 Cortex-M0、M3、M4 和 M7 这几种,每个内核又大概分为主流、高性能和低功耗。
7、之于 F1,F4(429 系列以上)除了内核不同和主频的提升外,升级的明显特色就是带了 LCD 控制器和摄像头接口,支持 SDRAM,这个区别在项目选型上会被优先考虑。
8、根据项目的具体需求先大概选择哪类内核的 MCU,
普通应用,不需要接大屏幕的一般选择 Cortex-M3 内核的 F1 系列,如果要追求高性能,需要大量的数据运算,且需要外接 RGB 大屏幕的则选择 Cortex-M4 内核的 F429 系列。明确了大方向之后,接下来就是细分选型,先确定引脚,引脚多的功能就多,价格也贵,具体得根据实际项目中需要使用到什么功能,够用就好。确定好了引脚数目之后再选择 FLASH 大小,相同引脚数的 MCU 会有不同的 FLASH 大小可供选择,这个也是根据实际需要选择,程序大的就选择大点的 FLASH,要是产品一量产,这些省下来的都是钱啊。有些月出货量以 KK(百万数量级)为单位的产品,不仅是 MCU,连电阻电容能少用就少用,更甚者连 PCB 的过孔的多少都有讲究。项目中的元器件的选型的水深的很,很多学问。
9、一句话概括:数据手册主要用于芯片选型和设计原理图时参考,参考手册主要用于在编程的时候查阅。
10、芯片四周是引脚,左下角的小圆点表示 1 脚,然后从 1 脚起按照逆时针的顺序排列(所有芯片的引脚顺序都是逆时针排列的)。开发板中把芯片的引脚引出来,连接到其他各种芯片上(比如传感器),然后在 STM32 上编程(实际就是通过程序控制这些引脚输出高电平或者低电平)来控制其他芯片工作,通过做实验的方式来学习 STM32 芯片的各个资源。
11、我们看到的 STM32 芯片是已经封装好的成品,主要由内核和片上外设组成。若与电脑类比,内核与外设就如同电脑上的 CPU 与主板、内存、显卡、硬盘的关系。
STM32F103 采用的是 Cortex-M3 内核,内核即 CPU,由 ARM 公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商 (SOC) 如 ST、TI、NXP 等,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如 GPIO、USART(串口)、I2C、SPI 等都叫做片上外设。
芯片(这里指内核,或者叫 CPU)和外设之间通过各种总线连接,其中驱动单元有 4 个,被动单元也有 4 个,具体见图 5‑4。为了方便理解,我们都可以把驱动单元理解成是 CPU 部分,被动单元都理解成外设。
12.1、 ICode 总线
ICode 中的 I 表示 Instruction,即指令。我们写好的程序经过编译之后都是一条条指令,存放在FLASH 中,内核要读取这些指令来执行程序就必须通过 ICode 总线,它几乎每时每刻都需要被使用,它是专门用来取指的。
12.2、 驱动单元
①、 DCode 总线
DCode 中的 D 表示 Data,即数据,那说明这条总线是用来取数的。我们在写程序的时候,数据有常量和变量两种,常量就是固定不变的,用 C 语言中的 const 关键字修饰,是放到内部的 FLASH当中的,变量是可变的,不管是全局变量还是局部变量都放在内部的 SRAM。因为数据可以被
Dcode 总线和 DMA 总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。
12.3 系统总线
系统总线主要是访问外设的寄存器,我们通常说的寄存器编程,即读写寄存器都是通过这根系统
总线来完成的。
12.4 DMA 总线
DMA 总线也主要是用来传输数据,这个数据可以是在某个外设的数据寄存器,可以在 SRAM,可以在内部的 FLASH。因为数据可以被 Dcode 总线和 DMA 总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。
被动单元
13.1 内部的闪存存储器
内部的闪存存储器即 FLASH,我们编写好的程序就放在这个地方。内核通过 ICode 总线来取里面的指令。
13.2 内部的 SRAM
内部的 SRAM,即我们通常说的 RAM,程序的变量,堆栈等的开销都是基于内部的 SRAM。内核通过 DCode 总线来访问它。
13.3 FSMC
FSMC 的英文全称是 Flexible static memory controller,叫灵活的静态的存储器控制器,是STM32F10xx 中一个很有特色的外设,通过 FSMC,我们可以扩展内存,如外部的 SRAM,NANDFLASH 和 NORFLASH。但有一点我们要注意的是,FSMC 只能扩展静态的内存,即名称里面的S:static,不能是动态的内存,比如 SDRAM 就不能扩展。
13.4 AHB 到 APB 的桥
从 AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特色外设。我们经常说的 GPIO、串口、I2C、SPI 这些外设就挂载在这两条总线上,这个是我们学习 STM32 的重点,就是要学会编程这些外设去驱动外部的各种设备。
14、存储器映射
在图 5‑4 中,被控单元的 FLASH,RAM,FSMC 和 AHB 到 APB 的桥(即片上外设),这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过 C 语言对它们进行数据的读和写)。
①、存储器映射
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配的,给存储器分配地址的过程就称为存储器映射,具体见图 5‑5。如果给存储器再分配一个地址就叫存储器重映射。
②、存储器区域功能划分
在这 4GB 的地址空间中,ARM 已经粗线条的平均分成了 8 个块,每块 512MB,每个块也都规定了用途,具体分类见表格 5‑1。每个块的大小都有 512MB,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。
在这 8 个 Block 里面,有 3 个块非常重要,也是我们最关心的三个块。Block0 用来设计成内部FLASH,Block1 用来设计成内部 RAM,Block2 用来设计成片上的外设,下面我们简单的介绍下这三个 Block 里面的具体区域的功能划分。
③、存储器 Block0 内部区域功能划分Block0 主要用于设计片内的 FLASH,我们使用的 STM32F103RCT6(MINI)的 FLASH 为 256KB,而 STM32F103VET6(指南者和拂晓)和 STM32F103ZET6(霸道)的 FLASH 都是 512KB,这三者都属于大容量。要在芯片内部集成更大的 FLASH 或者 SRAM 都意味着芯片成本的增加,往往
片内集成的 FLASH 都不会太大,ST 能在追求性价比的同时做到 512KB,实乃良心之举。Block内部区域的功能划分具体见表格 5‑2。
④、储存器 Block1 内部区域功能划分
Block1 用于设计片内的 SRAM。我们使用的 STM32F103RCT6(MINI)的 SRAM 为 48KB,STM32F103VET6(指南者和拂晓)和 STM32F103VET6(指南者)的 SRAM 都是 64KB,Block 内部区域的功能划分具体见表格 5‑3。
表格 5‑3 存储器 Block1 内部区域功能划分
⑤、 寄存器映射
我们知道,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。比如,我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x40010C0C(至于这个地址如何找到可以先跳过,后面我们会有详细的讲解),ODR 寄存器是 32bit,低 16bit 有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。现在我们通过 C 语言指针的操作方式,让 GPIOB的 16 个 IO 都输出高电平。
0x40010C0C 在我们看来是 GPIOB 端口 ODR 的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针,即 (unsigned int *)0x4001 0C0C,然后再对这个指针进行 * 操作。
15、STM32 的外设地址映射
片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1 挂载低速外设,APB2 和 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中 APB1 总线的地址最低,片上外设从这里开始,也叫外设基地址。
16、 外设寄存器
在 XX 外设的地址范围内,分布着的就是该外设的寄存器。以 GPIO 外设为例,GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把 GPIO 的引脚连接到 LED 灯的阴极,LED 灯的阳极接电源,然后通过 STM32 控制该引脚的电平,从而实现控制 LED 灯的亮灭。GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以GPIOB 端口为例。
这里的“复位”是将该位设置为 0 的意思,而“置位”表示将该位设置为 1;
C 语言的语法规定,结构体内变量的存储空间是连续的,其中 32位的变量占用 4 个字节,16 位的变量占用 2 个字节。
17、Listing 存放编译器编译时候产生的 c/汇编/链接的列表清单
Output 存放编译产生的调试信息、hex 文件、预览信息、封装库等
startup_stm32f10x_hd.s
启 动 文 件, 系 统 上 电 后 第 一 个 运 行 的 程 序, 由 汇 编 编 写,C 编 程 用 的 比 较少, 可 暂 时 不 管, 这 个 文 件 从 固 件 库 里 面 拷 贝 而 来, 由 官 方 提 供。 文 件 在这 个 目 录:STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm\startup_stm32f10x_hd.s。
tm32f10x.h
用户手动新建,用于存放寄存器映射的代码,暂时为空。
18、GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO被分成很多组,每组有 16 个引脚(也有可能少于 16 个),如型号为 STM32F103VET6 型号的芯片有 GPIOA、GPIOB、GPIOC 至 GPIOE 共 5 组 GPIO,该芯片一共 100 个引脚,其中 GPIO 引脚就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO 引脚接入到 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。最基本的输入功能是检测外部输入电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是否被按下。
推挽输出模式一般应用在输出电平为 0 和 3.3 伏而且需要高速切换开关状态的场合。在 STM32的应用中,除了必须用开漏模式的场合,我们都习惯使用推挽输出模式。
开漏输出一般应用在 I2C、SMBUS 通讯等需要“线与”功能的总线电路中。除此之外,还用在电平不匹配的场合,如需要输出 5 伏的高电平,就可以在外部接一个上拉电阻,上拉电源为 5 伏,并且把 GPIO 设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出 5 伏的电平。
例如我们使用 USART 串口通讯时,需要用到某个 GPIO 引脚作为通讯发送引脚,这个时候就可以把该 GPIO 引脚配置成 USART 串口复用功能,由串口外设控制该引脚,发送数据。
19.1 输入模式 (模拟/浮空/上拉/下拉)
在输入模式时,施密特触发器打开,输出被禁止,可通过输入数据寄存器 GPIOx_IDR 读取 I/O 状态。其中输入模式,可设置为上拉、下拉、浮空和模拟输入四种。上拉和下拉输入很好理解,默认的电平由上拉或者下拉决定。浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。模拟输入则用于 ADC 采集。
19.2 输出模式 (推挽/开漏)
在输出模式中,推挽模式时双 MOS 管以轮流方式工作,输出数据寄存器GPIOx_ODR 可控制 I/O输出高低电平。开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O 输出高阻态或低电平。输出速度可配置,有 2MHz/10MHz/50MHz 的选项。此处的输出速度即 I/O 支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。在输出模式时施密特触发器是打开的,即输入可用,通过输入数据寄存器 GPIOx_IDR 可读取 I/O的实际状态。
19.3、.3 复用功能 (推挽/开漏)
复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器 GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取 I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号。通过对 GPIO 寄存器写入不同的参数,就可以改变 GPIO 的工作模式,再强调一下,要了解具体寄存器时一定要查阅《STM32F10X-中文参考手册》中对应外设的寄存器说明。在 GPIO 外设中,控制端口高低控制寄存器 CRH 和 CRL 可以配置每个 GPIO 的工作模式和工作的速度,每 4 个位控制一个 IO,CRH 控制端口的高八位,CRL 控制端口的低 8 位,具体的看 CRH 和 CRL 的寄存器描述。
20、以上所有的这些 LED 灯的阴极都是连接到 STM32 的 GPIO 引脚,阳极接到 3.3V 电源,同时每个灯都分别接了一个限流电阻。只要我们控制 GPIO 引脚的电平输出状态,即可控制 LED 灯的亮灭。若您使用的实验板 LED 灯的连接方式或引脚不一样,只需根据我们的工程修改引脚即可,程序的控制原理相同。我们的目标是把 GPIO 的引脚设置成推挽输出模式并且默认下拉,输出低电平,这样就能让 LED灯亮起来了。
21、启动文件在这里只是简要的介绍下,关于这个文件的详解请参考后面的《启动文件详解》章节。
名为“startup_stm32f10x_hd.s”的文件,它里边使用汇编语言写好了基本程序,当 STM32 芯片上电启动的时候,首先会执行这里的汇编程序,从而建立起 C 语言的运行环境,所以我们把这个文件称为启动文件。该文件使用的汇编指令是 Cortex-M3 内核支持的指令,可参考《Cortex-M3 权威指南》中指令集章节。
startup_stm32f10x_hd.s 文件由官方提供,一般有需要也是在官方的基础上修改,不会自己完全重写。该文件从 ST 固件库里面找到,找到该文件后把启动文件添加到工程里面即可。不同型号的芯片以及不同编译环境下使用的汇编文件是不一样的,但功能相同。
对于启动文件这部分我们主要总结它的功能,不详解讲解里面的代码,其功能如下:
• 初始化堆栈指针 SP;
• 初始化程序计数器指针 PC;
• 设置堆、栈的大小;
• 初始化中断向量表;
• 配置外部 SRAM 作为数据存储器(这个由用户配置,一般的开发板可没有外部 SRAM);
• 调用 SystemIni() 函数配置 STM32 的系统时钟。
• 设置 C 库的分支入口“__main”(最终用来调用 main 函数);
先去除繁枝细节,挑重点的讲,主要理解最后两点,在启动文件中有一段复位后立即执行的程序,代码见代码清单 7_2。在实际工程中阅读时,可使用编辑器的搜索 (Ctrl+F) 功能查找这段代码在文件中的位置,搜索 Reset_Handler 即可找到。
连接 LED 灯的 GPIO 引脚,是要通过读写寄存器来控制的,就这样空着手,如何控制寄存器。我们知道寄存器就是给一个已经分配好地址的特殊的内存空间取的一个别名,这个特殊的内存空间可以通过指针来操作。在编程之前我们要先实现寄存器映射,有关寄存器映射的代码都统一写在 stm32f10x.h 文件中。
关于配置系统时钟我们在后面再写。当我们不配置系统时钟时,STM32 会把 HSI 当作系统时钟,HSI=8M,由芯片内部的振荡器提供。
(全彩 LED 灯由 3 个 IO 引脚控制其颜色
• 对于 F103-指南者和 F103-霸道开发板,配置的是 PB0
我们把以上连接到 LED 灯的 GPIO 引脚配置成通用推挽输出,输出的速度为 10M。即:对于 PC2和 PB0 引脚,我们要配置 GPIO 的端口配置低寄存器:CRL,见图 7_11。CRL 中包含 0-7 号引脚,每个引脚占用 4 个寄存器位。MODE 位用来配置输出的速度,CNF 位用来配置各种输入输出模式。
23、设置完 GPIO 的引脚,控制电平输出,以为现在总算可以点亮 LED 了吧,其实还差最后一步。由于 STM32 的外设很多,为了降低功耗,每个外设都对应着一个时钟,在芯片刚上电的时候这些时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。STM32 的所有外设的时钟由一个专门的外设来管理,叫 RCC(reset and clockcontrol),RCC 在《STM32F10X-中文参考手册》的第六章。关于 RCC 外设中的时钟部分,我们在后面的章节《RCC—使用 HSE/HSI 配置》中有详细的讲解,这里我们暂时先了解下。所有的 GPIO 都挂载到 APB2 总线上,具体的时钟由 APB2 外设时钟使能寄存器 (RCC_ APB2ENR)来控制。
22、CMSIS 标准及库层次关系
因为基于 Cortex 系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异
却导致软件在同内核,不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的 Cortex 微
控制器软件的兼容性问题,ARM 与芯片厂商建立了 CMSIS 标准 (Cortex MicroController Software
Interface Standard)。
所谓 CMSIS 标准,实际是新建了一个软件抽象层。
CMSIS 标准中最主要的为 CMSIS 核心层,它包括了:
• 内核函数层:其中包含用于访问内核寄存器的名称、地址定义,主要由 ARM 公司提供。
• 设备外设访问层:提供了片上的核外外设的地址和中断定义,主要由芯片生产商提供。
可见 CMSIS 层位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层,可
以为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,这对软件的移植是
有极大的好处的。STM32 的库,就是按照 CMSIS 标准建立的。
24、我们写 STM32F1 的工程,必须用到其中的四个文件:core_cm3.h、core_cmFunc.h、corecmInstr.h、core_cmSimd.h,其它的文件是属于其它内核的,还有几个文件是 DSP 函数库使用的头文件。core_cM3.c 文件有一些与编译器相关条件编译语句,用于屏蔽不同编译器的差异。里面包含了一些跟编译器相关的信息,如:“__CC_ARM ”(本书采用的 RVMDK、KEIL),“GNUC ”(GNU
编译器)、“ICC Compiler”(IAR 编译器)。这些不同的编译器对于 C 嵌入汇编或内联函数关键字的
语法不一样,这段代码统一使用“__ASM、__INLINE”宏来定义,而在不同的编译器下,宏自动
更改到相应的值,实现了差异屏蔽。
较重要的是在 core_cm3.c 文件中包含了“stdint.h”这个头文件,这是一个 ANSI C 文件,是独立
于处理器之外的,就像我们熟知的 C 语言头文件“stdio.h”文件一样。位于 RVMDK 这个软件的
安装目录下,主要作用是提供一些类型定义。
这些新类型定义屏蔽了在不同芯片平台时,出现的诸如 int 的大小是 16 位,还是 32 位的差异。
所以在我们以后的程序中,都将使用新类型如 uint8_t 、uint16_t 等。
在稍旧版的程序中还经常会出现如 u8、u16、u32 这样的类型,分别表示的无符号的 8 位、16 位、
32 位整型。初学者碰到这样的旧类型感觉一头雾水,它们定义的位置在 Stm32F103xx.h 文件中。
建议在以后的新程序中尽量使用 uint8_t、uint16_t 类型的定义。
core_cm3.c 跟启动文件一样都是底层文件,都是由 ARM 公司提供的,遵守 CMSIS 标准,即所有
CM3 芯片的库都带有这个文件,这样软件在不同的 CM3 芯片的移植工作就得以简化。
在 Device 文件夹下的是具体芯片直接相关的文件,包含启动文件、芯片外设寄存器定义、系统时
钟初始化功能的一些文件,这是由 ST 公司提供的。
• system_stm32f1xx.c 文件
文件目录:\Drivers\CMSIS\ Device\ST\stm32f1xx\Source\Templates
这个文件包含了 STM32 芯片上电后初始化系统时钟、扩展外部存储器用的函数,例如我们前两
章提到供启动文件调用的“SystemInit”函数,用于上电后初始化时钟,该函数的定义就存储在system_stm32f1xx.c 文件。STM32F103 系列的芯片,调用库的这个 SystemInit 函数后,系统时钟被
初始化为 72MHz,如有需要可以修改这个文件的内容,设置成自己所需的时钟频率。
其中的“strartup_STM32F103xx.s”即为 STM32F103 芯片的启动文件,前面两章工程
中使用的启动文件就是从这里复制过去的,strartup_STM32F103xe.s(xe 适用于大容量产品) 可兼
容 F103 的 ve,ze,rc 芯片。如果使用其它型号的芯片,要在此处文件夹选择其他对应的启动文件。
stm32F103xx.h 文件
文件目录:\Drivers\CMSIS\Device\ST\stm32f1xx\Include
stm32F103xx.h 这个文件非常重要,是一个 STM32 芯片底层相关的文件。它是我们前两章自己定
义的“stm32F103xx.h”文件的完整版,包含了 STM32 中所有的外设寄存器地址和结构体类型定
义,在使用到 STM32 HAL 库的地方都要包含这个头文件。
STM32F1xx_HAL_Driver 文件夹下有 inc(include 的缩写)跟 src(source 的简写)这两个文件夹,
这里的文件属于 CMSIS 之外的的、芯片片上外设部分。src 里面是每个设备外设的驱动源程序,
inc 则是相对应的外设头文件。src 及 inc 文件夹是 ST 的 HAL 库的主要内容,甚至不少人直接认
为 ST 的 HAL 库就是指这些文件,可见其重要性。
在 src 和 inc 文件夹里的就是 ST 公司针对每个 STM32 外设而编写的库函数文件,每个外设对应一
个 .c 和 .h 后缀的文件。我们把这类外设文件统称为:stm32f1xx_hal_ppp.c 或 stm32f1xx_hal_ppp.h
文件,PPP 表示外设名称。如在上一章中我们自建的 stm32f1xx_hal_gpio.c 及 stm32f1xx_hal_gpio.h
文件,就属于这一类。
如针对模数转换 (ADC) 外设,在 src 文件夹下有一个 stm32f1xx_hal_adc.c 源文件,在 inc 文件夹
下有一个 stm32f1xx_hal_adc.h 头文件,若我们开发的工程中用到了 STM32 内部的 ADC,则至少
要把这两个文件包含到工程里。
stm32f1xx_it.c:这个文件是专门用来编写中断服务函数的,在我们修改前,这个文件已经定义了
一些系统异常 (特殊中断) 的接口,其它普通中断服务函数由我们自己添加。但是我们怎么知道
这些中断服务函数的接口如何写?是不是可以自定义呢?答案当然不是的,这些都有可以在汇编
启动文件中找到,在学习中断和启动文件的时候我们会详细介绍
stm32f1xx_hal_conf.h:这个文件被包含进 stm32f103xx.h 文件。STM32HAL 库支持所有 STM32F1
型号的芯片,但有的型号芯片外设功能比较多,所以使用这个配置文件根据芯片型号增减 ST 库
的外设文件, 另外时钟源配置也是在这里进行设置。
在 ST 的 HAL 库函数中,一般会包含输入参数检查,即上述代码中的“assert_param”宏,当参数
不符合要求时,会调用“assert_failed”函数,这个函数默认是空的。
实际开发中使用断言时,先通过定义 USE_FULL_ASSERT 宏来使能断言,然后定义“assert_failed”
函数,通常我们会让它调用 printf 函数输出错误说明。使能断言后,程序运行时会检查函数的输
入参数,当软件经过测试,可发布时,会取消 USE_FULL_ASSERT 宏来去掉断言功能,使程序全
速运行。
6 描述了 STM32 库各文件之间的调用关系,这个图省略了 DSP 核和实时系统层部分的文件
关系。在实际的使用库开发工程的过程中,我们把位于 CMSIS 层的文件包含进工程,除了特殊
系统时钟需要修改 system_stm32f1xx.c,其它文件丝毫不用修改,也不建议修改。
对于位于用户层的几个文件,就是我们在使用库的时候,针对不同的应用对库文件进行增删(用
条件编译的方法增删)和改动的文件。
常用官方资料
•《STM32F10X-中文参考手册》
这个文件全方位介绍了 STM32 芯片的各种片上外设,它把 STM32 的时钟、存储器架构、及各种
外设、寄存器都描述得清清楚楚。当我们对 STM32 的外设感到困惑时,可查阅这个文档。以直
接配置寄存器方式开发的话,查阅这个文档寄存器部分的频率会相当高,但这样效率太低了。
•《STM32 规格书》
本文档相当于 STM32 的 datasheet,包含了 STM32 芯片所有的引脚功能说明及存储器架构、芯
片外设架构说明。后面我们使用 STM32 其它外设时,常常需要查找这个手册,了解外设对应到
STM32 的哪个 GPIO 引脚。
•《Cortex™-M3 内核编程手册》
本文档由 ST 公司提供,主要讲解 STM32 内核寄存器相关的说明,例如系统定时器、NVIC 等核
外设的寄存器。这部分的内容是《STM32F10X-中文参考手册》没涉及到的内核部分的补充。相
对来说,本文档虽然介绍了内核寄存器,但不如以下两个文档详细,要了解内核时,可作为以下
两个手册的配合资料使用。
•《Cortex-M3 权威指南》
这个手册是由 ARM 公司提供的,它详细讲解了 Cortex 内核的架构和特性,要深入了解 Cortex-M
内核,这是首选,经典中的经典。这个手册也被翻译成中文,出版成书,我们配套的资料里面有
提供中文版的电子版。
•《STM32F103xG_User_Manual.chm》
这个就是本章提到的库的帮助文档,在使用库函数时,我们最好通过查阅此文件来了解 HAL 库
提供了哪些外设、函数原型或库函数的调用的方法。也可以直接阅读源码里面的函数的函数说
明。
25、STM32Cube 是一个全面的软件平台,包括了 ST 产品的每个系列。(如,STM32CubeF1
是针对 STM32F1 系列)。平台包括了 STM32Cube 硬件抽象层和一套的中间件组件 (RTOS,USB,
FS,TCP/IP,Graphics,等等)。
26、STM32CubeMX建工程
.1 选择 CPU 型号
F103-指南者,选 STM32F103VETx 型号,以 F103-指南者为例,我们直接在搜索框输入型号 STM32F103VE 最终确认 STM32F103VETx 为我们实际使用型号。
2 确认时钟源
进入工程后打开 RCC 选项,选择 Crystal/Ceramic Resonator,即使用外部晶振作为 HSE 的时钟源。
3配置 IO 口
这个工程简单控制一个 LED 周期闪烁,我们只需要配置一个 IO 即可,大家可以根据自己的开发
板情况来配置,这里选定控制红色 LED 的引脚 PB5,通过搜索框搜索可以定位 IO 口的引脚位置,
图中会闪烁显示,配置 PB5 的属性为 GPIO_Output。
4 配置系统时钟
开发板的外部晶振为 8MHz,我们填入 8;通道选择 HSE;PLLM 选择为/1;倍频系数 N 选择为 x9;
系统时钟选择 PLLCLK;系统时钟设定为 72Mz;APB1 分频系数选择为/2 即 PCLK1 位 36MHz;
APB2 分频系数选择为/1 即 PCLK2 位 72MHz。
5 进一步配置 IO 的具体属性
点击 Configuration,进入系统详细配置,选择 GPIO,配置 PB5 的默认电平,开漏输出,无上下
拉,低速模式。引脚标签为 LED_R。
6 配置工程属性
为了防止出现,烧录以后仿真器无法连接的情况,我们在 Pinout 里将 SYS 里面的 Debug 设置成
Serial Wire, 这样问题得到解决。
7 接着选择 Project Manager 选项,配置工程的名称,路径,使用的 IDE 工具,堆栈大小。注意不要
使用中文路径和工程名称。
7 生成代码
点击 GENERATE CODE, 在设定的路径成功生成代码,选着打开工程。
8 添加用户测试代码
打开工程后在 main 函数中的主循环插入用户代码,目的是让红色 LED 周期闪烁。
Keil编译出现:Error: C9555E: Failed to check out a license.LICENSE ERROR (R207(3): REGISTRY READ ERROR) 重新破解
.9 配置下载调试工具
配置下载工具为 CMSIS-DAP,程序下载完后复位并运行。
debug选CMSIS_DAP Debugger, Erase Sectors, Reset and Run勾选,port选为SW。
10 下载验证
把编译好的程序下载到开发板并复位,可看到板子上的红色灯会周期闪烁。
keil5 出现 No Debug Unit Device found
https://blog.csdn.net/weixin_44011829/article/details/103911729
27、新建工程
Doc 用来存放程序说明的文件,由写程序的人添加
Libraries 存放的是库文件
Listing 存放编译器编译时候产生的 C/汇编/链接的列表清单
Output 存放编译产生的调试信息、hex 文件、预览信息、封装库等
Project 用来存放工程
User 用户编写的驱动文件
Doc 工程说明.txt
Libraries CMSIS:里面放着跟 CM3 内核有关的库文件
STM32F1xx_HAL_Driver:STM32 外设库文件
Listing 暂时为空
Output 暂时为空
Project 暂时为空
User stm32f1xx_hal_conf.h:用来配置库的头文件
stm32f1xx_it.h
stm32f1xx_it.c:中断相关的函数都在这个文
件编写,暂时为空
main.c:main 函数文件
在 C/C++ 选项卡中添加处理宏及编译器编译的时候查找的头文件路径。如果头文件路径添
加有误,则编译的时候会报错找不到头文件。
在这个选项中添加宏,就相当于我们在文件中使用“#define”语句定义宏一样。在编译器中添加
宏的好处就是,只要用了这个模版,就不用源文件中修改代码。
• STM32F103xE 宏:为了告诉 STM32HAL 库,我们使用的芯片类型是 STM32 型号是大容量
的,使 STM32HAL 库根据我们选定的芯片型号来配置。