一、开发环境
1、硬件平台:FS2410
2、主机:Ubuntu 10.10
3、内核版本:linux 2.6.35
4、交叉编译工具链:arm-none-linux-gnueabi-
二、LED原理图
三、详细代码分析
驱动文件led.c:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define GPFCON 0x56000050
#define GPFDAT 0x56000054
#define LED_ON 0x4800
#define LED_OFF 0x4801
#define LED_1 0x01
#define LED_2 0x12
#define LED_3 0x03
#define LED_4 0x04
static volatile unsigned int *gpfcon;
static volatile unsigned int *gpfdat;
static int led_major = 244; //LED主设备号
static struct cdev led_cdev; //定义LED字符设备结构体
int led_open(struct inode *inode , struct file *filp)
{
gpfcon = ioremap(GPFCON, 0x04); //地址映射
gpfdat = ioremap(GPFDAT, 0x04);
*gpfcon &= ~(0xff << 8); //设置GPF4-GPF7为输出
*gpfcon |= (0x55 << 8);
*gpfdat &= ~(0xf << 4); //4个LED点亮
return 0;
}
int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
void led_on(void)
{
*gpfdat &= ~(0xf << 4); //点亮4个LED灯
return ;
}
static int led_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case LED_1:
*gpfdat |= (0xf << 1); //点亮LED9
break;
case LED_2:
*gpfdat |= (0xf << 2); //点亮LED10
break;
case LED_3:
*gpfdat |= (0xf << 3); //点亮LED11
break;
case LED_4:
*gpfdat |= (0xf << 4); //点亮LED12
break;
case LED_ON:
led_on();
break;
default:
break;
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
void led_setup_cdev(struct cdev *dev, int index)
{
int err;
int devno = MKDEV(led_major, index);
cdev_init(dev, &led_fops); //初始化cdev并设置与file_operation关联
dev->owner = THIS_MODULE;
err = cdev_add(dev, devno, 1); //注册cdev
if(err < 0)
printk("failed to add led");
return;
}
static int __init led_init(void)
{
int result;
int devno = MKDEV(led_major, 0);
//分配设备号
if(devno)
result = register_chrdev_region(devno, 1, "led");
else
{
result = alloc_chrdev_region(&devno, 0, 1, "led");
led_major = MAJOR(devno);
}
if(result < 0)
return result;
printk("led_major : %d\n", led_major);
led_setup_cdev(&led_cdev, 0);
return result;
}
static void __exit led_exit(void)
{
cdev_del(&led_cdev); //注销设备号
unregister_chrdev_region(MKDEV(led_major, 0), 1); //释放设备号
return;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yhr");
测试文件led_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define LED_ON 0x4800
#define LED_OFF 0x4801
#define LED_1 0x01
#define LED_2 0x12
#define LED_3 0x03
#define LED_4 0x04
int main(void)
{
int led_fd;
int i;
if((led_fd = open("/dev/led", O_RDONLY)) < 0) //打开设备
{
perror("failed to open led");
exit(-1);
}
while(1)
{
ioctl(led_fd, LED_1, 0);
usleep(500000);
ioctl(led_fd, LED_2, 0);
usleep(500000);
ioctl(led_fd, LED_3, 0);
usleep(500000);
ioctl(led_fd, LED_4, 0);
usleep(500000);
ioctl(led_fd, LED_ON, 0);
usleep(500000);
}
return 0;
}
Makefile文件:
ifeq ($(KERNELRELEASE),)
# set your object kernel dir
KERNELDIR = /home/linux/linux-2.6.35
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
obj-m := led.o
endif
进入开发板的文件系统后:
#insmod led.ko
#mknod /dev/led c 244 0
#./led_test
之后就可以看到LED流水灯