环境:
编译平台:虚拟机上安装linux操作系统(fedroa 10)
应用平台:TQ2440移植linux(内核2.6.30.4)
编译器:交叉编译器arm-linu-gcc(4.3.3)
首先说明下编写驱动所涉及的的目录和文件:
1、TQ2440移植的系统内核在我PC上存放的路径:/opt/EmbedSky/linux2.6.30.4
2、编译器存放在:/opt/EmbedSky/4.3.3/bin
3、我们编写文件的工作目录在PC机的:/home/test
4、TQ2440启动NFS文件系统是挂载虚拟机linux目录:/opt /EmbedSky/root_nfs
5、我们将编译好对驱动模块*.ko,这放在:/opt/EmbedSky/root_nfs/lib 即放在TQ2440文件系统目录下/lib下
6、我们将编译好对驱动测试软件程序执行文件,放在:/opt/EmbedSky/root_nfs/usr/sbin/
驱动程序是用于连接硬件与系统内核:驱动编译方式有两种,一是编译入内核,二是编译成模块(可以像我们windows一样运行做系统下载驱动程序)。
这我们用编译模块驱动来介绍,因为这种方式比编译内核好;编译入内核需要重新编译内核文件,重新移植内核入TQ2440(开发板),对flash烧写次数多伤害大。
首先知道我们要编写4个文件:2个C文件,2个Makefile文件;分别是驱动源程序C文件(testdev.c),驱动源程序编译相关的Makefile文件,测试驱动程序的"软件程序"(testsoft.c),编译测试软件程序的Makefile。
开始编写:
第一步:在/home/test/testdev目录下建立testdev.c
#cat testdev.c //该命令是查看testdev.c的内容,且当前目录下运行。
[root@EmbedSky testdev]# cat testdev.c
/*************************************
NAME:testdev.c
COPYRIGHT:http://www.cnblogs.com/CJye/
*************************************/
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#define DEVICE_NAME "testdev"
#define LED_MAJOR 241
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON 1
#define IOCTL_LED_OFF 0
/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table [] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
static int testdev_open(struct inode *inode,struct file *file)
{
return 0;
}
static int testdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
if (arg > 4)
{
return -EINVAL;
}
switch(cmd)
{
case IOCTL_LED_ON:
// 设置指定引脚的输出电平为0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case IOCTL_LED_OFF:
// 设置指定引脚的输出电平为1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations testdev_fops = { //给内核结构体赋值
.owner = THIS_MODULE,
.open = testdev_open,
.ioctl = testdev_ioctl,
};
static char __initdata banner[] = "TQ2440/DIY LEDS,http://www.cnblogs.com/CJye/\n";
static int __init testdev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //将GPB5~8脚作为输出
s3c2410_gpio_setpin(led_table[i], 0); //GPB5~8输出低电平4个灯全部点亮
}
printk(banner);
ret=register_chrdev(LED_MAJOR,DEVICE_NAME,&testdev_fops); //向内核写入驱动模块
if(ret<0)
{
printk(DEVICE_NAME"can not register major number\n");
return ret;
}
}
static void __exit testdev_exit(void)
{
unregister_chrdev(LED_MAJOR,DEVICE_NAME);
printk(DEVICE_NAME"give up/cancel\n");
}
module_init(testdev_init); //insmod *.ko函数调用入口
module_exit(testdev_exit); //rmmod *.ko 函数调用入口
MODULE_LICENSE("GPL");
MODULE_AUTHOR("http://www.cnblogs.com/CJye/");
MODULE_DESCRIPTION("TQ2440/DIY LEDS Driver");
第二步:在/home/test/testdev目录下建立Makefile
#cat Makefile //该命令是查看testdev.c的内容,且当前目录下运行。
[root@EmbedSky testdev]# cat Makefile
PWD = $(shell pwd)
KERNELDIR =/opt/EmbedSky/linux-2.6.30.4
obj-m =testdev.o
modules:
make -C $(KERNELDIR) M=$(PWD) modules
cp -f testdev.ko /opt/EmbedSky/root_nfs/lib/.
clean:
make -C $(KERNELDIR) M=$(PWD) clean
rm -f $(PWD)/*~
#ls //该命令是查看编译生成的文件,在当前目录运行。
[root@EmbedSky testdev]# ls
Makefile Module.symvers testdev.ko testdev.mod.o
modules.order testdev.c testdev.mod.c testdev.o
第三步:在/home/test/testsoft目录下建立testsoft.c
#cat testdev.c //该命令是查看testdev.c的内容,且当前目录下运行。
[root@EmbedSky testsoft]# cat testsoft.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> //open() close()
#include <unistd.h> //read() write()
#define DEVICE_NAME "/dev/testdev" //this is dev name
#define LED_ON 1
#define LED_OFF 0
int main(void)
{
int fd;
int ret;
char *i;
printf("\nstart gpio_led_driver test\n\n");
fd = open(DEVICE_NAME,O_RDWR);
printf("fd = %d\n",fd);
if(fd==-1)
{
printf("open device %s error\n",DEVICE_NAME);
}
else
{
while(1) //对GPB5~8的LED控制操作
{
ioctl(fd,LED_ON,0);
ioctl(fd,LED_ON,2); //点亮GPB5,GPB7
ioctl(fd,LED_OFF,1); //关闭GPB6,GPB8
ioctl(fd,LED_OFF,3);
sleep(1);
ioctl(fd,LED_ON,1);
ioctl(fd,LED_ON,3); //点亮GPB6,GPB8
ioctl(fd,LED_OFF,0); //点亮GPB5,GPB7
ioctl(fd,LED_OFF,2);
sleep(1);
}
ret = close(fd);
printf("ret=%d\n",ret);
printf("close gpio_led_drier test\n");
}
return 0;
}
第四步:在/home/test/testsoft目录下建立Makefile
#cat Makefile //该命令是查看Makefile的内容,且当前目录下运行。
[root@EmbedSky testsoft]# cat Makefile
CC =/opt/EmbedSky/4.3.3/bin/arm-linux-gcc
OBJ =testsoft.o
EXEC =testsoft
$(EXEC):$(OBJ)
$(CC) -o $@ $^
copy :
cp -f $(EXEC) /opt/EmbedSky/root_nfs/usr/sbin/.
clean:
rm -f $(EXEC) *.o *~
rm -f /opt/EmbedSky/root_nfs/usr/sbin/$(EXEC)
#ls //该命令是查看编译生成的文件,在当前目录运行。
[root@EmbedSky testsoft]# ls
Makefile testsoft.c testsoft.o testsoft
到此,驱动文件编译工作完成了。
下面通过windons上secureCRT终端进入TQ2440操作。
TQ2440如果有相关的GPIO控制LED的驱动,可以先停用,然后再加载我们自己的驱动。
#/etc/rc.d/init.d/ledctl stop ///etc/rc.d/init.d目录下是一些TQ2440启动文件系统自启动的应用程序,根据这命令格式可以停用相应的应用程序。
ledctl这是我启动文件系统加载的一个LED跑马灯,要加载下面我们的应用程序,所以我就先停用这个ledctl。
#cd /lib
#insmod testdev.ko //加载这个模块到内核
#lsmod //查看是否有这个模块
#cd /dev //进入系统驱动文件目录
#mknod testdev c 241 0 //给testdev.ko建议一个设备节点,字符型驱动文件,主设备号241与驱动文件里一致,次设备号0
#cd /usr/sbin //进入测试驱动软件程序执行文件存放目录
#ls //查看是否有testsoft执行文件。
# ./testsoft //此时你实现对LED灯控制效果就出现在你眼前了。
是不是很轻松,很有成就,那就去开只香槟庆祝下。