GPIO 应该是每个嵌入式设备都避免不了的。最近在做项目的时候,也遇到这方面的问题,所以简单总结一下
现在内核里面多了gpiod的来控制gpio口,相对于原来的形式,使用gpiod的好处是我们申请后不进行free也没有什么问题。但是你要是使用原来的方式后,一定要记得释放。不释放的话可能会有问题。
#旧的GPIO使用实例
DTS文件
det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;
驱动文件调用
-
gc5025->det_pin = of_get_named_gpio_flags(node,
"det-gpios",
0, &det_flags);
-
camera_det_irq = gpio_to_irq(gc5025->det_pin);
-
gc5025->det_value = gpio_get_value(gc5025->det_pin);
-
/*判断注册终端*/
-
if(camera_det_irq){
-
if (gpio_request(gc5025->det_pin,
"camera-irq-gpio")) {
-
printk(
"gpio %d request failed!\n", gc5025->det_pin);
-
gpio_free(gc5025->det_pin);
-
return IRQ_NONE;
-
}
-
ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH,
"det-gpio", NULL);
-
if (ret !=
0) {
-
free_irq(camera_det_irq, NULL);
-
dev_err(dev,
"Failed to request IRQ: %d\n", ret);
-
return ret;
-
}
-
}
# 新的GPIOD文档
Linux 内核文档
https://www.kernel.org/doc/Documentation/gpio/consumer.txt
#头文件
我们需要包含头文件
#include <linux/gpio/consumer.h>
看头文件里面包含的函数列表
desc_to_gpio devm_get_gpiod_from_chi devm_gpiod_get devm_gpiod_get_array devm_gpiod_get_array_op devm_gpiod_get_index devm_gpiod_get_index_op devm_gpiod_get_optional devm_gpiod_put devm_gpiod_put_array fwnode_get_named_gpiod gpio_to_desc gpiod_cansleep gpiod_count gpiod_direction_input gpiod_direction_output gpiod_direction_output_ gpiod_export gpiod_export_link gpiod_get gpiod_get_array gpiod_get_array_optiona gpiod_get_direction gpiod_get_index gpiod_get_index_optiona gpiod_get_optional gpiod_get_raw_value gpiod_get_raw_value_can gpiod_get_value gpiod_get_value_canslee gpiod_is_active_low gpiod_put gpiod_put_array gpiod_set_array_value gpiod_set_array_value_c gpiod_set_debounce gpiod_set_raw_array_val gpiod_set_raw_array_val gpiod_set_raw_value gpiod_set_raw_value_can gpiod_set_value gpiod_set_value_canslee gpiod_to_irq gpiod_unexport
#获取gpio描述符和释放
使用一下两个函数获取GPIO设备,多个设备时需要附带index参数。函数返回一个GPIO描述符,或一个错误编码,可以使用IS_ERR()进行检查:
-
struct gpio_desc *gpiod_get(
struct device *dev,
const char *con_id,
-
enum gpiod_flags flags)
-
-
struct gpio_desc *gpiod_get_index(
struct device *dev,
-
const char *con_id, unsigned
int idx,
-
enum gpiod_flags flags)
或者也可以使用如下两个函数获取可用设备:
-
struct gpio_desc *gpiod_get_optional(
struct device *dev,
-
const char *con_id,
-
enum gpiod_flags flags)
-
-
struct gpio_desc *gpiod_get_index_optional(
struct device *dev,
-
const char *con_id,
-
unsigned
int index,
-
enum gpiod_flags flags)
使用如下函数同时获取多个设备:
-
struct gpio_descs *gpiod_get_array(
struct device *dev,
-
const char *con_id,
-
enum gpiod_flags flags)
该函数返回一个GPIO描述结构体:
-
struct gpio_descs {
-
unsigned
int ndescs;
-
struct gpio_desc *desc[];
-
}
一个GPIO描述符可以使用如下函数释放:
-
void gpiod_put(
struct gpio_desc *desc)
-
void gpiod_put_array(
struct gpio_descs *descs)
需要注意GPIO描述符被释放后不可再使用,而且不允许使用第一个函数来释放通过序列获取得到GPIO描述符。
#举个例子
#dts文件
-
gc5025: gc5025@
37 {
-
status =
"okay";
-
compatible =
"galaxycore,gc5025";
-
reg = <
0x37>;
-
clock-frequency = <
400000>;
-
-
pinctrl-names =
"default";
-
pinctrl
-0 = <&cif_clkout_m0>;
-
-
clocks = <&cru SCLK_CIF_OUT>;
-
clock-names =
"xvclk";
-
-
avdd-supply = <&vcc2v8_dvp>;
-
dovdd-supply = <&vcc1v8_dvp>;
-
dvdd-supply = <&vdd1v2_dvp>;
-
-
reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
-
pwdn-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
-
det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;
-
-
rockchip,camera-module-index = <
0>;
-
rockchip,camera-module-facing =
"front";
-
rockchip,camera-module-name =
"CMK-CW4191-FG1";
-
rockchip,camera-module-lens-name =
"CK5502";
-
-
port {
-
ucam_out: endpoint {
-
remote-endpoint = <&mipi_in_ucam>;
-
data-lanes = <
1
2>;
-
};
-
};
-
};
驱动文件调用:
-
gc5025->reset_gpio = devm_gpiod_get(dev,
"reset", GPIOD_OUT_LOW);
-
if (IS_ERR(gc5025->reset_gpio))
-
dev_warn(dev,
"Failed to get reset-gpios\n");
-
gc5025->pwdn_gpio = devm_gpiod_get(dev,
"pwdn", GPIOD_OUT_LOW);
-
if (IS_ERR(gc5025->pwdn_gpio))
-
dev_warn(dev,
"Failed to get pwdn-gpios\n");
-
/*新的GPIO子系统方式,这种方式不需要手动释放资源*/
-
gc5025->det_gpio = devm_gpiod_get(dev,
"det", GPIOD_OUT_LOW);
-
if (IS_ERR(gc5025->det_gpio))
#GPIO使用
#设置GPIO口方向
-
int gpiod_direction_input(
struct gpio_desc *desc)
-
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口电平
访问分为两种,一种是通过储存器读写实现的,这种操作属于原子操作,不需要等待,所以可以在中断处理程序中使用:
-
int gpiod_get_value(
const
struct gpio_desc *desc);
-
void gpiod_set_value(
struct gpio_desc *desc,
int value);
-
还有一种访问必须通过消息总线比如I2C或者SPI,这种访问需要在总线访问队列中等待,所以可能进入睡眠,此类访问不能出现在IRQ handler。可以使用如下函数分辨这些设备:
int gpiod_cansleep(const struct gpio_desc *desc)
使用如下函数读写:
-
int gpiod_get_value_cansleep(
const
struct gpio_desc *desc)
-
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);
输出的是高电平
这几个函数如下:
-
int gpiod_get_raw_value(
const
struct gpio_desc *desc)
-
void gpiod_set_raw_value(
struct gpio_desc *desc,
int value)
-
int gpiod_get_raw_value_cansleep(
const
struct gpio_desc *desc)
-
void gpiod_set_raw_value_cansleep(
struct gpio_desc *desc,
int value)
-
int gpiod_direction_output_raw(
struct gpio_desc *desc,
int value)
raw-value 的意思就是不在乎DTS里面的ACTIVE,我set 高电平,就是高电平。逻辑关系汇总如下:
-
Function (example) active-low property physical line
-
gpiod_set_raw_value(desc,
0); don’t care low
-
gpiod_set_raw_value(desc,
1); don’t care high
-
gpiod_set_value(desc,
0);
default (active-high) low
-
gpiod_set_value(desc,
1);
default (active-high) high
-
gpiod_set_value(desc,
0); active-low high
-
gpiod_set_value(desc,
1); active-low low
可以使用如下函数判断一个设备是否是低电平有效的设备。
int gpiod_is_active_low(const struct gpio_desc *desc)
#设置多个输出
这个没使用过 使用如下函数设置一组设备的输出值
-
void gpiod_set_array_value(unsigned
int array_size,
-
struct gpio_desc **desc_array,
-
int *value_array)
-
void gpiod_set_raw_array_value(unsigned
int array_size,
-
struct gpio_desc **desc_array,
-
int *value_array)
-
void gpiod_set_array_value_cansleep(unsigned
int array_size,
-
struct gpio_desc **desc_array,
-
int *value_array)
-
void gpiod_set_raw_array_value_cansleep(unsigned
int array_size,
-
struct gpio_desc **desc_array,
-
int *value_array)
-
#兼容旧版本
旧的GPIO系统使用基于标号的结构而不是基于描述符。可以使用如下两个函数进行相互转换:
-
int desc_to_gpio(
const
struct gpio_desc *desc)
-
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().
#举例子
-
/*新的GPIO子系统方式,这种方式不需要手动释放资源*/
-
gc5025->det_gpio = devm_gpiod_get(dev,
"det", GPIOD_OUT_LOW);
-
if (IS_ERR(gc5025->det_gpio))
-
dev_warn(dev,
"Failed to get det-gpios\n");
-
camera_det_irq = gpiod_to_irq(gc5025->det_gpio);
-
/*新gpio子系统转成旧gpio子系统*/
-
gc5025->det_pin = desc_to_gpio(gc5025->det_gpio);
-
/*读取上电gpio电平*/
-
gc5025->det_value = gpio_get_value(gc5025->det_pin);
-
-
/*判断注册终端*/
-
if(camera_det_irq){
-
ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH,
"det-gpio", NULL);
-
if (ret !=
0) {
-
free_irq(camera_det_irq, NULL);
-
dev_err(dev,
"Failed to request IRQ: %d\n", ret);
-
return ret;
-
}
-
}
#调试
移植驱动阶段或者调试阶段的工程中,难免想知道当前gpio的电平状态。当然很easy。万用表戳上去不就行了。是啊!硬件工程师的思维。作为软件工程师自然是要软件的方法。下面介绍两个api接口。自己摸索使用吧。点到为止。
-
static inline
int gpio_export(unsigned gpio,
bool direction_may_change);
-
static inline
int gpio_export_link(
struct device *dev,
const char *name, unsigned gpio);
在你的driver中调用以上api后,编译下载。去/sys/class/gpio目录看看有什么发现。
回复「 篮球的大肚子」进入技术群聊
回复「1024」获取1000G学习资料