第五章 GPIO示例

第五章 GPIO示例

功夫不负有心人,相信学习至此大家已经掌握了基础内容介绍的知识。我们希望通过前面的章节你已经掌握了W55MH32开发的工具和方法。下面我们将和大家一起来学习W55MH32 的一些基础外设,这些外设实际项目中经常会用到,希望大家认真学习和掌握,以便将来更好、更快的完成实际项目开发。

后面我们将采取一章一实例的方式,介绍 W55MH32 常用外设的使用,通过本篇的学习,我们将带领大家进入W55MH32 的精彩世界。

本章将通过一个经典的跑马灯程序,带大家开启 W55MH32之旅。通过本章的学习,我们将了解到 W55MH32的 IO 口作为输出使用的方法。

本章分为如下 4 个小节:

1 W55MH32 GPIO 简介

2 寄存器描述

3 程序设计

4 下载验证

1 W55MH32 GPIO 简介

每个 GPI/O 端口有两个 32 位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个 32 位数据寄存器(GPIOx_IDR 和 GPIOx_ODR),一个 32 位置位/复位寄存(GPIOx_BSRR),一个 16 位复位寄存器GPIOx_BRR)和一个 32 位锁定寄存器(GPIOx_LCKR)。根据数据手册中列出的每个 I/O 端口的特定硬件特征,GPIO 端口的每个位可以由软件分别配置成多种模式。

-------- 输入浮空

-------- 输入上拉

-------- 输入下拉

-------- 模拟输入

-------- 开漏输出

-------- 推挽式输出

-------- 推挽式复用功能

-------- 开漏复用功能

每个 I/O 端口位可以自由编程,然而必须按照 32 位字访问 I/O 端口寄存器(不允许半字或字节访问)。GPIOx_BSRR 和 GPIOx_BRR 寄存器允许对任何 GPIO 寄存器进行读/更改的独立访问;这样,在读和更改访问之间产生 IRQ 时不会发生危险。

下图给出了一个 I/O 端口位的基本结构。

GPIO 的基本结构图

如上图所示,可以看到右边只有 I/O 引脚,这个 I/O 引脚就是我们可以看到的芯片实物的引脚,其他部分都是 GPIO 的内部结构。

① 保护二极管

保护二极管共有两个,用于保护引脚外部过高或过低的电压输入。当引脚输入电压高于VDD 时,上面的二极管导通,当引脚输入电压低于 VSS 时,下面的二极管导通,从而使输入芯片内部的电压处于比较稳定的值。虽然有二极管的保护,但这样的保护却很有限,大电压大电流的接入很容易烧坏芯片。所以在实际的设计中我们要考虑设计引脚的保护电路。

② 上拉、下拉电阻

它们阻值大概在 30~50K 欧之间,可以通过上、下两个对应的开关控制,这两个开关由寄存器控制。当引脚外部的器件没有干扰引脚的电压时,即没有外部的上、下拉电压,引脚的电平由引脚内部上、下拉决定,开启内部上拉电阻工作,引脚电平为高,开启内部下拉电阻工作,则引脚电平为低。同样,如果内部上、下拉电阻都不开启,这种情况就是我们所说的浮空模式。浮空模式下,引脚的电平是不可确定的。引脚的电平可以由外部的上、下拉电平决定。需要注意的是,W55MH32 的内部上拉是一种"弱上拉",这样的上拉电流很弱,如果有要求大电流还是得外部上拉。

③ 施密特触发器

对于标准施密特触发器,当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;当输入在正负向阈值电压之间,输出不改变,也就是说输出由高电准位翻转为低电准位,或是由低电准位翻转为高电准位,对应的阈值电压是不同的。只有当输入电压发生足够的变化时,输出才会变化,因此将这种元件命名为触发器。这种双阈值动作被称为迟滞现象,表明施密特触发器有记忆性。从本质上来说,施密特触发器是一种双稳态多谐振荡器。施密特触发器可作为波形整形电路,能将模拟信号波形整形为数字电路能够处理的方波波形,而且由于施密特触发器具有滞回特性,所以可用于抗干扰,以及在闭回路正回授/负回授配置中用于实现多谐振荡器。下面看看比较器跟施密特触发器的作用的比较,就清楚的知道施密特触发器对外部输入信号具有一定抗干扰能力,如图所示。

比较器的(A)和施密特触发器(B)作用比较

④ P-MOS 管和 N-MOS 管

这个结构控制 GPIO 的开漏输出和推挽输出两种模式。开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。推挽输出:这两只对称的 MOS 管每次只有一只导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载拉电流。推拉式输出既能提高电路的负载能力,又能提高开关速度。上面我们对 GPIO 的基本结构图中的关键器件做了介绍,下面分别介绍 GPIO 八种工作模式对应结构图的工作情况。

1、输入浮空

输入浮空模式:上拉/下拉电阻为断开状态,施密特触发器打开,输出被禁止。输入浮空模式下,IO 口的电平完全是由外部电路决定。如果 IO 引脚没有连接其他的设备,那么检测其输入电平是不确定的。该模式可以用于按键检测等情景。

输入浮空模式

2、输入上拉

输入上拉模式:上拉电阻导通,施密特触发器打开,输出被禁止。在需要外部上拉电阻的时候,可以使用内部上拉电阻,这样可以节省一个外部电阻,但是内部上拉电阻的阻值较大,所以只是"弱上拉",不适合做电流型驱动。

输入上拉模式

3、输入下拉

输入下拉模式:下拉电阻导通,施密特触发器打开,输出被禁止。在需要外部下拉电阻的时候,可以使用内部下拉电阻,这样可以节省一个外部电阻,但是内部下拉电阻的阻值较大,所以不适合做电流型驱动。

输入下拉模式

4、模拟功能

模拟功能:上下拉电阻断开,施密特触发器关闭,双 MOS 管也关闭。其他外设可以通过模拟通道输入输出。该模式下需要用到芯片内部的模拟电路单元单元,用于 ADC、DAC、MCO 这类操作模拟信号的外设。

模拟功能

5、开漏输出

开漏输出模式:W55MH32 的开漏输出模式是数字电路输出的一种,从结果上看它只能输出低电平 Vss 或者高阻态,常用于 IIC 通讯(IIC_SDA)或其它需要进行电平转换的场景。

开漏输出模式

6、推挽输出

推挽输出模式:W55MH32的推挽输出模式,从结果上看它会输出低电平 VSS或者高电平VDD推挽输出跟开漏输出不同的是,推挽输出模式 P-MOS 管和 N-MOS 管都用上。

如果输出数据寄存器①的值为 0,经过"输出控制"取反操作后,输出逻辑 1 到 P-MOS 管的栅极,这时 P-MOS 管就会截止,同时也会输出逻辑 1 到 N-MOS 管的栅极,这时 N-MOS 管就会导通,使得 IO 引脚接到 VSS,即输出低电平。如果输出数据寄存器的值为 1 ,经过"输出控制"取反操作后,输出逻辑 0 到 N-MOS 管的栅极,这时 N-MOS 管就会截止,同时也会输出逻辑 0 到 P-MOS 管的栅极,这时 P-MOS 管就会导通,使得 IO 引脚接到 VDD,即输出高电平。

由上述可知,推挽输出模式下,P-MOS 管和 N-MOS 管同一时间只能有一个管是导通的。当 IO 引脚在做高低电平切换时,两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都有较大的提高。

另外在推挽输出模式下,施密特触发器也是打开的,我们可以读取 IO 口的电平状态。由于推挽输出模式下输出高电平时,是直接连接VDD,所以驱动能力较强,可以做电流型驱动,驱动电流最大可达 25mA,但是芯片的总电流有限,所以并不建议这样用,最好还是使用芯片外部的电源。

推挽输出模式

7、开漏式复用功能

开漏式复用功能:一个 IO 口可以是通用的 IO 口功能,还可以是其它外设的特殊功能引脚,这就是 IO 口的复用功能,如图所示。一个 IO 口可以是多个外设的功能引脚,我们需要选择其中一个外设的功能引脚。当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器。除了复用功能外,其它的结构分析请参考开漏输出模式。

另外在开漏式复用功能模式下,施密特触发器也是打开的,我们可以通过输入数据寄存器读取 IO 口的电平状态,同时外设也可以读取 IO 口的信息。

开漏式复用功能

8、推挽式复用功能

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

推挽式复用功能

2 GPIO 寄存器描述

2.1 端口配置低寄存器(GPIOx_CRL)(x=A..G)

2.2 端口配置高寄存器(GPIOx_CRH)(x=A..G)

2.3 端口输入数据寄存器(GPIOx_IDR)(x=A..G)

2.4 端口输出数据寄存器(GPIOx_ODR)(x=A..G)

2.5 端口位设置/清除寄存器(GPIOx_BSRR)(x=A..G)

2.6 端口位清除寄存器(GPIOx_BRR)(x=A..G)

2.7 端口配置锁定寄存器(GPIOx_LCKR)(x=A..G)

当执行正确的写序列设置了位 16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁定GPIO 端口的配置。在规定的写入操作期间,不能改变 LCKP[15:0]。当对相应的端口位执行了LOCK 序列后,在下次系统复位之前将不能再更改端口位的配置。

每个锁定位锁定控制寄存器(CRL,CRH)中相应的 4 个位。

3 程序设计

3.1 GPIO_IOInput例程

此代码为一个基于W55MH32的嵌入式程序,主要功能是对 GPIO 输入进行测试,同时配置 UART 串口通信用于输出系统时钟信息和按键按下的提示信息。

主函数main ()

复制代码
int main(void)`
`{`
`    RCC_ClocksTypeDef clocks;`
`    delay_init();`
`    UART_Configuration(115200);`
`    RCC_GetClocksFreq(&clocks);`

`    printf("\n");`
`    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",`
`           (float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000,`
`           (float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000, (float)clocks.ADCCLK_Frequency / 1000000);`
`    printf("GPIO IO Input Tset.\n");`

`    GPIO_Configuration();`

`    while (1)`
`    {`
`    }`
`}
  • 对延时函数进行初始化。
  • 把串口波特率配置为 115200。
  • 获取系统时钟频率并输出。
  • 配置 GPIO。
  • 进入无限循环。

GPIO 配置函数GPIO_Configuration ()

复制代码
void GPIO_Configuration(void)`
`{`
`    GPIO_InitTypeDef GPIO_InitStructure;`
`    NVIC_InitTypeDef NVIC_InitStructure;`
`    EXTI_InitTypeDef EXTI_InitStructure;`

`    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);`

`    GPIO_InitStructure.GPIO_Pin   = GPIO_PIN1_TEST;`
`    GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_TEST;`
`    GPIO_InitStructure.GPIO_Mode  = GPIO_MODE_TEST;`
`    GPIO_Init(GPIO_GROUP_TEST, &GPIO_InitStructure);`

`    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);`

`    NVIC_InitStructure.NVIC_IRQChannel                   = EXTI1_IRQn;`
`    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;`
`    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 3;`
`    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;`
`    NVIC_Init(&NVIC_InitStructure);`

`    EXTI_InitStructure.EXTI_Line    = EXTI_Line1;`
`    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;`
`    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;`
`    EXTI_InitStructure.EXTI_LineCmd = ENABLE;`
`    EXTI_Init(&EXTI_InitStructure);`
`}
  • 开启 GPIOA 和 AFIO 的时钟。
  • 对 GPIO 引脚进行初始化。
  • 把 GPIO 引脚配置为外部中断线。
  • 对 NVIC(嵌套向量中断控制器)进行配置。
  • 对外部中断进行配置,触发方式为下降沿触发。

UART 配置函数UART_Configuration ()

复制代码
void UART_Configuration(uint32_t bound)`
`{`
`    GPIO_InitTypeDef  GPIO_InitStructure;`
`    USART_InitTypeDef USART_InitStructure;`

`    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);`
`    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);`

`    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;`
`    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;`
`    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;`
`    GPIO_Init(GPIOA,` `&GPIO_InitStructure);`

`    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;`
`    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;`
`    GPIO_Init(GPIOA,` `&GPIO_InitStructure);`

`    USART_InitStructure.USART_BaudRate            = bound;`
`    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;`
`    USART_InitStructure.USART_StopBits            = USART_StopBits_1;`
`    USART_InitStructure.USART_Parity              = USART_Parity_No;`
`    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;`
`    USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;`

`    USART_Init(USART_TEST,` `&USART_InitStructure);`
`    USART_Cmd(USART_TEST, ENABLE);`
`}
  • 开启 USART1 和 GPIOA 的时钟。
  • 对 USART1 的发送和接收引脚进行初始化。
  • 对 USART1 进行初始化,设置波特率、数据位、停止位等参数。
  • 使能 USART1。

外部中断处理函数EXTI1_IRQHandler ()

复制代码
void EXTI1_IRQHandler(void)`
`{`
    `if` `(EXTI_GetITStatus(EXTI_Line1)` `== SET)`
    `{`
`        delay_ms(10);`
        `if` `(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1)` `== Bit_SET)`
        `{`
`            printf("The key is pressed\n");`
        `}`
    `}`
`    EXTI_ClearITPendingBit(EXTI_Line1);`
`}
  • 当检测到外部中断触发时,进行消抖处理。
  • 若按键确实被按下,则通过串口输出提示信息。
  • 清除中断标志位。

3.2 GPIO_IOOut例程

  • 初始化部分:delay_init():初始化延时函数。
    • UART_Configuration(115200):配置串口,波特率为 115200。
    • RCC_GetClocksFreq(&clocks):获取系统时钟频率信息,并通过串口打印出来。
    • GPIO_Configuration():配置 GPIOB 的 PB0、PB2、PB3 为推挽输出模式。
  • 主循环部分:

以 200ms 为间隔依次将 PB0、PB2、PB3 置高电平,再依次将它们置低电平,循环执行。

复制代码
` `while` `(1)`
    `{`
`        GPIO_SetBits(GPIO_GROUP_TEST, GPIO_PIN1_TEST);`
`        printf("LED1 ON\n");`
`        delay_ms(200);`
`        GPIO_SetBits(GPIO_GROUP_TEST, GPIO_PIN2_TEST);`
`        printf("LED2 ON\n");`
`        delay_ms(200);`
`        GPIO_SetBits(GPIO_GROUP_TEST, GPIO_PIN3_TEST);`
`        printf("LED3 ON\n");`
`        delay_ms(200);`
`        GPIO_ResetBits(GPIO_GROUP_TEST, GPIO_PIN1_TEST);`
`        printf("LED1 OFF\n");`
`        delay_ms(200);`
`        GPIO_ResetBits(GPIO_GROUP_TEST, GPIO_PIN2_TEST);`
`        printf("LED2 OFF\n");`
`        delay_ms(200);`
`        GPIO_ResetBits(GPIO_GROUP_TEST, GPIO_PIN3_TEST);`
`        printf("LED3 OFF\n");`
`        delay_ms(200);`
    `}`
`}

4 下载验证

4.1 GPIO_IOInput例程

4.2 GPIO_IOOut例程

相关推荐
charlie11451419111 天前
Kotlin 的 apply / with / run 详解
开发语言·kotlin·程序设计·面对对象
hz_zhangrl2 个月前
CCF-GESP 等级考试 2025年6月认证C++七级真题解析
c++·程序设计·gesp·gesp2025年6月·c++七级·c++七级解析
WIZnet2 个月前
第二十一章 W55MH32 PHY配置示例
以太网·wiznet·phy·高性能以太网单片机·w55mh32·自动协商·phy模式配置
WIZnet2 个月前
第二十七章 W55MH32 Interrupt示例
物联网·以太网·wiznet·中断·高性能以太网单片机·w55mh32·toe
hz_zhangrl2 个月前
CCF-GESP 等级考试 2025年6月认证C++六级真题解析
c++·青少年编程·程序设计·gesp·gesp2025年6月·c++六级
WIZnet2 个月前
第十一章 W55MH32 SMTP示例
arm开发·以太网·wiznet·smtp·高性能以太网单片机·w55mh32·网络邮件
WIZnet 中国社区官方博客3 个月前
【第二十三章 IAP】
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·iap简介·iap程序设计
WIZnet 中国社区官方博客3 个月前
第二章 开发板与芯片介绍
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·系统框架·开发板介绍·硬件资源
WIZnet 中国社区官方博客3 个月前
第十三章 RTC 实时时钟
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·rtc简介·寄存器描述