一.RK3288--1.led驱动

1.时钟树


在这里插入图片描述
在这里插入图片描述

2.需要控制的LED的GPIO引脚

![GPIO8](https://img-blog.csdnimg.cn/20200604232133943.png?x-oss-pro
GPIO8_A1,找到对应的时钟控制描述
在这里插入图片描述
在这里插入图片描述
通过Adress maping 找到这个寄存器的地址:0xFF760000 + 0x0198
时钟使能:CRU_CLKGATE14_CON (0xFF760000 + 0x0198);
IO复用: GRF_GPIO8A_IOMUX (0xFF770000 + 0x0080);
在这里插入图片描述
在这里插入图片描述

IO方向: GPIO8_SWPORTA_DDR (0xFF7F0000 + 0x0004);
IO数据: GPIO8_SWPORTA_DR (0xFF7F0000 + 0x0000,);

在rk3288_firefly.dts添加设备节点


myled-node {
	xxx_led = "led_for_test";
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "rk3288_testled";
	status = "okay";
	reg = <	0xFF760198   4  /* 时钟使能*/
		    0xFF770080   4     /* IO 复用*/
		 	0xFF7F0004   4     /* 设置方向*/
			0xFF7F0000   4>;   /* 数据设置*/
};

然后在内核根目录下 make dtbs,然后更换这个文件: /boot/rk3288-frefly.dtb

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <asm/mach/map.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/amba/bus.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>

#define DTSLED_CNT 1 /* 设备号个数 */
#define DTSLED_NAME "rk3288_dtsled" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */

/*  设备结构体 */
struct dtsled_dev{
dev_t devid;              /* 设备号 */
struct cdev cdev;         /* cdev */
struct class *class;      /* 类 */
struct device *device;    /* 设备 */
int major;                /* 主设备号 */
int minor;                /* 次设备号 */
struct device_node *nd;    /* 设备节点 */
};

struct dtsled_dev rk3288_dtsled;  /* led设备 */

/* 映射后的寄存器虚拟地址指针 */
static void __iomem  * CRU_CLKGATE14_CON;
static void __iomem  * GRF_GPIO8A_IOMUX ;
static void __iomem  * GPIO8A_SWPORTA_DDR;
static void __iomem  * GPIO8A_SWPORTA_DR ;

void led_switch(u8 sta)
{
	u32 val =0;
	if(sta == LEDON)
	{
		val = readl(GPIO8A_SWPORTA_DR);
		val &= ~(1<<1);
		writel(val,GPIO8A_SWPORTA_DR);      //GPIO8_A1输出低电平
	}
	else if(sta == LEDOFF)
	{
		val = readl(GPIO8A_SWPORTA_DR);
		val |= (1<<1);
		writel(val,GPIO8A_SWPORTA_DR);  //GPIO8_A1输出高电平
	}
}

/*
* @description : 打开设备
 * @param – inode : 传递给驱动的 inode
* @param – filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file * filp)
{
	filp->private_data = &rk3288_dtsled;  //设置私有数据
	return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *off_t)
{
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *off_t)
{
	int ret;
	unsigned char databuf[1];
	unsigned char ledstat;

	ret = copy_from_user(databuf,buf,cnt);
	if(ret < 0)
	{
		printk("kernel write failed \n");
		return -1;
	}
	ledstat = databuf[0];

	if(ledstat == LEDON)
	{
		led_switch(LEDON);
	}
	else if(ledstat == LEDOFF)
	{
		led_switch(LEDOFF);
	}
	return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
	printk("led release \n");
	return 0;
}

static struct file_operations rk3288_dtsled_fops ={
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = led_release,
};

/*
* 驱动的入口函数
 */
static int __init led_init(void)
{
	int ret;
	const char *str;
	unsigned int regdata[14];
	struct property *proper;
	unsigned int  val =0;
	/* 获取设备树中的属性数据 */

	//1.获取设备节点
	rk3288_dtsled.nd = of_find_node_by_path("/myled-node");
	if(rk3288_dtsled.nd == NULL)
	{
		printk("myled-node can not find \n");
	}
	else 
	{
		printk("myled-node has been found \n");
	}

	//2.获取属性内容
	proper = of_find_property(rk3288_dtsled.nd,"compatible",NULL);
	if(proper == NULL)
	{
		printk("compatible property find failed \n");
	}
	else 
	{
		printk("compatible = %s \n",(char *)proper->value);
	}

	/* 3、获取 status 属性内容 */
 	ret = of_property_read_string(rk3288_dtsled.nd, "status", &str);
	if(ret < 0){
 		printk("status read failed!\r\n");
 	} else {
 		printk("status = %s\r\n",str);
	}

	/* 4、获取 reg 属性内容 */
	ret = of_property_read_u32_array(rk3288_dtsled.nd, "reg", regdata, 8);
	if(ret < 0) {
			printk("reg property read failed!\r\n");
		} else {
			u8 i = 0;
			printk("reg data:\r\n");
			for(i = 0; i < 8; i++)
			printk("%#X ", regdata[i]);
			printk("\r\n");
		}

		{
			struct platform_device *op = of_find_device_by_node(rk3288_dtsled.nd);
		printk("num_resources: %d \n", op->num_resources);
		}
		
#if 1
 /* 1、寄存器地址映射 */
 	CRU_CLKGATE14_CON = ioremap(regdata[0], regdata[1]);
 	GRF_GPIO8A_IOMUX = ioremap(regdata[2], regdata[3]);
 	GPIO8A_SWPORTA_DDR = ioremap(regdata[4], regdata[5]);
 	GPIO8A_SWPORTA_DR = ioremap(regdata[6], regdata[7]);
 #else
		/* 初始化LED */
		
	CRU_CLKGATE14_CON = of_iomap(rk3288_dtsled.nd, 0);
	//GRF_GPIO8A_IOMUX =  of_iomap(rk3288_dtsled.nd, 1);
	//GPIO8A_SWPORTA_DDR = of_iomap(rk3288_dtsled.nd, 2);
	//GPIO8A_SWPORTA_DR =  of_iomap(rk3288_dtsled.nd, 3);
#endif
#if 1
	//使能GPIO8的时钟
	val = readl(CRU_CLKGATE14_CON);
	val &= ~(0<<8);
	val |= (1<<(8+16));
	writel(val,CRU_CLKGATE14_CON);

	//设置GPIO8_A1的复用功能
	val = readl(GRF_GPIO8A_IOMUX);  
	val &= ~(0<<2); 
	val |= (3<<(2+16)); 
	writel(val,GRF_GPIO8A_IOMUX);

	//设置GPIO8_A1的方向输出
	val = readl(GPIO8A_SWPORTA_DDR);
	val |= 1<<1;
	writel(val,GPIO8A_SWPORTA_DDR);

	//默认关闭led
	val = readl(GPIO8A_SWPORTA_DR);
	val |= (1<<1);
	writel(val,GPIO8A_SWPORTA_DR);
#endif	

	/* 注册字符设备驱动 */
	//1.创建设备号
	if(rk3288_dtsled.major){
		rk3288_dtsled.devid = MKDEV(rk3288_dtsled.major,0);
		register_chrdev_region(rk3288_dtsled.devid,DTSLED_CNT,DTSLED_NAME);
	}else{
		alloc_chrdev_region(&rk3288_dtsled.devid,0, DTSLED_CNT,
							DTSLED_NAME); //申请设备号
		rk3288_dtsled.major = MAJOR(rk3288_dtsled.devid);
		rk3288_dtsled.minor = MINOR(rk3288_dtsled.devid);
	}
	printk("rk3288_dtsled major=%d,minor=%d \n",rk3288_dtsled.major,rk3288_dtsled.minor);

	//初始化cdev
	rk3288_dtsled.cdev.owner = THIS_MODULE;
	cdev_init(&rk3288_dtsled.cdev,&rk3288_dtsled_fops);

	//添加一个cdev
	cdev_add(&rk3288_dtsled.cdev,rk3288_dtsled.devid,DTSLED_CNT);

	//创建类
	rk3288_dtsled.class = class_create(THIS_MODULE,DTSLED_NAME);
	if(IS_ERR(rk3288_dtsled.class))
		return PTR_ERR(rk3288_dtsled.class);

	//创建设备
	rk3288_dtsled.device = device_create(rk3288_dtsled.class,NULL,rk3288_dtsled.devid,
											NULL,DTSLED_NAME);
	if(IS_ERR(rk3288_dtsled.device)){
		return PTR_ERR(rk3288_dtsled.device);
	}
	return 0;
}

static void __exit led_exit(void)
{
	iounmap(CRU_CLKGATE14_CON);
	iounmap(GRF_GPIO8A_IOMUX);
	iounmap(GPIO8A_SWPORTA_DDR);
	iounmap(GPIO8A_SWPORTA_DR);

	//注销字符设备驱动
	cdev_del(&rk3288_dtsled.cdev); //删除cdev
	unregister_chrdev_region(rk3288_dtsled.devid,DTSLED_CNT);  //注销设备号

	device_destroy(rk3288_dtsled.class,rk3288_dtsled.devid);
	class_destroy(rk3288_dtsled.class);

}
module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

//这里of_iomap会报错:在这里插入图片描述先留个坑,后面解决·······································

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

#define LEDON 1
#define LEDOFF 0
int main(int argc, char *argv[])
{
	int fd,retvalue;
	char *filename;
	unsigned char databuf[1];

	if(argc != 3)
	{
		printf("error usage \n");
		return -1;
	}

	filename = argv[1];
	fd = open(filename,O_RDWR);
	if(fd < 0)
	{
		printf("file %s open failed \n", filename);
	}

	databuf[0] = atoi(argv[2]);

	retvalue = write(fd,databuf,sizeof(databuf));
	if(retvalue < 0)
	{
		printf("led write failed \n");
		close(fd);
		return -1;
	}

	retvalue = close(fd);
	printf("test nor \n");
}

Makefile文件


# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = /home/book/mysamba/RK_3288_SDK/linux-4.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f ledtest

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o



obj-m += rk3288_led_driver.o 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值