Linux 创建gpiochip 流程和使用流程

创建gpiochip的流程

定义gpio_chip结构: 在GPIO控制器驱动中,首先要定义一个gpio_chip结构体,并初始化其各个字段。

struct gpio_chip my_gpio_chip = {
    .label = "my_gpio_chip",
    .base = -1,  // 让内核自动分配基地址
    .ngpio = 32,  // 比如该控制器管理32个GPIO引脚
    .owner = THIS_MODULE,
    .set = my_gpio_set_value,
    .get = my_gpio_get_value,
    .direction_input = my_gpio_direction_input,
    .direction_output = my_gpio_direction_output,
    // 其他必需的操作函数指针
};
struct gpio_chip my_gpio_chip = {
    .label = "my_gpio_chip",
    .base = -1,  // 让内核自动分配基地址
    .ngpio = 32,  // 比如该控制器管理32个GPIO引脚
    .owner = THIS_MODULE,
    .set = my_gpio_set_value,
    .get = my_gpio_get_value,
    .direction_input = my_gpio_direction_input,
    .direction_output = my_gpio_direction_output,
    // 其他必需的操作函数指针
};

实现操作函数: 实现上面结构体中的各个函数,比如设置输入输出方向、设置和获取引脚值。

static void my_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚的值
}

static int my_gpio_get_value(struct gpio_chip *chip, unsigned offset) {
    // 获取GPIO引脚的值
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
    // 设置GPIO引脚为输入
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚为输出并设置初始值
}
static void my_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚的值
}

static int my_gpio_get_value(struct gpio_chip *chip, unsigned offset) {
    // 获取GPIO引脚的值
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
    // 设置GPIO引脚为输入
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚为输出并设置初始值
}

注册gpiochip: 在驱动的初始化函数中,使用gpiochip_add函数注册这个gpio_chip。

static int __init my_gpio_driver_init(void) {
    int ret;
    ret = gpiochip_add(&my_gpio_chip);
    if (ret) {
        pr_err("Failed to add gpiochip\n");
        return ret;
    }
    pr_info("gpiochip added successfully\n");
    return 0;
}

static void __exit my_gpio_driver_exit(void) {
    gpiochip_remove(&my_gpio_chip);
    pr_info("gpiochip removed successfully\n");
}

module_init(my_gpio_driver_init);
module_exit(my_gpio_driver_exit);
static int __init my_gpio_driver_init(void) {
    int ret;
    ret = gpiochip_add(&my_gpio_chip);
    if (ret) {
        pr_err("Failed to add gpiochip\n");
        return ret;
    }
    pr_info("gpiochip added successfully\n");
    return 0;
}

static void __exit my_gpio_driver_exit(void) {
    gpiochip_remove(&my_gpio_chip);
    pr_info("gpiochip removed successfully\n");
}

module_init(my_gpio_driver_init);
module_exit(my_gpio_driver_exit);

设备树支持(可选): 对于设备树支持的驱动,还需要在设备树中添加GPIO控制器的描述,并在驱动中解析这些节点。

设备树节点示例:
dts文件:

gpio@12340000 {
    compatible = "my,gpio";
    reg = <0x12340000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
};
gpio@12340000 {
    compatible = "my,gpio";
    reg = <0x12340000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
};

内核解析设备树示例:

static int my_gpio_probe(struct platform_device *pdev) {
    struct resource *res;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    // 解析资源,映射寄存器等
    return gpiochip_add(&my_gpio_chip);
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "my,gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = "my_gpio",
        .of_match_table = of_match_ptr(my_gpio_of_match),
    },
};

module_platform_driver(my_gpio_driver);
static int my_gpio_probe(struct platform_device *pdev) {
    struct resource *res;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    // 解析资源,映射寄存器等
    return gpiochip_add(&my_gpio_chip);
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "my,gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = "my_gpio",
        .of_match_table = of_match_ptr(my_gpio_of_match),
    },
};

module_platform_driver(my_gpio_driver);

使用gpiochip的流程

  • 请求和释放GPIO引脚:

在驱动或应用程序中,可以使用gpio_request和gpio_free来请求和释放一个GPIO引脚。

int gpio = 20;  // 想要使用的GPIO引脚编号
int ret;

ret = gpio_request(gpio, "my_gpio_user");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO %d\n", gpio);
    return ret;
}

// 使用完毕后释放GPIO
gpio_free(gpio);
int gpio = 20;  // 想要使用的GPIO引脚编号
int ret;

ret = gpio_request(gpio, "my_gpio_user");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO %d\n", gpio);
    return ret;
}

// 使用完毕后释放GPIO
gpio_free(gpio);
  • 设置和读取GPIO引脚值:

使用gpio_set_value和gpio_get_value函数来设置和读取GPIO引脚的值。

gpio_set_value(gpio, 1);  // 将引脚设置为高电平
int value = gpio_get_value(gpio);  // 读取引脚的当前值
gpio_set_value(gpio, 1);  // 将引脚设置为高电平
int value = gpio_get_value(gpio);  // 读取引脚的当前值
  • 设置GPIO方向:

使用gpio_direction_input和gpio_direction_output函数来设置GPIO引脚的方向。

ret = gpio_direction_input(gpio);  // 将引脚设置为输入
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as input\n", gpio);
    return ret;
}

ret = gpio_direction_output(gpio, 1);  // 将引脚设置为输出,并初始化为高电平
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as output\n", gpio);
    return ret;
}
ret = gpio_direction_input(gpio);  // 将引脚设置为输入
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as input\n", gpio);
    return ret;
}

ret = gpio_direction_output(gpio, 1);  // 将引脚设置为输出,并初始化为高电平
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as output\n", gpio);
    return ret;
}

用户空间操作:

在用户空间,可以通过sysfs接口或者/dev/gpiochip字符设备来操作GPIO引脚。

  • 使用sysfs接口:
echo 20 > /sys/class/gpio/export  # 请求GPIO 20引脚
echo out > /sys/class/gpio/gpio20/direction  # 设置方向为输出
echo 1 > /sys/class/gpio/gpio20/value  # 设置值为高电平
cat /sys/class/gpio/gpio20/value  # 读取值
echo 20 > /sys/class/gpio/unexport  # 释放GPIO 20引脚
echo 20 > /sys/class/gpio/export  # 请求GPIO 20引脚
echo out > /sys/class/gpio/gpio20/direction  # 设置方向为输出
echo 1 > /sys/class/gpio/gpio20/value  # 设置值为高电平
cat /sys/class/gpio/gpio20/value  # 读取值
echo 20 > /sys/class/gpio/unexport  # 释放GPIO 20引脚

使用/dev/gpiochip字符设备(需要编写对应的用户空间程序):

  • 用户可以编写程序使用ioctl来操作GPIO引脚,这提供了比sysfs更高效的方式。
// 示例代码需要包含 <linux/gpio.h><fcntl.h>
int fd = open("/dev/gpiochip0", O_RDONLY);
struct gpiochip_info info;
ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);  // 获取芯片信息

// 设置和读取的更多示例可以参考相应的用户空间库或文档
// 示例代码需要包含 <linux/gpio.h><fcntl.h>
int fd = open("/dev/gpiochip0", O_RDONLY);
struct gpiochip_info info;
ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);  // 获取芯片信息

// 设置和读取的更多示例可以参考相应的用户空间库或文档

总结来说,创建和使用gpiochip涉及到内核和用户空间的多层次交互,主要通过定义结构体、实现必要的操作函数、注册芯片、请求和释放GPIO引脚、以及设置和读取引脚状态等步骤来完成。设备树的支持还需要解析设备树节点,并在驱动中根据这些描述进行相应的初始化工作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值