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

镜像文件如下,如上图所示,只会下载勾选的镜像。
代码语言:JavaScript代码运行次数:0运行复制
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
一、准备SDK源码
实际上,firefly官网已经有具体的步骤,但官网会经常更新,所以这里再简单的介绍一遍流程。
1.下载Linux-SDK源码包
这里是官网链接

2.解压Linux-SDK源码包
将下载好的Linux-SDK源码包拷贝至虚拟机,虚拟机安装好7z解压缩工具和git工具。
代码语言:javascript代码运行次数:0运行复制
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
最后需要注意本地分支是否是firefly,如果不是,切换之。

解压后文件夹如下:

3.编译Linux内核源码
这里是直接进入Linux内核源码目录下进行编译,也可以使用官方的build.sh脚本编译,这个官网有教程,这里不再介绍。
代码语言:javascript代码运行次数:0运行复制
#交叉编译器目录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文件
这里仅仅介绍了内核镜像文件的生成,对于开头提到的其他文件,由于不是本文的重点,这里也不再介绍,大家可以去查看官网教程。需要注意的是内核一定要编译通过,因为后面的驱动需要能够编译通过的内核源码。
二、应用层操作GPIO
RK3288的GPIO号对应的引脚可以通过如下文件查看:
代码语言:javascript代码运行次数:0运行复制
cat /sys/kernel/debug/pinctrl/pinctrl/pins
代码语言:javascript代码运行次数:0运行复制
[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)............
开发板上两个LED对应的引脚是:
代码语言:javascript代码运行次数:0运行复制
pin 249 (gpio8-1)=====> bluepin 250 (gpio8-2)=====> yellow
可以通过export GPIO的方式操作这两个GPIO:
代码语言:javascript代码运行次数:0运行复制
[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]#
direction:GPIO的方向,可以设置为in或者out value:0低电平 其他值高电平
开发板上两个LED已经应用为LED子系统(gpio8-1,gpio8-2),需要取消这个应用才可以使用sys文件操作GPIO,方法如下:
代码语言:javascript代码运行次数:0运行复制
Device Drivers > LED Support LED Support for GPIO connected LEDs
三、LED设备驱动
前面我们已经准备好了能够编译通过的linux内核源码,现在我们可以编写Linux设备驱动了,由于我们使用的是带设备树的Linux内核,所以驱动的编写和不带设备树的内核是有一点区别的,但总体流程不变。
1.firefly-rk3288 设备树文件
firefly-rk3288 设备树文件位于/firefly-sdk/kernel/arch/arm/boot/dts目录下,对于led设备,我们需要打开rk3288-firefly.dtsi文件,找到led设备节点:
代码语言:javascript代码运行次数:0运行复制
leds {compatible = "gpio-leds";work {gpios = ;label = "firefly:blue:user";linux,default-trigger = "rc-feedback";pinctrl-names = "default";pinctrl-0 = ;};power {gpios = ;label = "firefly:green:power";linux,default-trigger = "default-on";pinctrl-names = "default";pinctrl-0 = ;};};
rk3288开发板共有两个led,分别对应GPIO8_A1和GPIO8_A2,但是我们在驱动程序中需要通过设备树获取到这两个GPIO的值。所以下面介绍几个常用设备树操作函数(#include
a.通过绝对路径,获取设备节点
代码语言:javascript代码运行次数:0运行复制
static inline struct device_node *of_find_node_by_path(const char *path)
b.通过父节点和名称,获取设备树子节点
代码语言:javascript代码运行次数:0运行复制
static inline struct device_node *of_get_child_by_name(const struct device_node *node,const char *name)
b.通过节点和名称,获取GPIO引脚号
代码语言:javascript代码运行次数:0运行复制
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)
2.firefly-rk3288 LED设备驱动编写
带设备树的LED驱动与不带设备树的驱动区别在于,带设备树的LED驱动需要在程序中从设备树中获取需要的GPIO编号,然后就是字符设备驱动的那一套流程了。
驱动源码文件如下:
代码语言:javascript代码运行次数:0运行复制
#include <linux>//模块加载卸载函数#include <linux>//内核头文件#include <linux>//数据类型定义#include <linux>//file_operations结构体#include <linux>//class_create等函数#include <linux>#include <linux>/*包含printk等操作函数*/#include <linux>/*设备树操作相关的函数*/#include <linux>/*gpio接口函数*/#include <linux>#include <linux>/*cdev_init cdev_add等函数*/#include <asm>/*gpio接口函数*/#include <asm>/*__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:%drn",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 <p>MakeFile文件如下:</p>代码语言:javascript<i class="icon-code"></i>代码运行次数:<!-- -->0<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" fill="none"><path d="M6.66666 10.9999L10.6667 7.99992L6.66666 4.99992V10.9999ZM7.99999 1.33325C4.31999 1.33325 1.33333 4.31992 1.33333 7.99992C1.33333 11.6799 4.31999 14.6666 7.99999 14.6666C11.68 14.6666 14.6667 11.6799 14.6667 7.99992C14.6667 4.31992 11.68 1.33325 7.99999 1.33325ZM7.99999 13.3333C5.05999 13.3333 2.66666 10.9399 2.66666 7.99992C2.66666 5.05992 5.05999 2.66659 7.99999 2.66659C10.94 2.66659 13.3333 5.05992 13.3333 7.99992C13.3333 10.9399 10.94 13.3333 7.99999 13.3333Z" fill="currentcolor"></path></svg>运行<svg width="16" height="16" viewbox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 15.5V3.5H14.5V15.5H4.5ZM12.5 5.5H6.5V13.5H12.5V5.5ZM9.5 2.5H3.5V12.5H1.5V0.5H11.5V2.5H9.5Z" fill="currentcolor"></path></svg>复制<pre class="prism-token token line-numbers 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
驱动编译成功后会生成一个.ko文件,将.ko文件拷贝到开发板上并加载,出现如下提示表明驱动加载成功。

接下来,我们还需要编写一个测试APP,用于测试驱动是否正常工作,测试APP的源码如下:
代码语言:javascript代码运行次数:0运行复制
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys>#include <sys>#include <fcntl.h>#include <termios.h>#include <errno.h>#include <limits.h>#include <asm>#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 <p>将上述源码用交叉编译器编译,即可生成可执行文件,将该可执行文件加上执行权限拷贝到开发上并执行,开发板的蓝色LED灯应该就开始闪烁起来了。</p></pthread.h></time.h></asm></limits.h></errno.h></termios.h></fcntl.h></sys></sys></unistd.h></stdlib.h></stdio.h>