1.1.1 什么是 STM32
1.STM32 概述
STM32 微控制器是意法半导体(ST Microelectronics,ST)有限公司出品的一系列微控制
器(Micro Controller Unit,MCU)的统称。
意法半导体有限公司于 1987 年 6 月成立,由意大利的 SGS 微电子公司和法国的 Thomson
半导体公司合并而成(以下简称 ST 公司 ),是世界上最大的半导体公司之一。
STM32 微控制器基于 Arm Cortex®-M0、M0+、M3、M4 和 M7 内核,这些内核是专门为高
性能、低成本和低功耗的嵌入式应用设计的。STM32 微控制器按内核架构可以分为以下产品系列。
通用微处理器产品系列:STM32MP1。
高性能产品系列:STM32F2、STM32F4、STM32F7、STM32H7。
主流产品系列:STM32F0、STM32F1、STM32F3。
超低功耗产品系列:STM32L0、STM32L1、STM32L4、STM32L4+。
无线系列:STM32WB。
图 1-1-1 展示了 STM32 微控制器的产品家族。
2.STM32 微控制器的命名规则
各个型号的 STM32 微控制器在封装形式、引脚数量、静态随机存储器(SRAM)和闪存的
大小、最高工作频率(影响产品的性能)等方面有所不同,开发人员可根据应用需求选择最合
适的 STM32 微控制器来完成项目设计。STM32 微控制器型号的各部分含义介绍如图 1-1-2
所示。
下面以一个具体的 STM32 微控制器型号(STM32F407ZGT6)为例来介绍型号中各部分的
含义,如表 1-1-1 所示。
3.STM32F4 系列微控制器的系统架构
STM32F4 系列微控制器在片上集成了各种功能部件,各部件之间通过总线相连。这些功能
部件包括内核(Core)、系统时钟发生器、复位电路、程序存储器、数据存储器、中断控制器、
调试接口以及各种外设等。
STM32F4 系列微控制器的片上外设有:通用输入/输出(GPIO)端口、定时器(Timer)、
模数转换器(ADC)、数模转换器(DAC)、通用同步/异步收发器(USART)、安全数字输入/输
出(SDIO)接口、串行外设接口(SPI)、内部集成电路(IIC)接口、控制器区域网络(CAN)
总线等。
图 1-1-3 展示了 STM32F4 系列微控制器的系统架构。
从图 1-1-3 中可以看到,主系统由 32 位多层先进的高性能总线(AHB)矩阵构成。借助
AHB 矩阵,MCU 可以实现主控总线到被控总线的访问。这样,即使多个高速外设同时运行,系
统也可以实现并发访问和高效运行。
1.1.2 什么是 Arm
Arm(Advanced RISC Machines)公司是微处理器行业中的一家知名企业,它设计了大量高
性能、低能耗且廉价的微处理器。Arm 公司的产品适用于多个领域,如嵌入式控制、消费/教育
类多媒体、数字信号处理和移动式应用等。目前全世界超过 95%的智能手机和便携式计算机的
微处理器都采用 Arm 架构。
1990 年 11 月,Arm 公司在英国剑桥正式成立,专门从事基于精简指令集计算机(Reduced
Instruction Set Computer,RISC)技术的芯片设计开发工作。作为一家知识产权供应商,Arm
公司并不直接生产芯片,只出售芯片设计技术的授权,而由合作公司生产具体的芯片。世界各大
半导体生产商从 Arm 公司购买其设计的微处理器核心,根据不同的应用领域加入适当的外围电
路,生产出各具特色的基于 Arm 内核的微处理器芯片。目前全世界有几十家半导体公司与 Arm
公司签订了硬件技术使用许可协议,其中包括
Intel、IBM、三星半导体、NEC、SONY、飞利
浦和美国国家半导体等大公司。Arm 公司软件
系统的合作伙伴包括微软、SUN 和 MRI 等知名
公司。图 1-1-4 展示了 Arm 公司与合作伙伴的
关系。
综上所述,“Arm”既代表一家公司(从事
基于 RISC 技术的芯片设计工作,只出售芯片设
计技术的授权)的名称,又代表一种技术(微处理器核心设计),还代表一类微处理器(由半导
体公司生产的基于 Arm 架构的微处理器)。
Arm 处理器经过多年的发展,产生了 Arm v1、v2、v3、v4、v5、v6、v7、v8 等不同版本的
内核架构。自 Arm v7 架构开始,内核命名将 Cortex 作为前缀,分为 Cortex-A、Cortex-R 和
Cortex-M 三大系列。三大系列分工明确:Cortex-A 系列内核面向尖端的基于虚拟内存的操作
系统和用户应用,如移动领域的微处理器(MPU);Cortex-R 系列内核面向对实时性要求较高的
应用;Cortex-M 系列内核主要用于微控制器。STM32 系列微控制器就是基于 Arm v7 架构中的
Cortex-M 系列内核设计出来的。
1.1.3 用 STM32 能做什么
随着电子技术、计算机技术、通信技术的发展,嵌入式技术已无处不在。从随身携带的可穿戴智
能设备到智慧家庭中的远程抄表系统、智能洗衣机和智能音箱,再到智慧交通中的车辆导航、流量控
制和信息监测等,各种创新应用及需求不断涌现。
电子产品得以快速更新、淘汰,其组成部分中最基础的底层架构芯片 — 微控制器(MCU)
功不可没。目前 MCU 已成为电子产品及行业应用解决方案中不可或缺的一部分。
ST 公司在 2007 年发布了首款搭载 Arm Cortex-M3 内核的 32 位 MCU。在之后的十余年时
间里,STM32 产品线又相继加入了基于 Arm Cortex-M0、Cortex-M4 和 Cortex-M7 内核的产
品,产品线覆盖了通用型、低成本、超低功耗、高性能低功耗以及甚高性能等类型。正是由于
STM32 系列微控制器拥有完整的产品线和简单易用的应用开发生态系统,越来越多的领域的电
子产品才将 STM32 系列微控制器作为主流的解决方案,涵盖智能硬件、智能家居、智慧城市、
智慧工业、智能驾驶等领域。图 1-1-5 是生活中的一些常见的使用 STM32 系列微控制器的电
子产品。
1.1.4 学习 STM32 必备的知识基础是什么
STM32 系列微控制器应用开发使用的编程语言主要是 C 语言。在应用开发的过程中,用户
还经常需要阅读电路原理图、集成电路芯片的数据手册(Datasheet)等专业资料。因此在学习
STM32 前,应先学习“C 语言程序设计”“模拟电子电路分析与应用”“数字电子电路分析与应
用”等课程。上述课程可为学习 STM32 奠定模拟和数字电子电路分析设计的原理与方法、基本
的 C 语言程序设计语法与规范等知识基础。
1.1.5 学习 STM32 需要哪些工具与平台,有什么好的学习方法
1.学习 STM32 所需的工具与平台
在开始 STM32 学习之前,我们需要先挑选一块合适的 STM32 开发板。初学者不应盲目地
追求开发板的功能,应以够用为原则,重点关注开发板配套的学习资料与视频是否详细、全面。
目前市面上可供选购的 STM32 开发板主要有两种:最小系统板和外设齐全的开发板,分别如
图 1-1-6(a)和图 1-1-6(b)所示。上述两种开发板各有优缺点:从价格上来说,最小系统
板比外设齐全的开发板便宜;从提升硬件电路的构建能力来说,在使用最小系统板进行学习时,
需要自行搭建外设的应用电路,这有助于学习者更好地理解外设电路的原理,并能够提高其电路
板设计与制作的能力;从使用的便利性来说,外设齐全的开发板具有绝对的优势,学习者使用这
种开发板可以方便地完成芯片性能的测试、程序功能的验证以及想法创意的快速应用。
ST 公司官网显示,支持STM32 开发的集成开发环境(Integrated Development Environment,
IDE)有 20 余种,其中包括商业版 IDE 和免费的 IDE。目前比较常用的商业版 IDE 有 MDK-ARM-
STM32 和 IAR-EWARM,免费的 IDE 有 SW4STM32、TrueSTUDIO 和 CoIDE 等。另外,ST 公
司官方推荐使用 STM32CubeMX 软件可视化地进行芯片资源和管脚的配置,然后生成项目的
源程序,最后导入 IDE 中进行编译、调试与下载。常见的支持 STM32 开发的 IDE 如图 1-1-7
所示。
2.学习 STM32 的方法
如果想在短时间内上手 STM32 微控制器的开发(即“入门 STM32”),初学者需要采用科学
的学习方法,制定一个完善的学习计划并严格按照计划实施。
首先,不要把时间过多地花在犹豫上。人们在学习一项新技术前,可能都会经历“犹豫期”。他们
会先查阅资料,了解新技术并解决心中的一些疑问,如这个技术难不难、需要什么基础、适合不适合
自己等。适当的前期调研是必要的,但过多地思索却没有实际行动是永远不可能入门的。因此,如果
你对STM32开发感兴趣,那么请不要过多地犹豫,根据后面介绍的方法按部就班地开始学习吧。
然后,准备好必需的 STM32 开发板、开发软件和学习文档。STM32 开发板与开发软件的选
择可参考前文,此外,我们还需要准备以下几个必备的学习文档:一是《STM32F4xx 中文参考
手册》,它介绍了 STM32F4 系列微控制器各种外设的工作原理;二是《STM32 标准外设库使用
手册》,它介绍了 STM32 标准外设库函数 API 的调用方法和使用实例;三是某个型号的 STM32
微控制器的 Datasheet,该文档有助于我们了解 MCU 的电气性能、管脚分布与外设功能;四是
开发板生产厂商编制的产品使用手册。
最后,一个很重要的环节是制定学习计划。
(1)快速浏览一遍文档
STM32 开发涉及的文档内容很多,没必要也无法一次性全部看完,但 STM32 开发的通用基
础部分必须得看,如存储器和总线架构、电源控制、备份寄存器、复位和时钟控制、GPIO 引脚
及其功能复用、中断等。具体某个外设的工作原理与使用方法在用到时再仔细阅读即可。
(2)制定分阶段目标
STM32 的学习过程可以分为以下 3 个阶段。
第一阶段是“找感觉”阶段。拿到 STM32 开发板之后,先把厂家配套的开发板使用手册浏
览一遍,以熟悉开发板上的硬件组成。接下来可以按照使用手册中与开发板配套的测试例程的操
作步骤,操作一遍开发板。本阶段的学习能让你找到感觉并建立自信。
第二阶段是“模仿”阶段。在了解了 STM32 开发的基本流程之后,可以选取一些例程,详
细分析其工作原理与实现方法,并对例程的功能进行修改,以达到不同的运行效果。本阶段的学
习能让你获得成就感。
第三阶段是“自由发挥”阶段。在熟练掌握 STM32 的开发流程并具备一定的开发经验之后,
你可以选取并开发一个综合性较强的小项目。在项目的开发过程中,应严格按照实际的项目开发
流程实施,不可遗漏一些重要的环节,如需求分析、系统功能描述、程序流程图绘制与软件文档
编写等。通过本阶段的学习,你将积累宝贵的项目开发实战经验。
经过以上 3 个阶段的学习,你就可入门 STM32 微控制器的开发!
3.经验之谈
(1)遇到问题怎么办
先谈谈对待问题的态度:遇到问题时不要抱怨。俗话说得好:出现问题是给你学习的机会。
因此解决问题的过程可以促进能力的提升。我们应摆正态度,正视问题。
另外,遇到问题时不要马上四处求助。当问题刚出现时,你可能还无法看清问题的全貌,也
无法用最合适的语言将问题表述清楚以使别人理解并做出回答。因此在遇到问题时,应先查资料,
自己尝试解决问题。
如果经过一番分析,你仍然无法解决问题且必须向他人请教时,应注意提问题的艺术,即不
问不具体的问题。如“FreeRTOS 如何移植?”这个问题,需要非常大的篇幅才能阐述清楚,因
此类似的问题是需要避免的。换个角度,如果我们将项目实施过程中遇到的具体问题提出来并向
技术“大师”请教,有经验的人可能几句话就可以解答,因此这才是正确的提问方式。
(2)学习过程中注意总结经验
在 STM32 的学习过程中,可能会遇到很多问题,你通过自己的努力解决了这些问题,并获
得了成就感,但是过了一段时间你可能会忘记当时的解决方法与步骤。因此在学习过程中,花一
点时间进行经验总结是非常有必要的。如果我们能将总结文档发布到博客中进行分享,则可为遇
到相同问题的人提供帮助,何乐而不为呢?
1.1.6 如何搭建 STM32F4 系列微控制器的最小系统
一般来说,STM32F4xx 微控制器的最小系统应包含以下 6 个部分。
1.STM32F4xx 微控制器
STM32F407ZGT6、STM32F405RGT6 等型号的微控制器均属于 STM32F4xx 微控制器。
2.电源
最小系统板的电源可采用多种不同的芯片组合方案。如果最小系统板采用 12V 直流电压适
配器供电,则电源可采用“LM2596+ASM1117”的芯片组合方案,其中 LM2596 芯片将 12V 直
流电压转换为 4V,ASM1117 芯片将 4V 直流电压转换为 3.3V 后为微控制器供电。电源的电路原
理图如图 1-1-8 所示。
3.外部晶振
STM32F4 系列微控制器的最小系统板需要安装两个外部晶振:高频晶振(频率为 8MHz 或
者 25MHz)和低频晶振(频率为 32.768kHz)。外部晶振的电路原理图如图 1-1-9 所示。
4.JTAG 仿真调试接口
用户通过 JTAG 仿真调试接口将 STM32 开发板与 J-Link 或 ST-Link 仿真器相连,然后通
过集成开发环境进行程序下载或完成在线调试。JTAG 仿真调试接口的电路原理图如图 1-1-10
所示。
5.启动模式配置电路
STM32 微控制器有 3 种启动模式,它们对应的存储介质都是芯片内置的,分别是主 Flash
存储器、系统存储器和内置 SRAM。用户通过配置两个启动模式选择引脚(BOOT0 和 BOOT1)
可选择从哪个存储介质启动。启动模式配置方法见表 1-1-2。
启动模式配置电路的原理图如图 1-1-11 所示。
6.人机接口——按键与 LED
为了验证最小系统板的功能,我们通常会在开发板上设计若干个 LED 与按键。LED 用
于显示程序的运行情况,按键用于输入用户的指令。人机接口的电路原理图如图 1-1-12
所示。
1.1.7 STM32 的软件开发模式有哪些
1.STM32 的软件开发库
在学习 STM32 的软件开发模式之前,我们有必要先了解一下 STM32 的软件开发库。ST 公
司为开发者提供了多个软件开发库,如标准外设库、HAL 库和 LL 库等。另外,ST 公司还针对
F0 与 L0 系列的 MCU 推出了 STM32Snippets。
上面提到的几个软件开发库中,标准外设库推出时间最早,HAL 库次之,LL 库是最近才
新增的。目前 LL 库支持的芯片较少,尚未覆盖全系列产品。ST 公司为这些软件开发库配套
了齐备的开发文档,为开发者的使用提供了极大的方便。接下来分别对以上几种软件开发库
进行介绍。
(1)STM32Snippets
STM32Snippets 是 ST 公司推出的提供高度优化且立即可用功能的寄存器级代码段集合,以
最大限度地发挥 STM32 微控制器应用设计的性能和能效的软件开发库。寄存器级的编程虽然可
降低内存占用率,节省宝贵的处理器时钟周期,降低电源电流消耗,但通常需要开发者花费很多
时间和精力研究产品手册。另外,这种开发模式的缺点是代码在不同系列的 STM32 微控制器之
间没有可移植性。
(2)标准外设库
标准外设库(Standard Peripherals Library)是对 STM32 微控制器进行了完整封装的库,
它包括了 STM32 微控制器所有外设的驱动描述和应用实例,为开发者访问底层硬件提供了一
个中间函数 API。通过标准外设库,开发者无须深入掌握底层硬件的细节就可以轻松驱动外设,
快速部署应用。因此,使用标准外设库可以减少开发者驱动片上外设的编程工作量,降低时间
成本。
标准外设库早期的版本也称为固件函数库或固件库,它是目前使用最多的库,缺点是不支持
近期推出的 L0、L4 和 F7 等系列的 MCU。
ST 公司为各个不同系列的 MCU 提供的标准外设库的内容是有区别的。例如,STM32F1xx
的库和 STM32F4xx 的库在文件结构与内部实现上有所不同。因此基于标准外设库开发的程序在
不同系列的 MCU 之间的可移植性较差。
(3)HAL 库与 LL 库
为了减少开发者的工作量,提高程序开发效率,ST 公司发布了一个新的软件开发工具产品
— STM32Cube。这个产品由图形化配置工具 STM32CubeMX、库函数(HAL 库与 LL 库)以
及一系列中间件(RTOS、USB 库、文件系统、TCP/IP 协议栈和图形库等)构成。
硬件抽象层(Hardware Abstraction Layer,HAL)库是 ST 公司为 STM32 系列微控制器推
出的硬件抽象层嵌入式软件,它可以提高程序在不同系列产品之间的可移植性。
与标准外设库相比,HAL 库表现出了更高的抽象整合水平。HAL 库的应用程序编程接口
(API)集中关注各外设的公共函数功能,定义了一套通用的、对用户友好的 API 函数,开发者可
以轻松地将程序从一个系列的 STM32 微控制器移植到另一个系列。目前,HAL 库支持 STM32
全系列产品,它是 ST 公司未来主推的库。
低层(Low Layer,LL)库是 ST 最近新增的库,与 HAL 库捆绑发布,其说明文档也与 HAL
库的文档编写在一起。例如,在 STM32L4xx 的 HAL 库说明文档中,新增了 LL 库这一部分。
下面从可移植性、程序优化、易用性、程序可读性和支持硬件系列等方面对各软件开发库进
行比较,结果如表 1-1-3 所示。
目前,各软件开发库对不同系列 STM32 微控制器的支持情况如表 1-1-4 所示。
2.STM32 的软件开发模式
开发者可基于 ST 公司提供的软件开发库进行应用程序的开发,常用的 STM32 软件开发模
式主要有以下 3 种。
(1)基于寄存器的开发模式
基于寄存器编写的代码简练、执行效率高。这种开发模式有助于开发者从细节上了解 STM32
系列微控制器的架构与工作原理,但由于 STM32 系列微控制器的片上外设多且寄存器功能复杂,
因此开发者需要花费很多时间和精力研究产品手册。这种开发模式的另一个缺点是:基于寄存器
编写的代码后期维护难、可移植性差。总体来说,这种开发模式适合有较强编程功底的开发者。
(2)基于标准外设库的开发模式
基于标准外设库的开发模式对开发者的能力要求较低,开发者只要会调用 API 函数即可编写
程序。基于标准外设库编写的代码容错性好且后期维护简单,但是其运行速度相对于基于寄存器
编写的代码偏慢。另外,基于标准外设库的开发模式与基于寄存器的开发模式相比不利于开发者
深入掌握 STM32 系列微控制器的架构与工作原理。总体来说,这种开发模式适合想要快速入门
的初学者,因此大多数初学者会选择这种开发模式编写代码。
(3)基于 STM32Cube 的开发模式
开发者基于 STM32Cube 开发软件的流程如下:
首先,根据应用需求使用图形化配置工具对 MCU 片上外设进行配置;
然后,生成基于 HAL 库或 LL 库的初始代码;
最后,将生成的代码导入集成开发环境并进行编辑、编译和运行。
基于 STM32Cube 的开发模式的优点有以下 3 个。
① 初始代码框架自动生成,简化了开发者新建工程、编写初始代码的过程。
② 图形化配置工具操作简单、界面直观,为开发者节省了查询数据手册以了解引脚与外设
功能的时间。
③ HAL 库的特性决定了基于 STM32Cube 编写的代码可移植性最好。
当然,这种开发模式也有其缺点,如函数调用关系较复杂、程序执行效率偏低以及对初学者
不友好等。
本书在综合考虑各种软件开发模式难易程度的基础上,选取了对初学者比较友好的“基于标
准外设库的开发模式”展开讨论。
1.1.8 STM32F4 标准外设库的文件结构是怎样的
截至本书出版,在 ST 公司官网上可下载到的 STM32F4 标准外设库的最新版本是 V1.8.0,
将压缩包解压后的文件结构如图 1-1-13 所示。
对图 1-1-13 中文件夹与文件的说明如下:
_htmresc 文件夹存放 ST 公司的 Logo;
Libraries 文件夹存放标准外设库的源代码与启动文件等;
Project 文件夹存放官方范例与工程模板;
Utilities 文件夹存放 ST 公司官方开发板的示例代码,可作为学习的参考文件;
.chm 文件是 STM32F4 标准外设库的 API 说明与编程示例。
用户在使用标准外设库进行 STM32 开发时,需要用到的库函数主要在 Libraries 文件夹中。
任务 1.2 STM32F4 标准外设库工程的建立
1.2.1 任务分析
本任务要求在 Keil μVision5 集成开发环境下建立基于 STM32F4 标准外设库的工程,在工程
中添加必要的文件,正确地配置、编译工程,并将其下载至开发板中运行。
本任务涉及的知识点有:
STM32F4 标准外设库中的重要文件;
STM32F4 标准外设库帮助文档的使用方法;
基于 STM32F4 标准外设库的工程的建立步骤。
1.2.2 知识链接
1.认识 STM32F4 标准外设库中的重要文件
通过对任务 1.1 的学习,我们已经了解了 STM32F4 标准外设库的文件结构。但如果要建立
基于 STM32F4 标准外设库的工程,则仅了解库的文件结构是不够的,还要进一步学习库里的重
要文件。
工程建立所需的文件主要位于STM32F4标准外设库的Libraries文件夹和Project文件夹中。
Libraries 文件夹中包含 CMSIS 与 STM32F4xx_StdPeriph_Driver 两个子文件夹。Project 文件夹
中包含 stm32f4xx_it.c 和 stm32f4xx_conf.h 文件。
(1)CMSIS 文件夹
CMSIS 文件夹的结构如图 1-2-1 所示。
图 1-2-1 中的 Device 和 Include 文件夹中存放了建立工程所需的文件,它们的介绍如下。
① stm32f4xx.h
文件路径:Libraries\CMSIS\Device\ST\STM32F4xx\Include。
stm32f4xx.h 文件包括 STM32F4 系列微控制器的中断优先级定义、外设寄存器的结构体类
型定义和寄存器操作的宏定义等。
② system_stm32f4xx.c
文件路径:Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates。
system_stm32f4xx.c 文件定义了一个重要函数,即 STM32F4 芯片通电后的系统时钟初始化
函数 — SystemInit()函数。这个函数在启动文件中用汇编语言调用,并根据相关配置参数进行
系统时钟的初始化。如 STM32F407ZGT6 的系统时钟默认被初始化为 168 MHz。
③ system_stm32f4xx.h
文件路径:Libraries\CMSIS\Device\ST\STM32F4xx\Include。
system_stm32f4xx.h 文件是 system_stm32f4xx.c 的头文件。
④ startup_stm32f40_41xx.s
文件路径:Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm。
startup_stm32f40_41xx.s文件是STM32F4系列微控制器在MDK-Arm-STM32集成开发环
境中的启动文件,它是用汇编语言编写的。若用户使用不同的集成开发环境,则应选择相应的启
动文件。
⑤ core_cm4.h、core_cmFunc.h、corecmInstr.h、core_cmSimd.h
文件路径:Libraries\CMSIS\Include。
Include 文件夹中包含了遵循 CMSIS 标准的 Cortex-M 内核通用的头文件,它们为基于
Cortex-M 内核的 SoC 芯片外设提供了内核 API,定义了与内核相关的寄存器。STM32F4 系列微控
制器的工程中需要core_cm4.h、core_cmFunc.h、corecmInstr.h、core_cmSimd.h 这4 个头文件。
(2)STM32F4xx_StdPeriph_Driver 文件夹
STM32F4xx_StdPeriph_Driver 文件夹的结构如图 1-2-2 所示。
由图1-2-2 可知,STM32F4xx_StdPeriph_Driver 文件夹中有两个子文件夹,分别是inc 文件夹
和src 文件夹。其中,src 文件夹存放STM32F4 系列微控制器所有外设的驱动程序源代码,inc 文件
夹存放外设驱动程序源代码相应的头文件。这两个文件夹里的内容是标准外设库的核心内容。
(3)中断服务程序文件 stm32f4xx_it.c
STM32F4 系列微控制器的中断服务程序一般在 stm32f4xx_it.c 文件中进行编写,相应的头
文件是 stm32f4xx_it.h。在 ST 公司官方工程模板的这个文件里已经定义了一些系统异常(特殊
中断)的接口函数,我们还可在此文件中自行定义普通中断服务程序函数。上述两个文件的路径:
Project\STM32F4xx_StdPeriph_Templates。
(4)标准外设库功能裁减文件 stm32f4xx_conf.h
STM32F4 系列微控制器的外设种类繁多,为了精简工程并提高编译速度,可将暂时不用的
外设库文件从工程中删除,通过配置 stm32f4xx_conf.h 文件可实现上述功能。该文件的路径:
Project\STM32F4xx_StdPeriph_Templates。
2.正确使用标准外设库的帮助文档
我们在使用 STM32F4 标准外设库进行程序设计时,经常要调用某种外设驱动的库函数。库
函数的数量庞大,为了实现快速调用,我们应熟练掌握库函数的查阅方法。
STM32F4 标准外设库提供了一个帮助文档,名为“stm32f4xx_dsp_stdperiph_lib_um.chm”,
通过这个文档可以快速查阅函数原型、功能说明、参数说明、返回值及其出处。
打开帮助文档后,依次展开标签:Modules→STM32F4xx_StdPeriph_Driver,可找到各种外
设(如 ADC、DMA、TIM、USART 等)的库函数说明,如图 1-2-3 所示。
从图 1-2-3 中可以看到,GPIO_ReadInputData()函数被选中。右侧窗格显示了函数原型、
功能说明、参数说明、返回值、出处等信息。如果用户对相关的结构体类型存在疑问,可单击超
链接跳至其定义处进行查看。
1.2.3 任务实施
1.建立工程文件夹
一个 STM32 工程通常包含多个源代码。随着对 STM32 学习的深入,我们建立的工程将会
越来越复杂,包含的源代码数量也会越来越多。为了实现工程的科学管理,我们应对工程文件夹
的命名与结构进行合理规划。
STM32 工程包含的文件主要有以下几类:用户编写的应用层程序文件、硬件外设驱动程序
文件、STM32F4 标准外设库文件、Cortex-M4 内核相关文件与启动文件等。为了使工程结构更
加清晰,我们可通过建立文件夹来分门别类地存放上述文件。
先建立名为“STM32F407_Template”的工程文件夹,并在其下建立 5 个子文件夹。工程子
文件夹的名称与用途如表 1-2-1 所示。
2.为工程的子文件夹添加文件
工程文件夹建好以后,在新建工程前,我们应将必要的文件先加入相应的子文件夹中。
(1)USER 文件夹
将 stm32f4xx.h、system_stm32f4xx.c、system_stm32f4xx.h、stm32f4xx_it.c、stm32f4xx_it.h
和 stm32f4xx_conf.h 文件复制到 USER 文件夹中,这些文件的具体路径见 1.2.2 节。另外,新建
main.c 文件并存入此文件夹中。
(2)HARDWARE 文件夹
本工程未涉及硬件驱动程序的编写,故此文件夹暂时留空。
(3)FWLIB 文件夹
将 STM32F4xx_StdPeriph_Driver 文件夹中的 inc 和 src 文件夹中的所有内容复制到 FWLIB
文件夹中,并保留原始文件架构。
(4)CORE 文件夹
将startup_stm32f40_41xx.s、core_cm4.h、core_cmFunc.h、corecmInstr.h 和core_cmSimd.h
文件复制到 CORE 文件夹中,这些文件的具体路径见 1.2.2 节。
3.新建工程
打开 Keil μVision5 软件,单击“Project”菜单下的“new uVision Project”选项新建工程,
保存工程名为“Template”,并将其存放在 USER 文件夹中。
工程保存完毕后,工程建立向导会弹出“MCU 型号选择”对话框,用户可根据开发板所用
的 MCU 型号进行配置,然后单击“OK”按钮进行保存。MCU 型号选择界面如图 1-2-4 所示,
图中选取的 MCU 型号为 STM32F407ZGTx。
4.新建组(Group)并添加相应文件
工程建好以后,为了使工程架构更加清晰,我们应将程序文件归类分组。分组的依据是工程
子文件夹的架构。我们在 Keil μVision5 软件开发窗口左侧的 Project 窗格上单击右键,在弹出的
快捷菜单中选择“Manage Project Items…”选项,即可进入工程管理窗口,如图 1-2-5 所示。
根据工程子文件夹的架构,我们为“Groups”栏添加 USER、HARDWARE、FWLIB、CORE
等几个分组,并在分组中添加相应的程序文件。完成后的工程分组情况如图 1-2-6 所示。
此处主要添加“.c”和“.s”文件,“.h”文件无须添加。
5.配置文件是否加入编译
STM32F407 微控制器没有 FMC、DMA2D 和 LTDC 外设,将所有的 STM32F4 标准外设库
文件都加入工程以后,工程在编译时会出错。我们可将相应的库文件从工程中屏蔽以解决此问题,
具体的操作流程如下:
(1)在要屏蔽的文件上右击;
(2)在弹出的快捷菜单中选择“Options for File …”菜单项;
(3)在弹出的配置界面中,取消“Include In Target Build”复选框的勾选;
(4)单击“OK”按钮确认。
编译选项配置的具体步骤如图 1-2-7 和图 1-2-8 所示。
6.工程配置
在工程中加入必要的文件后,还须对工程进行配置才能对其进行编译。主要配置步骤如下。
(1)晶振频率配置
晶振频率应根据开发板实际使用的 MCU 型号进行配置。单击 Keil μVision5 软件工具栏上
的“魔术棒”图标,弹出“工程配置”对话框后,切换至“Target”标签。编者使用的 STM32F4
开发板上的晶振频率为 8 MHz,所以应将 Xtal(MHz)后的数字修改为“8.0”,如图 1-2-9
所示。
(2)编译输出文件配置
工程编译过程将产生很多中间文件,可专门指定一个目录对它们进行存放。将“工程配置”
对话框中的“Target”标签切换至“Output”标签后,选择表 1-2-1 中提到的 OBJ 文件夹,单
击“Select Folder for Objects”按钮,即可对编译输出文件的存放路径进行配置。另外,如果需
要编译产生“.hex”文件,可勾选“Create HEX File”复选框,如图 1-2-10 所示。
(3)重要宏定义与包含头文件的路径配置
STM32F4 标准外设库支持 F4 全系列产品的编程调用,型号包括 STM32F40x、STM32F41x
和 STM32F42X 等,各型号在外设数量、Flash 容量等方面均有差异。为了提高代码的易用性,
STM32F4 标准外设库采用预编译的方法决定源代码是否参与编译。
编者使用的 STM32F4 开发板上的 MCU 型号为 STM32F407ZGT6,为了使标准外设库中相
应的代码生效,需要增加“STM32F40_41xxx”宏定义,同时增加“USE_STDPERIPH_DRIVER”
宏定义,代表使用标准外设库进行程序开发,如图 1-2-11 所示。
在使用 Keil μVision5 进行工程编译之前,我们需要指定“.h”头文件所在的路径,否则在编
译时会出现无法找到头文件的情况。单击“Include Paths”右侧的按钮,可将需要包含的“.h”
头文件所在的路径都加入其中,如图 1-2-11 所示。
(4)仿真器类型配置
Keil μVision5 支持的仿真器类型很多。工程编译成功以后,在将程序下载到 MCU 中或者运
行软件仿真之前,我们应根据实际所用的仿真器类型对工程进行配置。将“工程配置”对话框中
的“C/C++”标签切换到“Debug”标签后,可以看到“Debug”配置界面分左右两边,左侧的
内容与软件仿真有关,右侧的内容涉及程序下载与硬件在线仿真。单击右侧的下拉按钮,可以看
到 Keil μVision5 支持 13 种仿真器,包括 J-LINK/J-TRACE Cortex、CMSIS-DAPDebugger、
ST-LinkDebugger 等,如图 1-2-12 所示。
(5)仿真器参数配置
STM32 微控制器支持通过 JTAG 接口或 SW 接口与仿真器相连进行在线调试。完整的 JTAG
接口为 20Pin,接口体积大且占用较多的 GPIO 引脚资源,一般用于 JLink 仿真器。而 SW 接口
只需 3 根连线即可实现,一般用于 ST-Link 仿真器。单击图 1-2-12 右上角的“Settings”按钮,
打开“仿真器参数配置”对话框,用户可根据所使用的仿真器进行 Port 参数的配置,如图 1-2-13
所示。如果将开发板与仿真器正确连接并通电,该对话框还可显示识别到的 MCU。
(6)Flash 参数配置
Keil μVision5支持STM32全系列MCU的开发,各型号的Flash 类型(内置或外置)或Flash容
量都不尽相同,因此在程序下载前应根据MCU 型号正确配置Flash参数。在“仿真器参数配置”对话
框中单击“Flash Download”标签,在“Programming Algorithm”选项区可进行Flash 参数的配置。
STM32F407ZGT6 型号 MCU 的内置 Flash 容量为 1MB,单击“Add”按钮后选择正确的选
项即可。一般来说,如果在新建工程的“MCU 型号选择”步骤中选择了特定型号的 MCU,Keil
μVision5 将会自动完成 Flash 参数配置工作。具体配置如图 1-2-14 所示。
7.编译并下载工程
完成工程的所有配置之后,可对工程进行编译并下载。先编写一个简单的 main()函数,在
“main.c”文件中输入以下代码:
#include "stm32f4xx.h"
void Delay(__IO uint32_t nCount);
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
/* 无限循环 */
while (1)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
Delay(0x7FFFFF);
GPIO_ResetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
Delay(0x7FFFFF);
}
}
/**
* @brief 软件延时函数
* @param None
* @retval None
*/
void Delay(__IO uint32_t nCount)
{
while(nCount--){}
}
单击工具栏中的“Build(功能键 F7)”按钮可对工程进行编译,编译无误后单击“Download
(功能键 F8)”按钮可将工程下载至开发板运行,如图 1-2-15 所示。
任务 1.3 STM32F4 系统时钟的配置
1.3.1 任务分析
本任务要求完成 STM32F4 开发板(板上连接的外部晶振频率为 8 MHz)系统时钟的配置,
具体配置要求如下:
系统时钟(SYSCLK)配置为 168 MHz;
高性能总线时钟(HCLK)配置为 168 MHz;
高速外设总线时钟(PCLK2)配置为 84 MHz;
低速外设总线时钟(PCLK1)配置为 42 MHz。
时钟(Clock)是微控制器的脉搏。在数字系统中,所有部件要正常工作都离不开时钟。时
钟之于数字系统,如心跳之于人,其重要性不言而喻。
一般情况下,我们在使用标准外设库进行 STM32 开发时无须对系统时钟进行配置,原
因是 STM32 开发板上提供的晶振频率基本上都与 ST 公司官方标准的晶振频率相同。但对
于一些特殊的应用场景(如超低功耗),我们可能需要对 MCU 进行降频,即对系统时钟进
行配置。
STM32F4 标准外设库为用户提供了复位与时钟控制(Reset and Clock Control,RCC)库,
用于系统时钟的配置。
本任务涉及的知识点有:
STM32F4 系列微控制器的时钟源;
STM32F4 系列微控制器中的一些重要的时钟信号概念;
STM32F4 标准外设库中与 RCC 库配置相关的内容。
1.3.2 知识链接
1.STM32F4 的时钟源
与 51 系列单片机相比,STM32F4 的时钟系统更加复杂。由于 STM32F4 的系统架构复杂,
外设种类繁多,且每种外设所需的时钟频率不尽相同,因此系统有多个时钟源。STM32F4 的时
钟树图标明了各时钟源的信息,如图 1-3-1 所示。
图 1-3-1 中的标号处分别标明了 STM32F4 的 5 个时钟源,它们的介绍如下。
(1)高速内部时钟
高速内部时钟(High Speed Internal,HSI)由 STM32F4 系列微控制器芯片内部的 RC 振荡
器产生,其频率为 16MHz,可作为 SYSCLK 或锁相环(PLL)的时钟源。它位于图 1-3-1 中标
号所示的位置。
(2)高速外部时钟
高速外部时钟(High Speed External,HSE)由石英振荡器或陶瓷振荡器产生,也可直接使
用外部时钟源,其频率范围是 4~26MHz,可作为 SYSCLK 或锁相环(PLL)的时钟源。它位于
图 1-3-1 中标号所示的位置。
(3)低速内部时钟
低速内部时钟(Low Speed Internal,LSI)由 STM32F4 系列微控制器芯片内部的 RC 振荡
器产生,其频率为 32kHz,可供独立“看门狗”或实时时钟(RTC)使用。它位于图 1-3-1 中
标号所示的位置。
(4)低速外部时钟
低速外部时钟(Low Speed External,LSE)一般由频率为 32.768kHz 的石英振荡器产生,
可供独立“看门狗”或实时时钟(RTC)使用。它位于图 1-3-1 中标号所示的位置。
(5)锁相环倍频输出
锁相环(Phase Locked Loop,PLL)的主要作用是对其他输入时钟进行倍频,然后把时钟
信号输出到各个功能部件。STM32F4 系列微控制器有两个 PLL,一个是主 PLL,另一个是 PLLI2S,
它们位于图 1-3-1 中标号所示的位置。
主 PLL 的输入可以是 HSI 或 HSE,输出共有两路:一路输出 PLLP 提供的时钟信号 PLLCLK,
可作为系统时钟 SYSCLK 的时钟源,最高频率 168MHz;另一路输出 PLLQ,可用于生成 48MHz
的时钟信号 PLL48CK,可供给 USB OTG FS、随机数发生器和 SDIO 接口等使用。
PLLI2S 用于生成精准时钟 PLLI2SCLK,其可供给 I2S 总线接口以实现高品质音频输出。
2.几个重要的时钟信号概念
图 1-3-1 中的标号 A、B、C、D、E、F 处分别标明了 STM32F4 中几个重要的时钟信号,
它们的介绍如下。
(1)SYSCLK
系统时钟(SYSCLK)是 STM32 大部分器件的时钟源,它经 AHB 预分频器分频后分配到各
个部件,如图 1-3-1 中标号“A”位置所示。SYSCLK 的时钟源可以是 HSI、HSE 或 PLLCLK,
具体由时钟配置寄存器(RCC_CFGR)的 SW 位进行配置。
(2)HCLK
先进的高性能总线(Advanced High-Performance Bus,AHB)时钟(High-Performance
Clock,HCLK)由 AHB 预分频器分频后得到,分频系数可以是 1、2、4、8、16、64、128、256、
512 等,由 RCC_CFGR 的 HPRE 位进行配置,一般选择 1 分频。HCLK 供 Cortex-M4 内核使
用,它决定了 STM32 系列微控制器的运算速度和数据存取速度,一般也称为主频,如图 1-3-1
中标号“B”位置所示。
(3)SysTick
系统定时器(SysTick)是 Cortex-M4 内核的一个外设,它是一个 24bit 的向下递减的计数
器,一般用于产生时基信号,如实时操作系统中的系统心跳信号就是由 SysTick 产生的。SysTick
的时钟源一般设置为 SYSCLK 的 1/8,如图 1-3-1 中标号“C”位置所示。
(4)FCLK
自由运行时钟(Free Running Clock,FCLK)独立于 HCLK,它用于采样中断信号以及为
调试模块计时。在处理器休眠期间,FCLK 可确保采样到中断信号或跟踪到休眠事件,如图 1-3-1
中标号“D”位置所示。
(5)PCLK1 和 PCLK2
外设时钟(Peripheral Clock,PCLK)是 APB 上的时钟信号。STM32F4 上有两条 APB,分别是低速外设总线(APB1)和高速外设总线(APB2)。APB1 上的时钟信号为 PCLK1,APB2
上的时钟信号为 PCLK2。PCLK1 和 PCLK2 由 SYSCLK 分别经 APB1 预分频器和 APB2 预分频
器分频后产生,分频系数可以是 1、2、4、8、16 等。
PCLK1 的最大频率为 42MHz,它主要为挂载在 APB1 上的低速外设提供时钟,如 USART2、
TIM2 和 TIM3 等。
PCLK2 的最大频率为 84MHz,它主要为挂载在 APB2 上的高速外设提供时钟,如 USART1、
TIM1 和 GPIO 等,如图 1-3-1 中标号“E”位置所示。
(6)TIMxCLK
从图 1-3-1 中标号“F”位置可以看到,APB 预分频器有一路输出接入定时器(Timer)的
倍频器。PCLK 信号经倍频后作为定时器时钟(TIMxCLK),倍频系数可选 1 或 2,即
TIMxCLK=PCLK×1(或 2),更详细的配置内容见本书 3.1.2 节。
1.3.3 任务实施
1.主 PLL 的配置
本任务要求将系统时钟(SYSCLK)配置为 168MHz,而外部晶振(HSE)频率仅为 8MHz,
因此必须使用主 PLL 倍频来实现。主 PLL 的树图如图 1-3-2 所示,我们需要为主 PLL 配置以下
参数:压控振荡器(VCO)输入时钟分频系数 M 、VCO 输出时钟倍频系数 N 、PLLCLK 时钟分
频系数 P 和 48MHz 时钟输出的分频系数 Q
根据《STM32F4xx 中文参考手册》, M 的取值范围为 2~63, N 的取值范围 192~432, P 的
取值为 2、4、6、8 中的一个, Q 的取值范围为 2~15。
从图 1-3-1 可知,SYSCLK 来源于 PLLCLK。当 HSE 为 8MHz 时,如果要使 PLLCLK 输出
为 168MHz,则须配置 M 值为 8, N 值为 336, P 值为 2。计算过程如下所示。
PLLCLK = (8MHz / M ) × ( N / P ) = 1MHz × (336 / 2) = 168MHz
同样地,配置 Q 值为 7 时,可使 PLL48CK 输出满足任务要求。计算过程如下所示。
PLL48CK = (8MHz / M ) × ( N / Q ) = 1MHz × (336 / 7) = 48MHz
2.根据开发板的硬件配置修改标准外设库的相关文件
STM32F4标准外设库的启动文件startup_stm32f40xx.s调用库函数SystemInit()进行系统时
钟配置后,进入用户主程序 main()函数。startup_stm32f40xx.s 文件是用汇编语言实现的,其调
用 SystemInit 库函数的具体实现如图 1-3-3 所示。
由于 STM32F4 标准外设库板载晶振的参数和主 PLL 的各项参数( M 、 N 、 P 、 Q )的默认配
置可能与开发者所用的开发板不匹配,因此需要根据开发板硬件配置对标准外设库相关文件进行
修改。需要修改的地方有以下几处。
一是开发板晶振频率的配置,此项配置位于stm32f4xx.h 文件的第144 行。ST 公司官方开发板
使用的晶振频率为 25 MHz,如果我们的开发板的板载晶振频率为 8 MHz,则需要将原有宏定义
“#define HSE_VALUE ((uint32_t)25000000)”修改为“#define HSE_VALUE ((uint32_t)8000000)”。
二是主 PLL 各项参数( M 、 N 、 P 、 Q )的配置,它们分别位于 system_stm32f4xx.c 文件的
第 371、384、401 和 403 行。如果板载晶振频率为 8 MHz,则需要将第 371 行原有的宏定义
“#define PLL_M 25”修改为“#define PLL_M 8”,即 将 VCO 输入时钟分频系数 M 的值由
25 改为 8。其他各项参数( N 、 P 、 Q )可根据项目实际需要进行配置。
3.为工程添加与 RCC 配置相关的库文件
本任务需要调用与 RCC 配置相关的库函数以完成系统时钟的配置,这些库函数都包含在
stm32f4xx_rcc.c 库文件中,因此我们应在工程中添加此文件,如图 1-3-4 所示。
4.调用与 RCC 配置相关的函数以进行系统时钟的配置
相关准备工作完成以后,我们就可以调用与 RCC 配置相关的函数进行系统时钟的配置。外
部时钟 HSE 作为时钟源的配置步骤如下。
(1)复位 RCC 寄存器值为默认值
RCC_DeInit();
(2)开启 HSE,并等待其工作稳定
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
(3)配置 AHB、APB2、APB1 的预分频系数
RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK = SYSCLK / 1 ( 1 分频)
RCC_PCLK2Config(RCC_HCLK_Div2); // PCLK2 = HCLK / 2 ( 2 分频)
RCC_PCLK1Config(RCC_HCLK_Div4); // PCLK1 = HCLK / 4 ( 4 分频)
(4)配置主 PLL 的参数
RCC_PLLConfig(RCC_PLLSource_HSE, 8, 336, 2, 7);
上述代码片段配置 M = 8, N = 336, P = 2, Q = 7。
(5)开启 PLL,并等待其工作稳定
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
(6)选择 PLLCLK 作为 SYSCLK 的时钟源,并等待其稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08);
5.编写系统时钟配置程序
复制一份任务 1.2 的工程,修改工程名为“RCC_Configuration”,在工程根目录下新建
“SYSTEM”文件夹,并建立子文件夹“sys”,新建“sys.c”和“sys.h”两个文件,在“sys.c”
文件中输入以下代码:
#include "sys.h"
/**
* @brief 使用外部晶振进行系统时钟的配置
* @param M: VCO 输入时钟分频系数
* @param N: VCO 输出时钟倍频系数
* @param P: PLLCLK 时钟分频系数
* @param Q: 48MHz 时钟输出的分频系数
* @retval None
*/
void HSE_RCC_Configuration(uint16_t M, uint16_t N, uint16_t P, uint16_t Q)
{
__IO uint32_t HSEStartUpStatus = 0;
RCC_DeInit();// 将 RCC 寄存器重新设置为默认值
RCC_HSEConfig(RCC_HSE_ON);// 打开 HSE ,并等待其工作稳定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/* HSE 启动成功 */
if (HSEStartUpStatus == SUCCESS)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // 调压器电压输出级别配置为 1
PWR->CR |= PWR_CR_VOS; // 工作时实现性能和功耗的平衡
RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK = SYSCLK / 1
RCC_PCLK2Config(RCC_HCLK_Div2); //PCLK2 = HCLK / 2
RCC_PCLK1Config(RCC_HCLK_Div4); //PCLK1 = HCLK / 4
/* 设置主 PLL 相关时钟分频系数 */
RCC_PLLConfig(RCC_PLLSource_HSE, M, N, P, Q);
/* 开启 PLL ,并等待其工作稳定 */
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* 配置 FLASH 预取值,指令缓存,数据缓存和等待状态 */
FLASH->ACR = FLASH_ACR_PRFTEN
| FLASH_ACR_ICEN
| FLASH_ACR_DCEN
| FLASH_ACR_LATENCY_5WS;
/* 选择 PLLCLK 作为 SYSCLK 的时钟源,并等待其稳定 */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08);
}
/* 如果 HSE 启动失败 */
else
{
//TODO 出错处理
}
}在“sys.h”文件中输入以下代码:#ifndef __SYS_H
#define __SYS_H
#include "stm32f4xx.h"
void HSE_RCC_Configuration(uint16_t M, uint16_t N, uint16_t P, uint16_t Q);
#endif
6.编写 main()函数
在“main.c”文件中输入以下代码并调用自定义的系统时钟配置函数,编译、下载并观察程
序运行情况。如果系统时钟配置成功,可以看到开发板上两个 LED 同亮同灭,并循环往复。
#include "sys.h"
void Delay(__IO uint32_t nCount);
int main(void)
{
/* 调用自定义的系统时钟配置函数 */
HSE_RCC_Configuration(8,336,2,7);
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
/* 无限循环 */
while (1)
{
GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
Delay(0x7FFFFF);
GPIO_ResetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
Delay(0x7FFFFF);
}
}
/**
* @brief 软件延时函数
* @param None
* @retval None
*/
void Delay(__IO uint32_t nCount)
{
while(nCount--){}
}