目录
1.概述
S32K144共有5个GPIO的端口实例化,从GPIOA到GPIOE。地址映射如下:

- 内核访问GPIO端口是零等待的,它的寄存器可以通过8位、16位、32位操作访问。
- 不论端口配置位数字输入还是数字输出,读取端口输入寄存器都会反应引脚当前的电平状态。
- GPIO模块的时钟来源只有BUS_CLK。
不同模式下的GPIO的操作和行为:

相关寄存器信息:

有关寄存器的具体信息此处不再列出,参考手册中均有描述,也是比较简单,各个单片机都差不多。
2.GPIO特性
2.1引脚中断
- 每个引脚均有中断标志位和使能寄存器
- 每个引脚支持边沿检测和电平检测
- 每个引脚均支持中断和DMA请求
- 低功耗模式下的同步唤醒功能
- 引脚中断在所有数字引脚的复用模式下有效
2.2数字输入滤波器
- 每个引脚的数字输入滤波器,可供任何复用到引脚上的数字外围设备使用
- 每个引脚都有单独的启用或旁路控制字段
- 有五位分辨率的数字输入滤波器的可选时钟源,用于设置滤波器大小
- 适用于所有数字引脚多路复用模式
2.3端口控制
- 每个引脚均可以进行上拉、下拉、无上下拉设置
- 可以设置强弱的两级控制,在不同供电电压下(3.3V、5V)输出能力有所不同,这一点在数据手册中有官方的测试数据。拉电流能力和灌电流能力均有所不同。
- 支持启用和禁用单个输入无源滤波器的单个输入无源滤波字段
- 支持模拟或引脚禁用、GPIO和多达六个芯片特定数字功能的独立多路复用器控制字段
- 引脚控制字段在所有数字引脚的复用模式下有效
特别注意:S32K144的数字输入寄存器不支持位寻址,所以在每次读取引脚输入寄存器时都是直接读取的整个GPIOx上的电平,如果想获取具体的引脚电平需要做数据的抽取。
2.3.GPIO全局控制寄存器
这个特性比较有意思,GPIO的每个引脚都有两个全局引脚控制和全局中断控制。笔者一直使用SDK来配置引脚初始化,并没有太关注这个特性(在SDK中的代码粗略的看了一下,貌似没有启动该功能)。下面是手册中关于这个特性的描述。
(1)全局引脚控制:全局引脚控制寄存器允许通过一次寄存器写入,以相同的值同时更新最多16个引脚的引脚控制寄存器的低半部分(低16位)。被锁定的寄存器无法通过全局引脚控制寄存器进行写入。
全局引脚控制寄存器的设计目的是让软件能够快速将同一端口内的多个引脚配置为相同的外设功能。该寄存器是只写寄存器,总是读为0。
(2)全局中断控制:这两个全局中断控制寄存器允许通过一次寄存器写入操作,以相同的值同时更新最多 16 个引脚的引脚控制寄存器高半部分(高16位)。
全局中断控制寄存器的设计目的是让软件能够快速将同一端口内的多个引脚配置为相同的中断参数(如触发方式、优先级等)。但引脚控制功能(如复用模式、上下拉等)无法通过这些全局中断控制寄存器进行配置。该寄存器是只写寄存器,总是读为0。
3.相关的控制函数
由于在第二篇中已经算是接触过GPIO,当时是开启了时钟输出引脚的功能,当使用配置工具进行配置时,只需要初始化GPIO即可实现功能。本文主要讲解SDK库中GPIO部分代码的结构以及相关函数的功能(具体的工具配置比较简单,即在配置工具上勾选需要的功能就行,这里就略过)。
3.1工具配置文件解读
在工具生成的文件中,配置代码全部放在pin_mux.c和pin_mux.h中,笔者举例一个配置文件如下:
#ifndef _PIN_MUX_H_
#define _PIN_MUX_H_
#include "pins_driver.h"
/***********************************************************************************************************************
* Definitions
**********************************************************************************************************************/
/*!
* @addtogroup pin_mux
* @{
*/
/***********************************************************************************************************************
* API
**********************************************************************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif
/*! @brief Definitions/Declarations for BOARD_InitPins Functional Group */
/*! @brief User definition pins */
#define LED_RED_PORT PTD
#define LED_RED_PIN 15U
#define BTN1_PORT PTC
#define BTN1_PIN 13U
/*! @brief User number of configured pins */
#define NUM_OF_CONFIGURED_PINS0 2
/*! @brief User configuration structure */
extern pin_settings_config_t g_pin_mux_InitConfigArr0[NUM_OF_CONFIGURED_PINS0];
#if defined(__cplusplus)
}
#endif
/*!
* @}
*/
#endif /* _PIN_MUX_H_ */
/***********************************************************************************************************************
* EOF
**********************************************************************************************************************/
在上述代码中,笔者配置了两个GPIO,分别位PTD15和PTC13,配置代码会以宏定义的方式重新定义GPIO的端口和引脚,笔者这里是LED_RED_PORT、LED_RED_PIN和BTN1_PORT、BTN1_PIN。
GPIO的具体寄存器的配置是使用了结构体数组的方式来定义,上述代码中是
extern pin_settings_config_t g_pin_mux_InitConfigArr0[NUM_OF_CONFIGURED_PINS0];
这个结构体数据在.h文件中声明位全局变量,在.c文件中定义具体的内容,结构体数组的大小NUM_OF_CONFIGURED_PINS0是用户具体配置多少个PIN,每个PIN的配置都是独立的配置结构体,.c中的代码如下:
#include "pin_mux.h"
/* clang-format off */
/*
* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************
BOARD_InitPins:
- options: {callFromInitBoot: 'true', coreID: core0}
- pin_list:
- {pin_num: '22', peripheral: PORTD, signal: 'port, 15', pin_signal: PTD15, direction: OUTPUT, initValue: state_1}
- {pin_num: '49', peripheral: PORTC, signal: 'port, 13', pin_signal: PTC13, identifier: BTN1, direction: INPUT, initValue: state_1}
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS ***********
*/
/* clang-format on */
/* Generate array of configured pin structures */
pin_settings_config_t g_pin_mux_InitConfigArr0[NUM_OF_CONFIGURED_PINS0] = {
{
.base = PORTC,
.pinPortIdx = 13U,
.pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED,
.driveSelect = PORT_LOW_DRIVE_STRENGTH,
.passiveFilter = false,
.mux = PORT_MUX_AS_GPIO,
.pinLock = false,
.intConfig = PORT_DMA_INT_DISABLED,
.clearIntFlag = false,
.gpioBase = PTC,
.direction = GPIO_INPUT_DIRECTION,
.digitalFilter = false,
.initValue = 1U,
},
{
.base = PORTD,
.pinPortIdx = 15U,
.pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED,
.driveSelect = PORT_LOW_DRIVE_STRENGTH,
.passiveFilter = false,
.mux = PORT_MUX_AS_GPIO,
.pinLock = false,
.intConfig = PORT_DMA_INT_DISABLED,
.clearIntFlag = false,
.gpioBase = PTD,
.direction = GPIO_OUTPUT_DIRECTION,
.digitalFilter = false,
.initValue = 1U,
},
};
配置参数有了,要想使用GPIO的功能就需要使用函数来驱动,GPIO的操作函数哎pin_driver.h文件中声明,常用到的函数如下:
//此函数用于初始化所有的GPIO配置,参数是引脚数和配置参数结构体数据的首地址
status_t PINS_DRV_Init(uint32_t pinCount,
const pin_settings_config_t config[]);
//向一个PIN写入电平值
void PINS_DRV_WritePin(GPIO_Type * const base,
pins_channel_type_t pin,
pins_level_type_t value);
//读取一个端口的所有引脚电平
pins_channel_type_t PINS_DRV_ReadPins(const GPIO_Type * const base);
//反转引脚电平(可以多个)
void PINS_DRV_TogglePins(GPIO_Type * const base,
pins_channel_type_t pins);
//置位引脚电平
void PINS_DRV_SetPins(GPIO_Type * const base,
pins_channel_type_t pins);
//清零引脚电平
void PINS_DRV_ClearPins(GPIO_Type * const base,
pins_channel_type_t pins);
其他更多的函数可以在pin_driver.h文件中找到函数声明,NXP的SDK命名规则还是很容易理解的。