瑞芯微RK3566的linux_GPIO驱动开发

一:开发环境VSCODE + SSH  开发板为泰山派RK3566

原理图

本次实验使用,gpio3_B4引脚,作为输出引脚,输出高电平点亮一个LED灯

二:瑞芯微平台驱动开发流程

采用分离分层的方式注册驱动,此使用上电后就会点亮led

嵌入式 Linux 系统的设计通常采用分层思想,以便更好地组织和管理系统的不同组成部分。
这种分层结构不仅有助于明确每层的功能和责任,还可以简化开发和维护工作,提高系统的模
块化和可扩展性。以下是嵌入式 Linux 系统的典型分层架构:

1. 硬件层

这是系统的最底层,包括所有的物理设备,如处理器、内存、输入/输出端口和其他外围设
备。硬件层是所有软件操作的基础,它直接与物理设备交互。

2. 驱动层

位于硬件层之上的是驱动层,它包括操作系统的所有硬件驱动程序。这些驱动程序负责管
理硬件设备的具体操作,如数据读写、设备控制和状态监控。驱动层的作用是为上层提供一个
抽象的、统一的接口,使上层软件不需要关心硬件的具体细节。

3. 操作系统内核层

内核是嵌入式 Linux 系统的核心,负责管理系统资源(如 CPU、内存和设备)、处理系统
调用以及执行基本的任务调度和内存管理。内核层还包括各种内核模块和内核级服务,如网络
协议栈、文件系统管理等。

4. 中间件层

中间件层位于内核层和应用层之间,提供了更高级别的服务和库,如数据库、多媒体处理、
网络通信框架和设备协议。中间件层的目的是简化应用程序的开发,提供跨平台的服务和接口,
以及封装复杂的功能。

5. 应用层

应用层包含运行在嵌入式设备上的所有用户级应用程序。这些应用程序利用下层提供的接
口和服务来执行特定的任务,如用户界面展示、数据处理、设备控制等。应用层是与用户直接
交互的层面,其性能和功能直接影响用户的体验。

6. 用户界面层

在某些复杂的嵌入式系统中,还可能有一个专门的用户界面层,用于管理图形用户界面
(GUI)或其他形式的交互界面。这一层通常依赖于底层的图形库或 GUI 框架来实现,如 Qt 或
其他专门的嵌入式 GUI 库。
这种分层架构使得嵌入式 Linux 系统的每个部分都可以独立开发和测试,提高了系统的可
维护性和可扩展性。此外,分层也有助于在系统设计时进行资源和性能的优化,确保系统能够
高效地运行在资源受限的嵌入式硬件上。
在操作系统中,尤其是在像 Linux 这样的现代操作系统中,内存被划分为两个主要区域:
用户空间和内核空间。这种划分提高了系统的安全性和稳定性,确保了用户程序不能直接访问
硬件资源或其他关键的系统资源。

采用gpio子系统和pinctrl子系统的方式

三:驱动框架


//probe函数,驱动匹配成功后执行此函数
static int xxx_probe(struct platform_device *pdev)
{
    //第一步:申请一片空间给gpio
    devm_kzalloc(struct device *dev, size_t size, gfp_t gfp); //内核的内存分配函数
    //第二部:从设备树中读取 GPIO 配置编号和标志
    of_get_named_gpio_flags(struct device_node *np,const char *list_name, int index, enum of_gpio_flags *flags); //读取设备树的 GPIO 配置编号和标志,通过返回值判断获取是否成功
    //获取成功后 申请GPIO并且配置GPIO
    gpio_direction_output(unsigned gpio, int value); //设置gpio为输出模式并且输出电平值

}

//驱动卸载函数
static int xxx_remove(struct platform_device *pdev)
{
    //卸载时要将gpio输出低电平
    gpio_direction_output(unsigned gpio, int value); //设置gpio为输出模式并且输出电平值
    //释放gpio空间
    devm_kfree(struct device * dev,void * p);
}

static const struct of_device_id xxx_of_match[] = {
    {.compatible = "xxxxxx"},  //用于匹配设备树的compatible 的字符串
    {/*  其他 */}
};

static struct platform_driver xxx_text = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "xxxx",                    //驱动名字用于和设备匹配
        .of_match_table = xxx_of_match,        //设备树匹配表
    },
    .prode = xxx_prode,                    //匹配成功后执行
    .remove = xxx_remove,                    //卸载
};

//驱动初始化函数
static int __init xxx_init(void)
{
    //平台驱动初始化
    return platform_driver_register (struct platform_driver *driver);
}

//驱动卸载函数
static void __exit xxx_exit(void)
{
    //平台驱动卸载
    platform_driver_unregister (struct platform_driver *driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("hqh");

完整代码:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>

typedef struct rk3566_gpios {
    int gpio_vlaue;
    int gpio_data;
}user_gpio;                //此结构体用于保存获取到gpio信息

int ret = 0;                
struct device_node *np;    //设备node号
enum of_gpio_flags flags;  //GPIO API定义 在<linux/gpio.h>和<linux/of_gpio.h>可查看详情
user_gpio *userled;

static int led_probe(struct platform_device *pdev)
{
    printk("start kernel led-----------------------------\r\n");
    np = pdev->dev.of_node;

    userled = devm_kzalloc(&pdev->dev,sizeof(user_gpio  *),GFP_KERNEL);
    if(userled < 0)
    {
        printk("can't kzalloc is fail\r\n");
        return -ENOMEM;
    }

    ret = of_get_named_gpio_flags(np,"led_gpio",0,&flags);
    if(!gpio_is_valid(ret))
    {
        printk("can't get gpio fail!\r\n");
        return -ENODEV;
    }
    else
    {
        printk("gpio_num = %d\r\n",ret);
        if(gpio_request(ret,"test-gpio"))
        {
            printk("can't gpio request fail!\r\n");
            gpio_free(ret);
            return -ENODEV;           
        }
        else
        {
            userled->gpio_data = ret;
            userled->gpio_vlaue = (flags == OF_GPIO_ACTIVE_LOW)?0:1;
            gpio_direction_output(userled->gpio_data,userled->gpio_vlaue);
            printk("gpio set vlaue success!\r\n");
        }

    }
    return 0;
}

static int led_remove(struct platform_device *pdev)
{
    np = pdev->dev.of_node;

    gpio_direction_output(userled->gpio_data,0);
    devm_kfree(&pdev->dev,&np);
    return 0;
}

static const struct of_device_id led_match_table[] = {
    {.compatible = "userled"},
    {}
};

static struct platform_driver test_gpio = {
    .remove = led_remove,
    .probe = led_probe,
    .driver = {
        .owner = THIS_MODULE,
        .of_match_table = led_match_table,
        .name = "led-test",
    },
};

static int __init userLed_init(void)
{
    return platform_driver_register(&test_gpio);
}


static void __exit userLed_exit(void)
{
    platform_driver_unregister(&test_gpio);
}

module_init(userLed_init);
module_exit(userLed_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("hqh");

 设备树的添加

1.添加gpio的信息

在设备树/home/hqh/hqh/rk/rk3566/kernel/arch/arm64/boot/dts/rockchip里找到tspi-rk3566-user-v10-linux.dts文件并且打开在根节点“/”下添加如下信息

	user_led {
		compatible = "userled";
		#address_cells = <1>;
		#size_cells = <1>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_user_led>;
		led_gpio = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>;
		status = "ok";
	};

2.添加pinctrl信息

在最末尾添加pinctrl描述信息

&pinctrl {
	user{
		pinctrl_user_led: uerled_grep{
			rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

注意:rk3566s-pinctrl.dtsi⽂件已经枚举了rk3566芯⽚所有iomux的实例,各模块⼀般不再需要创建iomux实例; 创建iomux实例需要遵循如下规则

1. 必须在pinctrl节点下

2. 必须以function+group的形式添加

3. function+group的格式如下

4. 遵循其他dts的基本规则

Makefile和Kconfig的添加

1.添加到Makefile

进入到/home/hqh/hqh/rk/rk3566/kernel/drivers/gpio路径下找到Makefile,在最末尾添加

obj-$(CONFIG_GPIO_USER) += gpio-userled.o         

2.添加到Kconfig

Kconfig和Makefile同目录,找到并且打开

在endif前面添加

config GPIO_USER
    tristate "this is GPIO LED support"
    default y
    help
      Sample code of gpio

四:编译烧录

回到RK3566的主目录./build.sh kernel,编译成功后将编译好的boot.img拷贝到windows下的烧录文件夹打开RKDevTool.exe

然后开始烧录

 可以看到gpio驱动加载成功,灯也亮了起来

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值