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

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

在这里插入图片描述
镜像文件如下,如上图所示,只会下载勾选的镜像。

boot.img ==================>  kernel/zboot.img
MiniLoaderALL.bin =========>  /u-boot/rk3288_loader_v1.08.258.bin
parameter.txt =============>  device/rockchip/rk3288/parameter-buildroot.txt
recovery.img ==============>  buildroot/output/rockchip_rk3288_recovery/images/recovery.img
rootfs.img ================>  buildroot/output/rockchip_rk3288/images/rootfs.ext2
trust.img =================>  u-boot/trust.img
uboot.img =================>  u-boot/uboot.img

一、准备SDK源码

  实际上,firefly官网已经有具体的步骤,但官网会经常更新,所以这里再简单的介绍一遍流程。

1.下载Linux-SDK源码包

这里是官网链接
在这里插入图片描述

2.解压Linux-SDK源码包

  将下载好的Linux-SDK源码包拷贝至虚拟机,虚拟机安装好7z解压缩工具和git工具。

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-bundle
git rebase FETCH_HEAD

最后需要注意本地分支是否是firefly,如果不是,切换之。
在这里插入图片描述
解压后文件夹如下:
在这里插入图片描述

3.编译Linux内核源码

  这里是直接进入Linux内核源码目录下进行编译,也可以使用官方的build.sh脚本编译,这个官网有教程,这里不再介绍。

#交叉编译器目录firefly-sdk/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin

cd /RK3288/firefly-sdk/kernel #进入内核源码目录
export ARCH=arm
export 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号对应的引脚可以通过如下文件查看:

cat /sys/kernel/debug/pinctrl/pinctrl/pins 
[root@rk3288:/sys/kernel/debug/pinctrl/pinctrl]# cat /sys/kernel/debug/pinctrl/p
inctrl/pins
registered pins: 264
pin 0 (gpio0-0)
pin 1 (gpio0-1)
pin 2 (gpio0-2)
pin 3 (gpio0-3)
pin 4 (gpio0-4)
pin 5 (gpio0-5)
......
......

开发板上两个LED对应的引脚是:

pin 249 (gpio8-1)=====> blue
pin 250 (gpio8-2)=====> yellow

可以通过export GPIO的方式操作这两个GPIO:

[root@rk3288:/sys/class/gpio]# echo 250 > export
[root@rk3288:/sys/class/gpio]# cd gpio250
[root@rk3288:/sys/devices/platform/pinctrl/gpio/gpio250]# ls
active_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,方法如下:

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设备节点:

	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 <linux/of.h>):

a.通过绝对路径,获取设备节点

static inline struct device_node *of_find_node_by_path(const char *path)

b.通过父节点和名称,获取设备树子节点

static inline struct device_node *of_get_child_by_name(
					const struct device_node *node,
					const char *name)

b.通过节点和名称,获取GPIO引脚号

static inline int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
2.firefly-rk3288 LED设备驱动编写

带设备树的LED驱动与不带设备树的驱动区别在于,带设备树的LED驱动需要在程序中从设备树中获取需要的GPIO编号,然后就是字符设备驱动的那一套流程了。

驱动源码文件如下:

#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");

MakeFile文件如下:

obj-m:=led_driver.o
PWD:=$(shell pwd)
KDIR:=/RK3288/firefly-sdk/kernel
all:
	$(MAKE) -C $(KDIR) M=$(PWD) 
clean:
	rm -rf *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions

驱动编译成功后会生成一个.ko文件,将.ko文件拷贝到开发板上并加载,出现如下提示表明驱动加载成功。
在这里插入图片描述

接下来,我们还需要编写一个测试APP,用于测试驱动是否正常工作,测试APP的源码如下:

#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;   
}

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

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值