Linux下的gpio,gpiod

这篇博客介绍了GPIO控制在Linux内核中的演进,从传统的GPIO使用方式到gpiod子系统的变化。gpiod子系统提供了更安全的资源管理,不再需要手动释放GPIO资源。文章详细比较了旧的GPIO请求和释放函数与gpiod接口的差异,并给出了DTS配置和驱动代码示例。此外,还讨论了GPIO的方向设置、电平读写、中断处理以及调试方法。
摘要由CSDN通过智能技术生成

GPIO 应该是每个嵌入式设备都避免不了的。最近在做项目的时候,也遇到这方面的问题,所以简单总结一下

现在内核里面多了gpiod的来控制gpio口,相对于原来的形式,使用gpiod的好处是我们申请后不进行free也没有什么问题。但是你要是使用原来的方式后,一定要记得释放。不释放的话可能会有问题。

#旧的GPIO使用实例

DTS文件


    
    
  1. det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;

驱动文件调用


    
    
  1. gc5025->det_pin = of_get_named_gpio_flags(node,  "det-gpios"0, &det_flags);
  2.     camera_det_irq = gpio_to_irq(gc5025->det_pin);
  3.     gc5025->det_value = gpio_get_value(gc5025->det_pin);
  4.      /*判断注册终端*/
  5.      if(camera_det_irq){
  6.          if (gpio_request(gc5025->det_pin,  "camera-irq-gpio")) {
  7.             printk( "gpio %d request failed!\n", gc5025->det_pin);
  8.             gpio_free(gc5025->det_pin);
  9.              return IRQ_NONE;
  10.         }
  11.         ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH,  "det-gpio", NULL);
  12.          if (ret !=  0) {
  13.             free_irq(camera_det_irq, NULL);
  14.             dev_err(dev,  "Failed to request IRQ: %d\n", ret);
  15.              return ret;
  16.         }
  17.     }

# 新的GPIOD文档

Linux 内核文档

https://www.kernel.org/doc/Documentation/gpio/consumer.txt

#头文件

我们需要包含头文件

#include <linux/gpio/consumer.h>

    
    

看头文件里面包含的函数列表


    
    
  1. desc_to_gpio
  2. devm_get_gpiod_from_chi
  3. devm_gpiod_get
  4. devm_gpiod_get_array
  5. devm_gpiod_get_array_op
  6. devm_gpiod_get_index
  7. devm_gpiod_get_index_op
  8. devm_gpiod_get_optional
  9. devm_gpiod_put
  10. devm_gpiod_put_array
  11. fwnode_get_named_gpiod
  12. gpio_to_desc
  13. gpiod_cansleep
  14. gpiod_count
  15. gpiod_direction_input
  16. gpiod_direction_output
  17. gpiod_direction_output_
  18. gpiod_export
  19. gpiod_export_link
  20. gpiod_get
  21. gpiod_get_array
  22. gpiod_get_array_optiona
  23. gpiod_get_direction
  24. gpiod_get_index
  25. gpiod_get_index_optiona
  26. gpiod_get_optional
  27. gpiod_get_raw_value
  28. gpiod_get_raw_value_can
  29. gpiod_get_value
  30. gpiod_get_value_canslee
  31. gpiod_is_active_low
  32. gpiod_put
  33. gpiod_put_array
  34. gpiod_set_array_value
  35. gpiod_set_array_value_c
  36. gpiod_set_debounce
  37. gpiod_set_raw_array_val
  38. gpiod_set_raw_array_val
  39. gpiod_set_raw_value
  40. gpiod_set_raw_value_can
  41. gpiod_set_value
  42. gpiod_set_value_canslee
  43. gpiod_to_irq
  44. gpiod_unexport

#获取gpio描述符和释放

使用一下两个函数获取GPIO设备,多个设备时需要附带index参数。函数返回一个GPIO描述符,或一个错误编码,可以使用IS_ERR()进行检查:


    
    
  1. struct gpio_desc *gpiod_get( struct device *dev, const char *con_id,
  2. enum gpiod_flags flags)
  3. struct gpio_desc *gpiod_get_index( struct device *dev,
  4. const char *con_id, unsigned int idx,
  5. enum gpiod_flags flags)

或者也可以使用如下两个函数获取可用设备:


    
    
  1. struct gpio_desc *gpiod_get_optional( struct device *dev,
  2. const char *con_id,
  3. enum gpiod_flags flags)
  4. struct gpio_desc *gpiod_get_index_optional( struct device *dev,
  5. const char *con_id,
  6. unsigned int index,
  7. enum gpiod_flags flags)

使用如下函数同时获取多个设备:


    
    
  1. struct gpio_descs *gpiod_get_array( struct device *dev,
  2. const char *con_id,
  3. enum gpiod_flags flags)

该函数返回一个GPIO描述结构体:


    
    
  1. struct gpio_descs {
  2. unsigned int ndescs;
  3. struct gpio_desc *desc[];
  4. }

一个GPIO描述符可以使用如下函数释放:


    
    
  1. void gpiod_put( struct gpio_desc *desc)
  2. void gpiod_put_array( struct gpio_descs *descs)

需要注意GPIO描述符被释放后不可再使用,而且不允许使用第一个函数来释放通过序列获取得到GPIO描述符。

#举个例子

#dts文件


    
    
  1. gc5025: gc5025@ 37 {
  2. status = "okay";
  3. compatible = "galaxycore,gc5025";
  4. reg = < 0x37>;
  5. clock-frequency = < 400000>;
  6. pinctrl-names = "default";
  7. pinctrl -0 = <&cif_clkout_m0>;
  8. clocks = <&cru SCLK_CIF_OUT>;
  9. clock-names = "xvclk";
  10. avdd-supply = <&vcc2v8_dvp>;
  11. dovdd-supply = <&vcc1v8_dvp>;
  12. dvdd-supply = <&vdd1v2_dvp>;
  13. reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
  14. pwdn-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
  15. det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;
  16. rockchip,camera-module-index = < 0>;
  17. rockchip,camera-module-facing = "front";
  18. rockchip,camera-module-name = "CMK-CW4191-FG1";
  19. rockchip,camera-module-lens-name = "CK5502";
  20. port {
  21. ucam_out: endpoint {
  22. remote-endpoint = <&mipi_in_ucam>;
  23. data-lanes = < 1 2>;
  24. };
  25. };
  26. };

驱动文件调用:


    
    
  1.     gc5025->reset_gpio = devm_gpiod_get(dev,  "reset", GPIOD_OUT_LOW);
  2.      if (IS_ERR(gc5025->reset_gpio))
  3.         dev_warn(dev,  "Failed to get reset-gpios\n");
  4.     gc5025->pwdn_gpio = devm_gpiod_get(dev,  "pwdn", GPIOD_OUT_LOW);
  5.      if (IS_ERR(gc5025->pwdn_gpio))
  6.         dev_warn(dev,  "Failed to get pwdn-gpios\n");
  7.      /*新的GPIO子系统方式,这种方式不需要手动释放资源*/
  8.     gc5025->det_gpio = devm_gpiod_get(dev,  "det", GPIOD_OUT_LOW);
  9.      if (IS_ERR(gc5025->det_gpio))

#GPIO使用

#设置GPIO口方向


    
    
  1. int gpiod_direction_input( struct gpio_desc *desc)
  2. int gpiod_direction_output( struct gpio_desc *desc, int value)

#检查GPIO口是方向

int gpiod_get_direction(const struct gpio_desc *desc)

    
    

函数返回GPIOF_DIR_IN或者GPIOF_DIR_OUT

#读取GPIO口电平

访问分为两种,一种是通过储存器读写实现的,这种操作属于原子操作,不需要等待,所以可以在中断处理程序中使用:


    
    
  1. int gpiod_get_value( const struct gpio_desc *desc);
  2. void gpiod_set_value( struct gpio_desc *desc, int value);

还有一种访问必须通过消息总线比如I2C或者SPI,这种访问需要在总线访问队列中等待,所以可能进入睡眠,此类访问不能出现在IRQ handler。可以使用如下函数分辨这些设备:

int gpiod_cansleep(const struct gpio_desc *desc)

    
    

使用如下函数读写:


    
    
  1. int gpiod_get_value_cansleep( const struct gpio_desc *desc)
  2. void gpiod_set_value_cansleep( struct gpio_desc *desc, int value)

#active-low和raw-value

active-low & raw value有些设备采用低电平有效的方式输出逻辑信号。此时低电平输出1,高电平输出0。此时可以通过访问raw_value的方式来访问实际电路上的值,与逻辑处理无关:假设我们在DTS里面这样设置

reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;

    
    

然后我们这样调用

gpiod_set_value_cansleep(gc5025->reset_gpio, 1);

    
    

因为DTS里面的active 状态是 GPIO_ACTIVE_LOW,所以这个代码输出的是 低电平

gpiod_set_value_cansleep(gc5025->reset_gpio, 0);

    
    

输出的是高电平

这几个函数如下:


    
    
  1. int gpiod_get_raw_value( const struct gpio_desc *desc)
  2. void gpiod_set_raw_value( struct gpio_desc *desc, int value)
  3. int gpiod_get_raw_value_cansleep( const struct gpio_desc *desc)
  4. void gpiod_set_raw_value_cansleep( struct gpio_desc *desc, int value)
  5. int gpiod_direction_output_raw( struct gpio_desc *desc, int value)

raw-value 的意思就是不在乎DTS里面的ACTIVE,我set 高电平,就是高电平。逻辑关系汇总如下:


    
    
  1. Function (example) active-low property physical line
  2. gpiod_set_raw_value(desc, 0); don’t care low
  3. gpiod_set_raw_value(desc, 1); don’t care high
  4. gpiod_set_value(desc, 0); default (active-high) low
  5. gpiod_set_value(desc, 1); default (active-high) high
  6. gpiod_set_value(desc, 0); active-low high
  7. gpiod_set_value(desc, 1); active-low low

可以使用如下函数判断一个设备是否是低电平有效的设备。

int gpiod_is_active_low(const struct gpio_desc *desc)

    
    

#设置多个输出

这个没使用过 使用如下函数设置一组设备的输出值


    
    
  1. void gpiod_set_array_value(unsigned int array_size,
  2. struct gpio_desc **desc_array,
  3. int *value_array)
  4. void gpiod_set_raw_array_value(unsigned int array_size,
  5. struct gpio_desc **desc_array,
  6. int *value_array)
  7. void gpiod_set_array_value_cansleep(unsigned int array_size,
  8. struct gpio_desc **desc_array,
  9. int *value_array)
  10. void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
  11. struct gpio_desc **desc_array,
  12. int *value_array)

#兼容旧版本

旧的GPIO系统使用基于标号的结构而不是基于描述符。可以使用如下两个函数进行相互转换:


    
    
  1. int desc_to_gpio( const struct gpio_desc *desc)
  2. struct gpio_desc *gpio_to_desc(unsigned gpio)

注意不能使用一套API的方法释放另一套API获取的设备

#和中断IRQ相关

使用如下函数获取一个GPIO设备对应的IRQ中断号

int gpiod_to_irq(const struct gpio_desc *desc)

    
    

返回值时一个IRQ number,或者一个负数的错误代码。得到的中断号可以传递给函数request_irq(),free_irq().

#举例子


    
    
  1. /*新的GPIO子系统方式,这种方式不需要手动释放资源*/
  2. gc5025->det_gpio = devm_gpiod_get(dev, "det", GPIOD_OUT_LOW);
  3. if (IS_ERR(gc5025->det_gpio))
  4. dev_warn(dev, "Failed to get det-gpios\n");
  5. camera_det_irq = gpiod_to_irq(gc5025->det_gpio);
  6. /*新gpio子系统转成旧gpio子系统*/
  7. gc5025->det_pin = desc_to_gpio(gc5025->det_gpio);
  8. /*读取上电gpio电平*/
  9. gc5025->det_value = gpio_get_value(gc5025->det_pin);
  10. /*判断注册终端*/
  11. if(camera_det_irq){
  12. ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH, "det-gpio", NULL);
  13. if (ret != 0) {
  14. free_irq(camera_det_irq, NULL);
  15. dev_err(dev, "Failed to request IRQ: %d\n", ret);
  16. return ret;
  17. }
  18. }

#调试

移植驱动阶段或者调试阶段的工程中,难免想知道当前gpio的电平状态。当然很easy。万用表戳上去不就行了。是啊!硬件工程师的思维。作为软件工程师自然是要软件的方法。下面介绍两个api接口。自己摸索使用吧。点到为止。


    
    
  1. static inline int gpio_export(unsigned gpio, bool direction_may_change);
  2. static inline int gpio_export_link( struct device *dev, const char *name, unsigned gpio);

在你的driver中调用以上api后,编译下载。去/sys/class/gpio目录看看有什么发现。

  回复「 篮球的大肚子」进入技术群聊

回复「1024」获取1000G学习资料

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值