STM32入门指南
这个系列的文章是《一个嵌入式操作系统的实现》系列文章的基础。 操作系统是连接硬件和软件的中间接口。它一方面管理着系统的硬件资源分配和驱动,例如CPU、内存、输入输出设备等, 另一方面对用户的应用程序进行调度,提供方便可靠地接口供应用程序访问系统资源。而对处理器和计算机硬件结构的清楚认知是实现一个操作系统的基础。
本系列选用意法半导体的STM32F407系列的芯片作为研究对象,通过介绍MCU的工作机理、片上外设的访问方法,了解一个嵌入式系统的处理器。 在此基础之上,介绍常见的诸如串口、USB、以太网等各种不同的通信接口,驱动和访问EEPROM,SD卡,硬盘等存储设备,以了解常见外设的驱动方法。
STM32是目前被广泛使用的一种单片机。意法半导体提供了很多给力的图形化工具,可以自动生成一些代码。非常适合快速上手,以及产品的原型实现。 再加上正点原子等一些国内的论坛提供的各种各样手把手教程,为我们提供了很好的入门材料,参照例程一步一步的做就一定能够得到想要的功能。 但它们很少解释系统背后的设计理念,读者难免会产生很多疑问。查阅了一些手册和资料,也看了不少网络上的博文,我的疑惑逐渐得到了解释。 我想应该还有人纠结过类似的问题,在这里尝试以我所理解的方式予以解答,希望能有所帮助。不足之处还希望各位读者指出。
关于开发板,我们选用的是正点原子的探索者。主要是因为它的外设相对比较丰富,配套的资料比较多,可以参考的源码和文档比较详尽。 最早曾用过一段时间的iCore3,想着这块板子上有一颗FPGA,以后可能会扩展很多功能,但目前来看当时想的有点多。由于刚刚更换开发板, 一些例程和文章还没有来得及更新,请见谅。
关于开发环境,目前先在Windows环境下使用IDE入门,以后会在Linux下搭建一个完整的工具链。 程序的源码托管在了Github上。 原先iCore3开发板的代码托管在这里。
第一部分:MCU基础
在本部分中,我们先用μVision一步一步的新建了一个STM32的工程,并介绍了一些关于工程文件和源代码管理的个人习惯。然后分析了STM32的启动过程,解释从源代码到目标代码我们都需要做些什么工作。 我希望通过前两篇文章,能够对MCU的基本工作原理有所了解。
接下来我们开始编写代码控制STM32。首先介绍了STM32的时钟系统,然后通过GPIO驱动一个LED灯介绍MCU上外设的访问方法,最后利用外部中断的机制实现了通过按键控制MCU的方法。 时钟系统是操作任何MCU都必须了解的内容,它是MCU得以运行的基础。本质上,MCU对外部设备的输出就是控制对应引脚的高低电平,接受外设数据的输入则是捕获引脚的电平。 中断机制的存在,使得MCU可以及时的响应外设的各种触发信号,而不需要从软件上不断的查询,提高了效率。
基本上,清楚了系统时钟、外设访问方式,以及中断机制,我们就可以完全控制一款MCU了。所以,我也希望能够通过这部分的讲解入门。
新建一个STM32工程 | 本来想偷点懒,略过工程文件的建立不写。但后来发现,对于入门来说新建一个工程也可以有很多疑问。 比如说,有些教程中讲新建一个工程时,先建立了一堆目录(USER, SYSTEM, PRO等等),然后向这些目录里拷贝了很多文件。 那么这些目录和文件都是干什么的,每次新建工程的时候都要这么做吗? 新建了一个工程之后,除了我们添加的文件之外,系统还给自动生成了很多文件,这些文件都是做什么的? 可以随便删吗? 代码下载 |
STM32的启动过程 | 刚开始接触STM32的时候,拿到网上的例程长得都差不多,都有一个main函数,其中有一个while(1)的死循环。
在死循环之前做了各种初始化工作,在死循环中或者中断函数中完成主要的功能。 我就好奇main函数跟其它的函数也没什么不同,芯片上电后都做了什么就能够执行main函数里的内容了? 我们编译出来的代码烧到了哪里,芯片才能够找到并执行呢? 代码下载 |
STM32的系统时钟设置 | 网上的例程一般都是基于库函数的实现,在库函数中已经对芯片所用的时钟做了初始化配置。
如果是开发板附带的例程,我们就几乎不用考虑系统时钟的问题,因为官方提供的库或者例程中已经配置好了。 但是看了手册之后发现STM32的时钟是一个挺复杂的系统,我们需要考虑如何给系统提供时钟?什么样的时钟源比较稳定精确? 外部的晶振通常只有几兆或者十几兆的频率,怎么才能够给芯片提供一个一百多兆的总线时钟? 代码下载 |
通用IO端口驱动LED灯 | 例程中最多的就是如何驱动各种外设,我们也知道驱动外设的最根本方法就是写各种各样的寄存器。 但是当我们写一个寄存器的时候,MCU怎么就知道了所写的寄存器就是我们想要控制的设备? MCU与电路板上其它设备之间的通信最终都会归结到引脚的电平上,如何确定一个引脚是输入还是输出? 提到引脚的电平,就会牵扯到上拉、下拉、开漏、推挽等名词,这些名词都是什么意思? 代码下载 |
外部中断控制LED灯 | 中断是MCU响应外部事件的主要方式。MCU处理中断时需要经过三个步骤:1. 保存当前CPU运行现场;
2. 执行中断服务函数;3. 恢复CPU运行现场。 那么这里所说的CPU运行现场指的是什么?都需要保存些什么内容? MCU上的中断那么多,怎么确定一个中断的服务函数? 代码下载 |
第二部分:MCU进阶
在本部分中,我们介绍一些比较常用的技术。它们可以提高系统的运行效率和可靠性。
STM32的位映射空间 | "读-改-写"的过程很繁琐,而我们对于按位访问片上外设寄存器的需求却是很强烈的。为了提高处理器和代码效率, Cortex-M4中引入了一种称为位映射空间(Bit-Banding)的技术(一些资料中称它为位带技术,我只是觉得位带一词比较费解, 所以就造了一个位映射空间的名词)。 代码下载 |
直接内存访问-DMA | 进行DMA操作时,我们需要提供至少DMA请求信号、数据源地址、数据目标地址三个要素。在STM32中这三个要素具体应该如何提供? 同一时间可能有多个外设请求进行DMA操作,但是一次只能响应一个外设,为了防止各个外设之间竞争冲突,人们设计了一个握手机制, 在握手之后,DMA控制器就可以正确的把源地址中的数据传送到目标地址中。 DMA控制过程有三个部分:1. 从源地址中装载数据,2. 把装载的数据写到目标地址中,3. 控制计数寄存器自减以标识剩余传送数据长度。 代码下载 |
看门狗复位 | 在刚解除单片机的时候就听说过看门狗,当时听说它可以防止程序跑飞,就以为是一个很高大上的事物。
后来发现它本质上就是一个计时器,计数溢出后会导致系统重启,就觉得这是一个没用的东西。
因为,程序该跑飞还是会跑飞的,只是它能监测到而已,重启只是重新开始,并不能够恢复到跑飞之前的状态。 但现在开始看一些操作系统的东西,发觉它还是很有用的。因为操作系统的一个重要工作就是在各个进程之间切换来切换去的, 中间一旦出现错误,跑飞的可能性很大。 所以现在看来看门狗还是很有用的东西,应该有一个进程持续的喂狗。 代码下载 |
片上闪存保存参数 | 很多时候,我们需要保存一些参数。但是这些参数往往需要针对不同的系统环境有不同的取值,不适合硬编码。如果参数数量很少, 专门为它们安装一个存储器件又显得很浪费。实际上,我们完全可以用STM32的片上缓存中空闲的部分来保存这些参数。 代码下载 |
应用程序自编程 | 应用程序自编程(In-Application Programming, IAP)是一种在系统运行过程中实现对片上Flash的部分区域擦写,实现对系统的在线更新。 这样,在产品发布之后可以通过预留的通信接口实现固件的升级。 代码下载 |
第三部分:MCU通信
本部分介绍除了以太网之外的各种各样与MCU相关的通信内容。以太网参见第七部分。
串口通信(1) | 对于MCU而言,串口可以说是一个不可或缺的外设。
它是人们进行调试的一个主要手段,很多芯片都支持通过串口启动或者烧录程序。
在网上的很多教程中讲完GPIO以后就会介绍串口通信。
本文主要介绍STM32的USART,同时也会介绍一下串口通信相关的基础知识。 大二那会儿做飞思卡尔智能车的时候,第一次接触单片机,看到串口就提到了TTL电平、RS232、RS485等各种各样的标准或者说是名词, 当时就很晕,一个简单的串口通信至于弄出这么多名堂吗? 此外,从USART的全称Universal Synchronous Asynchronous Receiver Transmitter可以看出,串行通信有同步通信和异步通信之分, 这里的同步和异步指的是什么呢? 代码下载 |
串口通信(2) | 在上一节中,我们实现了串口的基本收发功能。为了提高系统运行效率,尽量减少中断次数和忙等待时间, 我们将在本节中通过DMA的形式实现串口的收发功能。 代码下载 |
SPI通信 | SPI是一种高速的、全双工、同步、串行的通信方式。它只需要四根信号线既可以实现通信,一般是TTL电平可以在芯片之间直接用引脚互联。 它是一种主从结构的通信形式,支持一主多从的连接方式,但同一时间主设备只能与一个从设备通信。具体与哪个从设备通信需要由片选信号来决定。 代码下载 |
I2C通信 | I2C就是IIC或者I2C,是一种半双工的、同步、串行通信方式。只需要SCL和SDA两个信号线即可实现通信。与SPI一样由主设备发起通信,
并控制时钟信号。I2C没有片选信号线,而是通过寻址的方式选择从设备进行通信的。 很多人都说STM32的I2C有点问题,所以都是用GPIO模拟的。我以前也是这样做的,最近在想这么久了意法半导体应该做了一些改进吧。 于是又用硬件的I2C试了一下,没什么毛病。 代码下载 |
CAN通信 | CAN是一种非常成功的总线协议,它在汽车和工控的场景下得到了广泛的应用。它是一种串行的、可多主发送的总线。STM32F407为之提供了一个控制器bxCAN, 我们还需要提供一个物理层的芯片,把控制器输出的TTL电平,转换为CAN协议规定的电平方式。 代码下载 |
第四部分:片上计时器
STM32F407提供了丰富的计时器外设,最多可以支持12个16位和两个32位的计时器。 它们除了像内核计时器SysTick那样提供基本的计时和中断功能外, 还在此基础上扩展了控制输出PWM信号、输入信号捕捉等高级的功能。所以专门拿出来讲。
计时器之时基单元 | 时基单元是计时器工作的基础,它提供了基本的计时和中断触发功能。计时器的其它高级功能都是在它的基础上实现的。 本文通过时基单元的计时中断控制LED灯闪烁,来介绍其工作方式。 代码下载 |
计时器之PWM信号 | PWM全称Pulse Width Modulation,就是通过对脉冲宽度进行调制,以获得期望的波形。它在很多控制方面都有应用,最典型的就是电机控制。 STM32通过输出比较提供了一种简单方便高效地生成PWM信号的方法。本文利用PWM信号控制LED灯的强度。 代码下载 |
计时器之输入捕获 | 利用计时器的输入捕获功能我们可以测量特定事件发生的时间。虽然我们也可以用外部中断的形式考察引脚上的电平, 但这种方法会受到各种各样的干扰和限制,并不精确。在本文中,我们用输入捕获的方式分别测量了PWM信号的频率和占空比, 还是很精准的,虽然没有给出数据。 代码下载 |
第五部分:片上外设的常用功能
除了满足一些常见的通信形式,以及计时器的需要,STM32还为我们提供了一些复杂的接口用于扩展存储,挂接摄像头等复杂的外设。 此外,它还提供了多通道的ADC转换器,可以方便的处理一些模拟信号,驱动传感器。
ADC模拟数字转换 | ADC转换通常用于对模拟量的传感器数据进行采样。STM32提供了三个ADC转换器,可以通过软件或者硬件触发。 为了提高系统的效率,STM32定义了regular channel和inject channel,使得我们可以一次触发对多个通道进行采集, 借助DMA机制可以有效的降低处理器的负担。 代码下载 |
SD存储卡 | SD存储卡是目前移动设备上普遍使用的存储设备,一些设备中所说的TF卡是一种MicroSD卡。 这里简要介绍2006年发布的2.0版本的SD卡协议。 详细解释和实现了一个SD卡的枚举过程。 代码下载 |
数字摄像头接口DCMI | 在STM32的芯片上,提供了DCMI接口用于驱动数字摄像头。
DCMI是一种并行的同步数据接口,可以接收8位到14位的CMOS摄像头模块发出的数据。
本文将详细介绍DCMI的结构和使用方法,虽然该模块可以使用内嵌码的形式同步图像数据,
但本文中只简单的提到了,并不做过多的介绍。 在本文的最后参考正点原子的教程和示例驱动OV2640摄像头。 代码下载 |
静态存储控制器FSMC | MCU自带的FLASH和SRAM资源是十分有限的,当然对于嵌入式应用来说一般也就够用了,但避免不了一些大量消耗内存的应用, 比如说图像处理。对于这类对内存要求较高的应用,我们往往需要扩展一个FLASH或者SRAM。STM32提供的FSMC就是用来完成这项功能的。 代码下载 |
第六部分:常见板级外设
FSMC驱动8080接口液晶屏 | 显示器是计算机系统中一个重要的输出设备,本文根据探索者开发板的例程,介绍了一种8080接口的液晶屏驱动。 关于液晶屏上如何播放视频,显示比较复杂的桌面,以后会有一个专题详细介绍。 代码下载 |
第七部分:以太网方案
以太网之W5500 | W5500是一款全硬件TCP/IP协议芯片,集成了TCP/IP协议栈、10/100M的MAC和PHY,
只用一颗芯片就可以在我们的应用中扩展出网络连接。 其与宿主之间通过SPI通信,提供Socket网络编程API,驱动软件编写也很方便。 最多支持8个Socket,有32KB的缓存读写缓存空间,用户可以根据需要自由分配各个Socket的读写缓存大小。 代码下载 |
参考资源
- 《M3M4权威指南》
- STM32的Cortex-M4编程手册(PM0214)
- STM32F407参考手册Reference Manual(RM0090)
- STM32F407数据手册Datasheet
- iCore3电路原理图
- 探索者原理图
- 探索者的开发教程
- Modern Operating Systems, 3rd
火攻第十二
孙子曰:凡火攻有五:一曰火人,二曰火积,三曰火辎,四曰火库,五曰火队。
行火必有因,因必素具。发火有时,起火有日。时者,天之燥也;日者,月在箕、壁、翼、轸也。凡此四宿者,风气之日也。凡火攻,必因五火之变而应之: 火发于内,则早应之于外;火发而其兵静者,待而勿攻,极其火力,可从而从之,不可从则上。火可发于外,无待于内,以时发之,火发上风,无攻下风,昼风久,夜风止。 凡军必知五火之变,以数守之。
故以火佐攻者明,以水佐攻者强,水可以绝,不可以夺。
夫战胜攻取而不隋其功者凶,命曰“费留”。故曰:明主虑之,良将隋之,非利不动,非得不用,非危不战。主不可以怒而兴师,将不可以愠而功战,合于利而动, 不合于利而上。怒可以复喜,愠可以复说,亡国不可以复存,死者不可以复生。故明主慎之,良将警之,此安国全军之道也。