第3章
CHAPTER 3


STM32单片机应用基础









演示视频


3.1STM32单片机简介

英特尔公司在20世纪80年代推出了MCS51系列单片机,该系列单片机包括了很多品种,如8031、8051、8751、8032、8052、8752等,其中8051是最典型的产品,该系列其他单片机都是在8051的基础上进行功能的增、减而来的,所以人们习惯于用8051来称呼MCS51系列单片机。英特尔公司将MCS51的核心技术授权给了很多其他公司,这些公司使用以MSC51架构为核心,根据自身的需求,设计出自己的51单片机,如 ATMEL的AT89C51、国产宏晶的STC89C51。

51单片机在工业界和教育界产生过巨大的影响,时至今日依然经常可以见到51单片机的身影。但51单片机是8位的,随着电子和计算机技术的发展,其并不能满足所有的应用场合。与此同时英国的ARM公司在20世纪90年代做出了一个意义深远的决定: 自己不制造芯片,只将芯片的设计方案授权给其他公司来生产。意法半导体公司(STMicroelectronics,ST公司)正是在这种情况下推出了基于ARMv7架构的32位单片机,即STM32系列单片机。STM32系列单片机产品多样化、性价比高、开发简单,迅速占领了中低端单片机市场,成为单片机市场一颗耀眼的明星。

STM32系列单片机除了在单片机内部集成了ARM公司提供的微处理器内核外,还在单片机内部集成了大量的片上外设,如USART(串口)、I2C、SPI等。STM32系列单片机从微处理器的内核上可以分为CortexM0、M3、M4、M7等,每个内核又根据性能和功耗分为几个系列,如F0、F1、F2、F3、F4、F5、F6、F7等属于高性能型,L0、L1、L4等属于低功耗型。单片机内部集成的片上外设是ST公司自己开发的电路模块,如存储器模块、ADC模块和GPIO模块等,这些模块各个具体单片机是不同的,应用的时候要查阅单片机芯片的使用手册(查阅不是全文阅读)。ST公司基于Arm Cortex内核的32位MCU和MPU系列产品如图3.1.1所示。



图3.1.1ST公司基于Arm Cortex内核的32位MCU和MPU系列产品


STM32单片机的开发方式有以下三种: 

(1) 寄存器模式: 这种方式直接操作单片机内部各个电路模块对应的控制寄存器,例如,设置单片机第H组的GPIO的模式寄存器MODER,需要编写类似“GPIOH_MODER=0x00100000”这样的代码。这种开发方式需要先搞清楚单片机的内部结构和寄存器每一位的含义,早期的MCS51单片机或MSP430单片机开发一般采用这种方式。但是,对STM32这种结构较为复杂的单片机,初学者如果没有MCS51单片机或MSP430单片机开发经验,一般很难上手。

(2) 标准外设库(SPL)函数模式: ARM公司为CortexM内核提供了Cortex微控制器软件接口标准(CMSIS),ST公司又为单片机上的外设编写了驱动库函数,开发者无须过多关注寄存器每一位的具体含义,只需搞清楚库函数的参数和作用即可编写类似“GPIOH.GPIO_MODE = GPIO_MODE_OUT”这样的代码,开发者很容易理解上面的语句是把GPIO的模式设置为输出,这相对于寄存器开发模式又更进一步接近人类语言,便于开发者和程序阅读者理解。

(3) HAL/LL库函数模式: HAL(Hardware Abstract Layer)和LL(Low Layer)库是ST公司推出的另外一种驱动库,相对于SPL库的优势在于配置了单片机的图形化配置软件STM32CubeMX,可以对单片机的资源和外设进行图形化配置,并针对不同的集成开发环境(IDE)工具软件生成基于HAL/LL库的外设初始化程序和IDE程序框架,开发者只需要把主要精力用于应用程序的编写,节省了大量的时间和精力,特别适合没有单片机开发经验的初学者快速入门。

本书采用HAL/LL库函数模式,使用STM32CubeMX软件配置单片机硬件资源,采用STM32CubeIDE编写应用程序,采用STM32CubeProgrammer工具烧录单片机可执行文件。

3.2STM32单片机片上资源和开发板

使用STM32单片机和使用其他任何一款芯片一样,首先要搞清楚芯片的主要功能、引脚分配以及功能寄存器的配置。各种图书和网络资料从不同的角度介绍了STM32单片机的结构、功能和用法,具有一定的参考价值; 但出于权威性和知识产权考虑,优先从ST公司官网获取资料。本书不像其他教材一样详细介绍所有片上资源,而是在用到时逐步讲解,建议读者购买一块STM32单片机开发板,配合本书按步骤边阅读边操作。

3.3STM32CubeMX

无论是寄存器开发模式还是标准外设库函数开发模式,都需要掌握大量寄存器相关知识,给初学者造成一种STM32开发很难、门槛很高、不容易学通的感觉,很多人在开始不久就放弃了。STM32CubeMX的出现解决了这一问题,利用STM32CubeMX可以轻松配置STM32单片机的硬件寄存器,快速上手。

3.3.1下载和安装STM32CubeMX

STM32CubeMX可以到官网下载安装文件,也可以在一些开发论坛下载。STM32CubeMX提供三种操作系统的若干版本,如图3.3.1所示,一般选择对应操作系统的最新版本下载。



图3.3.1STM32CubeMX提供三种操作系统版本


下载后双击安装文件,STM32CubeMX的安装界面如图3.3.2所示,第一个界面如图3.3.2(a)所示,单击“Next”,后续如有协议需要单击同意,按提示安装直至完成,不建议更改安装目录,不建议在安装路径中使用中文。安装的最后一步如图3.3.2(b)所示。



图3.3.2STM32CubeMX的安装界面




图3.3.2(续)


启动STM32CubeMX后界面如图3.3.3所示,左侧为打开已有工程,中间为新建工程,右侧为检查更新软件和安装嵌入式软件包(MCU驱动库),单击“INSTALL/REMOVE”后界面如图3.3.4所示,选择单片机对应系列的驱动库安装即可。界面最上面的“File”菜单栏提供与工程文件相关的操作,“Window”和“Help”菜单栏提供一些窗口和辅助操作。



图3.3.3启动STM32CubeMX后的界面




图3.3.4单击“INSTALL/REMOVE”后界面


3.3.2新建一个STM32CubeMX项目

新建STM32CubeMX项目有三种方式: 一是新建基于STM32系列MCU的项目; 二是新建基于ST公司开发板的项目; 三是新建基于其他公司MCU迁移到STM32系列MCU的项目。本节新建基于STM32系列MCU的项目,在STM32CubeMX的启动界面中的“New Project”下面选择“ACCESS TO MCU SELECTOR”按钮,进入MCU型号选择界面,如图3.3.5所示,在“Commercial Part Number”后面输入自己的MCU型号,筛选到MCU型号后选择,然后单击图3.3.6右上角的“Start Project”,弹出如图3.3.7所示的MCU配置界面。



图3.3.5MCU型号选择界面




图3.3.6选择MCU后的界面




图3.3.7MCU配置界面


MCU配置界面包括四部分: 引脚与配置界面“Pinout & Configuration”,配置MCU的系统内核、外设和引脚; 时钟配置界面“Clock Configuration”,配置MCU的时钟信号频率; 项目管理界面“Project Manager”,对项目进行设置; 工具界面“Tools”,对MCU功耗等性能进行分析。

3.3.3STM32CubeMX项目设置示例

本节通过点亮发光二极管的示例展示STM32CubeMX项目配置方法。发光二极管电路如图3.3.8所示,发光二极管的阴极分别与单片机的PH10、PH11和PH12引脚相连。



图3.3.8发光二极管电路

1. Pinout & Configuration设置


Pinout & Configuration设置区域包含系统核心(System Core)、模拟电路(Analog)、定时器(Timers)、互联(Connectivity)、多媒体(Multimedia)等内核和片上外设的设置区域,单击图3.3.7左侧“>”符号后会展开若干模块的名称,单击每个模块名称后就出现了该模块的详细设置,如图3.3.9所示。STM32CubeMX的最大功能就是将以往文字化的模块配置方式图形化,把“填空题”改成“选择题”,极大地降低了初学者的学习难度。



图3.3.9Pinout & Configuration设置区域


在利用STM32CubeMX进行项目开发时,SYS和RCC是任何一个项目都必须要设置的模块。SYS模块设置如图3.3.9所示,主要设置调试接口和时钟源,本书采用串口调试,所以将图3.3.9中的Debug方式由“Disable”改为“Serial Wire”,其余设置不变。RCC模块主要设置系统时钟源,设置界面如图3.3.10所示,本项目计划采用单片机的外部高速时钟源,所以将图3.3.10所示High Speed Clock(HSE)由“Disable”改为“Crystal/Ceramic Resonator”,其余设置不变。



图3.3.10RCC模块设置界面


本例中用到了GPIO引脚,所以还要设置GPIO模块。单击图3.3.9中System Core下面的GPIO模块,可以看到有些GPIO端口已经被设置,这是因为SYS设置中采用串口调试占用了PA13和PA14,RCC设置中使用外部高速时钟源占用了PH0和PH1,如图3.3.11所示。



图3.3.11RCC设置中使用了PH0和PH1


本例中三个发光二极管连接到了PH10、PH11和PH12,所以还需要设置这三个端口。在图3.3.11中单击右下方芯片的PH10引脚,在弹出的选项中选择“GPIO_Output”选项,即设置好了PH10,如图3.3.12所示。按照同样的方法将PH11和PH12设置为输出模式。



图3.3.12设置PH10为“GPIO_Output”选项


在图3.3.12中间的“GPIO Mode and Configuration”设置区域将引脚PH10、PH11和PH12的别名设置为LED0、LED1和LED2,设置完成后该区域如图3.3.13所示。这样设置的好处是: 以后在单片机的应用程序设计中可通过对LED0、LED1和LED2赋值而改变引脚PH10、PH11和PH12的电平,程序可读性更高。



图3.3.13设置引脚PH10、PH11和PH12的别名


2. Clock Configuration设置

Clock Configuration设置主要是设置单片机系统的时钟,在图3.3.12上单击“Clock Configuration”后出现如图3.3.14所示界面,首先在HSE模块左边的“input frequency”处输入石英晶体的实际频率,笔者手头的开发板采用25MHz晶体振荡器,所以输入“25”。本例中只是演示GPIO的用法,对时钟频率无特殊要求,可在HCLK(MHz)范围内(180MHz)设置150MHz。



图3.3.14Clock Configuration设置


3.3.4STM32CubeMX项目生成代码

在STM32CubeMX项目设置的最后一步为生成项目代码,首先单击图3.3.7上侧中间的“Project Manager”菜单,出现如图3.3.15所示界面,单击“Project”菜单,设置项目名称和项目保存的位置。不要将项目保存在有中文名的文件路径下,项目名称也不要取中文名。本书在D盘里新建了一个STM32Cube文件夹,将项目保存在D:\STM32Cube里,设置项目名称为MXProject_LED,并将应用软件开发工具“Toolchain/IDE”设置为“STM32CubeIDE”(因为下一步计划采用STM32CubeIDE作为开发工具),如图3.3.15所示。



图3.3.15设置项目保存地址和名称


设置完项目保存地址和名称后单击“Code Generator”菜单,勾选“Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral”前面的小方框,为生成代码做准备,如图3.3.16所示,这样做是为了让STM32CubeMX生成相关程序框架和包含“.c”和“.h”的程序代码。



图3.3.16设置“Code Generator”


最后单击图3.3.7右上角的“GENERATE CODE”菜单生成项目代码,此时STM32CubeMX已经给应用软件源程序做好了一个框架,并且“编写”好了系统初始化的代码,下一步就可以在应用软件开发IDE中进一步添加代码。打开D:\STM32Cube文件夹,可以看到文件夹里多了一个名为MXProject_LED的文件夹,文件夹里的文件和文件夹如图3.3.17所示。



图3.3.17MXProject_LED文件夹的界面


3.4STM32CubeIDE

利用STM32CubeMX只是对STM32单片机的硬件进行了初始化,并且做了一个源程序的框架,要完成软件的全部功能还需要编写应用程序,这就要使用源程序的编辑和编译集成开发环境。STM32单片机的集成开发环境有很多,如IAR、Keil等,本书采用STM32CubeIDE开发环境。

3.4.1下载和安装STM32CubeIDE

ST公司官方提供Linux、macOS和Windows等几种版本的STM32CubeIDE,本书以Windows版本为例介绍。下载、解压后以管理员身份运行即可安装。安装过程中若提示错误可能是计算机用户名设置中文名导致环境变量中有中文,修改环境变量即可正常安装。

安装完成后,双击桌面生成的图标启动STM32CubeIDE。出现如图3.4.1所示界面,显示要选择一个文件目录设置为工作空间。更改STM32CubeIDE的工作空间目录为D:\STM32Cube,与3.3节中STM32CubeMX项目相同。



图3.4.1STM32CubeIDE更改工作空间界面


更改工作空间文件夹后单击下一步,进入初始界面,如图3.4.2所示。界面左边是新建或打开工程的快速入口,中间是一些关于STM32CubeIDE的快速链接,右边是一些ST公司相关网站链接。选择“Start new project from STM32CubeMX file(.ioc)”,选择D:\STM32Cube\MXProject_LED文件夹中以“.ioc”结尾的文件,如图3.4.3所示,然后单击“Finish”。打开STM32CubeMX工程文件后,STM32CubeIDE的界面如图3.4.4所示。



图3.4.2STM32CubeIDE初始界面




图3.4.3选择STM32CubeMX工程文件



在图3.4.4的界面中出现了STM32CubeMX的界面,其实STM32CubeIDE里集成了STM32CubeMX功能,不过界面较小,并且功能不如单独的STM32CubeMX强大,建议使用独立的STM32CubeMX软件。

3.4.2在STM32CubeIDE里编写主程序

单击图3.4.4中“Core”左侧的“>”符号,可以看到更多的文件夹和文件,双击“main.h”文件可以看到文件的详细内容,如图3.4.5所示。


在图3.4.5中可以看到定义了LED0、LED1和LED2变量,并且对应好了引脚PH10、PH11和PH12。



图3.4.4打开STM32CubeMX工程文件后STM32CubeIDE的界面




图3.4.5“main.h”文件的详细内容


单击“main.c”文件可以看到详细内容如图3.4.6所示(为便于展示删除了部分注释)。“main.c”文件包含了“main.h”头文件,声明并定义了SystemClock_Config(void)函数和MX_GPIO_Init(void)函数,给系统时钟和部分引脚做了初始化,这些工作都是STM32CubeMX软件替程序设计者做的,程序设计者只需要在While循环里添加功能代码即可,极大地减小了程序设计者的工作量和降低了程序设计者的入门难度。



图3.4.6“main.c”文件的详细内容


本例的设计任务是让发光二极管依次点亮和熄灭,所以在While循环中添加了部分代码,如图3.4.7所示。其中HAL_Delay()为库函数,表示延迟以毫秒为单位的CPU时间。

HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin,GPIO_PIN_RESET)函数向GPIO引脚写入数据,第一个参数为GPIO端口号,本例选择了LED0的端口号也就是GPIOH; 第二个参数为GPIO引脚编号,本例选择了LED0_Pin,也就是PH10; 第三个参数为高低电平,本例选择了GPIO_PIN_RESET,也就是向PH10写入了低电平,二极管点亮。这样选择是因为在“main.h”中定义了LED0_GPIO_Port为GPIOH,LED0_Pin为PH10,这是STM32CubeMX里配置后生成的。这样做的好处是: 如果更换引脚,只需要在STM32CubeMX配置,而不需要更改主程序。

HAL_Delay(1000)将单片机空转1000ms,发光二极管的状态保持。



图3.4.7While循环中的部分代码



HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin,GPIO_PIN_SET)函数向GPIO引脚写入高电平,将发光二极管熄灭。

HAL_Delay(1000)将单片机时钟空转1000ms,发光二极管的状态保持。

以上程序合起来的功能就是让发光二极管亮1s,灭1s,while(1)内的代码是循环执行的,执行的效果就导致二极管不停闪烁。


3.4.3在STM32CubeIDE里构建项目和下载程序
1. 选择构建方式

主程序编写完成后即可构建项目,构建的方式有Debug和Release,Debug用于在线调试,Release用于最终发布。本例只是调试程序,所以选择Debug,选择Project→Properties→C/C++Build→Configuration菜单命令,选择构建项目方式,如图3.4.8所示。



图3.4.8选择构建项目方式


2. 连接计算机与开发板

STM32CubeIDE只支持JLINK和STLINK仿真器下载程序到STM32单片机,所以建议读者购买开发板与JLINK或STLINK仿真器。下载前先用JLINK或STLINK仿真器将开发板与计算机的USB连接,如图3.4.9所示。



图3.4.9开发板与计算机的USB连接方式


3. 检测和更新仿真器的固件

开发板上电后计算机会自动检测仿真器并提示是否更新仿真器的固件程序,如果没有反应,则可以选择Help→ STLINK更新菜单命令进行检测和更新仿真器的固件,如图3.4.10所示。



图3.4.10检测和更新仿真器的固件


4. 程序下载

项目构建无误、开发板连接成功后,将可执行程序下载到开发板。选择Run→Debug菜单命令或者快捷工具栏上的Debug按钮即可下载并调试程序。本例在下载成功后若观察到3个发光二极管依次闪烁(重复点亮并熄灭),则可判定实验成功。



源程序


3.5STM32单片机开发实例

本节通过中断应用实例进一步介绍利用STM32CubeMX、STM32CubeIDE以及HAL库函数开发STM32单片机的方法。

单片机的主程序一般是一个while(1)循环,CPU循环运行while(1)循环内的语句,当某个外设或内部条件发生异常时,才跳转到相应的异常处理程序去处理和应对,处理完毕后继续运行while(1)循环内的语句,这个过程称为中断。STM32单片机根据型号不同有不同的中断来源,具体需要查阅单片机的参考手册,但都有GPIO外部中断。CPU处理中断的过程以及中断程序的编写方法都是类似的,所以本节以GPIO外部中断为例进行讲解。

1. 单片机外围电路

单片机STM32F429外围电路如图3.5.1所示,GPIO端口PA0外接触发按键,按键悬空时,PA0为低电平,按键按下时,PA0为高电平。PH10、PH11和PH12外接发光二极管,当PH10、PH11和PH12为低电平时,发光二极管点亮,否则熄灭。



图3.5.1单片机STM32F429外围电路


2. 设计目标

当按下按键时,发光二极管闪烁三下后熄灭,闪烁时间间隔1s。

3. 工作原理

单片机STM32F429的外部中断(EXTI)有23个中断源,其中EXTI0~EXTI15对应GPIO引脚中断,如EXTI0对应PX0(X的取值可以为A、B、C、D、E、F、G、H和I),EXTI1对应PX1(X的取值可以为A、B、C、D、E、F、G、H和I),其余一直到EXTI15类似。由此可知,PA0可以引起EXTI0中断。

GPIO中断程序设计过程(图3.5.2)如下: ①

利用GPIO引脚初始化函数HAL_GPIO_Init()设置PA0为中断工作方式; ②

利用HAL_NVIC_SetPriority ()函数设置中断优先级; ③

利用HAL_NVIC_EnableIRQ()函数启用中断; ④

利用EXTI0_IRQHandler()函数中的HAL_GPIO_EXTI__IRQHandler()设置EXT0的中断服务例程(CPU参数入栈、清除中断标志、跳转到中断服务函数和CPU参数出栈等一系列CPU的操作,本例的设计者不用关心); ⑤

编写中断服务函数HAL_GPIO_EXTI_Callback()(CPU检测到中断发生后跳转到中断服务函数HAL_GPIO_EXTI_Callback()让发光二极管闪烁)。



图3.5.2中断程序设计过程


4. 设计实现

图3.5.2中HAL_GPIO_Init()、HAL_NVIC_SetPriority()、HAL_NVIC_EnableIRQ()、EXTI0_IRQHandler()
利用STM32CubeMX设置生成,流程如下: 

(1) 在D盘新建MX_Project_inter文件夹。

(2) 在STM32CubeMX中创建项目,保存名称为MXProject_inter.ioc,路径为新建的MX_Project_inter文件夹,具体步骤参考3.3节。配置Debug接口为Serial Wire,设置HCLK为168MHz。

(3) 设置按键和LED对应的GPIO设置如图3.5.3所示,LED对应引脚和3.3节相同,按键对应GPIO端口PA0设置为上跳沿触发外部中断。



图3.5.3按键和LED对应的GPIO设置


(4) 在System Core组件面板里单击NVIC,设置中断优先级,如图3.5.4所示。首先在Priority Group下拉列表里选择优先级分组,将表示中断优先级的4位二进制数分为2+2位,前两位用于抢占优先级,后两位用于次优先级(也可以做其他分配方式)。然后在“EXTI line0 interrupt”打钩,并将抢占优先级和次优先级都设置为1(也可以做其他级别设置,但不要将抢占优先级的级别设为0,因为HAL_Delay()函数用到的定时器中断已经设置为0这个最高优先级)。



图3.5.4在NVIC里设置中断优先级


(5) 设置完成后选择应用开发环境为STM32CubeIDE并生成应用程序框架。

(6) 生成应用程序框架后打开main.c可以看到如下代码,系统初始化后是一个while(1)循环。

#include "main.h"

#include "gpio.h"

void SystemClock_Config(void);

int main(void)

{

HAL_Init( );

SystemClock_Config( );             //时钟初始化

MX_GPIO_Init( );                 //GPIO初始化

while (1)

{

}

}

(7) 打开main.h可以看到如下宏定义,为应用程序编写名称提供依据。

#define KeyInter_Pin GPIO_PIN_0

#define KeyInter_GPIO_Port GPIOA

#define KeyInter_EXTI_IRQn EXTI0_IRQn

#define LED0_Pin GPIO_PIN_10

#define LED0_GPIO_Port GPIOH

#define LED1_Pin GPIO_PIN_11

#define LED1_GPIO_Port GPIOH

#define LED2_Pin GPIO_PIN_12

#define LED2_GPIO_Port GPIOH

(8) 打开STM32CubeIDE开发工具,进入开发环境后首先切换工作空间到MX_Project_inter文件夹,然后根据MXProject_inter.ioc文件新建应用程序工程。打开gpio.c文件,在“/* USER CODE BEGIN 2 */”和“/* USER CODE END 2 */”之间的代码沙箱里编写中断服务函数,主要内容是如果检测到PA0(KeyInter_Pin)的中断标志后将三个发光二极管依次点亮1s。主程序While(1)循环中为空。中断服务程序代码如下: 

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

{

if (GPIO_Pin == KeyInter_Pin)

{

HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin,GPIO_PIN_RESET);

HAL_Delay(1000);

HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin,GPIO_PIN_SET);

HAL_Delay(1000);

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin,GPIO_PIN_RESET);

HAL_Delay(1000);

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin,GPIO_PIN_SET);

HAL_Delay(1000);

HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_RESET);

HAL_Delay(1000);

HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin,GPIO_PIN_SET);

HAL_Delay(1000);

}

}

(9) 编写完成后保存,执行Run→Debug菜单命令或者单击快捷工具栏上的Debug按钮即可下载并调试程序(要保证程序没有错误以及开发板连接到计算机并上电)。本例在下载成功后每按下一次PA0引脚上的按键就观察到三个发光二极管依次点亮1s并熄灭。

小结

本章对STM32单片机的基本概念、开发语言与工具、设计应用系统的流程进行了详细介绍,并实际设计了外部引脚中断应用实例。通过本章的学习读者可以在参考STM32单片机硬件资源的基础上初步设计STM32应用系统。

思考题

1. STM32单片机与Arduino和MCS51单片机之间的区别是什么?

2. STM32单片机应用的流程是什么?

3. 到官网或其他论坛查阅STM32单片机的硬件资源和开发方法,进一步学习STM32单片机的应用。

扩展阅读: 我国MCU产业的发展 




扩展阅读