【RV1126】使用gpiolib框架

文章目录

  • 史上最简单:增加GPIO控制功能
  • 是如何实现的呢?GPIOLIB框架
    • Linux 驱动实现
  • 控制引脚输出高低电平
  • 综合测试

这一套非常方便!

史上最简单:增加GPIO控制功能

如果是想增加GPIO控制只需要修改设备树就可以做到!

下面这个设备树描述了复用功能,比如你想将UART3的RXD引脚用作GPIO输入中断,你就得在下面的设备树中将UART3节点部分注释掉。

kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

然后在下面的设备树中写入该引脚的相关描述

kernel/arch/arm/boot/dts/pro-rv1126.dts

直接在rp_gpio节点下面添加buzzer即可:

// GPIO_FUNCTION_OUTPUT      0 
// GPIO_FUNCTION_INPUT       1
// GPIO_FUNCTION_IRQ         2
// GPIO_FUNCTION_FLASH       3
// GPIO_FUNCTION_OUTPUT_CTRL 4

//rpgpio init
rp_gpio {
	status = "okay";
	compatible = "rp_gpio";
	/**********************新增GPIO***************************/
	buzzer{
		gpio_num = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	/********************************************************/
	ir_led{
		gpio_num = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	
	bl_led{
		gpio_num = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>;
		gpio_function = <0>;			 //0:output 1:input
	};
	     
	gpio3c2{
		gpio_num = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; 
		gpio_function = <0>;
	};  
	...
};
  • compatible: 表示兼容的意思,它的作用就是定义一系列的字符串,用来指定硬件是否兼容相应的驱动,其它的部分接下来分析。

添加成功后又该如何操作控制 GPIO呢?

以控制摄像头补光灯亮灭为例,摄像头的补光灯对应的 proc 设备节点为:bl_led

在/proc/rp_gpio目录下:

echo 0 > bl_led # 该引脚输出低电平,关闭灯光
echo 1 > bl_led # 该引脚输出高电平,打开灯光
# 又以蜂鸣器为例
echo 0 > buzzer # 该引脚输出低电平,关闭蜂鸣器
echo 1 > buzzer # 该引脚输出高电平,打开蜂鸣器

是如何实现的呢?GPIOLIB框架

Linux 驱动实现

内核中 rp_gpio 这个驱动可以用来驱动它,经过 linux grep 命令的查看,可以看到该驱动源代码的位置位于drivers/rongpin/rp_gpio.c :

./drivers/rongpin/rp_gpio.c:275:static int rp_gpio_remove(struct platform_device *pdev)
./drivers/rongpin/rp_gpio.c:281:static const struct of_device_id
rp_gpio_of_match[] = {
./drivers/rongpin/rp_gpio.c:282:  { .compatible = "rp_gpio" },
./drivers/rongpin/rp_gpio.c:286:static struct platform_driver rp_gpio_driver = {
./drivers/rongpin/rp_gpio.c:287:  .probe = rp_gpio_probe,
./drivers/rongpin/rp_gpio.c:288:  .remove = rp_gpio_remove,
./drivers/rongpin/rp_gpio.c:290:        .name      = "rp_gpio",
./drivers/rongpin/rp_gpio.c:291:        .of_match_table =
of_match_ptr(rp_gpio_of_match),
./drivers/rongpin/rp_gpio.c:295:module_platform_driver(rp_gpio_driver);

这部分是厂商自己实现的 gpio 驱动,驱动代码提供了普适的 GPIO 操作接口:open、write、read:

static const struct file_operations gpio_ops = {
   .owner      = THIS_MODULE,
   .open      = gpio_open,
   .write      = gpio_write,
   .read      = gpio_read,
};

文件目录:kernel/drivers/rongpin/rp_gpio.c

驱动实现也很简单,使用的是 GPIOLIB 框架来控制这些 GPIO ,驱动会去设备树中搜索 compatible 属性:rp_gpio,找到了这个属性设备树和驱动就匹配上了,具体定义如下:

static const struct of_device_id rp_gpio_of_match[] =
{
    { .compatible = "rp_gpio" },
    { }
};
static struct platform_driver rp_gpio_driver =
{
    .probe = rp_gpio_probe,
    .remove = rp_gpio_remove,
    .driver = {
        .name = "rp_gpio",
        .of_match_table = of_match_ptr(rp_gpio_of_match),
    },
};
module_platform_driver(rp_gpio_driver);
MODULE_LICENSE("GPL");

驱动匹配成功正常工作时最先开始运行的是 probe 函数,也就是最先调用的是 rp_gpio_probe ,实现如下:

static int rp_gpio_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    struct device_node *child_np;
    struct device *dev = &pdev->dev;
    static struct proc_dir_entry *root_entry_gpio;
    enum of_gpio_flags  gpio_flags;
    int ret = 0;
    int gpio_cnt = 0;
    char gpio_name_num[20];
    int gpio_in_cnt = 0;
    int cnt = 0;
    //向内核申请内容给gpio_data变量
    gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct
                             rp_gpio_data), GFP_KERNEL);

    if (!gpio_data)
    {
        dev_err(&pdev->dev, "failed to allocate memory\n");
        return -ENOMEM;
    }

    //获取子节点的数量
    gpio_data->gpio_dts_num = of_get_child_count(np);
    printk("rp_gpio prepare build %d gpio\n", gpio_data->gpio_dts_num);

    if (gpio_data->gpio_dts_num == 0)
    {
        dev_info(&pdev->dev, "no gpio defined\n");
    }

    /* create node */
    //在proc目录下创建rp_gpio节点
    root_entry_gpio = proc_mkdir("rp_gpio", NULL);
    /*遍历所有节点*/
    for_each_child_of_node(np, child_np)
    {
        /* parse dts */
        /*从设备树中获取节点对应的GPIO编号以及标志*/
        gpio_data->rp_gpio_num[gpio_cnt].gpio_num =
            of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);

        if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num))
        {
            return -1;
        }

        gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
        gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
        gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
        /*读取节点中的 32 位整数的值*/
        of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));
        printk("rp_gpio request %s\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

        /*通过获取到的gpio_function的数值进行判断*/
        switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function)
        {
            /*如果配置为输入,则走这个分支*/
            case GPIO_FUNCTION_INPUT : /* init input gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }
                else
                {
                    printk("success request gpio %d in\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    /*设置GPIO方向为输入*/
                    gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    //gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
                    event_flag = gpio_flags;
                    of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
                    of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
                    gpio_in_cnt++;
                }

                break;

            /*如果配置为输出,则走这个分支*/
            case GPIO_FUNCTION_OUTPUT : /* init output gpio */
                /*申请GPIO*/
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    ret = gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d out\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                }

                break;

            /*这个是厂商自己定义的,暂时不清楚是什么作用,应该是预留的,和GPIO_FUNCTION_OUTPUT的功
            能一致*/
            case GPIO_FUNCTION_FLASH :
                ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");

                if (ret < 0)
                {
                    printk("gpio%d request error\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    return ret;
                }
                else
                {
                    /*设置GPIO的方向为输出,并且设置具体的数值*/
                    gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, !gpio_data->rp_gpio_num[gpio_cnt].action);
                    printk("success request gpio%d flash\n", gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
                    gpio_cnt++;
                }

                break;
        }

        /*拼接GPIO名称以及编号*/
        sprintf(gpio_name_num, gpio_data -> rp_gpio_num[gpio_cnt].gpio_name, gpio_cnt);
        /*在PROC目录下创建设备节点*/
        proc_create(gpio_name_num, 0666, root_entry_gpio, &gpio_ops);
        gpio_cnt++;
    }

    /*如果GPIO的功能被定义为输入的作用*/
    if (gpio_in_cnt > 0)
    {
        /*定义一个定时器*/
        timer_setup(&(gpio_data->mytimer), send_event, 0);
        gpio_data->mytimer.expires = jiffies + msecs_to_jiffies(10000);
        add_timer(&(gpio_data->mytimer));
        /*注册input子系统,将这个功能描述成一个输入设备*/
        /* init struct input_dev */
        gpio_data->input = devm_input_allocate_device(dev);
        gpio_data->input->name = "gpio_event";
        gpio_data->input->phys = "gpio_event/input1";
        gpio_data->input->dev.parent = dev;
        gpio_data->input->id.bustype = BUS_HOST;
        gpio_data->input->id.vendor = 0x0001;
        gpio_data->input->id.product = 0x0001;
        gpio_data->input->id.version = 0x0100;

        for(cnt = 0; cnt < gpio_cnt; cnt++)
        {
            if (gpio_data->rp_gpio_num[cnt].gpio_function == 1)
            {
                input_set_capability(gpio_data->input, EV_KEY, gpio_data -> rp_gpio_num[cnt].gpio_event);
            }
        }

        ret = input_register_device(gpio_data->input);
    }

    /*设置platform驱动数据,将数据挂载到pdev->dev.driver_data*/
    platform_set_drvdata(pdev, gpio_data);
    return 0;
}

控制引脚输出高低电平

使用UART3的RXD引脚
【RV1126】使用gpiolib框架_第1张图片修改:kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi

/*&uart3 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart3m2_xfer>;
	status = "okay";
};*/

修改:kernel/arch/arm/boot/dts/pro-rv1126.dts

//rpgpio init
	rp_gpio {
		status = "okay";
		compatible = "rp_gpio";

        buzzer{
            gpio_num = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
			gpio_function = <0>;			 //0:output 1:input
        };
...

重新编译Kernel和设备树就可以了。
在开发板上看看设备节点

[root@RV1126_RV1109:/]# ls /proc/rp_gpio/
bl_led  gpio0b7  gpio2b7  gpio2c2  gpio2c4  gpio3c2  otg_host
buzzer  gpio2b4  gpio2c1  gpio2c3  gpio2c6  ir_led

OK,出来了!
可以使用命令进行测试:

  • 引脚输出高电平:echo 1 > /proc/rp_gpio/buzzer
  • 引脚输出低电平:echo 0 > /proc/rp_gpio/buzzer

引脚接LED、蜂鸣器、继电器都可以。

综合测试

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
    int fd = 0;
    struct input_event buttons_event;
    unsigned long cur_ms = 0;

    static int flg = 0;
    
    fd = open("/dev/input/event3", O_RDWR);
    if (fd < 0)
        printf("can't open!\n");
 
    while (1)
    {
        read(fd, &buttons_event, sizeof(struct input_event));
 
//        if(buttons_event.type == EV_SYN)
//            continue;

        if((buttons_event.code == 11) && (buttons_event.value == 0x00))
        {
            if(flg == 0)
            {
                system("echo 1 > /proc/rp_gpio/buzzer");
                printf("echo 1\r\n");
                flg = 1;
            }
            else
            {
                system("echo 0 > /proc/rp_gpio/buzzer");
                printf("echo 0\r\n");
                flg = 0;
            }
        }
        
 
        cur_ms = (buttons_event.time.tv_sec * 1000) + (buttons_event.time.tv_usec/1000);
 
        //打印时间,事件类型,事件码,事件值
        printf("cur_ms:%ld type:0x%x code:%d value:%d\n",
            cur_ms,
            buttons_event.type,
            buttons_event.code,
            buttons_event.value);
    }
    close(fd);
    return 0;
}

编译:

arm-linux-gnueabihf-gcc key-buzzer.c

你可能感兴趣的:(RV1126,嵌入式Linux驱动,单片机,物联网,嵌入式硬件)