[2014.3.20]mini2440用ioremap写LED驱动程序

2 篇文章 0 订阅
2 篇文章 0 订阅

这两天移植在实验室和师兄测试接收机,没有什么时间学嵌入式。现在这个程序还只是一个半成品,暂且贴在这,有很多地方还不完美,回来再写个更好的。果然把裸机代码加个init open read write close exit的外壳就成驱动了,但是要使用ioremap把寄存器物理地址映射为虚拟地址,否则会出现segmentation fault错误。最后别忘了iounmap取消映射。

led_driver.c(半成品)

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");	//使用GPL版权协议
MODULE_AUTHOR("wolf");	//作者

/*********GPIO寄存器物理地址*********/
/*按键寄存器*/
#define pGPGCON	0x56000060
#define pGPGDAT	0x56000064
#define pGPGUP	0x56000068
/*LED寄存器*/
#define pGPBCON	0x56000010
#define pGPBDAT	0x56000014
#define pGPBUP	0x56000018
/*********GPIO寄存器物理地址*********/

static int *GPGCON, *GPGDAT, *GPGUP;	//按键寄存器虚拟地址
static int *GPBCON, *GPBDAT, *GPBUP;	//LED寄存器虚拟地址
static int major_devnum;	//主设备号
//static int mode;		//模式1跑马灯,模式2按键点灯

/*********打开设备*********/
static int openDevice(struct inode* inode, struct file* file)
{
	/*LED全亮*/
	*GPBDAT &= ~((1<<5)|(1<<6)|(1<<7)|(1<<8));
	printk(KERN_ALERT "Device Opened.");
	return 0;
}
/*********打开设备*********/

/*********关闭设备*********/
static int closeDevice(struct inode* inode, struct file* file)
{
	/*LED全灭*/
	*GPBDAT |=  ((1<<5)|(1<<6)|(1<<7)|(1<<8));
	printk(KERN_ALERT "Device Closed.");
	return 0;
}
/*********关闭设备*********/

/*文件操作结构体*/
struct file_operations fOps =
{
	.open = openDevice,
	.release = closeDevice
};

/*********装载驱动*********/
static int __init loadDriver(void)
{
	/*地址映射*/
	GPGCON = ioremap(pGPGCON, 4);
	GPGDAT = ioremap(pGPGDAT, 4);
	GPGUP = ioremap(pGPGUP, 4);
	GPBCON = ioremap(pGPBCON, 4);
	GPBDAT = ioremap(pGPBDAT, 4);
	GPBUP = ioremap(pGPBUP, 4);
	/*设置管脚属性*/
	*GPGCON &= ~((3<<6)|(3<<10)|(3<<12)|(3<<14));
	*GPGUP	&= ~((1<<3)|(1<<5)|(1<<6)|(1<<7));
	*GPBCON &= ~((1<<11)|(1<<13)|(1<<15)|(1<<17));
	*GPBCON |=  ((1<<10)|(1<<12)|(1<<14)|(1<<16));
	*GPBUP  &= ~((1<<5)|(1<<6)|(1<<7)|(1<<8));
	/*LED全灭*/
	*GPBDAT |=  ((1<<5)|(1<<6)|(1<<7)|(1<<8));
	/*注册文件操作结构体*/
	major_devnum = register_chrdev(0, "wolf_key_LED", &fOps);
	if(major_devnum < 0)
	{
		printk(KERN_ALERT "Driver load failed.");
		return major_devnum;
	}	
	else
	{
		printk(KERN_ALERT "Driver %d loaded.", major_devnum);
		return 0;
	}
}
/*********装载驱动*********/

/*********卸载驱动*********/
static void __exit unloadDriver(void)
{
	*GPBDAT &= ~((1<<5)|(1<<6)|(1<<7)|(1<<8));
	/*取消地址映射*/
	iounmap(GPGCON);
	iounmap(GPGDAT);
	iounmap(GPGUP);
	iounmap(GPBCON);
	iounmap(GPBDAT);
	iounmap(GPBUP);
	/*注销文件操作结构体*/
	unregister_chrdev(major_devnum, "wolf_key_LED");
	printk(KERN_ALERT "Driver unloaded.");
}
/*********卸载驱动*********/

/*设置装载卸载驱动时执行的函数*/
module_init(loadDriver);
module_exit(unloadDriver);

【运行现象】
加载insmod led_driver.ko,灯全灭;
卸载rmmod led_driver ,灯全亮。open和close函数还没有测试。
网上的程序都信誓旦旦地说只要insmod或rmmod,LED灯就会出现相应的变化,但是事实上,尽管已经打印出了printk里面的信息,但是LED灯还是按照二进制时钟的方式运行。纠结了一天,才发现原来在insmod执行的一刹那,LED灯确实灭了,但是之后又继续它的二进制时钟,应该是GPBDAT里面的数据又被别的程序改了的缘故。
解决方法是:打开/etc/init.d/rcS,这个文件是mini2440的启动流程,其中/etc/rc.d/init.d/leds start那一行是启动LED二进制时钟程序。就是这个程序在一直改GPBDAT里面的内容,用#把这行注释掉。这样开机的时候就不会启动LED service了,自己写的驱动程序的作用就显现出来了。

【注意事项】

1. __init函数不能返回正值,否则在insmod的时候会说什么“init suspiciously returned xxx, it should follow 0/-E conventionsys_init_module: loadingmodule anyway..”加载失败

2.写应用程序时用的函数都是C库函数,而驱动程序用的都是内核提供的函数,互相之间不能通用。否则应用程序也可以随便行使内核功能,就天下大乱了。

3.本来我想在一个for循环里不停地给*GPBDAT赋值,来让LED灯的状态持续的时间更长一些的。没想到不论我把循环次数设得多大,这个循环都能瞬间完成。后来才想到是编译器觉得这个for循环纯属废话,所以就把它给优化了,把它改成赋一次值就完了。编译器本想让程序运行的更迅速的,它不知道我的本意恰恰是延时。解决办法是,给GPBDAT设定为volatile的,于是和它有关的运算就都不会被优化了,延时就实现了。

4.指针++,--,+n,-n实际上是加减这么多个sizeof(它所指向的类型),比如int*型指针p,p++其实是p+sizeof(int),而不是真的给p加1.

【遗留问题】

1.深究一下request_mem_region和release_mem_region函数

2.深究一下volatile的用法

3.把程序完善了,写个测试程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值