完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子水星 STM32F4/F7 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子 第六章 跑马灯实验 任何一个单片机,最简单的操作莫过于 IO 口的高低电平控制了,本章将通过一个经典的跑马灯程序,带大家开启 STM32F7 之旅,通过本章的学习,你将了解到 STM32F7 的 IO 口作为输出使用的方法。在本章中,我们将通过代码控制 ALIENTEK 水星 STM32 开发板上的两个LED 灯 DS0 和 DS1 交替闪烁,实现类似跑马灯的效果。 本章分为如下五个小节: 6.1 STM32F7 IO 口简介 6.2 硬件设计 6.3 软件设计 6.4 下载验证 6.5 STM32CubeMX 配置 IO 口输入 6.1 STM32F7 IO 简介 本章将要实现的是控制ALIENTEK水星 STM32开发板上的两个 LED 实现一个类似跑马灯 的效果,该实验的关键在于如何控制 STM32F7 的 IO 口输出。了解了 STM32F7 的 IO 口如何输 出的,就可以实现跑马灯了。通过这一章的学习,你将初步掌握 STM32F7 基本 IO 口的使用, 而这是迈向 STM32F7 的第一步。 这一章节因为是第一个实验章节,所以我们在这一章将讲解一些知识为后面的实验做铺垫。 为了小节标号与后面实验章节一样,这里我们不另起一节来讲。 在讲解 STM32F7 的 GPIO 之前,首先打开我们光盘的第一个 HAL 库版本实验工程跑马灯 实验工程(光盘目录为“ : 4,程序源码标准例程-库函数版本实验1跑马灯/USER/ LED.uvproj”) , 可以看到我们的实验工程目录如下图 6.1.1 所示: 图 6.1.1 跑马灯实验目录结构 接下来我们逐一讲解一下我们的工程目录下面的组以及重要文件。 ① 组HALLIB下面存放的是ST官方提供的HAL库文件,每一个源文件stm32f7xx_hal_ppp.c 都对应一个头文件 stm32f7xx_hal_ppp.h。分组内的源文件我们可以根据工程需要添加和删除。 这里对于跑马灯实验,我们需要添加 11 个源文件。 ② 组 CORE 下面存放的是固件库必须的核心头文件和启动文件。这里面的文件用户不需要 修改。大家可以根据自己的芯片型号选择对应的启动文件。 ③ 组 SYSTEM 是 ALIENTEK 提供的共用代码,这些代码在第五章都有详细讲解。 ④ 组 HARDWARE 下面存放的是每个实验的外设驱动代码,他的实现是通过调用 HALLIB下面的 HAL 库文件函数实现的,比如 led.c 中函数调用 stm32f7xx_hal_gpio.c 内定义的函数对 led 进行初始化,这里面的函数是讲解的重点。后面的实验中可以看到会引入多个源文件。 ⑤ 组 USER 下面存放的主要是用户代码。但是 system_stm32f7xx.c 文件用户不需要修改, 同时 stm32f7xx_it.c 里面存放的是中断服务函数,这两个文件的作用在 3.3 节有讲解。main.c 函 数主要存放的是主函数了。 工程分组情况我们就讲解到这里,接下来我们就要进入我们跑马灯实验的讲解部分了。这 里需要说明一下,我们在讲解 HAL 库之前会首先对重要寄存器进行一个讲解,这样是为了大 家对寄存器有个初步的了解。大家学习 HAL 库,并不需要记住每个寄存器的作用,而只是通 过了解寄存器来对外设一些功能有基本的了解,这样对以后的学习也很有帮助。 相对于 STM32F1 来说,STM32F7 的 GPIO 设置显得更为复杂,也更加灵活,尤其是复用 功能部分,比 STM32F1 改进了很多,使用起来更加方便。 STM32F7 每组通用 I/O 端口包括 4 个 32 位配置寄存器(MODER、OTYPER、OSPEEDR 和 PUPDR)、2 个 32 位数据寄存器(IDR 和 ODR)、1 个 32 位置位/复位寄存器 (BSRR)、 1 个 32 位锁定寄存器 (LCKR) 和 2 个 32 位复用功能选择寄存器(AFRH 和 AFRL)等。 这样,STM32F7 每组 IO 有 10 个 32 位寄存器控制,其中常用的有 4 个配置寄存器+2 个数 据寄存器+2 个复用功能选择寄存器,共 8 个,如果在使用的时候,每次都直接操作寄存器配置 IO,代码会比较多,也不容易记住,所以我们在讲解寄存器的同时会讲解是用库函数配置 IO 的方法。 同 STM32F1 一样,STM32F7 的 IO 可以由软件配置成如下 8 种模式中的任何一种: 1、输入浮空 2、输入上拉 3、输入下拉 4、模拟输入 5、开漏输出 6、推挽输出 7、推挽式复用功能 8、开漏式复用功能 关于这些模式的介绍及应用场景,我们这里就不详细介绍了,感兴趣的朋友,可以看看这 个帖子了解下:http://www.openedv.com/posts/list/32730.htm 。接下来我们详细介绍 IO 配置常 用的 8 个寄存器: MODER、OTYPER、OSPEEDR、PUPDR、ODR、IDR 、AFRH 和 AFRL。 同时讲解对应的 HAL 库配置方法。 首先看 MODER 寄存器,该寄存器是 GPIO 端口模式控制寄存器,用于控制 GPIOx (STM32F7 最多有 9 组 IO,分别用大写字母表示,即 x=A/B/C/D/E/F/G/H/I,下同)的工作模 式,该寄存器各位描述如表表 6.1.2 所示: 表 6.1.2 GPIOx MODER 寄存器各位描述 该寄存器各位在复位后,一般都是 0(个别不是 0,比如 JTAG 占用的几个 IO 口),也就是 默认条件下一般是输入状态的。每组 IO 下有 16 个 IO 口,该寄存器共 32 位,每 2 个位控制 1 个 IO,不同设置所对应的模式见表 6.1.1 描述。 然后看 OTYPER 寄存器,该寄存器用于控制 GPIOx 的输出类型,该寄存器各位描述见表 表 6.1.3 所示: 表 6.1.3 GPIOx OTYPER 寄存器各位描述 该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器 低 16 位有效,每一个位控制一个 IO 口。设置为 0 是推挽输出,设置为 1 是开漏输出。复位后, 该寄存器值均为 0,也就是在输出模式下 IO 口默认为推挽输出。 然后看 OSPEEDR 寄存器,该寄存器用于控制 GPIOx 的输出速度,该寄存器各位描述见表 表 6.1.4 所示: 表 6.1.4 GPIOx OSPEEDR 寄存器各位描述 该寄存器也仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存 器每 2 个位控制一个 IO 口,复位后,该寄存器值一般为 0。 然后看 PUPDR 寄存器,该寄存器用于控制 GPIOx 的上拉/下拉,该寄存器各位描述见表表 6.1.5 所示: 表 6.1.5 GPIOx PUPDR 寄存器各位描述 该寄存器每 2 个位控制一个 IO 口,用于设置上下拉,这里提醒大家,STM32F1 是通过 ODR 寄存器控制上下拉的,而 STM32F7 则由单独的寄存器PUPDR控制上下拉,使用起来更加灵活。 复位后,该寄存器值一般为 0。 前面,我们讲解了 4 个重要的配置寄存器。顾名思义,配置寄存器就是用来配置 GPIO 的 相关模式和状态,接下来我们讲解怎么在 HAL 库中初始化 GPIO 配置。 GPIO 相 关 的 函 数 和 定 义 分 布 在 HAL 库文件 stm32f7xx_hal_gpio.c 和 头 文 件 stm32f7xx_hal_gpio.h 文件中。 在 HAL 库中,操作四个配置寄存器初始化 GPIO 是通过 HAL_GPIO_Init 函数完成: void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);该函数有两个参数,第一个参数是用来指定需要初始化的 GPIO 对应的 GPIO 组,取值范 围为 GPIOA~GPIOK。第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。 下面我们看看这个结构体的定义。首先我们打开我们光盘的跑马灯实验,然后找到 HALLIB 组 下面的 stm32f7xx_hal_gpio.c 文件,定位到 HAL_GPIO_Init 函数体处,双击入口参数类型 GPIO_InitTypeDef 后右键选择“Go to definition of …”可以查看结构体的定义如下: typedef struct{ uint32_t Pin; //指定 IO 口 uint32_t Mode; //模式设置 uint32_t Pull; //上下拉设置 uint32_t Speed; //速度设置 uint32_t Alternate;//复用映射配置}GPIO_InitTypeDef;结构体有 5 个成员变量,关于怎么来确定这 5 个成员变量的取值范围,请参考 4.7 小节内 容。下面我们通过一个 GPIO 初始化实例来讲解这个结构体的成员变量的含义。 初始化 GPIO 的常用格式是: GPIO_InitTypeDef GPIO_Initure;GPIO_Initure.Pin=GPIO_PIN_0; //PB0 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速HAL_GPIO_Init(GPIOB,&GPIO_Initure);上面代码的意思是设置 PB0 端口为推挽输出模式,输出速度为高速,上拉。 从上面初始化代码可以看出,结构体 GPIO_Initure 的第一个成员变量 Pin 用来设置是要初 始化哪个或者哪些 IO 口。第二个成员变量 Mode 是用来设置对应 IO 端口的输出输入端口模式, 这个变量实际配置的是我们前面讲解的 GPIOx 的 MODER 寄存器。第三个成员变量 Pull 是用 来设置上拉还是下拉,配置的是 GPIOx PUPDR 寄存器。第四个成员变量 Speed 用来设置输出 速度,配置的是 GPIOx OSPEEDR 寄存器。第五个成员变量 Alternate,我们在 4.4 小节引脚复 用器和映射已经讲解,它是用来设置引脚的复用映射的。 这些入口参数的取值范围怎么定位,怎么快速定位到这些入口参数取值范围的枚举类型, 在我们上面章节 4.7 的“快速组织代码”章节有讲解,不明白的朋友可以翻回去看一下,这里 我们就不重复讲解,在后面的实验中,我们也不会再重复讲解定位每个参数取值范围的方法。 看完了 GPIO 的参数配置寄存器,接下来我们看看 GPIO 输入输出电平控制相关的寄存器。 首先我们看 ODR 寄存器,该寄存器用于控制 GPIOx 的输出电平,该寄存器各位描述见表 6.1.6 所示: 表 6.1.6 GPIOx ODR 寄存器各位描述 该寄存器用于设置某个 IO 输出低电平(ODRy=0)还是高电平(ODRy=1),该寄存器也仅在输 出模式下有效,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器在 HAL 库中使用 不多,操作这个寄存器的库函数主要是 HAL_GPIO_TogglePin 函数: void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 该函数是通过操作 ODR 寄存器,达到取反 IO 口输出电平的功能。 接下来我们看看另一个非常重要的寄存器 BSRR,它叫置位/复位寄存器。该寄存器和 ODR 寄存器具有类似的作用,都可以用来设置 GPIO 端口的输出位是 1 还是 0。寄存器描述如下: 表 6.1.7 BSRR 寄存器各位描述 对于低 16 位(0-15),我们往相应的位写 1,那么对应的 IO 口会输出高电平,往相应的位写 0, 对 IO 口没有任何影响。高 16 位(16-31)作用刚好相反,对相应的位写 1 会输出低电平,写 0 没有任何影响。也就是说,对于 BSRR 寄存器,你写 0 的话,对 IO 口电平是没有任何影响的。 我们要设置某个 IO 口电平,只需要相关位设置为 1 即可。而 ODR 寄存器,我们要设置某个 IO 口电平,我们首先需要读出来 ODR 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置 某个或者某些 IO 口的目的,而 BSRR 寄存器,我们就不需要先读,而是直接设置即可,这在 多任务实时操作系统中作用很大。 BSRR 寄存器使用方法如下: GPIOA->BSRR=1<<1; //设置 GPIOA.1 为高电平 GPIOA->BSRR=1<<(16+1)//设置 GPIOA.1 为低电平; 库函数操作 BSRR 寄存器来设置 IO 电平的函数为: void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); 该函数用来设置一组 IO 口中的一个或者多个 IO 口的电平状态。比如我们要设置 GPIOB.5 输出 高,方法为: HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); //GPIOB.5 输出高 设置 GPIOB.5 输出低电平,方法为: HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_RESET); //GPIOB.5 输出低 接下来我们看看 IDR 寄存器,该寄存器用于读取 GPIOx 的输入数据,该寄存器各位描述 见表 6.1.8 所示: 表 6.1.8 GPIOx IDR 寄存器各位描述 该寄存器用于读取某个 IO 的电平,如果对应的位为 0(IDRy=0),则说明该 IO 输入的是低 电平,如果是 1(IDRy=1),则表示输入的是高电平。HAL 库操作该寄存器读取 IO 输入数据相 关函数: GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 该函数用来读取一组IO下一个或者多个IO口电平状态。比如我们要读取GPIOF.5的输入电平, 方法为: HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_5);//读取 PF5 的输入电平 该函数返回值就是 IO 口电平状态。 最后我们来看看 2 个 32 位复用功能选择寄存器(AFRH 和 AFRL),这两个寄存器是用 来设置 IO 口的复用功能的。实际上,在我们调用函数 HAL_GPIO_Init 的时候,如果我们设置 了初始化结构体成员变量 Mode 为复用模式,同时设置了 Alternate 的值,那么会在该函数内部 自动设置这两个寄存器的值,达到设置端口复用映射的目的。关于这两个寄存器的详细配置以 及相关库函数的使用,在我们前面 4.4 小节 IO 引脚复用和映射有详细讲解,这里我们只是简 要说明一下。 GPIO 相关的函数我们先讲解到这里。虽然 IO 操作步骤很简单,这里我们还是做个概括性 的总结,操作步骤为: 1) 使能 IO 口时钟,调用函数为__HAL_RCC_GPIOX_CLK_ENABLE(其中 X=A~K)。 2) 初始化 IO 参数。调用函数 HAL_GPIO_Init(); 3) 操作 IO 输入输出。操作 IO 的方法就是上面我们讲解的方法。 上面我们讲解了 STM32F7 IO 口的基本知识以及 HAL 库操作 GPIO 的一些函数方法,下面 我们来讲解我们的跑马灯实验的硬件和软件设计。 6.2 硬件设计 本章用到的硬件只有 LED(DS0 和 DS1)。其电路在 ALIENTEK 水星 STM32 开发板上默 认是已经连接好了的。DS0 接 PB1,DS1 接 PB0。所以在硬件上不需要动任何东西。其连接原 理图如图 6.2.1 下: 图 6.2.1 LED 与 STM32F767 连接原理图 6.3 软件设计 这是我们学习的第一个实验,所以我会手把手教大家怎么从我们前面讲解的 Template 工程 模板一步一步加入 HAL 库以及 led 相关的驱动函数到我们工程,使之跟我们光盘的跑马灯实验 工程一模一样。首先大家打开我们 3.3 小节新建的 HAL 库工程模板。如果您还没有新建,也可 以直接打开我们光盘已经新建好了的工程模板,路径为:“4,程序源码标准例程-库函数版本 实验 0-1 Template 工程模板-新建工程章节使用”(注意,是直接点击工程下面的 USER 目录下 面的 Tempate.uvprojx。)。 大家可以看到,我们模板里面的 HALLIB 分组下面,我们引入了所有的 HAL 库源文件和 对应的头文件,如下图 6.3.1: 图 6.3.1 Template 模板工程结构 实际上,这些大家可以根据工程需要添加,比如跑马灯实验并没有用到 ADC,我们可以在 工程中删掉文件 stm32f7xx_hal_adc.c,这样可以大大减少工程编译时间。跑马灯实验我们一共 使用到 HAL 库中 11 个源文件,具体哪 11 个请直接参考我们跑马灯实验工程,其他不用的源文 件大家可以直接在工程中删除。在工程的 Manage Project Items 页面,选择要删除文件所在的分 组,然后选中文件点击删除按钮即可。具体操作方法如下图 6.3.2 所示: 图 6.3.2 删除工程分组中的文件 接下来我们进入我们工程的目录,在工程根目录文件夹下面新建一个 HARDWARE 的文 件夹,用来存储以后与硬件相关的代码。然后在 HARDWARE 文件夹下新建一个 LED 文件夹, 用来存放与 LED 相关的代码。如图 6.3.3 所示: 图 6.3.3 新建 HARDWARE 文件夹 接 下 来, 我们 回到 我们 的 工程 ( 如果 是 使用的 上 面新 建的 工程 模板 , 那么 就是 Template.uvproj,大家可以将其重命名为 LED.uvproj),按 按钮新建一个文件,然后按 保存 在 HARDWARE->LED 文件夹下面,保存为 led.c,操作步骤如下图 6.3.4 和 6.3.5: 图 6.3.4 新建文件 图 6.3.5 保存 led.c 然后在 led.c 文件中输入如下代码(代码大家可以直接打开我们光盘的实验 1 跑马灯实验, 从 led.c 文件内复制过来),输入后保存即可: #include "led.h"//初始化 PB1 为输出.并使能时钟//LED IO 初始化void LED_Init(void){ GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_GPIOB_CLK_ENABLE(); //开启 GPIOB 时钟 GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0 GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速HAL_GPIO_Init(GPIOB,&GPIO_Initure); HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //PB0 置 1 ,默认灯灭 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1 置 1 ,默认灯灭}该代码里面就包含了一个函数 void LED_Init(void),该函数通过调用函数 HAL_GPIO_Init 实现配置PB0和PB1为推挽输出。关于函数HAL_GPIO_Init的使用方法在6.1小节有详细讲解。 这里需要注意的是:在配置STM32外设的时候,任何时候都要先使能该外设的时钟。使能GPIOB 时钟方法为: __HAL_RCC_GPIOB_CLK_ENABLE(); //使能 GPIOB 时钟 在设置完时钟之后,LED_Init 调用 HAL_GPIO_Init 函数完成对 PB0 和 PB1 的初始化配置, 然后调用函数 HAL_GPIO_WritePin 控制 LED0 和 LED1 输出 1(LED 灭)。至此,两个 LED 的 初始化完毕。这样就完成了对这两个 IO 口的初始化。这段代码的具体含义,大家可以看前面 6.1 小节,我们有详细的讲解。 保存 led.c 代码,然后我们按同样的方法,新建一个 led.h 文件,也保存在 LED 文件夹下面。 在 led.h 中输入如下代码: #ifndef _LED_H#define _LED_H#include "sys.h"//LED 端口定义#define LED0(n)(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET))#define LED0_Toggle (HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1))#define LED1(n)(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET))#define LED1_Toggle (HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0))void LED_Init(void);#endif这段代码中,对于宏定义标识符 LED0(n),它的值是通过条件运算符来确定:当 n=0 时, 标识符的值为 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET),也就是设置 PB1 输 出 低 电 平 , 当 n!=0 时 , 标 识 符 的 值 为 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET,也就是设置 PB1 输出低电平。所以 如果要设置 LED0 输出低电平,那么调用标识符 LED0(0)即可,当要设置 LED1 输出高电平, 调用标识符 LED0(1)即可。标识符 LED1(n)和 LED0(n)作用类似。对于标识符 LED0_Toggle 和 LED1_Toggle , 它 们 作 用 非 常 简 单 , 就 是 调 用 HAL 库函数 void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)来实现 IO 口输出电平取反操 作。这里我们定义好上面的宏定义之后,就可以直接通过直接操作宏定义来实现 LED0 和 LED1 的状态控制,方法如下: LED0(0);//PB1 输出低电平,LED0 亮LED1(1);//PB1 输出高电平,LED0 灭这里大家要注意,STM32F7 不支持位带操作,所以这里我们并没有像 F1/F4 一样通过位 带操作来实现 IO 口输出输入电平控制。 将 led.h 也保存一下。接着,我们在 Manage Project Itmes 管理里面新建一个 HARDWARE 的组,并把 led.c 加入到这个组里面,如图 6.3.6 所示: 图 6.3.6 给工程新增 HARDWARE 组 单击 OK,回到工程,然后你会发现在 Project Workspace 里面多了一个 HARDWARE 组, 在该组下面有一个 led.c 文件。如图 6.3.7 所示: 图 6.3.7 工程主界面 然后用之前介绍的方法(在 3.3 节介绍的)将 led.h 头文件的路径加入到工程里面,然后点 击 OK 回到主界面,如下图 6.3.8 所示: 图 6.3.8 添加 LED 目录到 PATH 回到主界面后,修改 main.c 文件内容如下(具体内容请参考跑马灯实验 main.c 文件): #include "sys.h"#include "delay.h"#include "usart.h"#include "led.h"int main(void){ Cache_Enable(); //打开 L1-Cache HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //延时初始化uart_init(115200); //串口初始化LED_Init(); //初始化 LED while(1) { HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);//LED0 对应引脚 PB1 拉低,亮,等同于 LED0(0) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);//LED1 对应引脚 PB0 拉高,灭,等同于 LED1(1) delay_ms(500); //延时 500ms HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);//LED0 对应引脚 PB1 拉高,灭,等同于 LED0(1) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);//LED1 对应引脚 PB0 拉低,亮,等同于 LED1(0) delay_ms(500); //延时 500ms }}代码包含了#include "led.h"这句,使得 LED0(n)、LED1(n)、LED_Init 等能在 main()函数里被调 用。main()函数非常简单,先调用Cache_Enable()函数使能I-Cache和D-Cache,然后调用HAL_Init 函数初始化 HAL 库,调用 Stm32_Clock_Init 进行时钟系统配置,调用 delay_init()函数进行延时 初始化。接着就是调用 LED_Init()来初始化 PB0 和 PB1 为推挽输出模式,最后在 while 死循环 里面实现 LED0 和 LED1 交替闪烁,间隔为 500ms。 上面是通过库函数来实现的 IO 操作,我们也可以修改 main()函数,通过直接操作相关寄存 器的方法来设置 IO,我们只需要将主函数修改为如下内容: int main(void){ Cache_Enable(); //打开 L1-Cache HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //延时初始化uart_init(115200); //串口初始化 LED_Init();while(1){ GPIOB->BSRR=GPIO_PIN_1; //LED0 亮 GPIOB->BSRR=GPIO_PIN_0<<16; //LED1 灭delay_ms(500);//延时 500ms GPIOB->BSRR=GPIO_PIN_1<<16; //LED0 灭GPIOB->BSRR=GPIO_PIN_0; //LED1 亮 delay_ms(500);//延时 500ms}}将主函数替换为上面代码,然后重新执行,可以看到,结果跟库函数操作效果一样。大家 可以对比一下。这个代码在我们跑马灯实验的 main.c 文件中有注释掉,大家可以替换试试。 然后按 ,编译工程,得到结果如图 6.3.9 所示: 图 6.3.9 编译结果 可以看到没有错误,也没有警告。从编译信息可以看出,我们的代码占用 FLASH 大小为: 6624 字节(6062+562),所用的 SRAM 大小为:1960 个字节(1944+16)。 这里我们解释一下,编译结果里面的几个数据的意义: Code:表示程序所占用 FLASH 的大小(FLASH)。 RO-data:即 Read Only-data,表示程序定义的常量(FLASH)。 RW-data:即 Read Write-data,表示已被初始化的变量(SRAM) ZI-data:即 Zero Init-data,表示未被初始化的变量(SRAM) 有了这个就可以知道你当前使用的 flash 和 sram 大小了,所以,一定要注意的是程序的大 小不是.hex 文件的大小,而是编译后的 Code 和 RO-data 之和。 接下来,大家就可以下载验证了。如果有 ST-LINK,则可以用 ST-LINK 进行在线调试(需 要先下载代码),单步查看代码的运行,STM32F7 的在线调试方法介绍请参见 3.4.2 小节。 6.4 下载验证 这里我们使用 ST LINK 下载(也可以通过其他仿真器下载,如果是 JLINK,必须是 V9 或 者以上版本,才可以支持 STM32F767!! 下同),关于 ST LINK 的详细设置,请参考:4.1 节, 设置完成后,在 MDK 里面点击 图标,就可以开始下载,如图 6.4.1 所示: 6.4.1 利用 ST LINK 下载代码 下载完之后,运行结果如图 6.4.2 所示: 图 6.4.2 程序运行结果 至此,我们的第一章的学习就结束了,本章作为 STM32F767 的入门第一个例子,介绍了 STM32F767 的 IO 口的使用及注意事项,同时巩固了前面的学习,希望大家好好理解一下。 6.5 STM32CubeMX 配置 IO 口输入 在讲解完使用 HAL 库操作 GPIO 口之后,本小节我们教大家怎么使用 STM32CubeMX 图 形化配置工具配置 GPIO 初始化过程。关于 STM32CubeMX 工具的入门使用在前面 4.8 小节我 们有手把手教大家入门该工具,本小节我们就不重复讲解入门部分,我们直接讲解在 STM32CubeMX 工具中怎么来配置 GPIO 口的相关参数。 首先大家打开 STM32CubeMX 工具,参考 4.8 小节内容进行 RCC 相关配置。这里大家也 可以直接打开 4.8 小节的 STM32CubeMX 工程直接在工程上面修改,该工程保存的光盘目录为: “4,程序源码标准例程-库函数版本实验0-3 Template工程模板-使用STM32CubeMX配置”。 这里大家会发现,我们在 4.8 小节实际上已经讲解了 GPIO 的配置,并且同样是以 PB0 和 PB1 为例。这里我们将详细解析在 STM32CubeMX 中配置 IO 口详细参数的过程。使用 STM32CubeMX 配置 GPIO 口的步骤如下: 第一步,打开 STM32CubeMX 工具,在引脚图中选择要配置的 IO 口。这里我们选择 PB0 为例,在弹出的下拉菜单中选择要配置的 IO 口模式,如下图 6.5.1 所示: 图 6.5.1 选择 IO 口模式 从上图可以看出,这里我们除了配置 IO 口为输入输出之外,还可以选择 IO 口的复用功能 或者作为外部中断引脚功能,比如我们要选择 IO 口复用为 ADC1 的通道 8 引脚,那么我们只 需要选选项 ADC1_IN8 即可。对于本章跑马灯实验,PB0 是作为输出,所以我们选 GPIO_Output 即可。 第二部,进入 Configuration->GPIO,在弹出的界面配置 IO 口的详细参数。如下图 6.5.2 和 图 6.5.3 所示: 图 6.5.2 进入 GPIO 详细参数配置界面 为例,在弹出的下拉菜单中选择要配置的 IO 口模式,如下图 6.5.1 所示: 图 6.5.1 选择 IO 口模式 从上图可以看出,这里我们除了配置 IO 口为输入输出之外,还可以选择 IO 口的复用功能 或者作为外部中断引脚功能,比如我们要选择 IO 口复用为 ADC1 的通道 8 引脚,那么我们只 需要选选项 ADC1_IN8 即可。对于本章跑马灯实验,PB0 是作为输出,所以我们选 GPIO_Output 即可。 第二部,进入 Configuration->GPIO,在弹出的界面配置 IO 口的详细参数。如下图 6.5.2 和 图 6.5.3 所示: 图 6.5.3 配置 IO 口详细参数 在 IO 口详细参数配置界面,点击我们要配置的 IO 口,会在窗口下方显示该 IO 口配置的 详细参数表,下面我们依次来解释这些配置项的含义: ① 选项 GPIO mode 用来设置输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开 漏)。本实验我们设置为推挽输出 Output Push Pull。 ② 选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置 为上拉(Pull-up)。 ③ 选项Mzximum ouput speed用来设置输出速度为高速(Hign)/快速(Fast)/中速(Medium)/低 速(Low)。本实验我们设置为高速 High。 ④ 选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,一般情况我们可以 不用设置,有兴趣的同学可以自由设置后查看生成后的代码就很容易明白其含义。 配置完 PB0 后,PB1 配置方法和参数都一模一样,这里我们就不重复配置。 然后我们参考 4.8 下节方法,生成工程源码。接下来打开工程的 main.c 文件可以看到,该 文件内部由 STM32CubeMX 生成了函数 MX_GPIO_Init,内容如下: static void MX_GPIO_Init(void){ GPIO_InitTypeDef GPIO_InitStruct;/* GPIO Ports Clock Enable */ __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_RESET); /*Configure GPIO pins : PB0 PB1 */ GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);}该函数的作用跟前面讲解的跑马灯实验中 LED_Init 函数作用一模一样,有兴趣的同学可以 直接修改跑马灯实验工程源码,把 LED_Init 函数内容修改为 MX_GPIO_Init 内容,大家会发现 实验效果一模一样。对于 IO 初始化后的默认状态,这里我们还需要根据自己需要来修改,也 就是修改代码行: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_RESET); 这里我们希望默认情况下,PB1 和 PB0 输出为高电平,也就是灯灭,所以这里我们需要修 改为: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_SET);一般情况下,STM32CubeMX 的主要作用是配置时钟系统和外设初始化函数。所以我们对 外设进行配置之后,生成外设初始化代码,然后把该代码应用到我们工程即可。关于本章中使 用 STM32CubeMX 配置 GPIO 方法,我们就给大家讲解到这里。 |
|
相关推荐
|
|
STM32配合可编程加密芯片SMEC88ST的防抄板加密方案设计
657 浏览 0 评论
2334 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
2137 浏览 3 评论
4767 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
2282 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-29 22:40 , Processed in 1.110771 second(s), Total 66, Slave 48 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号