资讯详情

【正点原子MP157连载】第十章 跑马灯实验-摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南

1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614 在这里插入图片描述

第十章 跑马灯实验

本章通过经典的跑马灯程序,引导大家了解STM32MP1的IO作为输出方法,我们将通过代码控制开发板上的两个 LED 灯 DS0 和 DS1 类似跑马灯的效果交替闪烁。 本章将分为以下几个部分: 10.1、STM32MP1 GPIO简介; 10.2、stm32mp1xx_hal_gpio.c文件; 10.3、LED灯简介; 10.四、硬件设计; 10.5、软件设计; 10.六、章节总结; 10.1 STM32MP157 GPIO简介 10.1.1 GPIO简介 GPIO(General-purpose input/output)即通用输入输出端口,是通用可编程IO用户可以通过程序控制接口(GPI)或通用输出(GPO)或一般输入输出(GPIO)功能。 在嵌入式系统的应用中,通常需要控制一些结构简单的外围设备或电路。这些结构简单的设备或电路通常只需要打开或关闭两种状态,例如LED此时使用灯的亮灭、继电器开关、蜂鸣器的声音和关闭等GPIO这些设备外接,用户可以通过GPIO在数字电路中控制和读取TTL电平的逻辑0和逻辑1,从而可以简单高效地控制设备。 10.1.2 STM32MP15的GPIO STM32MP157的GPIO有GPIOA至GPIOK和GPIOZ共12组GPIO,其中GPIOA~GPIOK每组有16个IO,而GPIOZ有8个IO。所有的GPIO所有具有中断功能的中断功能GPIO都可以被Cortex-M4和Cortex-A共享访问,而GPIOZ可用于TrustZone安全相关设置(当用于此项目时仅限于Cortex-A7.相关外围设备的软件访问定义为安全访问,常用于安全解决方案。这里,STM32MP157共16*5 8=176 8=184个IO,但是正点原子开发板是144 个通用 GPIO 引脚。

表10.1.2. 1 GPIO汇总表 10.1.3 GPIO功能模式 STM32MP157的GPIO软件配置如下 8 任何一种模式: 1、输入浮空 2、输入上拉 3、输入下拉 4、模拟输入 5.具有上拉或下拉功能的泄漏输出 6.具有上拉或下拉功能的推拉输出 7.具有上拉或下拉功能的开漏复用功能 8.具有上拉或下拉功能的推拉复用功能 每个GPIO引脚可通过软件配置为输出(推拉或漏极开路,带或不上拉或下拉)、输入(带或不上拉或下拉)或外围设备复用功能。 让我们简要介绍一下上述模式: ?输入浮空:即逻辑器件的输入引脚既不上拉也不下拉,即引脚什么都不接,浮空。此时,输入引脚上的任何噪音都会改变输入端检测到的电平。检测引脚电平是不确定的,可能检测到高电平或低电平。 ?输入上拉:逻辑器件的输入引脚通过电阻和电源VCC相连,引脚固定在高电平。 ?输入下拉:逻辑器件的输入引脚通过电阻与地面GND连接,引脚固定在低电平。 ?模拟输入:指逻辑器件的输入引脚输入模拟信号(模拟量)。模拟量是与数字量对应的未转换连续变化量,通常用于ADC模拟输入。 ?开漏输出:"漏"指的是MOS管道泄漏极的输出端相当于三极管的集电极。默认情况下,泄漏只能输出低电平。为了获得具有驱动能力的高电平状态,需要增加拉电阻。 ?推拉输出:推拉输出的结构由两个三极管或MOS管道由互补信号控制,两根管道中的一根始终保持在截止日期,另一根处于导通状态。此时,电路可以真正输出高电平或低电平,平都具有驱动能力(即输出电流的能力)。 ?推挽式复用和开漏式复用可以理解GPIO不是普通的IO口使用,而是被用作第二功能的情况,例如片内外设功能(I2C的SCL和SDA)。推拉和开漏的功能与前面的解释相同。 对这些模式的电路结构分析,请参考以下10个.1.四节内容。 10.1.4 GPIO基本结构分析 我们知道了GPIO有八种工作模式。这些模式是如何实现的?让我们通过GPIO详细分析基本结构图,先看总框图,如下图所示。

图10.1.4. 1 GPIO基本结构图 如上图所示,右边只有I/O引脚,这个I/O引脚是芯片实物的引脚,我们可以看到。其他部分是GPIO内部结构。 ① 保护二极管 保护引脚外部过高或过低的电压输入有两个二极管。当引脚输入电压高于时VDD当引脚输入电压低于时,上述二极管导通VSS下二极管导通,使输入芯片内的电压处于相对稳定的值。虽然有二极管保护,但这种保护非常有限,大电压和大电流的接入很容易烧坏芯片。因此,在实际设计中要考虑引脚的保护电路。 ② 上下拉电阻 它们的阻值约为30~50K这两个开关可以由寄存器控制,上下两个对应的开关控制。当引脚外部设备不干扰引脚电压时,即无外部上下拉电压。引脚的电平由引脚内部的上下拉决定。如果引脚电阻高,则引脚电阻低。同样,如果内部上下拉电阻不打开,这种情况就是我们所说的浮动模式。在浮动模式下,引脚的电平是不确定的。引脚电平可由外部上下拉电平决定。需要注意的是,STM32内部上拉是一种弱上拉 ③ 特殊触发器施密 对于标准施密特触发器,当输入电压高于正阈值电压时,输出为高;当输入电压低于负阈值电压时,输出为低;当输入在正负阈值电压之间时,输出不会改变,即输出从高准位转为低准位,或从低准位转为高准位。只有当输入电压发生足够的变化时,输出才会发生变化,因此将该元件命名为触发器。这种双阈值动作被称为迟滞现象,表明施密特触发器有记忆性。施密特触发器本质上是一种双稳态多谐振荡器。 施密特触发器可用作波形整形电路,可将模拟信号波形整形成数字电路可处理的方波波形。此外,由于施密特触发器具有滞回特性,可用于抗干扰。其应用包括开回路配置中的抗干扰,以及闭回路正负回路配置中的多谐振荡器。 下面我们来看看比较器和施密特触发器的功能对比,清楚的知道施密特触发器对外部输入信号有一定的抗干扰能力,如下图所示。

图10.1.4. 2比较器的(A)和施密特触发器(B)作用比较 ④ P-MOS管和N-MOS管 结构控制GPIO泄漏输出和推拉输出有两种模式。泄漏输出:输出端相当于三极管的集电极,需要上拉电阻才能获得高电平状态。推拉输出:两者对称MOS每次只有一个导管,导管损耗小,效率高。输出不仅可以向负载灌注电流,还可以从负载中拉动电流。推拉输出不仅可以提高电路的负载能力,还可以提高开关速度。 上面我们对GPIO介绍了基本结构图中的关键设备,以下分别介绍GPIO结构图的工作情况对应于八种工作模式。 1、输入浮空 输入浮动模式:上拉/下拉电阻断开,施密特触发器打开,禁止输出。

图10.1.4. 输入浮空模式 2、输入上拉 输入上拉模式:上拉电阻导通,施密特触发器打开,禁止输出。

图10.1.4. 输入上拉模式 3、输入下拉 输入下拉模式:下拉电阻导通,施密特触发器打开,禁止输出。

图10.1.4. 输入下拉模式 4、模拟功能 模拟功能:上下拉电阻断开,施密特触发器关闭,双MOS关闭管道。其他外设也可以。过模拟通道输入输出。

图10.1.4. 6模拟功能模式 5、开漏输出 开漏输出模式:开漏模式下P-MOS是不工作的(即一直关闭),如果要控制IO口输出0,N-MOS导通,使得输出接VSS。如果要控制输出1,则N-MOS关闭,此时P-MOS和N-MOS都是关闭,引脚呈现高阻态,即不输出高电平也不输出低电平,所以这时要输出高电平就必须接上拉电阻。这时可以用内部上拉电阻,但是不推荐,我们建议接一个外部的上拉电阻。因为如果接内部上拉电阻,具有线与特性,即如果有很多开漏模式的引脚连在一起的时候,只有当所有引脚都输出高阻态,电平才为1,只要有其中一个为低电平时,就等于接地,使得整条线路都为低电平0。 另外在开漏输出模式下,施密特触发器是打开的,所以IO口引脚的电平状态会被采集到输入数据寄存器中,如果对输入数据寄存器进行读访问可以得到IO口的状态。也就是说开漏输出模式下,我们可以对IO口进行读数据。

图10.1.4. 7开漏输出模式 6、推挽输出 推挽输出模式:推挽输出跟开漏输出不同的是,推挽输出模式P-MOS管和N-MOS管都用上。如果要IO口输出高电平,即输出数据寄存器会往图中“输出控制”中输入高电平时,然后“输出控制”会输出低电平到P-MOS管,则上方的 P-MOS导通,同时“输出控制”会输出低电平到N-MOS管,则下方的 N-MOS 关闭,这时接在P-MOS的VDD与外部引脚连接,对外输出高电平。 如果要IO口输出低电平,即输出数据寄存器会往图中“输出控制”中输入低电平时,然后“输出控制”会输出高电平到P-MOS管,则上方的 P-MOS关闭,同时“输出控制”会输出高电平到N-MOS管,则下方的 N-MOS 导通,这时接在N-MOS的VSS与外部引脚连接,对外输出低电平。 当引脚高低电平切换时,两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都有很大的提高。 另外在推挽输出模式下,施密特触发器也是打开的,我们可以读取IO口的电平状态。

图10.1.4. 8推挽输出模式 7、开漏式复用功能 开漏式复用功能:一个IO口可以是通用的IO口功能,还可以是其他外设的特殊功能引脚,这就是IO口的复用功能。一个IO口可以是多个外设的功能引脚,我们需要选择作为其中一个外设的功能引脚。当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器。除了复用功能外,其他的结构分析请参考开漏输出模式。 另外在开漏式复用功能模式下,施密特触发器也是打开的,我们可以读取IO口的电平状态,同时外设可以读取IO口的信息。

图10.1.4. 9开漏式复用功能 8、推挽式复用功能 推挽式复用功能:复用功能介绍请查看开漏式复用功能,结构分析请参考推挽输出模式,这里不再赘述。

图10.1.4. 10推挽式复用功能 10.1.5 GPIO寄存器介绍 STM32MP157的每组GPIO端口都有以下寄存器: 4个32位配置寄存器(MODER, OTYPER, OSPEEDR和PUPDR); 2个32位数据寄存器(IDR和ODR),1个32位设置/重置寄存器(BSRR); 1个32位设置/重置寄存器(BSRR); 1个32位定寄存器(LCKR); 2个32位复用功能选择寄存器(AFRH和AFRL)。 注: 为了后面章节描述不被混淆,这里说明一下引脚(Pin)、IO口、IO端口、GPIO端口和端口位的关系。 引脚就是芯片直接外接的管腿,例如VCC引脚、GND引脚和串口引脚等,它就是芯片外接的一个个的管腿或者管脚; 端口就是芯片内部(CPU单元)和外部引脚的接口组,一组有好几个引脚,例如80C51单片机的 P0端口有P0.0至P0.7共8个引脚; IO口其实就是有输入输出功能的引脚; IO端口就是具有输入输出功能的端口; GPIO端口英文名字是General-purpose I/Os,I/O后面加了个s,看样子是有很多个IO口,就是通用的输入输出IO端口,例如STM32MP157的GPIOA端口,有PA0至PA15共16个引脚。习惯上,大多数人也称GPIO端口就是IO端口,引脚就是IO口。端口位就是端口的某个位,例如GPIOA端口有16个位(16个引脚)。 下面我们将带大家理解本章用到的寄存器,没有介绍到的寄存器后面用到的时候会继续介绍。这里主要是带大家学会怎么理解这些寄存器的方法,其他寄存器理解方法是一样的。因为寄存器太多不可能一个个列出来讲,以后基本就是只会把重要的寄存器拿出来讲述,希望大家尽快培养自己学会看手册的能力。

  1. MODER(端口模式控制寄存器)

图10.1.5. 1 MODER寄存器 MODER用于控制GPIOx(x等于A 至K,和Z,下同)的工作模式,是32位可读可写寄存器,每两位寄存器为一组控制一个IO口(IO口编号为y,y等于0至15,共16个IO)的模式,由软件写入以配置I / O模式为: 00: 输入模式 01:通用输出模式 10:复用功能模式 11: 模拟模式 例如控制GPIOA的第0个IO口为输出模式,则配置GPIOA的MODER0为01(即第0位为1,第1位为0)。 此寄存器的复位值为0xFFFFFFFF,每一位都是1,表示每个IO默认都是模拟模式。 2. OTYPER(端口输出类型寄存器)

图10.1.5. 2 OTYPER寄存器 OTYPER用于控制 GPIOx 的输出类型(推挽输出或开漏输出),是32位寄存器,只有低 16 位有效,每一个位控制一个 IO 口,当配置位为0的时候为推挽输出模式,配置位为1的时候为开漏输出模式,复位后,该寄存器值均为 0,即复位后默认为推挽输出模式。该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00)下不起作用。 3. OSPEEDR(端口输出速度寄存器)

图10.1.5. 3 OSPEEDR寄存器 OSPEEDR寄存器用于控制 GPIOx 的输出速度等级, 属于32位可读可写寄存器,每 2 个位控制一个 IO 口,由软件写入以配置I / O速度为: 00:低速 01:中速 10:快速 11:高速 该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00)下不起作用。复位后该寄存器值一般为 0,即默认处于低速等级。 4. PUPDR(端口上拉下拉寄存器)

图10.1.5. 4 PUPDR寄存器 PUPDR寄存器用于控制 GPIOx 的上拉/下拉,属于32位可读可写寄存器,每 2 个位控制一个 IO 口,由软件写入以配置I O口是上拉或者下拉,复位后,该寄存器值一般为 0,即无上拉,无下拉状态: 00:无上拉或下拉 01: 上拉 10: 下拉 11: 保留 5. IDR(端口输入数据寄存器)

图10.1.5. 5 IDR寄存器 IDR用于读取 GPIOx 的输入电平状态,只有低16位有效,属于只读寄存器,每个位控制一个IO口,如果对应的位为 0(IDRy=0,y等于0至15),则说明该 IO口 输入的是低电平,如果是 1(IDRy=1),则表示该IO口输入的是高电平。复位后的低16位状态是未知的。 6. ODR(端口输出数据寄存器)

图10.1.5. 6 ODR寄存器 ODR寄存器用于控制 GPIOx 输出高低电平,属于可读可写寄存器,只有低16位有效,每个位控制一个IO口,如果对应的位为 0(ODRy=0,y等于0至15),则说明该 IO口 输出的是低电平,如果是 1(ODRy=1),则表示输出的是高电平。 控制 GPIOx 输出高低电平,也可以通过写BSRR寄存器(x 等于A至F)来分别设置和/或复位ODR位(后面我们会讲解)。ODR寄存器也仅在输出模式下有效,在输入模式(MODER[1:0]=00)下不起作用。 复位以后,ODR寄存器值默认为0x00000000,表示低16位IO口输出低电平。 7. BSRR(端口置位/复位寄存器)

图10.1.5. 7 BSRR寄存器 BSRR为32位置位/复位寄存器,只写寄存器,如果读取这些寄存器,那么返回值将是0x0000。 对寄存器的高16位写1,对应的ODR位将被复位,所以对应IO口为低电平。对寄存器的低16位写1,对应的ODR位将被置1,对应IO口为高电平。如果写 0,表示无动作,也就是说操作此寄存器写1才有效,写0是无效的。 简单地说, BSRR的高16位称作清除寄存器,低16位称作设置寄存器。 前面我们了解了ODR寄存器可以控制GPIOx对应IO口输出高低电平,两种寄存器的比较:ODR寄存器会被中断打断,BSRR 寄存器支持原子操作(原子操作是指操作过程不会被中断打断)。 复位以后,BSRR寄存器值默认为0x00000000,表示无动作。 8. BRR(端口清除寄存器)

图10.1.5. 8 BRR寄存器 BRR为位清除寄存器,属于只写寄存器,如果读取这些位将返回值0x0000。BRR只有低16位可用,这低16位与BSRR的高16位具有相同功能,即对低16位写1,对应IO口为低电平,写0表示无效。 复位以后,BRR寄存器值默认为0x00000000,表示无动作。 9. LCKR(端口配置锁存寄存器)

图10.1.5. 9 LCKR寄存器 LCKR寄存器使用的不多,该寄存器用于锁定端口位是否处于可以配置的状态,[31:17]位保留,[16:0]位的值用于配置LOCK键的写入顺序,当在端口的任何位上执行第一个锁定序列后,该位的配置将无法再修改,直到下一次MCU复位或外设复位以后才可以进行修改,即位被锁定了。 第16位为锁键位,写0表示端口配置锁定键未激活。写1表示端口配置锁定键已激活,LCKR寄存器被锁定,直到下一次MCU复位或外设复位为止。此位的写入序列是写1写0写1读0读1。 第15~0位是要锁定的端口位,这些位可读可写,但只能在第16位为0的时候可以写入。 其中,对某一位写0表示不锁定该位的配置,对某一位写1表示锁定该该位的配置。 那么,整体的LOCK键写入顺序是: WR LCKR[16] = ‘1’ + LCKR[15:0] /* 锁键位写1+[15:0]位的值 / WR LCKR[16] = ‘0’ + LCKR[15:0] / 锁键位写0+[15:0]位的值 / WR LCKR[16] = ‘1’ + LCKR[15:0] / 锁键位写1+[15:0]位的值 / RD LCKR / 读取LCKR寄存器 */ 复位以后,LCKR寄存器值默认为0x00000000,表示没有锁定端口位。 关于LCKR寄存器使用的相关函数HAL_GPIO_LockPin,下面的第10.2.1小节有讲到。 GPIO相关的寄存器我们就简单介绍到这里,整体分析一遍寄存器,对我们后面的实验是很有帮助的,实验虽然是基于HAL库来操作,但本质上操作的还是寄存器,原理还是要弄懂的。 10. 以上寄存器汇总 针对以上寄存器的说明,我们汇总出如下表格,方便大家需要的时候查阅。

表10.1.5. 1GPIO部分寄存器汇总表 10.2 GPIO相关的API函数 下面,我们介绍stm32mp1xx_hal_gpio.c文件中几个重要的API函数,后面的实验中我们将用到这些函数。 10.2.1 HAL_GPIO_Init函数 ●函数功能:根据GPIO_Init中的指定参数初始化GPIOx外设。 ●函数参数: GPIOx,其中x可以是(A…K和Z,下同)。 GPIO_Init,指向GPIO_InitTypeDef结构的指针,该结构包含指定GPIO外设的配置信息。 ●函数返回值:无 ●注意:HAL库的EXTI外部中断的设置功能整合到此函数里面,而不是单独独立一个文件,关于EXTI我们到外部中断实验再细讲。 HAL_GPIO_Init函数由于篇幅原因这里不列出函数的具体内容,大家可以直接在HAL库文件中查看。

1   void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
2   { 
        
3     /*省略代码*/
4           { 
        
5            		/*GPIO模式配置代码*/
6           }
7           { 
        
8            		/*外部中断配置代码*/
9           }
10      }
11    }
12  }
  1. 形参GPIOx 形参GPIOx是端口号,可以有以下的选择,这是整个芯片可以选择的GPIO组,正点原子的STM32MP157开发板引出了144 个GPIO,并没有把所有的GPIO(184个)都引出来。
stm32mp157dxx_cm4.h文件代码
	#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
    #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
    #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
    #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
    #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
    #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
    #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
    #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
    #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)
    #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
    #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE) 
GPIO_TypeDef结构体类型如下。可以看到GPIO_TypeDef结构体对GPIO相关的寄存器进行了封装,通过操作结构体成员即可操作寄存器。

stm32mp157dxx_cm4.h文件代码

 typedef struct
   { 
        
     __IO uint32_t MODER;
     __IO uint32_t OTYPER;
     __IO uint32_t OSPEEDR;
     __IO uint32_t PUPDR; 
     __IO uint32_t IDR; 
     __IO uint32_t ODR;
     __IO uint32_t BSRR;
    __IO uint32_t LCKR;
    __IO uint32_t AFR[2];
    __IO uint32_t BRR; 
  /*篇幅原因,此处省略剩下代码*/  
  } GPIO_TypeDef;
  1. 形参GPIO_Init GPIO_Init是一个GPIO_InitTypeDef 类型结构体,其定义如下: stm32mp1xx_hal_gpio.h文件代码
typedef struct
   { 
        
     uint32_t Pin;    			/*选择的引脚号*/
     uint32_t Mode;   			/*引脚模式配置*/
     uint32_t Pull;     		/*上拉/拉配置*/
     uint32_t Speed;    		/*引脚速度等级配置*/
     uint32_t Alternate;   	/*引脚复用配置*/
   }GPIO_InitTypeDef;
GPIO_InitTypeDef 结构体对选择的引脚号、引脚工作模式、引脚上/下拉模式配置、引脚速度等级以及引脚复用配置进行了封装。在调用HAL_GPIO_Init函数以后,GPIOx所对应引脚工作模式将被初始化成我们配置的模式。

该结构体很重要,下面对每个成员进行介绍。 成员Pin表示引脚号,范围:GPIO_PIN_0到 GPIO_PIN_15,另外还有GPIO_PIN_All和GPIO_PIN_MASK可选。 成员Mode是GPIO的模式选择,有以下选择项:

stm32mp1xx_hal_gpio.h文件代码
    #define GPIO_MODE_INPUT ((uint32_t)0x00000000U) /* 输入模式 */
    #define GPIO_MODE_OUTPUT_PP ((uint32_t)0x00000001U) /* 推挽输出 */
    #define GPIO_MODE_OUTPUT_OD ((uint32_t)0x00000011U) /* 开漏输出 */
    #define GPIO_MODE_AF_PP ((uint32_t)0x00000002U) /* 推挽式复用 */
    #define GPIO_MODE_AF_OD ((uint32_t)0x00000012U) /* 开漏式复用 */

    #define GPIO_MODE_AF GPIO_MODE_AF_PP /* 输入复用 */

    #define GPIO_MODE_ANALOG ((uint32_t)0x00000003U) /* 模拟模式 */
    /* 外部中断,上升沿触发检测 */
    #define GPIO_MODE_IT_RISING ((uint32_t)0x10110000U) 
    /* 外部中断,下降沿触发检测 */
    #define GPIO_MODE_IT_FALLING ((uint32_t)0x10210000U) 
    /* 外部中断,上升和下降双沿触发检测 */
    #define GPIO_MODE_IT_RISING_FALLING ((uint32_t)0x10310000U) 
    /* 外部事件,上升沿触发检测 */
    #define GPIO_MODE_EVT_RISING ((uint32_t)0x10120000U) 
    /* 外部事件,下降沿触发检测 */
    #define GPIO_MODE_EVT_FALLING ((uint32_t)0x10220000U) 
    /* 外部事件,上升和下降双沿触发检测 */
    #define GPIO_MODE_EVT_RISING_FALLING ((uint32_t)0x10320000U) 
成员Pull用于配置上下拉电阻,有以下选择项:
stm32mp1xx_hal_gpio.h文件代码
    #define GPIO_NOPULL ((uint32_t)0x00000000U) /* 无上下拉 */
    #define GPIO_PULLUP ((uint32_t)0x00000001U) /* 上拉 */
    #define GPIO_PULLDOWN ((uint32_t)0x00000002U) /* 下拉 */
成员Speed用于配置GPIO的速度,有以下选择项:
stm32mp1xx_hal_gpio.h文件代码
	#define GPIO_SPEED_FREQ_LOW ((uint32_t)0x00000000U) /* 低速 */
    #define GPIO_SPEED_FREQ_MEDIUM ((uint32_t)0x00000001U) /* 中速 */
    #define GPIO_SPEED_FREQ_HIGH ((uint32_t)0x00000002U) /* 快速 */
    #define GPIO_SPEED_FREQ_VERY_HIGH ((uint32_t)0x00000003U) /* 高速 */ 
成员Alternate用于配置具体的复用功能,关于复用功能,我们后面有专门的实验来讲解。 

10.2.2 HAL_GPIO_DeInit函数 ●函数功能:将GPIOx外设寄存器初始化为其默认复位值(各个寄存器复位时默认的值)。 ●函数参数:GPIOx和GPIO_Pin ●函数返回值:无 ●注意:HAL库的EXTI外部中断的设置功能整合到此函数里面,而不是单独独立一个文件,关于EXTI我们到外部中断实验再细讲。 HAL_GPIO_Init函数由于篇幅原因这里不列出函数的具体内容,大家可以直接在HAL库文件中查看。

1   void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)
2   { 
        
3   /*此处函数省略*/
4   }
10.2.3 HAL_GPIO_ReadPin函数
●函数功能:读取我们想要知道的引脚的电平状态。
●函数参数:GPIOx和GPIO_Pin
●函数返回值:输入端口引脚值,为0或1
1   GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
2   { 
        
3     GPIO_PinState bitstatus;
4   
5     /* 用于检查参数是否有效 */
6     assert_param(IS_GPIO_PIN(GPIO_Pin));
7   
8     if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
9     { 
        
10      bitstatus = GPIO_PIN_SET;
11    }
12    else
13    { 
        
14      bitstatus = GPIO_PIN_RESET;
15    }
16    return bitstatus;
17  }
HAL_GPIO_ReadPin函数是读取我们想要知道的引脚的电平状态,函数的返回值为0或1。我们分析一下这段代码。
第3行,GPIO_PinState是一个枚举类型,可选值是0或者1,此行定义了一个枚举常量bitstatus。
第6行,断言,assert_param函数实际上是一个宏定义,它的作用就是检测传递给函数的参数是否是有效的参数,如果检测的参数有效,则返回true,否则返回false。
第8至第11行,IDR是GPIOx端口的输入数据寄存器,用于读取 GPIOx 的某个IO口输入电平状态,每个位控制一个IO口,如果IDR的第0位为 0,则说明该GPIOx 端口的第0个IO口输入的是低电平,如果IDR的第0位为 1,则表示该GPIOx 端口的第0个IO口输入的是是高电平。
其中,GPIO_PIN_RESET表示0,GPIO_PIN_SET 表示1。
其中的"->"是表示访问结构体的指针对应对象下的IDR成员,->运算符优先级比位与&运算符优先级要高,GPIOx->IDR可以改写为(*GPIOx).IDR。GPIOx->IDR表示IDR寄存器的低16位的数值。GPIO_Pin为16位,表示要读取的第几个IO口(如果要读取第1个IO口,GPIO_Pin的值是0x0002U)。(GPIOx->IDR & GPIO_Pin)就表示读取IO口对应的IDR寄存器的值,如果IDR和GPIO_Pin进行位与后不等于0(说明对应的IDR位是1,表示此IO口输入的是高电平),则返回bitstatus为1,反之则返回0,这样就实现了读取对应引脚的电平状态。这里的GPIO_Pin是哪一个,由我们后面调用函数的时候给定的具体的参数来决定。

10.2.4 HAL_GPIO_WritePin函数 ●函数功能:让某个引脚设置为高电平或者低电平。 ●函数参数:GPIOx和GPIO_Pin ●函数返回值:无

1   void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,\ 	GPIO_PinState PinState)
2   { 
        
3     /* 用于检查参数是否有效 */
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  }
在前面第10.1.5小节我们已经分析GPIOx_BSRR寄存器的功能了,对BSRR寄存器的高16位写1,ODR的对应位将被复位,所以对应IO口为低电平,对BSRR寄存器的低16位写1, ODR的对应位将被置1,对应IO口为高电平,如果写 0,表示无动作。
HAL_GPIO_WritePin函数中GPIO_Pin 为16位类型数据,表示要操作的IO口,PinState 为枚举类型,其中GPIO_PIN_RESET为0,GPIO_PIN_SET为1。GPIOx->BSRR表示BSRR寄存器的高16位和低16位的值。
当PinState为0的时候,GPIOx->BSRR = GPIO_Pin表示将GPIO_Pin的值赋予BSRR的低16位,GPIO_Pin中哪一位是1,则对应的BSRR的那个位就被置1,对应的IO口输出高电平。	当PinState为1的时候,(uint32_t)GPIO_Pin为32位,GPIO_NUMBER为16位,<<表示左移,GPIO_Pin左移16位后,原来的低16位移动到高16位的位置,低16位为0。GPIOx->BSRR = (uint32_t)GPIO_Pin << GPIO_NUMBER表示对BSRR寄存器高16位所对应的 GPIO_Pin 位写1,对应引脚为低电平。
这里的GPIO_Pin由我们后面调用函数的时候给定的具体的参数来决定。

10.2.5 HAL_GPIO_TogglePin函数 ●函数功能:让某个GPIO口的电平翻转。 ●函数参数:GPIOx和GPIO_Pin ●函数返回值:无

1   void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
2   { 
        
3     /* Check the parameters */
4     assert_param(IS_GPIO_PIN(GPIO_Pin)); 	/* 用于检查参数是否有效*/
5   
6     if ((GPIOx->ODR & GPIO_Pin) != 0x00u)
7     { 
        
8       GPIOx->BRR = (uint32_t)GPIO_Pin;
9     }
10    else
11    { 
        
12      GPIOx->BSRR = (uint32_t)GPIO_Pin;
13    }
14  }
BRR寄存器我们在第10.1.5小节有讲解过,BRR为位清除寄存器,对低16位写1,对应IO口为低电平,写0表示无效。
第6行,读取GPIO的ODR寄存器某一位是否等于0,如果不于0,表示此IO口输出低电平,则执行第8行代码。
第8行表示将BRR寄存器的对应位写1,表示将此IO口设置为低电平。
第12行,如果读取GPIO的ODR寄存器某一位是等于0,表示此IO口输出低电平,通过对BSRR寄存器的对应位写1后,此IO口输出高电平。
根据上述分析,此函数对GPIO的某个IO口实现电平翻转。

10.2.6 HAL_GPIO_LockPin函数 ●函数功能:锁住 某个GPIO 引脚所涉及到的寄存器,锁定的寄存器为MODER, OTYPER, OSPEEDR, PUPDR, AFRL和AFRH。 ●函数参数:GPIOx和GPIO_Pin ●函数返回值:枚举型,HAL_OK(成功)、HAL_ERROR(错误)、HAL_BUSY(忙碌)、HAL_TIMEOUT(超时) ●注意: ①锁住的是某个引脚对应的寄存器的某个位,不是将寄存器的所有位都锁住。例如设置要锁住GPIOA的引脚GPIO_PIN_0,则GPIOA对应的寄存器的某一位: MODER(第01位)、OTYPER(第0位)、OSPEEDR(第01位)、PUPDR(第01位)、AFRL(第03位),AFRH寄存器由于是用于配置GPIO的8至第15位,所以没有被锁住。 ②锁定的GPIO引脚的那个位,不能再修改寄存器对应的位的配置(上面提到的寄存器),直到下一次复位以后才可以更改(复位以后LCKR寄存器的默认值是0x00000000,没有激活锁定键)。关于LCKR寄存器可以看前面的第10.1.5小节。 我们来看看这个函数的代码实现过程:

1   HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t \ 	GPIO_Pin)
2   { 
        
3     __IO uint32_t tmp = GPIO_LCKR_LCKK; 		/* 0x00010000 */
4 
5     /* 检查参数 */
6     assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));
7     assert_param(IS_GPIO_PIN(GPIO_Pin));
8 
9     /* 应用锁定键写入顺序 */
10    tmp |= GPIO_Pin;
11    /* 将LCKx位置1:LCKK ='1'+ LCK [15-0] */
12    GPIOx->LCKR = tmp;
13    /* 复位LCKx位:LCKK ='0'+ LCK [15-0] */
14    GPIOx->LCKR = GPIO_Pin;
15    /*将LCKx位置1:LCKK ='1'+ LCK [15-0] */
16    GPIOx->LCKR = tmp;
17    /* 读取LCKK寄存器。 必须完成此读取才能完成键锁定序列 */
18    tmp = GPIOx->LCKR;
19
20    /* 再次读LCKR寄存器的值以确认锁定处于活动状态 */
21    if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
22    { 
        
23      return HAL_OK;
24    }
25    else
26    { 
        
27      return HAL_ERROR;
28    }
29  }   
第3行,GPIO_LCKR_LCKK的值为0x00010000,表示第16位为1,其它位为0,此值写入tmp中;
第10行,将用户指定要锁的某个位和tmp相或,这样一来就确定了要锁定键。
第11到第18行,是LOCK键写入顺序为①~③步骤,写完以后记得读:
① WR LCKR[16] = ‘1’ + LCKR[15:0] 	/* 锁键位写1+[15:0]位的值 */
②WR LCKR[16] = ‘0’ + LCKR[15:0] 	/* 锁键位写0+[15:0]位的值 */

③WR LCKR[16] = ‘1’ + LCKR[15:0] /* 锁键位写1+[15:0]位的值 */ ④读取LCKK寄存器(写完以后必须要读) 关于stm32mp1xx_hal_gpio.c文件的API函数我们就介绍到这里,关于EXTI部分,我们后面对应的实验会进行讲解。 10.3 LED灯简介 LED(Light Emitting Diode Light)又名发光二极管,是一种把电转化为光的半导体器件。LED 灯工作电流很小,一般在0至15mA之间,亮度随电流的增大而变亮。 不同材料的发光二极管可以直接发出红、黄、蓝、绿、青、橙、紫、白色的光,下图是可以发出黄、红、蓝三种颜色的直插型二极管实物图,这种二极管长的一端是阳极,短的一端是阴极。

图10.3. 1发光二极管 下图是开发板上用的贴片二极管实物图。贴片二极管的正面一般都有颜色标记,有标记的那端就是阴极。

图10.3. 2贴片二极管 多个发光二极管封装在一起可以组合成LED数码管,例如显示数字8的7段数码管是由7个二极管组成,8段数码管比7端数码管多了一个二极管,显示一个点。数码管有共阴和共阳两种接法,如下图,前者通常称为共阴数码管,后者为共阳数码管。共阳极的接法是发光二极管的阳极接高电平,共阴极的接法是发光二极管的阴极接地。

图10.3. 3共阳和共阴极 二极管具有单向导电性,给二极管的阳极加上正向电压,电流大小约5mA 左右,二极管就可以发光了,在规定电流范围内,电流越大,二极管发出的光亮度越强。 10.4 硬件设计

  1. 例程功能 通过HAL库的API函数来驱动LED,实现LED0和LED1以500ms交替闪烁。
  2. 硬件资源

表10.4. 1硬件资源 3. 原理图 打开STM32MP157开发板底板原理图, 路径为: 开发板光盘A-基础资料\2、开发板原理图\《STM32MP15x底板原理图》。

图10.4.3. 1 LED与STM32MP157连接原理图 可以看出,LED0 接到了PI0引脚上,当PI0输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当PI0输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。LED1接在了PF3引脚上,同理,LED1的亮灭取决于PF3的输出电平,输出 0 就亮,输出 1 就灭。 10.5 软件设计 本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 3 LED。 10.5.1 程序设计流程 本章节我们通过HAL库的API函数来驱动LED,实现LED0和LED1以500ms交替闪烁。其中,我们会用到HAL_GPIO_WritePin和HAL_GPIO_TogglePin函数。实验程序的设计流程如下:

图10.5.1. 1实验程序设计流程 10.5.2 GPIO功能引脚配置 我们新建一个工程LED,进入STM32CubeMX插件配置界面后,在Pin搜索管脚处输入PI0,在引脚排列视图处可以看到一个引脚在闪烁,这个就是我们要找的PI0。

图10.5.1. 2搜索PI0引脚

我们点击闪烁的引脚PI0,在弹出的复用功能菜单中选择GPIO_Output。

图10.5.1. 3配置PI0引脚复用功能 配置完PI0的复用功能,我们还要配置Pin Reserved选项,即引脚保留,也就是将这个引脚给谁用。选中PI0,右键弹出设置项,我们选择Pin ReservedCortex-M4,这里注意,如果不配置此项,在生成工程代码的时候将不会看到有关这个Pin的初始化代码。

图10.5.1. 4将PI0给CM4内核使用 按照前面同样的方

标签: 电阻4000电阻贴片编号330118种直插三极管包直插三极管s9013hbsp62三极管贴片三极管130z

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台