基于mini2440的led字符设备驱动程序

已经大三了开始进行专业课的学习了,一学年的时间要学习完linux应用程序和linux驱动程序,按照进度感觉什么也学习不到。进入大三也有些烦躁,学习也有些急功近利,必须让自己沉下心读读书、敲敲代码了。


刚开始学习嵌入式linux驱动程序,看了些以前国嵌的视频发现一些函数根本找不到,很多的函数已经更新了。linux内核代码更新比较快,很多新的技术会很快取代旧的技术,这也是linux作为开源系统的特点。学习linux不能只记函数,而是要更深层次的了解其原理


在字符驱动中struct file、struct inode、struct cdev、struct file_operations、unsigned long copy_from_user、unsigned long copy_to_user是比较重要的几个结构体和函数,它们是内核空间与用户空间连接的桥梁。


基于mini2440的嵌入式linux的led驱动程序:

/*
 * led_device.c
 *
 *  Created on: 2016年11月3日
 *      Author: chy
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>

struct leds { /*led描述*/
	struct cdev led_dev;
	char val[2];
	volatile unsigned long *gpio_coun;
	volatile unsigned long *gpio_data;
};
static struct leds led;


static ssize_t open_leds(struct inode *led_inode,struct file *file_led) /*打开文件*/
{
	*led.gpio_coun &= (~(0x3 << 5 | 0x3 << 6 | 0x3 << 7 | 0x3 << 8));
	*led.gpio_coun |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
	*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
	*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);

	return 0;
}

static ssize_t write_leds(struct file *file_led,const char __user *bufer,size_t size,loff_t *off) /*点亮所有led*/
{
	int val,ssize;
	ssize = copy_from_user(&led.val,bufer,strlen(bufer)); /*将数据由用户区拷贝到内核区*/
	if(ssize < 0)
		return -1;

	val = led.val[0] - '0';

	if(!val)
		*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
	else
		*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);

	return 0;
}

static ssize_t read_leds(struct file *file_led,char __user *bufer,size_t size,loff_t *off) /*读取数据*/
{
	int f;
	f = copy_to_user((void *)bufer,&led.val,strlen(led.val)); /*将数据由内核区拷贝到用户区*/
	if(f < 0)
		return -1;

	return strlen(bufer);
}

static long ioctl_leds(struct file *file_led,unsigned int cmd,unsigned long arg) /*发送文件命令*/
{
	if((cmd !=0 && cmd != 1) || (arg > 3 || arg < 0))
		return -1;

	switch(cmd){
	case 0:
		 *led.gpio_data &= ~(0x1 << (arg + 5)); break;
	case 1:
		 *led.gpio_data |= (0x1 << (arg + 5)); break;
	}
	return 0;
}

static int close_file(struct inode *inode,struct file *filep) /*关闭文件*/
{
	*led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
	*led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);

	return 0;
}

struct file_operations file_oper = {
		.owner = THIS_MODULE,
		.open = open_leds,
		.write = write_leds,
		.read = read_leds,
		.unlocked_ioctl = ioctl_leds,
		.release = close_file,
};

dev_t dev_num;

static struct class *class_file = NULL;

static int  init_leds_device(void) /*加载驱动模块*/
{
	alloc_chrdev_region(&dev_num,0,1,"my_leds"); /*动态分配设备号*/

	led.led_dev.owner = THIS_MODULE; /*设备模板*/
	cdev_init(&led.led_dev,&file_oper); /*初始化cdev*/
	cdev_add(&led.led_dev,dev_num,1); /*注册设备*/

	led.gpio_coun = (volatile unsigned long *)ioremap(0x56000010, 4); /*led端口映射*/
	led.gpio_data = led.gpio_coun + 1;

	/*自动创建设备文件l*/
	class_file = class_create(THIS_MODULE,"my_leds");
	device_create(class_file,NULL,dev_num,NULL,"my_leds");

	return 0;
}

static void  exit_leds_device(void) /*卸载驱动模块*/
{
	cdev_del(&led.led_dev); /*注销设备*/

	/*删除设备文件*/
	device_destroy(class_file, dev_num);
	class_destroy(class_file);
    unregister_chrdev_region(dev_num,1); /*注销设备号*/

    /*取消端口映射*/
	iounmap(led.gpio_coun);

	return;
}

MODULE_LICENSE("GPL");
module_init(init_leds_device); /*加载模块*/
module_exit(exit_leds_device); /*卸载模块*/


驱动程序的Makefile:

obj-m:=./src/led_device.o
all:
	make -C /home/chy/linux-kernel/ M=$(PWD) modules
	cp -f ./src/led_device.ko ~/NFS/qu_dong/
clean:
	make -C /home/chy/linux-kernel/ M=$(PWD) clean
	rm -f ~/NFS/qu_dong/led_device.ko
 /home/chy/linux-kernel/  是开发板linux内核的路径,linux内核必须为编译过的且和开发板上跑的内核配置是一样的。不要使用友善之臂提供 编译内核的默认方法,自己重新配置内核时去掉友善之臂的led驱动模块。


验证驱动程序:

/*
 * led_test.c
 *
 *  Created on: 2016年11月3日
 *      Author: chy
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define Max 256
#define NO 0
#define OFF 1
#define ERR(msg,f) { \
	if(f < 0) { \
		fprintf(stderr,"%s",msg); \
		exit(1); \
	} \
}

int main(int argc,char *argv[])
{
	int file_id,write_num;
	char buf[Max] = "0";

	file_id = open("/dev/my_leds", O_RDWR); /*打开文件*/
	ERR("open file failed\n",file_id);
	printf("Led All NO\n");
	ERR("write file failed\n",write(file_id,"0",Max)); /*点亮所有led*/
	sleep(2);  /*进程睡眠2秒*/
	close(file_id); /*关闭文件*/
	sleep(1);

	file_id = open("/dev/my_leds", O_RDWR);/*打开文件*/
	ERR("open file failed\n",file_id);
	int i;

	/* 流水点亮、熄灭 */
	printf("流水\n");
	for(i = 0; i < 4; i++){
		ERR("ioctl failed\n",ioctl(file_id, NO,i));
		sleep(1);
		ERR("ioctl failed\n",ioctl(file_id, OFF,i));
	}

	/* 读取数据 */
	ERR("read file faile\n",read(file_id,&buf,Max));
	printf("read data is: %s\n",buf);

	close(file_id);
	exit(0);
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值