信息发布→ 登录 注册 退出

firefly-rk3288开发板Linux驱动——LED驱动

发布时间:2025-04-17

点击量:

本文主要介绍三个部分的内容:一、准备sdk源码 二、如何操作gpio 三、led设备驱动的实现。由于firefly官方一直在对源码进行更新,所以本文只以我正在用的版本介绍。此外,官方提供的下载工具版本不同需要准备的镜像文件(.img文件)也不同,因此,这里也只介绍我正在使用的版本。 sdk版本:firefly-sdk-20250629.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-20250629.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 = <&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>;};};

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 //模块加载卸载函数#include //内核头文件#include //数据类型定义#include //file_operations结构体#include //class_create等函数#include #include /*包含printk等操作函数*/#include /*设备树操作相关的函数*/#include /*gpio接口函数*/#include #include /*cdev_init cdev_add等函数*/#include /*gpio接口函数*/#include /*__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");

MakeFile文件如下:

代码语言:javascript代码运行次数:0运行复制
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 #include #include #include #include #include #include #include #include #include #include #include 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;   }

将上述源码用交叉编译器编译,即可生成可执行文件,将该可执行文件加上执行权限拷贝到开发上并执行,开发板的蓝色LED灯应该就开始闪烁起来了。

标签:# 官网  # 不带  # 加载  # 可以通过  # 这两个  # 镜像文件  # 板上  # 的是  # 创建一个  # 引脚  # linux  # include  # JavaScript  # red  # typedef  # 区别  # ai  # 工具  # git  
在线客服
服务热线

服务热线

4008888355

微信咨询
二维码
返回顶部
×二维码

截屏,微信识别二维码

打开微信

微信号已复制,请打开微信添加咨询详情!