Linux设备驱动--使用基本框架操作LED

目录

知识小点

相关函数

开发模板

开发过程


知识小点

1、MMU:MMU 全称叫做 Memory Manage Unit,也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。 MMU 主要完成的功能如下:
        ①、完成虚拟空间到物理空间的映射。
        ②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。

2、在使用MMU的Linux中,如果要驱动器件,那么需要完成获取物理地址对应的虚拟地址,一般使用ioremap 函 数和iounmap 函数。

相关函数

1、ioremap 函数:ioremap 函 数 用 于 获 取 指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间 , 定义在arch/arm/include/asm/io.h 文件中。

//原型:
#define ioremap(cookie,size) __arm_ioremap((cookie), (size),MT_DEVICE);

cookie:要映射给的物理起始地址。
size:要映射的内存空间大小(32位处理器一般填写4,也就是4B=32byte)。
mtype: ioremap 的类型,可以选择 MT_DEVICE、 MT_DEVICE_NONSHARED、
MT_DEVICE_CACHED 和 MT_DEVICE_WC, ioremap 函数选择 MT_DEVICE。
返回值: __iomem 类型的指针,指向映射后的虚拟空间首地址。
例如:

#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
static void __iomem* SW_MUX_GPIO1_IO03;
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);

2、iounmap 函数:卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射。

//原型
void iounmap (volatile void __iomem *addr);

例如:

iounmap(SW_MUX_GPIO1_IO03);

3、I/O 内存访问函数:

//读操作函数:
u8 readb(const volatile void __iomem *addr);
u16 readw(const volatile void __iomem *addr);
u32 readl(const volatile void __iomem *addr);//-->32位处理器一般用这个函数访问寄存器

//写操作函数:
void writeb(u8 value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)//-->32位处理器一般用这个函数访问寄存器

开发模板

驱动开发模板: 

#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <asm/mach/map.h>

#include <asm/uaccess.h>

#include <asm/io.h>

/***************************************************************

描述	   	: LED驱动文件。

***************************************************************/

#define LED_MAJOR		200		/* 主设备号 */

#define LED_NAME		"led" 	/* 设备名字 */



#define LEDOFF 	0				/* 关灯 */

#define LEDON 	1				/* 开灯 */

 

/* 寄存器物理地址 */

#define CCM_CCGR1_BASE				(0X020C406C)	

#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)

#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)

#define GPIO1_DR_BASE				(0X0209C000)

#define GPIO1_GDIR_BASE				(0X0209C004)



/* 映射后的寄存器虚拟地址指针 */

static void __iomem *IMX6U_CCM_CCGR1;

static void __iomem *SW_MUX_GPIO1_IO03;

static void __iomem *SW_PAD_GPIO1_IO03;

static void __iomem *GPIO1_DR;

static void __iomem *GPIO1_GDIR;



/*

 * @description		: LED打开/关闭

 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED

 * @return 			: 无

 */

void led_switch(u8 sta)

{

	u32 val = 0;

	if(sta == LEDON) {

		val = readl(GPIO1_DR);

		val &= ~(1 << 3);	

		writel(val, GPIO1_DR);

	}else if(sta == LEDOFF) {

		val = readl(GPIO1_DR);

		val|= (1 << 3);	

		writel(val, GPIO1_DR);

	}	

}



/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量

 * 					  一般在open的时候将private_data指向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int led_open(struct inode *inode, struct file *filp)

{

	return 0;

}



/*

 * @description		: 从设备读取数据 

 * @param - filp 	: 要打开的设备文件(文件描述符)

 * @param - buf 	: 返回给用户空间的数据缓冲区

 * @param - cnt 	: 要读取的数据长度

 * @param - offt 	: 相对于文件首地址的偏移

 * @return 			: 读取的字节数,如果为负值,表示读取失败

 */

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

	return 0;

}



/*

 * @description		: 向设备写数据 

 * @param - filp 	: 设备文件,表示打开的文件描述符

 * @param - buf 	: 要写给设备写入的数据

 * @param - cnt 	: 要写入的数据长度

 * @param - offt 	: 相对于文件首地址的偏移

 * @return 			: 写入的字节数,如果为负值,表示写入失败

 */

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

	int retvalue;

	unsigned char databuf[1];

	unsigned char ledstat;



	retvalue = copy_from_user(databuf, buf, cnt);

	if(retvalue < 0) {

		printk("kernel write failed!\r\n");

		return -EFAULT;

	}



	ledstat = databuf[0];		/* 获取状态值 */



	if(ledstat == LEDON) {	

		led_switch(LEDON);		/* 打开LED灯 */

	} else if(ledstat == LEDOFF) {

		led_switch(LEDOFF);	/* 关闭LED灯 */

	}

	return 0;

}



/*

 * @description		: 关闭/释放设备

 * @param - filp 	: 要关闭的设备文件(文件描述符)

 * @return 			: 0 成功;其他 失败

 */

static int led_release(struct inode *inode, struct file *filp)

{

	return 0;

}



/* 设备操作函数 */

static struct file_operations led_fops = {

	.owner = THIS_MODULE,

	.open = led_open,

	.read = led_read,

	.write = led_write,

	.release = 	led_release,

};



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static int __init led_init(void)

{

	int retvalue = 0;

	u32 val = 0;



	/* 初始化LED */

	/* 1、寄存器地址映射 */

  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);

	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);

  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);

	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);

	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);



	/* 2、使能GPIO1时钟 */

	val = readl(IMX6U_CCM_CCGR1);

	val &= ~(3 << 26);	/* 清楚以前的设置 */

	val |= (3 << 26);	/* 设置新值 */

	writel(val, IMX6U_CCM_CCGR1);



	/* 3、设置GPIO1_IO03的复用功能,将其复用为

	 *    GPIO1_IO03,最后设置IO属性。

	 */

	writel(5, SW_MUX_GPIO1_IO03);

	

	/*寄存器SW_PAD_GPIO1_IO03设置IO属性

	 *bit 16:0 HYS关闭

	 *bit [15:14]: 00 默认下拉

     *bit [13]: 0 kepper功能

     *bit [12]: 1 pull/keeper使能

     *bit [11]: 0 关闭开路输出

     *bit [7:6]: 10 速度100Mhz

     *bit [5:3]: 110 R0/6驱动能力

     *bit [0]: 0 低转换率

	 */

	writel(0x10B0, SW_PAD_GPIO1_IO03);



	/* 4、设置GPIO1_IO03为输出功能 */

	val = readl(GPIO1_GDIR);

	val &= ~(1 << 3);	/* 清除以前的设置 */

	val |= (1 << 3);	/* 设置为输出 */

	writel(val, GPIO1_GDIR);



	/* 5、默认关闭LED */

	val = readl(GPIO1_DR);

	val |= (1 << 3);	

	writel(val, GPIO1_DR);



	/* 6、注册字符设备驱动 */

	retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);

	if(retvalue < 0){

		printk("register chrdev failed!\r\n");

		return -EIO;

	}

	return 0;

}



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static void __exit led_exit(void)

{

	/* 取消映射 */

	iounmap(IMX6U_CCM_CCGR1);

	iounmap(SW_MUX_GPIO1_IO03);

	iounmap(SW_PAD_GPIO1_IO03);

	iounmap(GPIO1_DR);

	iounmap(GPIO1_GDIR);



	/* 注销字符设备驱动 */

	unregister_chrdev(LED_MAJOR, LED_NAME);

}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaowen");

应用程序开发模板:

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

/***************************************************************


描述	   	: chrdevbase驱测试APP。


使用方法	 :./ledtest /dev/led  0 关闭LED

		     ./ledtest /dev/led  1 打开LED		


***************************************************************/



#define LEDOFF 	0

#define LEDON 	1



/*

 * @description		: main主程序

 * @param - argc 	: argv数组元素个数

 * @param - argv 	: 具体参数

 * @return 			: 0 成功;其他 失败

 */

int main(int argc, char *argv[])

{

	int fd, retvalue;

	char *filename;

	unsigned char databuf[1];

	

	if(argc != 3){

		printf("Error Usage!\r\n");

		return -1;

	}



	filename = argv[1];



	/* 打开led驱动 */

	fd = open(filename, O_RDWR);

	if(fd < 0){

		printf("file %s open failed!\r\n", argv[1]);

		return -1;

	}



	databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */



	/* 向/dev/led文件写入数据 */

	retvalue = write(fd, databuf, sizeof(databuf));

	if(retvalue < 0){

		printf("LED Control Failed!\r\n");

		close(fd);

		return -1;

	}



	retvalue = close(fd); /* 关闭文件 */

	if(retvalue < 0){

		printf("file %s close failed!\r\n", argv[1]);

		return -1;

	}

	return 0;

}

开发过程

        开发过程与上一篇文章几乎一样,实际上就是将驱动部分的读写函数和寄存器操作进行挂钩,而操作寄存器需要通过MMU进行访问。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芯心智库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值