栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

firefly-rk3288j开发板--设备树下的 LED 驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

firefly-rk3288j开发板--设备树下的 LED 驱动

firefly-rk3288j开发板–设备树下的 LED 驱动

1 准备工作
开发板:aio-rk3288j
SDK版本:rk3288_linux_release_20210304
下载工具:Linux_Upgrade_Tool_v2.1
内核版本:4.4.194
文件系统:buildroot
Ubuntu版本:18.04
交叉编译工具:gcc version 6.3.1 20170404

2 硬件原理图

3 设备树文件修改
3.1 添加led设备节点
设备树文件位于内核kernel/arch/arm/boot/dts目录下,我们需要打开rk3288.dtsi、rk3288-linux.dtsi、rk3288-firefly-port.dtsi、rk3288-firefly-aio.dtsi.对于led设备只需要在rk3288-firefly-port.dtsi这文件,找打led设备节点:
leds {
compatible = “gpio-leds”;
work {

	label = "firefly:blue:power";
	linux,default-trigger = "ir-power-click";
	gpios = <&gpio8 1 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default";
	pinctrl-0 = <&led_power>;
	default-state = "on";		
};

user {

	label = "firefly:yellow:user";
	linux,default-trigger = "ir-user-click";
	gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default";
	pinctrl-0 = <&led_user>;
	default-state = "off";
};

};

编译内核,输入如下命令
./build.sh kernel
./build.sh updateimg

4 设备树常用 OF 操作函数
rk3288开发板共有两个led,分别对应GPIO8_A1和GPIO8_A2,但是我们在驱动程序中需要通过设备树获取到这两个GPIO的值。所以下面介绍几个常用设备树操作函数,这些 OF 函数原型都定义在 include/linux/of.h 文件中。
4.1 查找节点的 OF 函数
of_find_node_by_name 函数通过节点名字查找指定的节点。
of_find_node_by_type 函数通过 device_type 属性查找指定的节点
of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点
of_find_matching_node_and_match 函数通过 of_device_id 匹配表来查找指定的节点
of_find_node_by_path 函数通过路径来查找指定的节点
4.2 查找父/子节点的 OF 函数
of_get_parent 函数用于获取指定节点的父节点
of_get_next_child 函数用迭代的方式查找子节点
提取属性值的 OF 函数
of_find_property 函数用于查找指定的属性
of_property_count_elems_of_size 函数用于获取属性中元素的数量
of_property_read_u32_index 函数用于从属性中获取指定标号的 u32 类型数据值
of_property_read_u8/u16/u32/u64_array 这 4 个函数分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据
of_property_read_u8/u16/u32/u64 这四个函数就是用于读取这种只有一个整形值的属性,分别用 于读取 u8、u16、u32 和 u64 类型属性值
of_property_read_string 函数用于读取属性中字符串值
of_n_addr_cells 函数用于获取#address-cells 属性值
of_size_cells 函数用于获取#size-cells 属性值
5 LED驱动程序编写

#include //模块加载卸载函数
#include //内核头文件
#include //数据类型定义
#include //file_operations结构体
#include //class_create等函数
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define  DEVICE_NAME     "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, "firefly:yellow:user");
	    if(ret != 0) {
	     printk("gpio %d req failed:%drn",ledx.led_pin,ret);
	     gpio_free(ledx.led_pin); 
	     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 failedrn");
      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)
{
   
   ledx.led_node = of_find_node_by_path("/leds/user");
   printk("/leds:%prn",ledx.led_node); 
   if(ledx.led_node == NULL)
   {
     printk("find node by path fialed!rn"); 
      return -1;
   }
  
   
   ledx.led_pin = of_get_named_gpio(ledx.led_node,"gpios",0);
   printk("led_pin:%drn",ledx.led_pin);
   if(!gpio_is_valid(ledx.led_pin))
   {
      printk("get_gpio failed:%d rn",ledx.led_pin);
      return -1;
   }

   
   alloc_chrdev_region(&ledx.dev_id,0,1,DEVICE_NAME);

   
   cdev_init(&ledx.cdev,&led_fops);
 
   
   cdev_add(&ledx.cdev,ledx.dev_id,1);

   
   ledx.class = class_create(THIS_MODULE, "led_class");
   if(ledx.class == NULL)
   {
      printk("class_create failedrn");
      return -1;
   }

   
   ledx.device = device_create(ledx.class, NULL, ledx.dev_id, NULL, DEVICE_NAME);
   
   printk("module init okn");
   
   return 0;  
}

static void led_exit(void)
{
   
   cdev_del(&ledx.cdev);

   
   unregister_chrdev_region(ledx.dev_id, 1);

   
   device_destroy(ledx.class, ledx.dev_id);

   
   class_destroy(ledx.class);

   printk("module exit okn");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lsjml2022");

6 编写测试APP

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

int main(int argc, char *argv[])
{
   int fd, retvalue;
   char *filename;
   unsigned char databuf[1];

   if(argc != 3){
      printf("Error Usage!rn");
      return -1;
   }

   filename = argv[1];
   //打开驱动
   fd = open(filename, O_RDWR);
   if(fd < 0)
   {
      printf("file %s open failed!rn", argv[1]);
      return -1;
   }

   databuf[0] = atoi(argv[2]); 
   //文件写入数据
   retvalue = write(fd, databuf, sizeof(databuf));
   if(retvalue < 0)
   {
      printf("Control Failed!rn");
      close(fd);
      return -1;
   }

   
   retvalue = close(fd);
   if(retvalue < 0)
   {
      printf("file %s close failed!rn", argv[1]);
      return -1;
   }

   return 0;
}
}

7 编译驱动程序和测试APP
7.1 编译驱动程序

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

输入如下命令编译出驱动模块文件:
make -j8

编译成功后会生成一个.ko文件拷贝到开发板上并加载
7.2 编译测试app
输入如下命令编译测试 ledApp.c 这个测试程序:
arm-linux-gnueabihf-gcc ledApp.c -o ledApp
编译成功以后就会生成 ledApp 这个应用程序
7.3 运行测试
编译出来的.ko 和 ledApp 这两个文件拷贝到/lib/modules/4.4.194目录中,重启开发板,进入目录/lib/modules/4.4.194中输入加载led.ko驱动模块:
insmod led_driver.ko

驱动加载成功以后就可以使用ledAPP软件来测试驱动是否正常,输入如下命令打开LED灯:
./ledApp /dev/led 1 //打开LED灯
./ledApp /dev/led 0 //关闭 LED 灯
rmmod led.ko //卸载驱动模块

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/961602.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号