本文主要介绍三个部分的内容:一、准备sdk源码 二、如何操作gpio 三、led设备驱动的实现。由于firefly官方一直在对源码进行更新,所以本文只以我正在用的版本介绍。此外,官方提供的下载工具版本不同需要准备的镜像文件(.img文件)也不同,因此,这里也只介绍我正在使用的版本。 sdk版本:firefly-sdk-20200629.7z 下载工具版本:androidtool v2.58 u-boot:2017.09 linux内核:4.4.194 文件系统:buildroot

镜像文件如下,如上图所示,只会下载勾选的镜像。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">boot.img ==================> kernel/zboot.imgMiniLoaderALL.bin =========> /u-boot/rk3288_loader_v1.08.258.binparameter.txt =============> device/rockchip/rk3288/parameter-buildroot.txtrecovery.img ==============> buildroot/output/rockchip_rk3288_recovery/images/recovery.imgrootfs.img ================> buildroot/output/rockchip_rk3288/images/rootfs.ext2trust.img =================> u-boot/trust.imguboot.img =================> u-boot/uboot.img</code>
实际上,firefly官网已经有具体的步骤,但官网会经常更新,所以这里再简单的介绍一遍流程。
1.下载Linux-SDK源码包这里是官网链接

将下载好的Linux-SDK源码包拷贝至虚拟机,虚拟机安装好7z解压缩工具和git工具。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">7z x firefly-sdk-20200629.7z -r #递归解压主和子目录的内容git reset --hard cd ~/proj/Firefly-RK3288#2. 下载远程 bundle 仓库git clone https://github.com/FireflyTeam/bundle.git -b rk3288-linux-bundle#3. 若 clone 失败,可以前往 github 下载 bundle.zip:#4. 更新 SDK,并且后续更新不需要再次拉取远程仓库,直接执行以下命令即可./bundle/update rk3288-linux-bundle#5. 按照提示已经更新内容到 FETCH_HEAD,同步 FETCH_HEAD 到 firefly 分支git rebase FETCH_HEAD#6. 更新共用仓库./bundle/update common-linux-bundlegit rebase FETCH_HEAD</code>
最后需要注意本地分支是否是firefly,如果不是,切换之。

解压后文件夹如下:

这里是直接进入Linux内核源码目录下进行编译,也可以使用官方的build.sh脚本编译,这个官网有教程,这里不再介绍。
代码语言:javascript代码运行次数:0运行复制<code class="javascript">#交叉编译器目录firefly-sdk/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bincd /RK3288/firefly-sdk/kernel #进入内核源码目录export ARCH=armexport CROSS_COMPILE=arm-linux-gnueabihf- make firefly_linux_defconfig #使用内核默认配置make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig # 使用图形化配置make -j8 rk3288-firefly.img #编译 此过程包含设备树、uImage、zImage的编译以及image打包过程。最终生成需要的zboot.img文件</code>
这里仅仅介绍了内核镜像文件的生成,对于开头提到的其他文件,由于不是本文的重点,这里也不再介绍,大家可以去查看官网教程。需要注意的是内核一定要编译通过,因为后面的驱动需要能够编译通过的内核源码。
二、应用层操作GPIORK3288的GPIO号对应的引脚可以通过如下文件查看:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">cat /sys/kernel/debug/pinctrl/pinctrl/pins </code>
<code class="javascript">[root@rk3288:/sys/kernel/debug/pinctrl/pinctrl]# cat /sys/kernel/debug/pinctrl/pinctrl/pinsregistered pins: 264pin 0 (gpio0-0)pin 1 (gpio0-1)pin 2 (gpio0-2)pin 3 (gpio0-3)pin 4 (gpio0-4)pin 5 (gpio0-5)............</code>
开发板上两个LED对应的引脚是:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">pin 249 (gpio8-1)=====> bluepin 250 (gpio8-2)=====> yellow</code>
可以通过export GPIO的方式操作这两个GPIO:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">[root@rk3288:/sys/class/gpio]# echo 250 > export[root@rk3288:/sys/class/gpio]# cd gpio250[root@rk3288:/sys/devices/platform/pinctrl/gpio/gpio250]# lsactive_low device direction edge power subsystem uevent value[root@rk3288:/sys/devices/platform/pinctrl/gpio/gpio250]#</code>
direction:GPIO的方向,可以设置为in或者out value:0低电平 其他值高电平
开发板上两个LED已经应用为LED子系统(gpio8-1,gpio8-2),需要取消这个应用才可以使用sys文件操作GPIO,方法如下:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">Device Drivers > LED Support < > LED Support for GPIO connected LEDs </code>
前面我们已经准备好了能够编译通过的linux内核源码,现在我们可以编写Linux设备驱动了,由于我们使用的是带设备树的Linux内核,所以驱动的编写和不带设备树的内核是有一点区别的,但总体流程不变。
1.firefly-rk3288 设备树文件firefly-rk3288 设备树文件位于/firefly-sdk/kernel/arch/arm/boot/dts目录下,对于led设备,我们需要打开rk3288-firefly.dtsi文件,找到led设备节点:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">leds {compatible = "gpio-leds";work {gpios = <&gpio8 1 GPIO_ACTIVE_LOW>;label = "firefly:blue:user";linux,default-trigger = "rc-feedback";pinctrl-names = "default";pinctrl-0 = <&work_led>;};power {gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;label = "firefly:green:power";linux,default-trigger = "default-on";pinctrl-names = "default";pinctrl-0 = <&power_led>;};};</code>rk3288开发板共有两个led,分别对应GPIO8_A1和GPIO8_A2,但是我们在驱动程序中需要通过设备树获取到这两个GPIO的值。所以下面介绍几个常用设备树操作函数(#include
a.通过绝对路径,获取设备节点
代码语言:javascript代码运行次数:0运行复制<code class="javascript">static inline struct device_node *of_find_node_by_path(const char *path)</code>
b.通过父节点和名称,获取设备树子节点
代码语言:javascript代码运行次数:0运行复制<code class="javascript">static inline struct device_node *of_get_child_by_name(const struct device_node *node,const char *name)</code>
b.通过节点和名称,获取GPIO引脚号
代码语言:javascript代码运行次数:0运行复制<code class="javascript">static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)</code>
带设备树的LED驱动与不带设备树的驱动区别在于,带设备树的LED驱动需要在程序中从设备树中获取需要的GPIO编号,然后就是字符设备驱动的那一套流程了。
驱动源码文件如下:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">#include <linux/module.h>//模块加载卸载函数#include <linux/kernel.h>//内核头文件#include <linux/types.h>//数据类型定义#include <linux/fs.h>//file_operations结构体#include <linux/device.h>//class_create等函数#include <linux/ioctl.h>#include <linux/kernel.h>/*包含printk等操作函数*/#include <linux/of.h>/*设备树操作相关的函数*/#include <linux/gpio.h>/*gpio接口函数*/#include <linux/of_gpio.h>#include <linux/cdev.h>/*cdev_init cdev_add等函数*/#include <asm/gpio.h>/*gpio接口函数*/#include <asm/uaccess.h>/*__copy_from_user 接口函数*/#define DEVICE_NAME "rk3288_led"typedef struct{ struct device_node *led_node;//设备树节点 int led_pin;//GPIO 引脚 struct cdev cdev;//定义一个cdev结构体 struct class *class;//创建一个LED类 struct device *device;//创建一个LED设备 该设备是需要挂在LED类下面的 int major;//主设备号 dev_t dev_id;}led_typdef;static led_typdef ledx;//定义一个LED设备static int led_open(struct inode *inode, struct file *filp){ int ret = -1;retry:ret = gpio_request(ledx.led_pin, "led_yellow"); if(ret != 0) { printk("gpio %d req failed:%d\r\n",ledx.led_pin,ret); gpio_free(ledx.led_pin);//引脚被占用(错误码-16)释放掉 goto retry;}gpio_direction_output(ledx.led_pin,0);return 0;}static int led_release(struct inode* inode ,struct file *filp){gpio_free(ledx.led_pin);return 0;}static int led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ int ret; unsigned char pbuf[1];ret = __copy_from_user(pbuf,buf, 1);if(ret < 0){ printk("__copy_from_user failed\r\n"); return ret;}gpio_set_value(ledx.led_pin, pbuf[0]);return 0;}static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){ return 0;}static struct file_operations led_fops={.owner= THIS_MODULE,.open = led_open,.write= led_write,.read = led_read,.release= led_release,};static int __init led_init(void){ //struct device_node *node; /*获取设备节点*/ ledx.led_node = of_find_node_by_path("/leds/work"); printk("/leds:%p\r\n",ledx.led_node); if(ledx.led_node == NULL) { printk("find node by path fialed!\r\n"); return -1; } /*ledx.led_node = of_get_child_by_name(node, "user"); printk("/leds/work:%p\r\n",ledx.led_node); if(ledx.led_node == NULL) { printk("get child node by name fialed!\r\n"); return -1; }*/ /*获取GPIO信息*/ ledx.led_pin = of_get_named_gpio(ledx.led_node,"gpios",0); printk("led_pin:%d\r\n",ledx.led_pin); if(!gpio_is_valid(ledx.led_pin)) { printk("get_gpio failed:%d \r\n",ledx.led_pin); return -1; } /*申请设备号*/ alloc_chrdev_region(&ledx.dev_id,0,1,DEVICE_NAME); /*初始化一个cdev*/ cdev_init(&ledx.cdev,&led_fops); /*向cdev中添加一个设备*/ cdev_add(&ledx.cdev,ledx.dev_id,1); /*创建一个LED类*/ ledx.class = class_create(THIS_MODULE, "led_class"); if(ledx.class == NULL) { printk("class_create failed\r\n"); return -1; } /*在LED类下创建一个LED设备*/ ledx.device = device_create(ledx.class, NULL, ledx.dev_id, NULL, DEVICE_NAME); printk("module init ok\n"); return 0; }static void led_exit(void){/*删除LED类*/cdev_del(&ledx.cdev);/*释放LED设备号*/unregister_chrdev_region(ledx.dev_id, 1);/*注销LED设备*/device_destroy(ledx.class, ledx.dev_id);/*注销LED类*/class_destroy(ledx.class); printk("module exit ok\n");}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("xzx2020");</code>MakeFile文件如下:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">obj-m:=led_driver.oPWD:=$(shell pwd)KDIR:=/RK3288/firefly-sdk/kernelall:$(MAKE) -C $(KDIR) M=$(PWD) clean:rm -rf *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions</code>
驱动编译成功后会生成一个.ko文件,将.ko文件拷贝到开发板上并加载,出现如下提示表明驱动加载成功。

接下来,我们还需要编写一个测试APP,用于测试驱动是否正常工作,测试APP的源码如下:
代码语言:javascript代码运行次数:0运行复制<code class="javascript">#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <termios.h>#include <errno.h>#include <limits.h>#include <asm/ioctls.h>#include <time.h>#include <pthread.h>int main(void){ int fd = -1,i; char buf[1]={0}; fd = open("/dev/rk3288_led",O_RDWR); if(fd < 0) { printf("open /dev/rk3288_led fail fd=%d\n",fd); } for(i=0;i<50;i++) { buf[0] = 0; write(fd,buf,1); usleep(200000); buf[0] = 1; write(fd,buf,1); usleep(200000); } fd = close(fd); if(fd < 0) { printf("led test error\n"); } return 0; }</code>将上述源码用交叉编译器编译,即可生成可执行文件,将该可执行文件加上执行权限拷贝到开发上并执行,开发板的蓝色LED灯应该就开始闪烁起来了。
以上就是firefly-rk3288开发板Linux驱动——LED驱动的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号