RK:GPIO的基操、中断使用(一)
只是一些暗恋而已 https://blog.csdn.net/qq_30624591/article/details/87834629
http://wiki.t-firefly.com/zh_CN/AIO-3288C/driver_gpio.html
GPIO的使用场景
作为输入设备 :
光感设备、红外设备、GPIO 按键设备、等等为输入设备,高低电平变化由这些
设备来控制;
作为输出设备:
继电器、开关、需要由高低电平变化控制的设备均为输出设备;
一、RK3288 GPIO
1.1、查看gpio信息 cat /sys/kernel/debug/gpio
1.2、RK3288有9组GPIO bank:GPIO0~GPIO8,每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分(不是所有 bank 都有全部编号,例如 GPIO0 就只有A0~C7)。 所有的GPIO在上电后的初始状态都是输入模式,可以通过软件设为上拉或下拉,也可以设置为中断脚,驱动强度都是可编程的。 每个 GPIO 口除了通用输入输出功能外,还可能有其它复用功能。
1.3、PIN
'A0': 0, 'A1': 1, 'A2': 2, 'A3': 3, 'A4': 4, 'A5': 5, 'A6': 6, 'A7': 7, 'B0': 8, 'B1': 9, 'B2':10, 'B3':11, 'B4':12, 'B5':13, 'B6':14, 'B7':15, 'C0':16, 'C1':17, 'C2':18, 'C3':19, 'C4':20, 'C5':21, 'C6':22, 'C7':23, 'D0':24, 'D1':25, 'D2':26, 'D3':27, 'D4':28, 'D5':29, 'D6':30, 'D7':31,
1.4、RK323 GPIO
rk3288-tb_zk_r323.dts
gpio_0 = <&gpio8 6 GPIO_ACTIVE_HIGH>; gpio_1 = <&gpio8 7 GPIO_ACTIVE_HIGH>; gpio_2 = <&gpio8 8 GPIO_ACTIVE_HIGH>; gpio_3 = <&gpio8 9 GPIO_ACTIVE_HIGH>;
二、GPIO的基操
2.1、下面是常用的 GPIO API 定义:
#include <linux/gpio.h> #include <linux/of_gpio.h> enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, }; int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags); int gpio_is_valid(int gpio); int gpio_request(unsigned gpio, const char *label); void gpio_free(unsigned gpio); int gpio_direction_input(int gpio); int gpio_direction_output(int gpio, int v);
2.2、操作普通GPIO说明
2.2.1、在dts添加gpio的引用描述:在dts文件上添加,通常在设备树中 gpio的配置使用,
这里定义了一个pin脚作为一般的输出输入口
crush{ status = "okay"; compatible = "rockchip,crush"; blue_led_gpio = <&gpio8 1 GPIO_ACTIVE_HIGH>; };
2.2.2、解析dts并且获取gpio口:函数返回值就得到gpio号
int of_get_named_gpio_flags(struct device_node *np, const char *propname,
int index, enum of_gpio_flags *flags);
2.2.3、判断gpio口是否合法能用
int gpio_is_valid(int number)
2.2.4、申请gpio口:gpio_request的第一个参数是需要申请的gpio号。第二个参数是我们给该gpio起个名字,
申请成功后,可以通过/sys/kernel/debug/gpio文件查看到该GPIO的状态
int gpio_request(unsigned gpio, const char *label)
2.2.5、设置gpio口的方向,如果是设置方向为输出的话同时设置电平拉高或拉低
int gpio_direction_input(unsigned gpio); //设置gpio为输入 int gpio_direction_output(unsigned gpio, int value); //设置gpio为输出,且设置电平
2.2.6、操作gpio口(拉高或者拉低gpio的电平,value为1是拉高,0是拉低)
void gpio_set_value(unsigned int gpio, int value);
2.2.7、获取gpio口的状态:get到1为高电平,得到0为低电平
ret = gpio_get_value(unsigned gpio);
2.2.8、释放gpio口:在remove函数中添加对应的释放操作
gpio_free(int number)
三、RK3288 Android7.1 GPIO驱动控制LED灯亮灭
3.1、目的:a、/sys/class/crush/crush_gpio/device
b、对红蓝灯的read、write
3.2、crush.c
#include <linux/module.h> #include <linux/device.h> #include <linux/err.h> #include <linux/kdev_t.h> #include <linux/idr.h> #include <linux/gfp.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/delay.h> static struct class *crush_class;//定义设备类 struct crush_data { struct device *classdev; int blue; int red; int gpio0;//default input int gpio2;//default output 0 }; static struct crush_data *ch_data; static ssize_t show_name(struct device *dev,struct device_attribute *attr, char *buf) { return sprintf(buf, "crush gatsby\n"); } //执行cat 会调用到此函数 show对应的是read static ssize_t show_blue(struct device *dev,struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", gpio_get_value(ch_data->blue)); } //执行echo 会调用到此函数 store对应的是write static ssize_t store_blue(struct device *dev,struct device_attribute *attr, const char *buf, size_t count) { unsigned long blue; int rc; rc = kstrtoul(buf, 0, &blue); if (rc) return rc; if(blue == 0000) { gpio_direction_output(ch_data->blue,0); } else if(blue == 0001) { gpio_direction_output(ch_data->blue,1); } rc = count; return rc; } // 设备属性文件名 mode(权限) read write //0660 -rw-rw---- Linux4.4只能给 0660 static DEVICE_ATTR(name, 0660, show_name, NULL); static DEVICE_ATTR(blue, 0660, show_blue, store_blue); static struct attribute *crush_attributes[] = { &dev_attr_name.attr, &dev_attr_blue.attr, NULL }; static const struct attribute_group crush_attr_group = { .attrs = crush_attributes, }; //probe初始化函数 cdev 分配设备对象空间 static int crush_driver_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; int gpio, err; ch_data = devm_kzalloc(&pdev->dev, sizeof(struct crush_data),GFP_KERNEL); if (!ch_data) return -ENOMEM; err = sysfs_create_group(&pdev->dev.kobj, &crush_attr_group); if (err) return err; //在 /sys/class/ 目录下生成 crush_gpio ch_data->classdev=device_create(crush_class, &pdev->dev, MKDEV(0, 0), NULL,"crush_gpio", 0); gpio = of_get_named_gpio(np, "blue_led_gpio", 0); ch_data->blue=gpio; //判断GPIO是否合法能用 if (!gpio_is_valid(gpio)) { dev_err(&pdev->dev, "invalid blue_led_gpio %d\n", gpio); } return 0; } static int crush_driver_remove(struct platform_device *pdev) { device_unregister(ch_data->classdev); sysfs_remove_group(&pdev->dev.kobj, &crush_attr_group); return 0; } static const struct of_device_id crush_match[] = { { .compatible = "rockchip,crush" }, {}, }; static struct platform_driver crush_driver = { .probe = crush_driver_probe, .remove = crush_driver_remove, .driver = { .name = "crush", .owner = THIS_MODULE, .of_match_table = of_match_ptr(crush_match), }, }; static int __init crush_init(void) { printk("crush_init\n"); //在/sys/class目录下生成 crush目录 crush_class = class_create(THIS_MODULE, "crush"); if (IS_ERR(crush_class)) { printk("couldn't create sysfs crush class\n"); return PTR_ERR(crush_class); } return platform_driver_register(&crush_driver); } static void __exit crush_exit(void) { class_destroy(crush_class); platform_driver_unregister(&crush_driver); } module_init(crush_init); module_exit(crush_exit); MODULE_AUTHOR("Gatsby"); MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); MODULE_LICENSE("GPL");
3.3、DEVICE_ATTR Linux4.4 用不了0777
在 device\rockchip\common\init.rk30board.rc
chmod 0777 /sys/class/xh_custom/xh_custom_gpio/device/blue
四、gpio 做中断脚使用,xh_key 驱动解析
4.1、中断服务函数
irq_handler_t irq16_handler(int irq,void *dev_id)
4.2、从dts中转换gpio编号到对应irq号,把GPIO的PIN值转换为相应的IRQ值
static inline int gpio_to_irq(unsigned int gpio) { return __gpio_to_irq(gpio); }
4.3、中断注册函数
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id)
a、irq中断号(和平台架构相关,结合datasheet以及平台文件) IRQ_EINT(x)
b、中断处理函数: 中断发生时,系统调用这个函数,dev_id参数将被传递给它
c、中断标记:上升或下降中断触发
d、中断名字:
e、dev_id:一般使用设备的设备结构体或者NULL
f、返回值:request_irq()返回0表示成功,返回-EINVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用,且不能被共享。
4.4、中断卸载函数:
void free_irq(unsigned int irq, void *dev_id)
4.5、xh_key.c
//中断服务函数 static irqreturn_t gpio_key_irq(int irq, void *dev_id) { mod_timer(&key_timer, jiffies + (HZ /200)); //5ms return IRQ_HANDLED; } if (!gpio_is_valid(gpio)) { dev_err(&pdev->dev, "invalid gpio_0 %d\n", gpio); } else { //转换gpio编号到对应irq号 xh_data->irq = gpio_to_irq(xh_data->gpio0); //默认值 指定中断触发类型:上升沿有效 下降沿有效 request_irq(xh_data->irq, gpio_key_irq, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,"gpio_key_irq", xh_data); }
五.查看gpio 输入输出状态
cat /sys/kernel/debug/gpio