本文主要介紹三個部分的內容:一、準備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>