【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法
- 一、Android Linux 下 GPIO 配置方法(AP1 Domain - 6 * A55)
- 1.1 Kernel DTS 配置 GPIO 方法
- 1.1.1 初始化 GPIO 控制器
- 1.1.1.1 打开 AP1 GPIO4控制器
- 1.1.1.2 配置 gpio124 按键示例
- 1.1.1.3 配置 gpio4 驱动 led 实例
- 1.1.1.4 GPIO 相关操作函数
- 1.2 Kernel 使用 GPIO 实例
- 1.2.1 普通 gpio 操作实例
- 1.2.2 中断 gpio 操作实例
- 1.2.3 SD Card 中断 GPIO 分析
- 1.3 devmem 调试 GPIO 方法
- 1.3.1 基于 sysfs 接口访问 gpio
- 1.3.2 基于 debugfs 操作 gpio节点
- 1.3.3 devmem 调试说明
- 二、SafetyOS 下 GPIO 配置方法(Safety Domain - Dual Core R5)
- 2.1 SDToolBox GPIO 配置与烧录方法
- 2.2 SafetyOS 下配置GPIO
- 2.2.1 配置 PINMUX 为 GPIO
- 2.2.2 配置 GPIO 输出
- 2.2.3 配置 GPIO 输入
- 2.3 SafetyOS 下配置 GPIO 中断实例
- 2.4 SafetyOS 下配置 GPIO 调试方法
- 2.4.1 get_pin_info
- 2.4.2 操作寄存器
- 三、QNX 下 GPIO 配置方法(AP2 Domain - 1 * A55)
本 SemiDrive源码分析 之 Yocto源码分析 系列文章汇总如下:
- 《【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)》
- 《【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析》
- 《【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)》
- 《【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)》
- 《【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?》
- 《【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?》
- 《【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的》
- 《【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍》
- 《【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析》
- 《【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法》
在X9HP中提供了不同的domain,简单理解就是不同的相互隔离的CPU核,不同的Domain之间的GPIO控制器是不同的,
在X9HP上有三个Domain:
-
AP1 (6*A55):使用的是GPIO4 控制器 -
AP2 (1*A55):使用的是GPIO5 控制器 -
Safety (双核R5):使用的是GPIO1 控制器
除了GPIO控制器在不同的domain 之间不同外,pin也是有各自不同的domain的,
但是当它们作为gpio使用时,可以通过设置挂靠到对应域下的GPIO控制器上来在对应的domain下使用。
具体 pin 的gpio控制器的设置可以通过PC工具的SDConfigTool来进行。
在linux 下将一个pin当做gpio使用时,首先要确定它的domain是否是AP, 如果不是则需要进行修改;
大部分pin都是mux 类型即可以当做不同的功能,而一小部分 pin 是专用功能,所以 pin 本身涉及到 pin 功能、驱动强度、向上拉、方向、以及电平等设置;所有这些相关的内容都是通过PC工具进行配置。
配置完成后会生成对应的配置 bin 文件,将此文件烧录到对应的分区 system_config,在系统启动过程中会用到。
而如果 pin 本身是设置 ok的,则不需要更改此部分内容。
在 linux内核下gpio 的配置使用,比如gpio驱动的led,你需要根据你的原理图在第一步中查找具体的gpio号并填入到dts,而后驱动进行解析并使用。不建议直接在驱动中通过gpio 号来操作而不经过dts 设置解析的过程,因为一旦出问题,查找起来比较麻烦。
经过 SDConfigTool 配置后会生成对应的配置文件 system_config.bin,这个部分需要烧录到 system_config分区,safety domain 在系统启动过程中分读取此分区的配置信息并将其写入寄存器生效。
当Linux内核运行起来后之前的配置信息已经生效,所以内核初始化过程中主要就是解析dts 中设置的 gpio 信息并进行初始化设置,之后就根据各个进程需求在驱动中得到调用。
一、Android Linux 下 GPIO 配置方法(AP1 Domain - 6 * A55)
1.1 Kernel DTS 配置 GPIO 方法
1.1.1 初始化 GPIO 控制器
不同的domain使用不同的gpio控制器,这些控制器的dts配置定义,
因为是AP1的代码,所以我只留下GPIO4的配置,其他的GPIO3和GPIO5省略不动,如下:
gpio3: gpio@30410000 {
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30410000 0x0 0x10000>;
status = "disabled";
// ......省略......
};
gpio4: gpio@30420000 { // Android AP1 对应的GPIO控制器
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30420000 0x0 0x10000>; // gpio4 寄存器地址
status = "disabled";
port4a: gpio-controller@0 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;// 此项为GPIO控制器,内核驱动初始化过程中会生成对应的struct gpio_chip结构体
gpio-ranges = <&pinctrl 0 0 32>; //控制的GPIO范围,从gpio0 到 gpio31,共32个gpio
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <0>; // 当前组的编号为 port0
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
// dts 使用 int 时需要配置的单元数
interrupts = <GIC_SPI 236 IRQ_TYPE_LEVEL_HIGH>; //中断类:型软件中断,中断号:236,触发类型:高电平中断
};
port4b: gpio-controller@1 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 32 32>; //控制的GPIO范围,从gpio32 到 gpio63,共32个gpio
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <1>; // 当前组的编号为 port1
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
interrupts = <GIC_SPI 237 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:237,触发类型:高电
};
port4c: gpio-controller@2 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 64 32>; //控制的GPIO范围,从gpio64 到 gpio95,共32个gpio
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <2>; // 当前组的编号为 port2
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:238,触发类型:高电
};
port4d: gpio-controller@3 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 96 32>; //控制的GPIO范围,从gpio96 到 gpio127,共32个gpio
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <3>; // 当前组的编号为 port3
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
interrupts = <GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:239,触发类型:高电
};
port4e: gpio-controller@4 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 128 32>; //控制的GPIO范围,从gpio128 到 gpio159,共32个gpio
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <4>; // 当前组的编号为 port4
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
interrupts = <GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:240,触发类型:高电
};
};
gpio5: gpio@30430000 {
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30430000 0x0 0x10000>;
status = "disabled";
// ......省略......
};1.1.1.1 打开 AP1 GPIO4控制器
在dts中也搜到只有dts4在使用:
&gpio4 {
//pinctrl-names = "default";
//pinctrl-0 = <&pinctrl_gpiotouch_lvds &pinctrl_gpiotouch_mipi>;
status = "okay";
};1.1.1.2 配置 gpio124 按键示例
gpio-keys {
status = "okay";
compatible = "gpio-keys";
up {
label = "GPIO Key Power";
linux,code = <KEY_POWER>;
gpios = <&port4d 28 GPIO_ACTIVE_LOW>; //此gpio为4d组内偏移编号为28(124 - 96) 的gpio 即 gpio124,低有效
};
};以 gpio124 配置为按键为例,gpio124 是属于 port4d 中的 gpio,它的起始编号为 96,偏移为 28。
包含三个部分,第一部分是组名即4d组,中间的则是组内偏移编号,最后一个是有效性设置.
1.1.1.3 配置 gpio4 驱动 led 实例
leds {
status = "okay";
compatible = "gpio-leds";
led0: heartbeat {
label = "heartbeat";
default-state = "on";
linux,default-trigger = "heartbeat";
gpios = <&port4a 4 GPIO_ACTIVE_LOW>; //此gpio为4a组内偏移编号为4 (0+4) 的gpio 即 gpio4,低有效
};
};所谓的有效电平即设备处于工作 active 状态的电平级别,比如按键按下的电平,led 灯亮的电平。
1: 输出的情况: 以 gpiod_set_value 为例
GPIO_ACTIVE_HIGH: 代码逻辑写 1,输出高电平;代码逻辑写 0,输出低电平。
GPIO_ACTIVE_LOW: 代码逻辑写 0,输出高电平;代码逻辑写 1,输出低电平。
2: 输入的情况: 以 gpiod_get_value 为例
GPIO_ACTIVE_HIGH: 外部输入为高电平,返回 1,否则返回 0。
GPIO_ACTIVE_LOW: 外部输入为低电平,返回 1,否则返回 0。
1.1.1.4 GPIO 相关操作函数
struct gpio_chip
gpio 控制器抽象结构体,比如GPIO4 控制器里面有五组,那么则有五个控制器。划分的依据简单的理解为每一组都可以控制
0-31 范围的 gpio,而且识别访问可以通过简单的”偏移” 来进行
struct gpio_desc
gpio 描述符,即描述具体某个 gpio 的信息,包括 name、label 以及相关的flag 等
struct gpio_device
gpio chip 的内部状态存储结构,主要保存大部分的运行时数据,比如其中包含了 struct gpio_desc 数组,即存储此 chip 中的各个 gpio 的描述符,在初始化时构建。
目前 linux 内核提供了两套 gpio 操作接口,
-
legacy 接口通过gpio number 去操作gpio 函数以gpio_* 作为前缀, -
new 接口通过struct gpio_desc 去操作gpio, 函数以gpiod_* 作为前缀,目前内核推荐使用new 接口去操作gpio
- 通过解析
DTS 申请gpio
// 从设备的子节点获取一个 gpio 描述符
static inline struct gpio_desc *devm_fwnode_get_gpiod_from_child
(struct device *dev, const char *con_id, struct fwnode_handle *child,
enum gpiod_flags flags, const char *label)
// 根据功能字符串 con_id 来获取 gpio,
// 即 dts 中设置的 xxx-gpio=<...> 或者 xxx-gpios=<...>的属性
// 则可以通过将 con_id 设置为”xxx” 来获取对应的 struct gpio_desc,如果此处设置为 NULL,则直接使用 gpio 或者 gpios
struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)
// flags: 用于指定 gpio 默认的方向及电平,可被设置为如下值:
- GPIOD_ASIS // 不对 gpio 设置默认值
- GPIOD_IN // gpio 默认设置为输入
- GPIOD_OUT_LOW // gpio 默认设置为输出且输出低电平
- GPIOD_OUT_HIGH // gpio 默认设置为输出且输出高电平- 设置
gpio 方向
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);- 设置输出电平
void gpiod_set_value(struct gpio_desc *desc, , int value);- 读取输入电平
int gpiod_get_value(const struct gpio_desc *desc);- 释放
gpio 如果申请 gpio 的接口包含 devm 前缀,在驱动卸载 或 加载失败时,gpio 将自动被释放,
对于这类场景,不需要显式调用该函数释放 gpio 资源. 如果正常运行过程中需主动释放该gpio
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)- 获取
GPIO 对应的中断号
int gpiod_to_irq(const struct gpio_desc *desc);更多接 口见: kernel/include/linux/gpio/consumer.h
1.2 Kernel 使用 GPIO 实例
1.2.1 普通 gpio 操作实例
- 配置DTS
node {
...
reset-gpios = <&gpioctlr 43 GPIO_ACTIVE_HIGH>;
}- 代码操作
struct gpio_desc *reset;
int status;
reset = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset)) {
err = PTR_ERR(reset);
return err;
}
gpiod_set_value(reset, 1); // DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,输出高电平
gpiod_set_value(reset, 0); // DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,输出低电平
gpiod_direction_input(reset) // 将 GPIO 设置为输入
// DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,如果读到的是高电平,返回 1,如果读到的是低电平,返回 0
status = gpiod_get_value(reset)1.2.2 中断 gpio 操作实例
触摸屏 中断GPIO 以ds941_ds948_gt9xx 为例,DTS配置如下:
有关 《tca9539芯片手册》可以上TI官网下载
&i2c11 {
status = "okay";
tp_gpio: gpio@76 {
compatible = "ti,tca9539";
reg = <0x76>;
gpio-controller;
};
};
&i2c15 {
status = "disabled";
sync_enable = <1>;
serdes_gt9xx_16_j35:touch@5d {
compatible = "sdrv,ds941_ds948_gt9xx";
irq-gpios = <&port4e 16 0>; /*io96*/ // 配置 GPIO(128 + 16) = gpio144 中断引脚
reset-gpios = <&tp_gpio 1 0>; /*index 1*/ // 使用tca9539扩展的GPIO作为中断引脚
reg = <0x5d>;
dev_type = <0>; /* 0: main device, 1: aux device */
addr_ds941 = <0x0c>;
addr_ds948 = <0x2c>;
irq_channel = <3>;
reset_channel = <2>;
};
serdes_gt9xx_16_j35a:touch@14 {
compatible = "sdrv,ds941_ds948_gt9xx";
irq-gpios = <&port4e 17 0>; /*io97*/ //配置 GPIO(128 + 17) = gpio145 中断引脚
reset-gpios = <&tp_gpio 2 0>; /*index 2*/
reg = <0x14>;
dev_type = <1>; /* 0: main device, 1: aux device */
addr_ds941 = <0x0d>;
addr_ds948 = <0x3c>;
irq_channel = <3>;
reset_channel = <2>;
};
};代码操作如下:
可以看出,在ds941_ds948_goodix.c 中只是对dts中的内容进行了解析,而申请中断这些事则是由devm_request_threaded_irq进行统一封装了。
static int goodix_request_irq(struct goodix_ts_data *ts)
{
return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
NULL, goodix_ts_irq_handler, ts->irq_flags, ts->client->name, ts);
}
static int goodix_get_gpio_config(struct goodix_ts_data *ts)
{
int error;
struct device *dev;
struct gpio_desc *gpiod;
dev = &ts->client->dev;
/* Get the interrupt GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); // 解析 gpio144
ts->gpiod_int = gpiod;
/* Get the reset line GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); // 解析 reset引脚,使用tca9539扩展的GPIO作为中断引脚
ts->gpiod_rst = gpiod;
return 0;
}
static int goodix_ts_probe(struct i2c_client *client,const struct i2c_device_id *id){
error = goodix_get_gpio_config(ts);
// 解析 941 i2c地址: 0x0c
error = of_property_read_u32(client->dev.of_node, "addr_ds941", &val); ts->addr_ds941 = (u8)val;
// 解析 948 i2c地址: 0x2c
error = of_property_read_u32(client->dev.of_node, "addr_ds948", &val); ts->addr_ds948 = (u8)val;
// 解析 irq_channel: 3
error = of_property_read_u32(client->dev.of_node, "irq_channel", &val); ts->irq_channel = val;
// 解析 reset_channel: 2
error = of_property_read_u32(client->dev.of_node, "reset_channel", &val); ts->reset_channel = val;
error = of_property_read_u32(client->dev.of_node, "dev_type", &ts->dev_type);
error = of_property_read_u32(client->dev.of_node, "port_type", &ts->port_type);
}
static const struct of_device_id goodix_of_match[] = {
{ .compatible = "sdrv,ds941_ds948_gt9xx" },
{ }
};
MODULE_DEVICE_TABLE(of, goodix_of_match);
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
.id_table = goodix_ts_id,
.driver = {
.name = "Semidrive DS941 DS948 Goodix-TS",
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
.of_match_table = of_match_ptr(goodix_of_match),
.pm = &goodix_pm_ops,
},
};
module_i2c_driver(goodix_ts_driver);我们进入 devm_request_threaded_irq 函数中看一下:
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
{
struct irq_devres *dr;
int rc;
dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),GFP_KERNEL);
devname = dev_name(dev);
rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, dev_id);
dr->irq = irq;
dr->dev_id = dev_id;
devres_add(dev, dr);
return 0;
}1.2.3 SD Card 中断 GPIO 分析
SD Card 中断GPIO配置如下,同样 我也不知道 ext_gpio74 说明的是哪一个GPIO引脚,等后面到板子打印看下。
sdhci3: sdhci@341a0000 {
compatible = "snps,dwcmshc-sdhci";
reg = <0 0x341a0000 0 0x10000>;
interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
sdrv,scr_signals_ddr = <SCR_SEC__mshc3_ddr_mode>;
vmmc-supply = <&vmmc_3v3>;
vqmmc-supply = <&vqmmc_3v3>;
status = "disabled";
};
&i2c11 {
status = "okay";
ext_gpio74: gpio@74 {
compatible = "ti,tca9539"; // 使用tca9539扩展的GPIO作为中断引脚
reg = <0x74>;
gpio-controller;
};
&sdhci3 {
clocks = <&EMMC3>;
clock-names = "core";
max-frequency = <25000000>;
cd-gpios = <&ext_gpio74 14 GPIO_ACTIVE_LOW>; // 使用tca9539扩展的GPIO作为中断引脚
bus-width = <4>;
no-1-8-v;
no-mmc;
no-sdio;
disable-wp;
keep-power-in-suspend;
status = "okay";
};我们进代码找下 它对应的源码在哪:grep -rsn "snps,dwcmshc-sdhci" .
kernel/drivers/mmc/host/sdhci-of-dwcmshc.c:605: { .compatible = "snps,dwcmshc-sdhci" },
kernel/drivers/mmc/host/mmci.c:1743: ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);其代码如下:
static int mmci_probe(struct amba_device *dev, const struct amba_id *id)
{
if (!np) {
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
if (ret < 0) {
if (ret == -EPROBE_DEFER)
goto clk_disable;
else if (gpio_is_valid(plat->gpio_cd)) {
ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0);
}
}
}
ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
if (!dev->irq[1])
host->singleirq = true;
else {
ret = devm_request_irq(&dev->dev, dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
}
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
}1.3 devmem 调试 GPIO 方法
1.3.1 基于 sysfs 接口访问 gpio
获取
GPIO对应的Number
gpio Number 与 gpio 硬件编号相同,那我们通过 SDConfigtool 获取 gpio 硬件编号(也即 gpio Number),例如下图
GPIO_C13 pin 用作 gpio 功能时,其 gpio number 为 61
GPIO 操作通过命令导出
gpio61: echo 61 > export进入
gpio61 目录执行 gpio相关操作:
此功能通常用于验证
gpio 输入/输出值是否正确,即输入模式则 cat value 可以得到输入值,而输出模式
echo 1 或者 echo 0 到 value 则输出高或者低等
要导出某gpio,它必须是未被驱动 request 的状态,
即如果有驱动在使用并request了则会失败。
如果对应的pin被配置为其他功能(非 gpio),导出 gpio 操作将无效,
也就是只有在 pin 被配置为 gpio 的情况下,通过 sysfs 去操作 gpio 才有意义
1.3.2 基于 debugfs 操作 gpio节点
debugfs 下相关的节点则可以看到关于 gpio 总体的信息。
- 一共有五个
gpio chip,每个占用32 个gpio,号码从0 到159。 - 各个组中已经申请了的
gpio 的信息,包括gpio号、名字、输入输出以及高低、是否用作了中断等

1.3.3 devmem 调试说明
在 busybox 的杂项工具中提供了 devmem 工具,它可以在应用层进行物理地址的读写访问,
实际就是对/dev/mem 操作来完成
Usage: devmem ADDRESS [WIDTH [VALUE]]
Read/write from physical
ADDRESS Address to act upon :需要进行读写访问的物理地址
WIDTH Width (8/16/...) :访问数据类型
VALUE Data to be writte :如果是读操作省略;如果是写操作,表示需要写入的数据
基本测试用法
0xFFEF
0xFFFFFFEF
0xE寄存器部分说明

其中
m 表示对应的 gpio 组编号,GPIO4 一共有五个组,即0-4,得到的 Addr 即是偏移值,再加上控制器本身的
reg值则得到最终的物理内存地址。一个寄存器 32 位,刚好对应组中的32 个 gpio
port4a gpio4 操作实例首先进行地址的计算
gpio4 控制器地址: 0x30420000
dir 方向寄存器地址: 0x30420000 + 0x2000 + (0*0x10) = 0x30422000
out 输出寄存器地址:0x30420000 + 0x2400 + (0*0x10) = 0x3
方向寄存器结果: 其中第四位是
1 即表示输出模式。
0x08000010 = 1000 0000 0000 0000 0000 0001 0000输出寄存器结果: 其中第四位时 1 时 0 即设置时高时低来达到一个 heartbeat 的效果
0x08000010 = 1000 0000 0000 0000 0000 0001 0000
0x08000000 = 1000 0000 0000 0000 0000 0000 0000
二、SafetyOS 下 GPIO 配置方法(Safety Domain - Dual Core R5)
2.1 SDToolBox GPIO 配置与烧录方法
SDToolBox 工具路径位于: buildsystem\res\tools\release\SDToolBox
项目GPIO配置文件位于:buildsystem\rtos\lk_boot\chipcfg\generate\x9_high-plus

进入SDConfigTool 工具,打开项目文件:

完整界面截图如下,根据 硬件原理图做相关的配置:

配置完后,分别点如下三个Save 按钮分别保存 prj、bin、conf文件。
生成的 system_config.bin文件,这个部分需要烧录到 system_config 分区

2.2 SafetyOS 下配置GPIO
2.2.1 配置 PINMUX 为 GPIO
在 Safety Domain 中,可以通过代码配置 pinmux 为GPIO,该配置方法会覆盖PC工具配的system config中的配置。
pinmux 建议在 system config 中配置便于同一查看,此处只提供方法,不建议在代码中操作。
- 获取
GPIO 操作的handle
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)
- 设置
PINMUX
int hal_port_set_pin_mode(void *handle, const Port_PinType pin, const Port_PinModeType mode)
//使用的 GPIO 编号,在${freertos}\chipdev\port\sd_port\inc\port_cfg_def.h 文件中查找自己需要使用的。
方向:
PORT_PAD_IS__OUT 输出
PORT_PAD_IS__IN 输入
上下拉状态
PORT_PIN_IN_NO_PULL 悬空
PORT_PIN_IN_PULL_UP 上拉
PORT_PIN_IN_PULL_DOWN 下拉
功能
PORT_PIN_MODE_GPIO 引脚功能配置为 GPIO- 释放
GPIO 操作的handle
bool hal_port_release_handle(void **handle)
参数说明:
void **handle: 释放操作 GPIO 的句- 以
GPIO D8 为例,配置GPIO pinmux,代码例子:
const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;
const Port_PinModeType MODE_GPIO_D8_M0_GPIO_D8 = {
((uint32_t)PORT_PAD_POE__DISABLE | PORT_PAD_IS__OUT | PORT_PAD_SR__FAST |
PORT_PAD_DS__MID1 | PORT_PIN_IN_NO_PULL), ((uint32_t)PORT_PIN_MUX_FV__MIN | PORT_PIN_MUX_FIN__MIN |
PORT_PIN_OUT_PUSHPULL | PORT_PIN_MODE_GPIO), };
extern const domain_res_t g_iomuxc_res;
void backlight_enable(void)
{
static void *some_port_handle;
bool ret;
/* Port setup */
// 申请 与配置GPIO
ret = hal_port_creat_handle(&some_port_handle, g_iomuxc_res.res_id[0]);
ret = hal_port_set_pin_mode(some_port_handle, PIN_GPIO_D8_M0_GPIO_D8, MODE_GPIO_D8_M0_GPIO_D8);
......
// 使用完后释放GPIO
hal_port_release_handle(&some_port_handle);
}2.2.2 配置 GPIO 输出
Pinmux 配置成 GPIO 输出后,在 Safety 代码中可以操作 GPIO 输出高电平
操作步骤:
- 获取 GPIO 操作 handle
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)
- 控制 GPIO 输出高低电平
void hal_dio_write_channel(void *handle, const Dio_ChannelType ChannelId, const Dio_LevelType Level)
void *handle : 获取的操作 GPIO 的句柄
const Dio_ChannelType ChannelId: 操作的 GPIO
const Dio_LevelType Level: 输出高低电平;1:高电平,0:低电平- 释放获取的 GPIO handle:
bool hal_dio_release_handle(void **handle) - 以 GPIO D8 为例配置 GPIO 输出高低
const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;
extern const domain_res_t g_gpio_res;
void backlight_enable(void)
{
static void *some_dio_handle;
bool ret;
/* Dio setup */
ret = hal_dio_creat_handle(&some_dio_handle, g_gpio_res.res_id[0]);
hal_dio_write_channel(some_dio_handle, PIN_GPIO_D8_M0_GPIO_D8, 1);
.....
hal_dio_release_handle(&some_dio_handle);
}2.2.3 配置 GPIO 输入
Pinmux 配置成 GPIO 输入后,在 Safety 代码中可以获取 GPIO 输入的状态。
- 获取 GPIO 操作 handle
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)
参数说明:
void **handle: 获取的句柄
uint32_t dio_res_glb_idx: 传入 g_iomuxc_res.res_id[0]- 读取 GPIO 输入的高低电平
Dio_LevelType hal_dio_read_channel(void *handle, const Dio_ChannelType ChannelId, const Dio_LevelType Level)
参数说明
void *handle : 获取的操作 GPIO 的句柄
const Dio_ChannelType ChannelId: 读取的 GPIO
返回值:Dio_LevelType 读取的电平- 释放获取的 GPIO handle:
bool hal_dio_release_handle(void **handle) - 以 GPIO D8 为例配置 GPIO 获取输入状态,代码
const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;
extern const domain_res_t g_gpio_res;
void backlight_enable(void)
{
static void *some_dio_handle;
bool ret;
Dio_LevelType dio_value;
/* Dio setup */
ret = hal_dio_creat_handle(&some_dio_handle, g_gpio_res.res_id[0]);
dio_value = hal_dio_read_channel(some_dio_handle, PIN_GPIO_D8_M0_GPIO_D8);
........
hal_dio_release_handle(&some_dio_handle);
}2.3 SafetyOS 下配置 GPIO 中断实例
Pinmux 配置成 GPIO 输入后,Safety 下 GPIO
- 注册 GPIO 中断
int register_gpio_int_handler(uint16_t ChannelId, int irqflag, int_handler handler, void *arg)
参数说明:
uint16_t ChannelId: 使用的 GPIO
int irqflag: 中断触发方式
int_handler handler: 中断处理函数
void *arg: 中断触发时出入到中断处理函数的参数- 使能 GPIO 中断
int unmask_gpio_interrupt(uint16_t ChannelId)
参数说明:
uint16_t ChannelId: 使用的 GPI- 失能 GPIO 中断
int mask_gpio_interrupt(uint16_t ChannelId)
参数说明:
uint16_t ChannelId: 使用的 GPI- 以 GPIO D8 为例配置 GPIO 中断触发
static int lt9611_work_func(void *arg)
{
while (1) {
event_wait(&event_hdmi_hpd);
//do something
unmask_gpio_interrupt(PortConf_PIN_GPIO_D8);
}
return 0;
}
lt9611_thread = thread_create("lt9611", lt9611_work_func, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_detach_and_resume(lt9611_thread);
static enum handler_return d8_irq_handler(void *arg)
{
mask_gpio_interrupt(PortConf_PIN_GPIO_D8);
event_signal(&event_hdmi_hpd, false);
return INT_RESCHEDULE;
}
event_init(&event_hdmi_hpd, false, EVENT_FLAG_AUTOUNSIGNAL);
register_gpio_int_handler(PortConf_PIN_GPIO_D8, IRQ_TYPE_EDGE_FALLING, d8_irq_handler, NULL);
。。。。。。。。。
unmask_gpio_interrupt(PortConf_PIN_GPIO_D8);2.4 SafetyOS 下配置 GPIO 调试方法
2.4.1 get_pin_info
Safety 下可以通过此命令获取 9 系列芯片所有 PIN 的状态。
进入 safety Command terminal,输入 help,查看是否有 get_pin_info;若存在即可在Command terminal中使用。
使用方法 get_pin_info 或 get_pin_info [pin_index];不加 pin_index 参数会打印所有 PIN 的状态;
加 pin_index 获取指定 PIN 的状态。
以获取 GPIO_A4 的状态举例,并说明 get_pin_info 28
pin[28], pad addr: 0xfc521000, value: 0x1011
mux addr: 0xfc59c000, value: 0x0
gpio addr: 0xf0420040, value: 0x4001
pin[28] is set to GPIO, info as following:
GPIO controller: GPIO4
Direction: output
Level: Low
Pull Down/Up: Pull Down
PAD Mode: Push-Pull
Registers values as following:
PAD config value 0x1011
Mux config value 0x0
GPIO4 controller config value 0x4001
2.4.2 操作寄存器
以 GPIO_A4 为例
首先进行地址的计算
gpio4 控制器地址: 0xf0420000
dir 方向寄存器地址: 0xf0420000 + 0x2000 + (0*0x10) = 0xf0422000
out 输出寄存器地址: 0xf0420000 + 0x2400 + (0*0x10) = 0xf0422400- 通过
Safety 下的命令 dw 查看
dw 使用方法:
dw : display memory in words
dw [reg] [length]
mw 使用方法:
mw : modify word of memory
mw [reg]
0xf0422000: 08000010
0xf0422400: 08000000
0xf0422400: 08000010方向寄存器结果: 其中第四位是 1 即表示输出模式。
0x08000010 = 1000 0000 0000 0000 0000 0001 0000
输出寄存器结果: 其中第四位时 1 时 0 即设置时高时低来达到一个 heartbeat 的效果
0x08000010 = 1000 0000 0000 0000 0000 0001 0000
0x08000000 = 1000 0000 0000 0000 0000 0000 0000
三、QNX 下 GPIO 配置方法(AP2 Domain - 1 * A55)
目前 没有最新的QNX 代码,等后续 QNX 代码到了后再分析后更新吧。









