瑞芯微RK3308B用gpiod_get/set_value_cansleep函数读写PCA9555 GPIO扩展芯片的I/O口,并使用request_threaded_irq函数开启GPIO中断

PCA9555是一款基于I2C的GPIO扩展芯片,可以通过I2C接口扩展出16个I/O口。每个I/O口都可以单独作为输入口或者输出口,并且在linux驱动程序里面可以像普通GPIO口那样操作这些扩展出来的GPIO。使用gpiod_get_value_cansleep函数读取输入电平,使用gpiod_set_value_cansleep函数设置输出电平,甚至还可以用request_threaded_irq函数申请中断,而且每一个GPIO端口都可以单独申请中断。

【电路图】
接了6个LED灯和3个按键。
按键不需要接上/下拉电阻,直接一端接I/O口,另一端接地就行了。因为I2C通信速度较慢,所以程序里面不需要消抖。

【电路板】

【设备树:rk3308-pca9555.dtsi】

/* 
 * ./build.sh kernel-config里面需要勾选:
 * Device Drivers  --->
   -*- GPIO Support  --->
   I2C GPIO expanders  --->
   <*> PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports
   [*]   Interrupt controller support for PCA953x
 *
 * 参考文档:Luckfox_Nova_SDK_250430/kernel/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
 */
&i2c3 {
	status = "okay";

	pca9555: pca9555@20 {
		compatible = "nxp,pca9555";
		reg = <0x20>;
		interrupt-parent = <&gpio0>;
		interrupts = <RK_PB6 IRQ_TYPE_LEVEL_LOW>;
		interrupt-controller;
		#interrupt-cells = <2>;
		gpio-controller;
		#gpio-cells = <2>;
	};
};

/ {
	pca9555_test1 {
		compatible = "testboard,pca9555_test";
		led-gpios = <&pca9555 15 GPIO_ACTIVE_HIGH>, <&pca9555 14 GPIO_ACTIVE_HIGH>;
		button-gpio = <&pca9555 6 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&pca9555>;
		interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
	};
	pca9555_test2 {
		compatible = "testboard,pca9555_test";
		led-gpios = <&pca9555 12 GPIO_ACTIVE_HIGH>, <&pca9555 13 GPIO_ACTIVE_HIGH>;
		button-gpio = <&pca9555 7 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&pca9555>;
		interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
	};
	pca9555_test3 {
		compatible = "testboard,pca9555_test";
		led-gpios = <&pca9555 10 GPIO_ACTIVE_HIGH>, <&pca9555 11 GPIO_ACTIVE_HIGH>;
		button-gpio = <&pca9555 9 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&pca9555>;
		interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
	};
};

【C语言驱动程序:pca9555_test.c】

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>

struct pca9555_data {
	struct platform_device *pdev;
	struct gpio_descs *led_gpios;
	struct gpio_desc *button_gpio;
	unsigned long value;
};

static irqreturn_t pca9555_test_irq_handler(int irq, void *dev_id)
{
	int value;
	struct platform_device *pdev = dev_id;
	struct pca9555_data *drvdata = platform_get_drvdata(pdev);

	// 实测发现这里不需要对按键进行消抖
	// 只需要判断一下value==1,按一次按键,LED灯的状态就只会变化一次
	value = gpiod_get_value_cansleep(drvdata->button_gpio);
	dev_info(&pdev->dev, "irq=%d, gpio_value=%d\n", irq, value);
	if (value == 1) {
		drvdata->value = (drvdata->value + 1) & 3;
		gpiod_set_array_value_cansleep(drvdata->led_gpios->ndescs, drvdata->led_gpios->desc, drvdata->led_gpios->info, &drvdata->value);
	}
	return IRQ_HANDLED;
}

static int pca9555_test_probe(struct platform_device *pdev)
{
	int irq, ret;
	struct pca9555_data *drvdata;

	dev_info(&pdev->dev, "probe\n");
	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pca9555_data), GFP_KERNEL);
	if (drvdata == NULL)
		return -ENOMEM;
	drvdata->pdev = pdev;
	platform_set_drvdata(pdev, drvdata);

	// 获取GPIO引脚
	drvdata->led_gpios = devm_gpiod_get_array(&pdev->dev, "led", GPIOD_OUT_LOW);
	if (IS_ERR(drvdata->led_gpios)) {
		dev_err(&pdev->dev, "failed to get led gpios\n");
		return PTR_ERR(drvdata->led_gpios);
	}
	drvdata->button_gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN);
	if (IS_ERR(drvdata->button_gpio)) {
		dev_err(&pdev->dev, "failed to get button gpio\n");
		return PTR_ERR(drvdata->button_gpio);
	}

	// 申请GPIO中断
#if 0
	// 方法1: 申请button-gpio属性对应的GPIO中断
	// 需要在代码里面设置触发方式(电平或边沿触发)
	irq = gpiod_to_irq(drvdata->button_gpio);
	if (irq < 0) {
		dev_err(&pdev->dev, "gpiod_to_irq() failed\n");
		return irq;
	}
	dev_info(&pdev->dev, "gpiod_to_irq(): irq=%d\n", irq);
	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, pca9555_test_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, pdev->dev.of_node->name, pdev);
	if (ret != 0) {
		dev_err(&pdev->dev, "failed to request irq\n");
		return ret;
	}
#else
	// 方法2: 申请interrupt-parent和interrupts属性指定的中断
	// 触发方式是在设备树里面设置
	irq = of_irq_get(pdev->dev.of_node, 0);
	if (irq <= 0) {
		dev_err(&pdev->dev, "of_irq_get() failed, ret=%d\n", irq);
		return -ENODEV;
	}
	dev_info(&pdev->dev, "of_irq_get(): irq=%d\n", irq);
	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, pca9555_test_irq_handler, IRQF_ONESHOT, pdev->dev.of_node->name, pdev);
	if (ret != 0) {
		dev_err(&pdev->dev, "failed to request irq\n");
		return ret;
	}
#endif
	return 0;
}

static int pca9555_test_remove(struct platform_device *pdev)
{
	struct pca9555_data *drvdata = platform_get_drvdata(pdev);

	dev_info(&pdev->dev, "remove\n");
	drvdata->value = 0;
	gpiod_set_array_value_cansleep(drvdata->led_gpios->ndescs, drvdata->led_gpios->desc, drvdata->led_gpios->info, &drvdata->value);
	return 0;
}

static const struct of_device_id pca9555_test_match_ids[] = {
	{.compatible = "testboard,pca9555_test"},
	{}
};
MODULE_DEVICE_TABLE(of, pca9555_test_match_ids);

static struct platform_driver pca9555_test_driver = {
	.driver = {
		.name = "pca9555_test",
		.of_match_table = pca9555_test_match_ids
	},
	.probe = pca9555_test_probe,
	.remove = pca9555_test_remove
};

module_platform_driver(pca9555_test_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oct1158");

【Makefile】

KDIR ?= ../../Luckfox_Nova_SDK_250430/kernel
CROSS_COMPILE ?= ../../Luckfox_Nova_SDK_250430/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-

obj-m = pca9555_test.o

.PHONY: build clean

build:
	$(MAKE) -C $(KDIR) M=$(shell pwd) modules ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE)

clean:
	$(MAKE) -C $(KDIR) M=$(shell pwd) clean

【程序运行结果】
注意:如果按键按得比较快,有时触发中断时gpio_value会等于0。

root@rk3308b-buildroot:/root# lsmod
Module                  Size  Used by    Tainted: G
aic8800_bsp            73728  0
root@rk3308b-buildroot:/root#
root@rk3308b-buildroot:/root# insmod pca9555_test.ko
[ 1181.061097] pca9555_test pca9555_test1: probe
[ 1181.067957] pca9555_test pca9555_test1: of_irq_get(): irq=60
[ 1181.074173] pca9555_test pca9555_test2: probe
[ 1181.080873] pca9555_test pca9555_test2: of_irq_get(): irq=61
[ 1181.087108] pca9555_test pca9555_test3: probe
[ 1181.093700] pca9555_test pca9555_test3: of_irq_get(): irq=62
root@rk3308b-buildroot:/root# [ 1188.605249] pca9555_test pca9555_test1: irq=60, gpio_value=1
[ 1189.327816] pca9555_test pca9555_test1: irq=60, gpio_value=1
[ 1189.637688] pca9555_test pca9555_test1: irq=60, gpio_value=1
[ 1190.707084] pca9555_test pca9555_test1: irq=60, gpio_value=1
[ 1191.065025] pca9555_test pca9555_test2: irq=61, gpio_value=1
[ 1191.304011] pca9555_test pca9555_test2: irq=61, gpio_value=1
[ 1191.521441] pca9555_test pca9555_test2: irq=61, gpio_value=1
[ 1191.733588] pca9555_test pca9555_test2: irq=61, gpio_value=1
[ 1192.353480] pca9555_test pca9555_test3: irq=62, gpio_value=1
[ 1192.598903] pca9555_test pca9555_test3: irq=62, gpio_value=1
[ 1193.090824] pca9555_test pca9555_test3: irq=62, gpio_value=1
[ 1193.434381] pca9555_test pca9555_test3: irq=62, gpio_value=1

root@rk3308b-buildroot:/root# rmmod pca9555_test.ko
[ 1196.898846] pca9555_test pca9555_test3: remove
[ 1196.904631] pca9555_test pca9555_test2: remove
[ 1196.910470] pca9555_test pca9555_test1: remove
root@rk3308b-buildroot:/root#


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值