嵌入式课程---嵌入式Linux的LED灯驱动开发

实验环境准备

实验材料:GEC6818实验箱、电脑、
实验环境:VMware下Linux系统、arm-linux-交叉编译环境,windows系统、secureCRT平台

第一步:测试三个系统是否连通

  1. 使用串口线、以太网线连接pc机与实验箱

  2. 禁用pc机的防火墙,并禁用无线网卡

  3. 设置pc机上以太网网卡的ip地址、子网掩码、网关(注意,不可设为主动获取,需要手动设置,因为目前只连接pc机与实验箱)

  4. 在secureCRT下设置实验箱Linux系统的ip地址,使其与pc机处于同一网段下(此处注意,开启实验箱时,直接等待其进入Linux系统,而不是u-boot模式,接着按CTRL+c即可进入Linux系统命令行)

  5. 在pc机上的vmware虚拟机的Linux系统下设置ip地址(此处注意要先设置好与pc机的网络连接模式,这里选择桥接模式桥接到外部机的一张网卡(该网卡可能是虚拟出来的)
    在这里插入图片描述

  6. 前面三个ip地址一定要保证在同一网段下,例如pc机设ip为192.168.1.160、子网掩码为255.255.255.0、网关为192.168.1.1
    实验箱ip地址设为192.168.1.180,虚拟机上Linux系统ip地址设为192.168.1.170

  7. 然后确保两两相互能ping得通

第二步:编写并编译驱动与测试程序

1.编写LED灯驱动程序led_old.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/ioctl.h>


#define old_way 1
#define GPIOEOUT_PA   0xC001E000   //外设的物理地址
#define BUF_SIZE   256

struct cdev chrdev_test;
dev_t ndev;
int TestMajor = 0;              //主设备号
int TestMinor = 0;		//此设备号

char drv_name[]="chrtest" ;
char kbuf[BUF_SIZE] ;
char wbuf[BUF_SIZE] ;

unsigned int *GPIOEOUT_VA;         //0xC001E000
unsigned int *GPIOEOUTENB_VA;      //0xC001E004
struct resource *GPIOE13_LED;


static int test_opne(struct inode *inode, struct file *file)
{
	printk("<0>""chrdev is openning!\n");
	*GPIOEOUTENB_VA = (1 << 13);       //设置为输出模式
	*GPIOEOUT_VA  = (0 << 13);		//输出为0
	return 0;
}

static int test_release(struct inode *inode, struct file *file)
{
	printk("<0>""chrdev is closeing\n");
	return 0;	
}

static ssize_t test_read(struct file *file, char __user *buf,\

			size_t const count, loff_t *offset)
{
	if(count < BUF_SIZE)
	{
		if(copy_to_user(buf, wbuf, count))//执行完成后返回还需要拷贝的字节数,成功为0
		{
			printk("<0>""copy to user filed\n");
			return -EFAULT;
		}
	}
	else
	{
		printk("<0>""read size must be less than %d\n", BUF_SIZE);
		return -EINVAL;			
	}

	*offset +=count;  //文件位置指针更新
	return count;	
}

static ssize_t test_write(struct file *file, char __user *buf,\

			size_t const count, loff_t *offset)
{
	if(count < BUF_SIZE)//读写大小检查
	{
		if(copy_from_user(wbuf, buf, count))//执行完成后返回还需要拷贝的字节数,成功为0
		{
			printk("<0>""copy from user filed\n");
			return -EFAULT;
		}
	}
	else
	{
		printk("<0>""size must be less than %d\n", BUF_SIZE);
		return -EINVAL;			
	}

	*offset +=count;  //文件位置指针更新
	return count;	
	
}

//cmd 设置灯的状态 0 ----off 1 ----on
//args 设置哪一盏灯的:

static long test_ioctl(struct file* file, unsigned int cmd, unsigned int args)
{
	switch(cmd)
	{
		case 0:
		 	*GPIOEOUT_VA = (0 << 13);
			printk("<0>""cmd =0, args is not quest\n");
			break;		
		case 1:
			*GPIOEOUT_VA = (1 << 13);
			printk("<0>""cmd =1, args is not quest\n");
			break;
		default:
			printk("<0>""command is error!\n");
			break;					
	}
	return 0;
}

static const struct file_operations chrdev_fops = {
	.owner = THIS_MODULE,
	.open  = test_opne,
	.read  = test_read,
	.write = test_write,
	.unlocked_ioctl = test_ioctl,
	.release = test_release,
};

static int __init test_init(void)
{
	int ret;

#if old_way
	ret = register_chrdev(TestMajor, drv_name, &chrdev_fops);
	if(ret < 0)
	{
		printk("<0>""can not register majior\n");
		return ret;
	}
	else if(ret > 0)
	{
		printk("<0>""allocating majior successful \n");
		TestMajor = ret;
		ndev = MKDEV(TestMajor, TestMinor);
	}
	else
	{
		ndev = MKDEV(TestMajor, TestMinor);
	}


#else
	if(TestMajor)
	{
		ndev = MKDEV(TestMajor, TestMinor);//由主次设备号,得到设备号
		ret = register_chrdev_region(ndev, 1, drv_name);//注册字符设备编号
	}
	else
	{
		ret = alloc_chrdev_region(&ndev, TestMinor, 1, drv_name);//动态分配一个字符设备号,&ndev存放返回的值
		TestMajor = MAJOR(ndev);
	}
	if(ret < 0)
	{
		printk(KERN_WARNING "cannot get major %d \n", TestMajor);
		goto fail_chrdev_region;//出错处理
	}

	cdev_init(&chrdev_test, &chrdev_fops);
	chrdev_test.owner = THIS_MODULE;
	
	ret = cdev_add(&chrdev_test, ndev, 1);
	if(ret < 0)
	{
		printk("<0>""failed to cdev add\n");
		goto fail_chrdev_add;//出错处理goto
	}
#endif
	printk("<0>""this is the first demo of drvier, inserting successful !\n");//通过console输出一个字符串
	
	GPIOE13_LED = request_mem_region(GPIOEOUT_PA, 8, "LED_IO");
	if(GPIOE13_LED == NULL)
	{
		ret = -EFAULT;
		printk("<0>""can not request memory GPIOE13_LED");
		goto fail_request_mem; //出错处理
	}
	
	GPIOEOUT_VA = (unsigned int *) ioremap(GPIOEOUT_PA, 8);
	if(GPIOEOUT_VA == NULL)
	{
		ret = -EFAULT;
		printk("<0>""can not ioremap GPIOEOUT_VA");
		goto fail_ioremap; //出错处理
	}
	GPIOEOUTENB_VA = GPIOEOUT_VA + 1;
	return 0;
fail_ioremap:
	release_mem_region(GPIOEOUT_PA, 8);
fail_request_mem:
	cdev_del(&chrdev_test);
fail_chrdev_add:
	unregister_chrdev_region(ndev, 1);
fail_chrdev_region:
	return ret;
}

static void __exit test_exit(void)
{
	iounmap(GPIOEOUT_PA);
	release_mem_region(GPIOEOUT_PA, 8);
	unregister_chrdev_region(ndev, 1);
	cdev_del(&chrdev_test);
	printk("<0>""the driver is exiting!\n");
}

module_init(test_init);//驱动的入口函数会调用一个用户的初始化函数
module_exit(test_exit);//驱动的出口也函数会调用一个用户的退出函数
//驱动的描述信息: #modinfo *.ko ,驱动的描述信息并不是必需的。
MODULE_AUTHOR("kaoyangou");
MODULE_DESCRIPTION("the first demo of driver");
MODULE_LICENSE("GPL"); //遵循的协议

2. 编写驱动程序的Makefile文件

INSTALLDIR	=  /home/Hello

ifneq ($(KERNELRELEASE),)
obj-m:=led_old.o
else
KERNELDIR:=/home/Hello/demo/6818GEC/kernel
CROSS_COMPILE:=/home/Hello/demo/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-

PWD:=$(shell pwd)

default:
	mkdir -p $(INSTALLDIR)
	$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
	cp --target-dir=$(INSTALLDIR) led_old.ko

clean:
	rm -rf *.o *.order .*.cmd *.mod.c *.symvers
endif

编写LED灯测试程序led_oldtest.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	int fd, ret;
	fd = open("/dev/gec_led",O_RDWR);
	if(fd < 0)
	{
		perror("open:");
		return -1;
	}
	while(1)
	{
		ret = ioctl(fd, 1, 1);//LED OFF
		if (ret < 0)
		{
			perror("ioctl:");
			return -1;
		}
		sleep(1);
		ret = ioctl(fd, 0, 1);//LED OFF
		if (ret < 0)
		{
			perror("ioctl:");
			return -1;
		}
		sleep(1);
	}
	close(fd);
	return 0;
}

编写测试程序的Makefile文件

#
# Makefile for Linux application test example.
#
#---------------------------------------------------------------
# Linux application Makefile sample
# make          -- the optical output without debug info
# make clean    -- clean the object file(s)
# make install  -- install the execute file(s) to INSTALLDIR
#---------------------------------------------------------------
# History:
#			

INSTALLDIR	= /home/Hello/nfs_share

#---------------------------------	/* execute file(s) */
TESTFILE 	= led_oldtest
#---------------------------------	/* object file(s) */
SRCFILE		= led_oldtest.c
#---------------------------------	/* header file(s) */
TESTFILE_H	=

CROSS = arm-none-linux-gnueabi-
CC = $(CROSS)gcc
AS = $(CROSS)as
LD = $(CROSS)ld

CFLAGS  += -O2 -Wall

all:   $(TESTFILE)

$(TESTFILE):  $(SRCFILE) $(TESTFILE_H) Makefile	
	mkdir -p $(INSTALLDIR)
	$(CC) $(CFLAGS) -o $@ $@.c
	cp --target-dir=$(INSTALLDIR) $(TESTFILE) 

clean:
	rm -f $(TESTFILE)

第三步:编译生成led_old.ko与led_oldtest二进制执行文件

  1. 将驱动程序led_old.c与其对应Makefile文件放在同一目录下,然后在该目录下执行命令make,生成目标文件led_old.ko
  2. 将驱动程序led_oldtest.c与其对应Makefile文件放在同一目录下,然后在该目录下执行命令make,生成目标文件led_oldtest
    注意,以上的编译属于交叉编译,Makefile文件中已指定相应的交叉编译器

第四步:将led_old.ko文件与led_oldtest文件挂载到实验箱板子

  1. 将led_old.ko文件与led_oldtest文件统一放在虚拟机的nfs_share目录上
  2. 在secureCRT命令框使用mount命令进行挂载,命令中的ip地址是虚拟机的ip地址
mount -o nolock,tcp 192.168.0.190:/home/Hello/nfs_share /mnt

第五步:在实验箱装载驱动并使用程序进行测试

  1. 在secureCRT中输入命令lsmod,然后回车,在返回的消息中如果找到有led字眼的一行,则使用rmmod led进行卸载原有驱动,确保实验箱系统不存在led灯的驱动程序
lsmod    //查看已存在驱动

rmmod led   //卸载led驱动
  1. 进入到目录 /mnt,然后插入驱动led_old.ko,因为刚才把自己制作的驱动挂载到了/mnt目录下
insmod led_old.ko   
  1. 在/proc/devices 找到我们申请的主设备号和设备名
cat /proc/devices

在结果在找到的243 chrtest即为我们要的结果,注意chrtest设备名在驱动程序中已编写,当插入该驱动进实验箱时自动分配了这个243的主设备号

  1. 在 实验箱板子中创建设备文件的节点
    命令如下:
mknod /dev/gec_led c 243 0
命令解释:
mknod //创建
/dev/gec_led //设备的名字
c //表示字符设备
243 //刚刚我们查看到的主设备号
0 //次设备号,可以任意給值,但是不要大于 255
  1. 在实验箱板子中执行程序
    ./led_oldtest

观察结果,实验箱上其中一个led灯在闪烁

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值