iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第二十五章Cortex-M4 GPIO_LED实验
本章节最终所完成的实验例程存放路径为“iTOP-STM32MP157开发板网盘资料汇总\06_Cortex-M4实验例程\01_LED.zip”。
25.1什么是GPIO
GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能设备。
STM32MP157 的 GPIO 有 GPIOA 至 GPIOK 和 GPIOZ 共 12 组 GPIO,其中 GPIOA~GPIOK每组有 16 个 IO,而 GPIOZ 有 8 个 IO。所有的 GPIO 均带有中断功能,所有的 GPIO 都可以被Cortex-M4 和 Cortex-A7 共享访问,而 GPIOZ 可用于 TrustZone 安全性相关的设置(当用于此项时仅限于 Cortex-A7 访问),相关的外围设备的软件访问被定义为安全性访问,常用于安全解决方案中。这里,STM32MP157 共 16*5+8=176+8=184 个 IO,而我们的开发板总共引出了144个引脚。
25.2 实验目的
1)GPIO的工作模式的初步认识
2)STM32CubeIDE工具软件的使用与熟悉
3)GPIO口使用方法的学习
25.3 GPIO的工作模式
STM32MP157的GPIO可以通过软件的配置设置成以下八种工作模式:
1.GPIO浮空输入_IN_FLOATING模式
2.GPIO带上拉输入_IPU 模式
3.GPIO带下拉输入_IPD 模式
4.GPIO模拟输入_AIN 模式
5.GPIO开漏输出_OUT_OD 模式
6.GPIO推挽输出_OUT_PP模式
7.GPIO开漏复用输出_AF_OD模式
8.GPIO推挽复用输出_AF_PP模式
下面对这八种工作模式进行简单的分析:
浮空输入模式
当GPIO采用浮空输入模式时,STM32的引脚状态是不确定的,此时STM32得到的电平状态完全取决于GPIO外部的电平状态,所以说在GPIO外部的引脚悬空时,读取该端口的电平状态是个不确定的值。
上拉、下拉输入模式
由于浮空模式时,在GPIO外部连接的电路未工作时,STM32读取的GPIO状态是不确定的,所以可以通过带上拉或者下拉输入的模式先给MCU一个确定的状态,当外部电路电平状态发生变化时,易于MCU的判断。这样可以增强MCU的抗干扰能力
模拟输入模式
最常用的场合是ADC模拟输入,不像其他输入模式只有0和1,模拟输入模式可以读取到很细微变化的值。
推挽输出模式
推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止。这种结构既可以输出高电平,也可以输出低电平,可以用于连接数字器件。
推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
开漏输出模式
一般开漏输出模式时,如果外部不接上拉电阻时,只能输出低电平,所以要想输出高电平必须要外接上拉电阻。这样做的有一个好处,可以用来匹配不同的电平信号,也就是用于不同电压的系统之间的通信;另外,因为要输出高电平需要有外部的上拉电阻,所以在进行通信时,通信的速度也受到上拉电阻阻值的影响,阻值小时,通信速度可以很快,阻值大时,通信速度变慢,但也不能为了通信速度把上拉电阻用的很小,也要注意在电阻很小时,功耗会变大,所以要平衡好这个度。
复用推挽,开漏输出模式
这两种模式,可以理解为把GPIO配置为第二功能使用的时候的配置,并非单纯的用作IO输入或输出。
比如使用外设IIC时,我们需要把GPIO配置为复用推挽输出,用于数据通信功能。再比如串口通信的TX,以及SPI外设的GPIO使用就要把引脚设置为复用开漏输出。
25.4 LED电路的分析
我们的LED电路的原理图如下:
可以看到LED发光二极管由Q1和Q2两个L9014的NPN三极管控制通断,当三极管导通时LED发光二极管也会导通,当三极管截止时LED发光二极管也会截止,所以我们只需要通过控制两个GPIO引脚输出的高低电平来控制三极管的通断,进而控制LED发光二极管的亮灭。
以下为两个LED对应的控制引脚
LED2 | LED3 |
PE14 | PE1 |
25.5 实验步骤
25.5.1建立LED工程
首先我们打开STM32CubeIDE软件,进入软件界面之后,我们点击File属性,选择NEW下的STM32 Project的选项,如下图所示:
然后我们会进入下图所示界面:在Part Number选择框输入STM32MP157A,然后在右边的选择界面选择STM32MP157AAA,然后点击Next选项
在Project Name框中输入工程名字LED,然后点击Finish选项即可,如下图所示:
等待工程创建完毕,会询问我们是否要安装OpenSTLinux ,由于我们是在windows环境下,所以我们不需要安装,点击NO即可
至此我们的工程创建完毕,进入工程界面如下图所示界面:
25.5.2 GPIO功能引脚配置
首先我们在下面的搜索框之中输入我们要配置的引脚,我们在这里以PE1为例进行搜索,输入名称之后,对应的引脚在工程中会闪烁,如下图所示:
然后我们使用鼠标左键点击对应的引脚会弹出PE1的复用功能选择,我们在这里选择复用为GPIO_Output功能,如下图所示:
配置完复用功能之后,我们还要配置 Pin Reserved 选项,如果不配置此项,在生成工程代码的时候将不会看到有关这个 Pin 的初始化代码。继续选中 PE1,右键弹出设置项,我们选择Pin ReservedàCortex-M4。如下图所示:
第二个LED的控制管脚PE14按同样的方法进行配置。
配置完成之后打开左侧菜单的 System CoreàGPIO 进入 GPIO 模式配置界面:如下图所示:
点击对应的引脚配置之后会弹出右下方的管脚配置界面,如上图所示:
在下方会列出要配置选项的具体说明和我们要进行的配置。
1)选项 GPIO output level 用来设置IO口的输出电平的高低,这这里我们选择LOW
2)选项GPIO mode 用来设置 IO 口输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开漏)。本实验我们设置为推挽输出 Output Push Pull。
3)选项 GPIO Pull-up/Pull-down 用来设置 IO 口是上拉/下拉/没有上下拉。本实验我们设置为上拉(Pull-up)。
4)选项 Mzximum ouput speed 用来设置 IO 口输出速度为低速(Low)/中速(Medium)/高速 (Hign)/快速(Very High)。我们设置为高速 High 。
5)选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,这里我们填写为 LED3。按照如上要求设置后的界面如下(由于PE14的配置相同,只是最后的Label值不同,也在下方列了出来):
配置完成之后我们需要在Project Manage下的Code Generator选项下勾选 Generate peripheral initialization as a pair of ".c/.h' files per peripheral 选项,这样可以独立生成对应外设的初始化.h 和.c 文件(方便配置的查看),如下图所示:
25.5.3工程的生成与完善
在上述的步骤完成之后,按下键盘的“Ctrl+S”组合键保存保存 LED.ioc 文件,系统开始生成初始化代码,工程生成之后如下图所示:
然后我们进行工程的完善,以及添加对应的逻辑代码。
25.5.3.1 对应文件与文件夹的添加
首先在左侧的工程浏览页之中通过鼠标右键在LED_CM4的Core目录下创建名字为BSP的文件,具体步骤如下图所示:
然后在BSP目录下以同样的方法创建led.c文件和Include目录,以及Include目录下的led.h文件,创建完成如下图所示:
25.5.3.2 led.h文件的完善
我们对led.h文件进行代码的添加,将以下内容复制到led.h文件之中如下图所示:
1 #ifndef __LED_H
2 #define __LED_H
3 #include "gpio.h"
4 #define LED2(x) do{ x ? \
5 HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET) : \
6 HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); \
7 }while(0)
8 #define LED3(x) do{ x ? \
9 HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET) : \
10 HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); \
11 }while(0)
12 #define LED2_TOGGLE() do{ HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin); }while(0)
13 #define LED3_TOGGLE() do{ HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); }while(0)
14 void led_init(void);
15 #endif
下面我们将对该文件进行简单的讲解:
在第3行,我们引用了gpio.h文件,我们进入gpio.c文件之中,文件路径如下图所示:
在对应的位置下会看到MX_GPIO_Init()函数,很明显的可以看出该函数正是我们在3.5.2小节之中所配置的功能。
返回led.h文件之中,在4-13行便是四个宏定义,其中前两个宏定义是用来设置LED状态的,后两个宏定义是使LED状态进行反转的。具体功能是通过两个函数来实现的分别为HAL_GPIO_WritePin和HAL_GPIO_TogglePin。
在stm32mp1xx_hal_gpio.c文件之中可以找到HAL_GPIO_WritePin和HAL_GPIO_TogglePin的定义如下图所示:
1 void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
2 {
3 /* Check the parameters */
4 assert_param(IS_GPIO_PIN(GPIO_Pin));
5 assert_param(IS_GPIO_PIN_ACTION(PinState));
6
7 if (PinState != GPIO_PIN_RESET)
8 {
9 GPIOx->BSRR = GPIO_Pin;
10 }
11 else
12 {
13 GPIOx->BSRR = (uint32_t)GPIO_Pin << GPIO_NUMBER;
14 }
15 }
16
17 /**
18 * @brief Toggles the specified GPIO pins.
19 * @param GPIOx: Where x can be (A..K) to select the GPIO peripheral.
20 * @param GPIO_Pin: Specifies the pins to be toggled.
21 * @retval None
22 */
23 void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
24 {
25 /* Check the parameters */
26 assert_param(IS_GPIO_PIN(GPIO_Pin));
27
28 if ((GPIOx->ODR & GPIO_Pin) != 0x00u)
29 {
30 GPIOx->BRR = (uint32_t)GPIO_Pin;
31 }
32 else
33 {
34 GPIOx->BSRR = (uint32_t)GPIO_Pin;
35 }
36 }
HAL_GPIO_WritePin函数所实现的功能为设置对应的管脚的输出状态,GPIO_PIN_RESET为低电平、GPIO_PIN_SET为高电平。
HAL_GPIO_TogglePin函数所实现的功能为使对应管脚的输出状态反转(从高电平转为低电平,从低电平转为高电平)
25.5.3.3 led.c文件的完善
在led.c文件下,添加以下内容,以下内容实现的功能为定义led_init函数,调用该函数之后使LED2导通、LED3截止。
#include "./Include/led.h"
void led_init(void)
{
LED2(1);
LED3(0);
}
25.5.3.4 main.c文件的完善
我们要修改的main.c文件路径如下图所示:
打开main.c文件,为了规范我们在/* USER CODE BEGIN Includes */和/* USER CODE END Includes */之间添加以下内容
#include "../BSP/Include/led.h"
添加完成如下图所示:
然后在 /* USER CODE BEGIN 2 */和/* USER CODE END 2 */之间添加以下内容:
led_init();
通过调用led_init()函数来使LED2和LED3的初始状态分别为亮和灭。
然后在/* USER CODE BEGIN 3 */下添加以下逻辑代码
LED2_TOGGLE();
HAL_Delay(500);
LED3_TOGGLE();
HAL_Delay(500);
该逻辑代码所实现的功能为两个LED灯的状态进行依次反转,且每次翻转所持续的时间为500ms。
25.5.4工程的编译
在完成以上步骤之后我们点击工具栏的小锤子进行编译,编译图标如下图所示:
编译完成会在下方的终端中显示打印信息,如下图所示:
如果报错,需要自己根据错误的提示信息来进行问题的寻找和改正。
25.5.5工程的调试
由于STM32MP157的裸机部分和一般的单片机有些区别,他没有内部的存储,所以只能在程序编译成功之后,通过debug的方式来进行调试(将程序放在内存之中),调试过程如下:
首先,点击菜单栏中的小甲虫Debug调试按钮,弹出以下界面,
在弹出来的界面,按步骤,选择响应的属性(该步骤为Jlink的步骤,如果是STLink,调试探头选择对应的即可)。如下图所示:
选择完成之后,点击右下角的Debug按钮,点击之后,会进行再一次的编译,编译完成之后会弹出如下内容(作者用的是J-LinK),这里弹出的是J-link关于设备的选择,不同调试器的弹窗可能会不同
在弹出来的界面中,选择Accept接受,会弹出以下内容,继续点击下方的OK。
之后会来到设备选择界面,我们选择Cortex-M4,如下图所示:
选择Cortex-M4之后,点击右下角的OK,会弹出以下界面,选择右下角Switch.
然后会弹出一个新的页面,选择菜单栏的 resume,按钮开始调试。
此时,会看到开发板上两个LED灯在交替闪烁。
如果想关闭调试,则点击菜单栏的终止按钮即可。