基于整数的接口最为人所熟知。GPIO用整数标识,GPIO上需要执行的每个操作都使用该整数。以下是包含传统GPIO访问函数的头文件:
c
#include <linux/gpio.h>1
使用步骤
- 注册:
gpio_request(查看:cat /sys/kernel/debug/gpio) - 输出方向:
gpio_direction_output - 电平配置:
gpio_get_value - 中断:
gpio_to_irq
一、GPIO的申请和配置
1 申请GPIO
使用gpio_request()函数来申请并获取GPIO的使用权:
c
int gpio_request(unsigned gpio, const char *label);1
参数说明:
gpio:要使用的GPIO编号label:给这个GPIO起个名字(比如在系统文件/sys/kernel/debug/gpio里显示的名字)
注意
必须检查返回值。返回0表示成功,负数代表出错。
2 释放GPIO
使用完GPIO后,用gpio_free()释放资源:
c
void gpio_free(unsigned int gpio);1
3 检查GPIO有效性
使用gpio_is_valid()提前确认GPIO编号是否合法:
c
bool gpio_is_valid(int number);1
4 设置GPIO方向
- 输入模式:用
gpio_direction_input()设置 - 输出模式:用
gpio_direction_output()设置
c
int gpio_direction_output(unsigned gpio, int value);1
- 0 → 初始低电平
- 1 → 初始高电平
二、GPIO值的读写
1 原子上下文(如中断处理)
这类GPIO直接通过内存访问(比如芯片内置GPIO),不会导致等待:
c
static int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned int gpio, int value);1
2
2
2 非原子上下文(如I2C/SPI总线)
这类GPIO需要等待操作完成(可能会暂停程序):
c
static int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);1
2
2
关键区别
后者函数名带有cansleep,表示可能需要等待,不能在中断处理中使用。
三、GPIO转中断
1 获取中断号
用gpio_to_irq()将GPIO编号转为中断号:
c
int gpio_to_irq(unsigned gpio);1
2 注册中断处理
获取中断号后,用request_irq()或request_threaded_irq()注册处理函数:
c
// 示例代码
int gpio_int = ...; // 通过设备树等获取的GPIO编号
int irq_num = gpio_to_irq(gpio_int); // 转换为中断号
// 注册中断处理函数(这里用线程式中断为例)
error = devm_request_threaded_irq(&client->dev, irq_num,
NULL, my_interrupt_handler, // 无上半部,下半部是自定义函数
IRQF_TRIGGER_RISING | IRQF_ONESHOT, // 触发条件:上升沿,单次触发
input_dev->name, my_data_struct); // 中断名称和数据指针
if (error) {
// 处理注册失败
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
关键参数说明:
IRQF_TRIGGER_RISING:设置为上升沿触发IRQF_ONESHOT:确保中断处理完成后立即释放中断线
四、关键注意事项
注意事项
- 释放资源:使用完GPIO务必调用
gpio_free(),否则可能导致资源泄露 - 检查有效性:使用前最好用
gpio_is_valid()确认GPIO编号存在 - 睡眠判断:通过
gpio_cansleep()判断GPIO是否可能阻塞程序 - 上下文匹配:带
cansleep的函数不能在中断处理中使用 - 方向设置:必须先设置GPIO方向(输入/输出)才能读写值
五、实验
源代码下载:git clone git@gitee.com:yangxuesong314/linux-driver.git(若之前已git拉取代码可以忽略)
代码位于:linux-driver/09.GPIO子系统/02.基于整数的GPIO接口
c
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
static int gpio_num = 8;
static int irq_num;
static struct tasklet_struct my_tasklet;
static struct task_struct *blink_thread;
static volatile bool keep_blinking = true;
static int blink_thread_func(void *data)
{
int value = 0;
while (keep_blinking) {
gpio_set_value(gpio_num, value);
value = !value;
printk(KERN_ERR " GPIO %d\n", value);
msleep(500);
}
gpio_set_value(gpio_num, 0);
return 0;
}
static int __init my_module_init(void) {
int ret;
ret = gpio_request(gpio_num, "my_gpio");
if (ret) {
printk(KERN_ERR "Failed to request GPIO %d\n", gpio_num);
return ret;
}
gpio_direction_output(gpio_num,0);
blink_thread = kthread_run(blink_thread_func, NULL, "gpio_blinker");
if (IS_ERR(blink_thread)) {
gpio_free(gpio_num);
printk(KERN_ERR "Failed to create thread\n");
return PTR_ERR(blink_thread);
}
printk(KERN_INFO "Module initialized\n");
return 0;
}
static void __exit my_module_exit(void) {
free_irq(irq_num, NULL);
gpio_free(gpio_num);
tasklet_kill(&my_tasklet);
printk(KERN_INFO "Module exited\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
实验:驱动加载insmod gpio.ko