第五章 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例程

相关推荐
WIZnet 中国社区官方博客2 天前
【第二十三章 IAP】
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·iap简介·iap程序设计
WIZnet 中国社区官方博客4 天前
第二章 开发板与芯片介绍
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·系统框架·开发板介绍·硬件资源
WIZnet 中国社区官方博客4 天前
第十三章 RTC 实时时钟
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·rtc简介·寄存器描述
WIZnet 中国社区官方博客5 天前
第二十章 BKP
wiznet·高性能以太网单片机·w55mh32·单片机外设·bkp简介·bkp特性·rtc校准
WIZnet 中国社区官方博客5 天前
第十六章 I2C
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·i2c通讯·i2c主从模式
WIZnet9 天前
第二十八章 RTC——实时时钟
嵌入式硬件·时间戳·wiznet·高性能以太网单片机·w55mh32·rtc实时时钟·时钟分频
WIZnet24 天前
第十九章 ADC——电压采集
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·adc电压采集·adc模数转换·嵌入式学习教程
爱学习的章鱼哥2 个月前
如何实现一个可视化的文字编辑器(C语言版)?
c语言·编辑器·文本编辑器·程序设计·easyx
移动中的鸭子5 个月前
程序设计:排版、检验报告的上下标解决几种办法
算法·程序设计