Pin Control 框架提供了一个统一的接口来管理 SoC 的引脚功能复用和配置。现代 SoC 通常具有多功能引脚,可以配置为不同的功能(如 GPIO、UART、SPI 等)并具有各种电气属性(上拉、下拉、驱动强度等)。
RT-Thread 的 Pinctrl 框架集成了设备驱动模型,允许:
启用 Pin Control 框架支持。
路径: RT-Thread Components → Device Drivers → Using Pin Control (pinctrl)
说明: 启用后可以使用设备树配置引脚功能和参数。
通过 SCMI (System Control and Management Interface) 协议进行引脚控制。
路径: RT-Thread Components → Device Drivers → Using Pin Control (pinctrl) → Enable SCMI pinctrl driver
说明: 用于带有专用系统控制处理器的系统。
Simple pinctrl driver 支持单寄存器引脚配置。
路径: RT-Thread Components → Device Drivers → Using Pin Control (pinctrl) → Enable simple pinctrl driver
说明: 适用于简单的 SoC,其中每个引脚用单个寄存器配置。
pinctrl: pinctrl@11002000 {
compatible = "vendor,soc-pinctrl";
reg = <0x11002000 0x1000>;
#pinctrl-cells = <2>; /* 功能, 配置 */
/* 引脚组定义 */
uart0_pins: uart0 {
pins = <10 11>; /* TX, RX 引脚 */
function = <1>; /* UART 功能 */
drive-strength = <8>; /* mA */
bias-pull-up;
};
spi0_default: spi0_default {
pins = <20 21 22 23>; /* CLK, MOSI, MISO, CS */
function = <2>; /* SPI 功能 */
drive-strength = <12>;
};
spi0_sleep: spi0_sleep {
pins = <20 21 22 23>;
function = <0>; /* GPIO 功能 */
bias-pull-down;
};
};
uart0: serial@11003000 {
compatible = "vendor,uart";
reg = <0x11003000 0x1000>;
/* 引用引脚配置 */
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
};
spi0: spi@11004000 {
compatible = "vendor,spi";
reg = <0x11004000 0x1000>;
/* 多状态引脚配置 */
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi0_default>;
pinctrl-1 = <&spi0_sleep>;
};
/* 具有 GPIO 引用的设备 */
device@11005000 {
compatible = "vendor,device";
reg = <0x11005000 0x1000>;
reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
enable-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;
};
常用的引脚配置属性:
bias-disable: 禁用上拉/下拉bias-pull-up: 启用上拉bias-pull-down: 启用下拉drive-strength: 驱动强度(mA)input-enable: 启用输入缓冲器output-high: 设置输出为高电平output-low: 设置输出为低电平按索引应用引脚配置。
rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *dev, int index);
参数:
dev: 设备指针index: 设备树中的配置索引(0 表示 pinctrl-0)返回值:
RT_EOK: 成功-RT_EINVAL: 无效参数-RT_EIO: I/O 错误示例:
/* 应用默认配置(pinctrl-0) */
rt_pin_ctrl_confs_apply(spi_dev, 0);
/* 应用休眠配置(pinctrl-1) */
rt_pin_ctrl_confs_apply(spi_dev, 1);
按名称应用引脚配置。
rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *dev, const char *name);
参数:
dev: 设备指针name: pinctrl-names 中的配置名称返回值:
RT_EOK: 成功-RT_EINVAL: 无效参数或未找到名称-RT_EIO: I/O 错误示例:
/* 在设备初始化时应用默认引脚 */
rt_pin_ctrl_confs_apply_by_name(dev, "default");
/* 在挂起前切换到低功耗引脚 */
rt_pin_ctrl_confs_apply_by_name(dev, "sleep");
在设备树中查找引脚配置索引。
int rt_pin_ctrl_confs_lookup(struct rt_device *dev, const char *name);
参数:
dev: 设备指针name: pinctrl-names 中的配置名称返回值:
>= 0: 配置索引< 0: 未找到错误示例:
int idx = rt_pin_ctrl_confs_lookup(dev, "idle");
if (idx >= 0) {
rt_pin_ctrl_confs_apply(dev, idx);
}
从设备树获取 GPIO 引脚编号。
rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev,
const char *propname,
int index,
rt_uint8_t *out_mode,
rt_uint8_t *out_value);
参数:
dev: 设备指针propname: GPIO 属性名称(如 "reset-gpios")index: GPIO 数组中的索引(对于单个 GPIO 为 0)out_mode: 输出 GPIO 模式(可以为 NULL)out_value: 输出 GPIO 值(可以为 NULL)返回值:
>= 0: GPIO 引脚编号< 0: 错误示例:
rt_uint8_t mode, value;
rt_ssize_t pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, &mode, &value);
if (pin >= 0) {
/* 配置和使用 GPIO 引脚 */
rt_pin_mode(pin, mode);
rt_pin_write(pin, value);
}
计算 GPIO 属性中的引脚数量。
int rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname);
参数:
dev: 设备指针propname: GPIO 属性名称返回值:
>= 0: GPIO 引脚数量< 0: 错误示例:
int count = rt_pin_get_named_pin_count(dev, "gpios");
for (int i = 0; i < count; i++) {
rt_ssize_t pin = rt_pin_get_named_pin(dev, "gpios", i, NULL, NULL);
/* 使用引脚 */
}
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/pin.h>
struct my_spi {
struct rt_spi_bus parent;
void *base;
struct rt_device *dev;
/* GPIO 引脚 */
rt_ssize_t cs_pin;
rt_ssize_t reset_pin;
};
static rt_err_t my_spi_probe(struct rt_platform_device *pdev)
{
struct my_spi *spi;
struct rt_device *dev = &pdev->parent;
rt_err_t ret;
spi = rt_calloc(1, sizeof(*spi));
if (!spi)
return -RT_ENOMEM;
spi->dev = dev;
/* 应用默认引脚配置 */
ret = rt_pin_ctrl_confs_apply_by_name(dev, "default");
if (ret) {
rt_kprintf("Failed to apply default pinctrl: %d\n", ret);
goto err_free;
}
/* 获取 GPIO 引脚 */
spi->cs_pin = rt_pin_get_named_pin(dev, "cs-gpios", 0, NULL, NULL);
if (spi->cs_pin < 0) {
rt_kprintf("Failed to get CS GPIO\n");
ret = spi->cs_pin;
goto err_free;
}
spi->reset_pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, NULL, NULL);
if (spi->reset_pin >= 0) {
/* 配置复位引脚 */
rt_pin_mode(spi->reset_pin, PIN_MODE_OUTPUT);
rt_pin_write(spi->reset_pin, PIN_HIGH);
}
/* 配置 CS 引脚 */
rt_pin_mode(spi->cs_pin, PIN_MODE_OUTPUT);
rt_pin_write(spi->cs_pin, PIN_HIGH);
/* 注册 SPI 总线 */
ret = rt_spi_bus_register(&spi->parent, "spi0", &spi_ops);
if (ret)
goto err_free;
rt_platform_set_drvdata(pdev, spi);
rt_kprintf("SPI driver probed successfully\n");
return RT_EOK;
err_free:
rt_free(spi);
return ret;
}
static rt_err_t my_spi_remove(struct rt_platform_device *pdev)
{
struct my_spi *spi = rt_platform_get_drvdata(pdev);
/* 释放 GPIO 引脚 */
if (spi->reset_pin >= 0) {
rt_pin_write(spi->reset_pin, PIN_LOW);
}
rt_spi_bus_unregister(&spi->parent);
rt_free(spi);
return RT_EOK;
}
/* 电源管理钩子 */
static int my_spi_suspend(const struct rt_device *dev, rt_uint8_t mode)
{
/* 切换到休眠引脚配置 */
return rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "sleep");
}
static void my_spi_resume(const struct rt_device *dev, rt_uint8_t mode)
{
/* 恢复默认引脚配置 */
rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "default");
}
static const struct rt_device_ops spi_ops = {
.suspend = my_spi_suspend,
.resume = my_spi_resume,
};
static const struct rt_ofw_node_id my_spi_ids[] = {
{ .compatible = "vendor,my-spi" },
{ /* sentinel */ }
};
static struct rt_platform_driver my_spi_driver = {
.name = "my-spi",
.ids = my_spi_ids,
.probe = my_spi_probe,
.remove = my_spi_remove,
};
static int my_spi_drv_register(void)
{
rt_platform_driver_register(&my_spi_driver);
return 0;
}
INIT_SUBSYS_EXPORT(my_spi_drv_register);
#include <rtthread.h>
#include <rtdevice.h>
struct simple_pinctrl {
struct rt_device parent;
void *base;
rt_size_t ngroups;
};
static rt_err_t simple_pinctrl_set_mux(struct rt_pinctrl *ctrl,
rt_uint32_t group,
rt_uint32_t function)
{
struct simple_pinctrl *pinctrl = rt_container_of(ctrl, struct simple_pinctrl, parent);
void *reg = pinctrl->base + (group * 4);
/* 设置功能复用 */
rt_uint32_t val = readl(reg);
val &= ~0xFF;
val |= function & 0xFF;
writel(val, reg);
return RT_EOK;
}
static rt_err_t simple_pinctrl_set_config(struct rt_pinctrl *ctrl,
rt_uint32_t group,
rt_ubase_t config)
{
struct simple_pinctrl *pinctrl = rt_container_of(ctrl, struct simple_pinctrl, parent);
void *reg = pinctrl->base + (group * 4);
rt_uint32_t param = RT_PINCTRL_CONFIG_PARAM(config);
rt_uint32_t arg = RT_PINCTRL_CONFIG_ARG(config);
rt_uint32_t val;
val = readl(reg);
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
val &= ~(BIT(8) | BIT(9)); /* 清除上拉/下拉 */
break;
case PIN_CONFIG_BIAS_PULL_UP:
val |= BIT(8);
val &= ~BIT(9);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
val |= BIT(9);
val &= ~BIT(8);
break;
case PIN_CONFIG_DRIVE_STRENGTH:
val &= ~(0xF << 16);
val |= (arg & 0xF) << 16;
break;
default:
return -RT_EINVAL;
}
writel(val, reg);
return RT_EOK;
}
static const struct rt_pinctrl_ops simple_pinctrl_ops = {
.set_mux = simple_pinctrl_set_mux,
.set_config = simple_pinctrl_set_config,
};
static rt_err_t simple_pinctrl_probe(struct rt_platform_device *pdev)
{
struct simple_pinctrl *pinctrl;
rt_err_t ret;
pinctrl = rt_calloc(1, sizeof(*pinctrl));
if (!pinctrl)
return -RT_ENOMEM;
/* 映射寄存器 */
pinctrl->base = rt_dm_dev_iomap(&pdev->parent, 0);
if (!pinctrl->base) {
ret = -RT_EIO;
goto err_free;
}
/* 获取引脚组数量 */
rt_dm_dev_prop_read_u32(&pdev->parent, "pinctrl-groups", &pinctrl->ngroups);
/* 注册 pinctrl 设备 */
pinctrl->parent.ops = &simple_pinctrl_ops;
ret = rt_pinctrl_register(&pinctrl->parent, &pdev->parent);
if (ret)
goto err_unmap;
rt_platform_set_drvdata(pdev, pinctrl);
return RT_EOK;
err_unmap:
rt_dm_dev_iounmap(&pdev->parent, pinctrl->base);
err_free:
rt_free(pinctrl);
return ret;
}
static const struct rt_ofw_node_id simple_pinctrl_ids[] = {
{ .compatible = "simple-pinctrl" },
{ /* sentinel */ }
};
static struct rt_platform_driver simple_pinctrl_driver = {
.name = "simple-pinctrl",
.ids = simple_pinctrl_ids,
.probe = simple_pinctrl_probe,
};
始终应用默认配置
rt_pin_ctrl_confs_apply_by_name(dev, "default");
使用命名状态进行电源管理
/* 挂起 */
rt_pin_ctrl_confs_apply_by_name(dev, "sleep");
/* 恢复 */
rt_pin_ctrl_confs_apply_by_name(dev, "default");
检查 GPIO 可用性
rt_ssize_t pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, NULL, NULL);
if (pin >= 0) {
/* GPIO 可用 */
}
使用 pinctrl 进行专用功能,使用 GPIO 进行 GPIO
支持所有相关的配置参数
验证引脚组索引
if (group >= pinctrl->ngroups)
return -RT_EINVAL;
实现原子配置更新
症状: rt_pin_ctrl_confs_apply 返回错误
可能原因:
解决方案:
/* 检查 pinctrl 引用 */
int idx = rt_pin_ctrl_confs_lookup(dev, "default");
if (idx < 0) {
rt_kprintf("Pinctrl 'default' not found in DT\n");
}
/* 验证 pinctrl 驱动 */
if (!dev->pinctrl) {
rt_kprintf("No pinctrl controller available\n");
}
症状: GPIO 操作不起作用或引脚不响应
可能原因:
解决方案:
/* 对于 GPIO 使用,确保引脚复用为 GPIO 功能 */
/* 在设备树中或通过 pinctrl 驱动程序 */
症状: 从休眠恢复后外设不工作
可能原因:
解决方案:
/* 在恢复时始终恢复默认状态 */
static void my_device_resume(const struct rt_device *dev, rt_uint8_t mode)
{
rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "default");
/* 然后重新初始化硬件 */
}
components/drivers/include/drivers/pin.hcomponents/drivers/core/pinctrl.c