什么是GPIO控制器

GPIO(General Purpose Input/Output)控制器
控制和管理GPIO引脚的硬件或软件组件。在嵌入式系统和单板计算机(如Raspberry Pi、BeagleBone等)中,
GPIO引脚用于简单的输入和输出操作,例如读取按钮状态、控制LED、与传感器通信等

GPIO引脚:GPIO引脚可以配置为输入或输出模式。在输入模式下,它可以读取外部信号的状态(如按钮按下或释放)。
在输出模式下,它可以向外部设备发送信号(如点亮LED)。
GPIO控制器:它是一个硬件模块,负责管理多个GPIO引脚的配置和状态。
GPIO控制器通过寄存器来控制每个引脚的方向(输入/输出)、状态(高/低电平)和其他功能(如中断)。
GPIO控制器可以理解为对引脚的控制器,通过获取引脚、在引脚输出来控制其他硬件
配置引脚方向:将引脚设置为输入或输出。
读取引脚状态:获取输入引脚的当前电平(高或低)。
设置引脚状态:将输出引脚设置为高电平或低电平。

在用户空间有echo和cat操作来控制GPIO:
1.echo 18 > /sys/class/gpio/export //导出一个GPIO引脚(假设引脚编号为18),选中
2.echo out > /sys/class/gpio/gpio18/direction //设置为输出
3.echo in > /sys/class/gpio/gpio18/direction //设置为输入
4.echo 1 > /sys/class/gpio/gpio18/value //设置为高电平
5.echo 0 > /sys/class/gpio/gpio18/value //设置为低电平
6.cat /sys/class/gpio/gpio18/value //读取当前电平
7.echo 18 > /sys/class/gpio/unexport //取消导出引脚,取消选中在内核空间用GPIO子系统来控制引脚:(platform平台)

#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/platform_device.h>

#define GPIO_PIN 18  // 假设使用引脚18

static int gpio_demo_probe(struct platform_device *pdev)
{
    int ret;

    // 请求GPIO引脚
    ret = gpio_request(GPIO_PIN, "demo_gpio");
    if (ret) {
        dev_err(&pdev->dev, "Failed to request GPIO %d\n", GPIO_PIN);
        return ret;
    }

    // 设置GPIO方向为输出,并初始化为低电平
    ret = gpio_direction_output(GPIO_PIN, 0);
    if (ret) {
        dev_err(&pdev->dev, "Failed to set GPIO direction\n");
        gpio_free(GPIO_PIN);
        return ret;
    }

    // 设置GPIO为高电平
    gpio_set_value(GPIO_PIN, 1);

    dev_info(&pdev->dev, "GPIO demo driver probed\n");
    return 0;
}

static int gpio_demo_remove(struct platform_device *pdev)
{
    // 释放GPIO引脚
    gpio_free(GPIO_PIN);
    dev_info(&pdev->dev, "GPIO demo driver removed\n");
    return 0;
}

static const struct of_device_id gpio_demo_of_match[] = {
    { .compatible = "your_company,gpio-demo", },
    {},
};
MODULE_DEVICE_TABLE(of, gpio_demo_of_match);

static struct platform_driver gpio_demo_driver = {
    .probe = gpio_demo_probe,
    .remove = gpio_demo_remove,
    .driver = {
        .name = "gpio_demo",
        .of_match_table = gpio_demo_of_match,
    },
};

module_platform_driver(gpio_demo_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("GPIO Demo Driver")

内核空间中的操作需要注意参数列表
1.int gpio_request(unsigned gpio, const char *label);
gpio:要请求的GPIO引脚编号。
label:一个描述该GPIO用途的字符串标签。

2.gpio_free()
gpio_free() 函数用于释放先前请求的GPIO引脚,使其可供其他驱动程序或内核模块使用。

void gpio_free(unsigned gpio);
gpio:要释放的GPIO引脚编号。

3.设置输出方向int gpio_direction_output(unsigned gpio, int value);//value就是输出的电平,0为低电平,1为高电平
4.设置输出方向int gpio_direction_input(unsigned gpio);
5.设置引脚的电平:gpio_set_value(unsigned gpio, int value);

当你请求一个GPIO引脚时,你只是声明你将使用它,并且希望系统为你保留该资源。
在你完成对GPIO引脚的使用之前,不需要调用 gpio_free()。
但是一旦你不再需要使用该引脚(例如,在驱动程序的 remove 函数中或者在模块卸载时),
必须调用 gpio_free() 释放该资源。

其中的pr_err 是 Linux 内核中的一个宏,用于打印错误消息。它是内核日志记录系统的一部分,
允许内核模块和驱动程序将调试信息、错误消息和其他重要信息输出到内核日志中。
pr_err 的使用方式类似于标准C语言中的 printf 函数。以下是一个基本示例:

而dev_err 是 Linux 内核中的另一个宏,用于打印与设备相关的错误消息。它是设备驱动程序开发中常用的日志记录工具之一,允许开发人员将与特定设备相关的调试信息、错误消息和其他重要信息输出到内核日志中。

dev_err 的使用方式与 pr_err 类似,但它接受一个 struct device * 类型的参数,用于指定与该消息相关的设备。
以下是一个基本示例:
dev_err(&pdev->dev, "This is an error message with code %d\n", error_code);

&pdev->dev:是一个指向设备结构的指针,通常在驱动程序的 probe 函数或其他与设备相关的函数中使用。

以下是一些关于pr_err()和dev_err()的一些不同级别情况下的输出用法:
pr_*宏用于输出与整个系统相关的日志消息,不与特定设备绑定。

pr_emerg(fmt, ...):紧急情况,系统不可用。
pr_alert(fmt, ...):需要立即采取行动的情况。
pr_crit(fmt, ...):严重情况。
pr_err(fmt, ...):错误情况。
pr_warn(fmt, ...):警告情况。
pr_notice(fmt, ...):正常但重要的情况。
pr_info(fmt, ...):信息性消息。
pr_cont(fmt, ...):继续前一条日志消息。
pr_debug(fmt, ...):调试消息(在启用了调试时)。

dev_* 系列宏用于输出与特定设备相关的日志消息,通常在设备驱动程序中使用。

dev_emerg(dev, fmt, ...):紧急情况,系统不可用。
dev_alert(dev, fmt, ...):需要立即采取行动的情况。
dev_crit(dev, fmt, ...):严重情况。
dev_err(dev, fmt, ...):错误情况。
dev_warn(dev, fmt, ...):警告情况。
dev_notice(dev, fmt, ...):正常但重要的情况。
dev_info(dev, fmt, ...):信息性消息。
dev_dbg(dev, fmt, ...):调试消息(在启用了调试时)。probe 函数是设备驱动程序在总线平台(如 I2C、SPI、PCI、USB 等)上使用的回调函数
上边probe代码是在总线平台上的驱动的用法,如果单独写一个内核模块的话可以写一个flashlight_init方法来初始化
比如以下的具体例子:

​
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/leds.h>

#define FLASHLIGHT_GPIO_PIN 18  // 假设闪光灯连接在GPIO 18引脚

static struct led_classdev flashlight_led;

static void flashlight_brightness_set(struct led_classdev *led_cdev,
                                      enum led_brightness brightness)
{
    if (brightness == LED_OFF) {
        gpio_set_value(FLASHLIGHT_GPIO_PIN, 0);
    } else {
        gpio_set_value(FLASHLIGHT_GPIO_PIN, 1);
    }
}

static int __init flashlight_init(void)
{
    int ret;

    // 请求GPIO
    ret = gpio_request(FLASHLIGHT_GPIO_PIN,"demo_led");
    if(ret){
        pr_err("unable to request GPIO  flashlight\n");
        return ret;
    }
    

    // 设置GPIO方向为输出
    ret = gpio_direction_output(FLASHLIGHT_GPIO_PIN,1);
    if(ret){
        pr_err("failed to set goio direction for flashlight\n");
        gpio_free(FLASHLIGHT_GPIO_PIN);
        return ret;
    }

    // 初始化LED类设备
    flashlight_led.name = "flashlight";
    flashlight_led.brightness_set = flashlight_brightness_set;

    ret = led_classdev_register(NULL, &flashlight_led);
    if (ret) {
        pr_err("Unable to register LED class device for flashlight\n");
        gpio_free(FLASHLIGHT_GPIO_PIN);
        return ret;
    }

    pr_info("Flashlight driver initialized\n");
    return 0;
}

static void __exit flashlight_exit(void)
{
    led_classdev_unregister(&flashlight_led);
    gpio_free(FLASHLIGHT_GPIO_PIN);
    pr_info("Flashlight driver exited\n");
}

module_init(flashlight_init);
module_exit(flashlight_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Basic LED flash light driver");

​

总结:
独立内核模块:使用 flashlight_init 初始化函数进行资源的分配和初始化,flashlight_exit用于在卸载模块时清理资源。
总线平台驱动:使用 probe 函数进行设备的初始化和资源分配,remove 函数进行资源释放和清理。

通过闪光灯的例子,学习了gpio控制器的使用,也复习了设备驱动模板和单独模块的流程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值