1.1 GPIO构件制作之点亮小灯

时间:2024-05-31 08:10:25

1.1.1 GPIO基本概念

1.通用I/O(GPIO)的含义

所谓通用I/O,也记为GPIOGeneral Purpose I/O),即基本的输入/输出,有时也称并行I/O,或普通I/O,它是I/O的最基本形式。本书中使用正逻辑,电源(Vcc)代表高电平,对应数字信号“1”;地(GND)代表低电平,对应数字信号“0”。作为通用输入引脚,MCU内部程序可以通过端口寄存器获取该引脚状态,以确定该引脚是“1”(高电平)或“0”(低电平),即开关量输入。作为通用输出引脚,MCU内部程序通过端口寄存器控制该引脚状态,使得引脚输出“1(高电平)或“0(低电平),即开关量输出。大多数通用I/O引脚可以通过编程来设定其工作方式为输入或输出,称之为双向通用I/O

2.输出引脚的基本接法

作为通用输出引脚,MCU内部程序向该引脚输出高电平或低电平来驱动器件工作,即开关量输出,如图4-2所示,输出引脚O1O2采用了不同的方式驱动外部器件。一种接法是O1直接驱动发光二极管LED,当O1引脚输出高电平时,LED不亮;当O1引脚输出低电平时,LED点亮。这种接法的驱动电流一般在2mA10mA。另一种接法是O2通过一个NPN三极管驱动蜂鸣器,当O2引脚输出高电平时,三极管导通,蜂鸣器响;当O2引脚输出低电平时,三极管截止,蜂鸣器不响。这种接法可以用O2引脚上的几个mA的控制电流驱动高达100mA的驱动电流。若负载需要更大的驱动电流,就必须采用光电隔离外加其他驱动电路,但对MCU编程来说,没有任何影响。

 

3.上拉下拉电阻与输入引脚的基本接法

芯片输入引脚的外部有三种不同的连接方式:带上拉电阻的连接、带下拉电阻的连接和悬空”连接。通俗地说,若MCU的某个引脚通过一个电阻接到电源(Vcc)上,这个电阻被称为上拉电阻”;与之相对应,若MCU的某个引脚通过一个电阻接到地(GND)上,则相应的电阻被称为下拉电阻”。这种做法使得,悬空的芯片引脚被上拉电阻或下拉电阻初始化为高电平或低电平。根据实际情况,上拉电阻与下拉电阻可以取值在1KΩ10KΩ之间,其阻值大小与静态电流及系统功耗有关。

1.1 GPIO构件制作之点亮小灯

1.1.2 芯片GPIO引脚的基本特征

每个GPIO引脚都可以通过软件配置为推挽式或开放式输出、上下拉输入、无上下拉输入或作为外部复用功能(adc、spi、i2c等等)。而且,大部分的GPIO引脚可配置为数字复用或模拟复用。由于在AHB2总线上的映射,可以做到快速I/O切换。此外,如果需要,可以通过写入特定的序列,关闭I/O复用功能,以避免对I/O寄存器的错误写入。

STM32L431RCT6芯片引脚的负载电容为50pF,85℃下功耗为444mW。(VDDX-VSS),即外部主电源电压(包括VDD、VDDA、VBAT)在-0.4V到4V之间。经过VDD/VDDA电源线的总电流(供应电流)最大值为140mA,经过VSS地线的总电流,反向输出电流最大值为140mA;每个VDD电源引脚的最大电流不超过100mA,每个VSS地脚最大反向输出电流不超过100mA;任何I/O和控制引脚的反向输出电流不超过20mA,任何I/O和控制引脚的输出电流不超过20mA,所有I/Os和控制引脚的总注入电流不超过25mA。

1.1.3 GPIO模块编程寄存器

每个通用I/O端口包括4个32位配置寄存器(GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR和GPIOx_PUPDR)、2个32位数据寄存器(GPIOx_IDR和GPIOx_ODR)和1个32位置位/复位寄存器(GPIOx_BSRR)。此外,所有GPIO都包括1个32位锁定寄存器(GPIOx_LCKR)和2个32位复用功能选择寄存器(GPIOx_AFRH和GPIOx_AFRL)。

1GPIO端口模式寄存器(GPIOx_MODER)(x=A to E and H

复位值:

     0xABFF FFFF(GPIOx_MODER寄存器掩码为0xABFF FFFF,使用端口A)

     0xFFFF FEBF(GPIOx_MODER寄存器掩码为0xFFFF FEBF,使用端口B)

     0xFFFF FFFF(GPIOx_MODER寄存器掩码为0xFFFF FFFF,使用端口C..E)

     0x0000 000F(GPIOx_MODER寄存器掩码为0x0000 000F,使用端口H)

数据位

D31~D30

D29~D28

D27~D26

D3~D2

D1~D0

MODE15[1:0]

MODE14[1:0]

MODE13[1:0]

MODE1[1:0]

MODE[1:0]

D31~D0(MODEy[1:0],y∈[0,15]):x端口y引脚配置位。这些位通过软件写入,用于配置I/O模式。00:输入模式;01:通用输出模式;10:复用功能模式(可复用为ADC,SPI模块功能引脚);11:模拟模式(芯片复位后,引脚默认模式)。eg:MODE15[1:0]=01,X端口15号引脚被配置为通用输出模式(类似的情况,下面就不再描述)。

 

2GPIO端口输出类型寄存器(GPIOx_OTYPER)(x=A to E and H

数据位

D31~D16

D15

D14

D13

D12

D11

D2

D1

D0

0

OT15

OT14

OT13

OT12

OT11

OT2

OT1

OT0

D31~D16:保留,必须保持复位值。

D15~D0(OTy[1:0],y∈[0,15]):x端口y引脚配置位。这些位通过软件写入,用于配置I/O输出类型。OTy=0:推挽输出(复位状态);OTy=1:开漏输出。GPIOx_OTYPER寄存器复位值为0,

3GPIO端口输出速度寄存器(GPIOx_OSPEEDR)(X=A to E and H

复位值:

      0x0C00 0000(端口A)

      0x0000 0000(其他端口)

数据位

D31~D30

D29~D28

D27~D26

D3~D2

D1~D0

OSPEED15[1:0]

OSPEED14[1:0]

OSPEED13[1:0]

OSPEED1[1:0]

OSPEED0[1:0]

D31~D0(OSPEEDy[1:0],y∈[0,15]):x端口y引脚配置位。这些位通过软件写入,用于配置引脚输出速度。00:低速;01:中速;10:高速;11:超高速。

4GPIO端口上拉/下拉寄存器(GPIOx_PUPDR)(X=A to E and H

GPIOx_PUPDR寄存器与GPIOx_OSPEEDR寄存器结构类似,有16个PUPDy[1:0]位(y∈[0,15]),即16个引脚配置位。这些位通过软件写入,用于配置I/O引脚上拉或下拉。00:y引脚无上拉或下拉;01:y引脚上拉;10:y引脚下拉;11:保留。

5GPIO端口输入数据寄存器(GPIOx_IDR)(X=A to E and H

GPIOx_IDR寄存器与GPIOx_OTYPER寄存器结构类似,D31~D16位保留,必须保持复位值,即0。D15~D0(IDy,y∈[0,15]),端口输入数据位。这些位为只读位,它们包含相应I/O引脚的电平信息。

6GPIO端口输出数据寄存器(GPIOx_ODR)(X=A to E and H

GPIOx_ODR寄存器与GPIOx_OTYPER寄存器结构类似,D31~D16位保留,必须保持复位值,即0。D15~D0(OD y,y∈[0,15]):端口输出数据位。这些位可通过软件读取和写入,该位决定着被配置为输出引脚的电平的高低。若OD5=0,5号引脚为低电平;反之,为高电平。

7GPIO端口位置1/复位寄存器(GPIOx_BSRR)(X=A to E and H

数据位

D31~D16

D15~D0

BR[15:0]

BS[15:0]

D31~D0:这些位为只写,读取这些位可返回值0x0000,复位值为0。

D31~D16(BRy,y∈[0,15]):端口x复位位y, 0:不会对相应的ODx位执行任何操作;1:复位相应的ODx位,即将ODx位写0。

注:如果同时对BSx和BRx置位,则BSx的优先级更高。

D15~D0(BSy,y∈[0,15]):端口x置位位y, 0:不会对相应的ODx位执行任何操作;1:将相应的ODx位置1。

8GPIO端口位复位寄存器(GPIOx_BRR)(X=A to E and H

GPIOx_BRR寄存器与GPIOx_OTYPER寄存器结构类似,D31~D16位保留,必须保持复位值,即0。D15~D0(BR[15:0]):端口x复位位y(y=0..15),这些位为只写。读取这些位可返回值0x00000:不会对相应的ODx位执行任何操作;1:复位相应的ODx位。

9GPIO端口配置锁定寄存器(GPIOx_LCKR)(X=A to E and H

当正确的写序列应用到第 16 位(LCKK)时,此寄存器将用于锁定端口位的配置。位[15:0]的值用于锁定GPIO的配置。在写序列期间,不能更改LCKR[15:0]的值。将LOCK序列应用到某个端口位后,在执行下一次MCU复位或外设复位之前,将无法对该端口位的值进行修改。

GPIOx_LCKR寄存器与GPIOx_OTYPER寄存器结构类似,D31~D16位保留,必须保持复位值,即0。D16(LCKK):锁定键,可随时读取此位。可使用锁定键写序列对其进行修改。LCKK=0:端口配置锁定键未**。LCKK=1:端口配置锁定键已**,GPIOx_LCKR寄存器被锁定,直到下一次MCU复位或外设复位。

锁定键写序列:

WR LCKR[16]=‘1’+LCKR[15:0]

WR LCKR[16]=‘0’+LCKR[15:0]

WR LCKR[16]=‘1’+LCKR[15:0]

RD LCKR

RD LCKR[16]=‘1’(此读操作为可选操作,但它可确认锁定已**)

D15~D0(LCK[15:0]):端口x锁定位,这些位都是读/写位,但只能在LCKK位等于“0”时执行写操作。0:端口配置未锁定;1:端口配置已锁定。

10GPIO复用功能低位寄存器(GPIOx_AFRL)(X=A to E and H

数据位

D31~D28

D27~D24

D23~D20

D11~D8

D7~D4

D3~D0

AFSEL7[3:0]

AFSEL6[3:0]

AFSEL5[3:0]

AFSEL2[3:0]

AFSEL1[3:0]

AFSEL0[3:0]

D31~D0(AFSELy[3:0],y∈[0,7]):端口x引脚y的复用功能选择,这些位通过软件写入,用于配置复用功能I/O,复位值为0。

eg:GPIOC_AFSEL0[3:0]=0100(AF4),则C端口0号引脚被配置为I2C3_SCL功能引脚。

AFSELy选择:该4位二进制值为0000~1111,分别记为AF0~AF15

11GPIO复用功能高位寄存器(GPIOx_AFRH)(X=A to E and H

GPIOx_AFRH寄存器的结构与GPIOx_AFRL寄存器的结构类似。D31~D0(AFSEL y[3:0],y∈[8,15]):端口x引脚y的复用功能选择,这些位通过软件写入,用于配置复用功能I/O。

之所以会存在AFRL,AFRH两个复用功能寄存器,是因为AFSELy(y∈[0,15])分别与SYS_AF,TIM1/TIM2/LPTIM1,TIM1/TIM2,USART2,I2C1/I2C2/I2C3,SPI1/SPI2,SPI3,USART1/USART2/USART3,LPUART1,CAN1/TSC ,QUADSPI,SDMMC1/COMP1/COMP2/SWPMI1,SAI1,TIM2/TIM15/TIM16/LPTIM2,EVENTOUT功能模块分别相对应,但AFSEL11没有对应复用功能。而且,EVENTOUT是用于多个ARM之间同步用。与用GPIO输出相比较,GPIO至少要3条指令输出一个脉冲,并且访问总线有延时,而输出EVENTOUT只用 一条SEV指令,脉冲直接由内核控制,延迟最小。

1.1.4 GPIO编程基本步骤

GPIO编程具体实例可参“User_STM32_Light_Simple_20200423”,该工程实现了直接地址改变小灯亮暗状态的基本功能。但需要注意PortB端口的基地址为0x48000400,bsrr寄存器偏移量为0x18(十进制偏移量为24),brr寄存器偏移量为0x28(十进制为40)。

但是,vuint_32* gpio_bsrr=(vuint_32*)(gpio_ptr+0x18);

gpio_bsrr 的地址为0x48000460非0x48000418,故所加值为偏移量的1/4。点亮小灯具体步骤如下:

1)使能PORTB端口时钟

//使能PORTB时钟。

RCC_AHB2=(vuint_32*)0x4002104C;

*RCC_AHB2|=(1<<1);

2)计算引脚模式寄存器、位置1寄存器、复位寄存器基地址

//变量定义,对应GPIO各寄存器基地址

vuint_32* gpio_mode=(vuint_32*)0x48000400;

vuint_32* gpio_bsrr=(vuint_32*)(gpio_ptr+0x6);   

vuint_32* gpio_brr=(vuint_32*)(gpio_ptr+0xA);  

3)//配置引脚功能模式,定义为输出引脚

vuint_32 temp;

//先将mode7(红灯),mode8(绿灯),mode9(蓝灯)均清零

temp=~(uint_32)((3<<18)|(3<<16)|(3<<14));

*gpio_mode&=temp;

//mode7(红灯),mode8(绿灯),mode9(蓝灯)均设置为01

temp=(uint_32)((1<<18)|(1<<16)|(1<<14));

*gpio_mode|=temp;     //PB9PB8PB7设置为gpio输出引脚

4)改变小灯亮暗状态

//设置引脚为低电平,点亮小灯

temp=(uint_32)((1<<7)|(1<<8)|(1<<9));  // temp=(uint_32)(7<<7);

*gpio_brr|=temp;     //PB7,PB8,PB9为低电平,红绿蓝灯亮

//设置引脚为高电平,熄灭小灯

temp=(uint_32)(7<<7);

*gpio_bsrr=temp;     //PB8为高电平,红绿蓝灯灭