您的位置:首页 > 其它

基于S3C2440开发板LED灯驱动移植

2013-03-13 18:16 190 查看
上一篇文章简述了如何写一个实用性较强的驱动模板,今天我们给这个模板填充一些必要的代码,让它变成一个可用的led驱动程序,实际上大多数字符类驱动也都是基于这种模板形式。

#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>

#include <asm/atomic.h>

#include <asm/unistd.h>

/*以上头文件包含如果搞不懂具体起什么作用,建议从系统驱动中拷贝*/

/*#define definition*/

#define DEVICE_NAME "gpio_drv" //设备名称 和文件系统里设备节点名称区分开

#define FIRST_MAJOR 0 //主设备号

#define LED_ON 1 //点亮

#define LED_OFF 0 //熄灭

static unsigned long led_table[] =

{

S3C2410_GPB5,

S3C2410_GPB6,

S3C2410_GPB7,

S3C2410_GPB8,

};

static unsigned int led_cfg_table[] =

{

S3C2410_GPB5_OUTP,

S3C2410_GPB6_OUTP,

S3C2410_GPB7_OUTP,

S3C2410_GPB8_OUTP,

};

/*上面两个数组采用源码的基于S3C2410头文件配置好的,如果需要自己配置 参考S3C2440用户手册*/

//.open func 应用程序将来调用的open函数 主要配置io口

static int myfirst_drv_open(struct inode *inode, struct file *filp)

{

int i;

for(i = 0; i < 4; i++)

{

s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);

}

printk("myfirst_drv_open\n");

/*配置GPIB5 6 7 8 为输出*/

return 0;

}

//.write func 应用程序将来调用write部分 *file问文件句柄 *buf传入的用户(应用程序)数据地址 count为字节长度 一般为4的整数倍 前面buf为地址 一条指令4个字节

static ssize_t myfirst_drv_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos)

{

int val; //接收用户数据

int i;;

printk("myfirst_drv_write\n");

copy_from_user(&val, buf, count); // copy_to_user();
将用户空间地址buf的数据交互到内核空间 val地址上

if (val == 1) //点亮LED灯

{

for(i = 0; i < 4; i++)

{

s3c2410_gpio_setpin(led_table[i], 0);

}

printk("led on\n");

}

else

{

for(i = 0; i < 4; i++)

{

s3c2410_gpio_setpin(led_table[i], 1);

}

printk("led off\n");

}

return 0;

}

//ioctl控制 应用程序将来调用的ioctl函数*file为应用层返回的文件句柄,cmd为操作命令 arg为传入的参数

static int gpio_ioctl(

struct inode *inode,

struct file *file,

unsigned int cmd,

unsigned long arg)

{

if (arg > 4)

{

return -EINVAL;

}

switch(cmd)

{

case LED_ON:

// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负0

s3c2410_gpio_setpin(led_table[arg], 0);

return 0;

case LED_OFF:

// 璁剧疆鎸囧畾寮曡剼鐨勮緭鍑虹數骞充负1

s3c2410_gpio_setpin(led_table[arg], 1);

return 0;

default:

return -EINVAL;

}

}

//define the file_operations file_operations结构体

static struct file_operations myfirst_drv_fops = {

.owner = THIS_MODULE,

.open = myfirst_drv_open,

.write = myfirst_drv_write,

.ioctl = gpio_ioctl,

};

static struct class *myfirst_drv_class; //定义一个类 方便在/dev目录下建立一个节点

int major; //自动分配主设备号

int first_drv_init(void)

{

//int ret;

//ret = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);

major = register_chrdev(FIRST_MAJOR, DEVICE_NAME, &myfirst_drv_fops);

//if(ret < 0)//

if(major < 0)

{

printk("myfirst_drv can not register major number!\n");

return major;

}

/*注册一个类 使得mdev可以在/dev目录下自动建立设备节点 不需要手动mknod dev c 224 0这样的操作*/

myfirst_drv_class = class_create(THIS_MODULE, DEVICE_NAME);

if(IS_ERR(myfirst_drv_class))

{

printk("error, fail to init myfirst_drv_class");

return -1;

}

/*如果上面成功注册进mdev的话 下面自动在/dev目录下创建一个设备节点*/

device_create(myfirst_drv_class, NULL, MKDEV(major, 0), NULL, "GPIO_DRV"); //这个就是在/dev下创建的MYFIRST_DRV 设备节点了 应用程序调用

printk("MYFIRST_DRV initialized! \n");

return 0;

}

void first_drv_exit(void)

{

unregister_chrdev(major, DEVICE_NAME); //卸载驱动

device_destroy(myfirst_drv_class, MKDEV(major, 0)); //删掉设备节点

class_destroy(myfirst_drv_class); //注销类

printk("rm -rm MYFIRST_DRV! \n");

}

module_init(first_drv_init);

module_exit(first_drv_exit);

MODULE_LICENSE("GPL"); //获得授权

该驱动makefile和上一篇一样 这里不再重复填上

下面看我们的测试程序 如何通过write open ioctl来操作硬件的?

#include <unistd.h>

#include <sys/ioctl.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#define LED_ON 1

#define LED_OFF 0

static int fd; //用于存放打开设备时返回的文件句柄

//例如我们想通过设备文件向驱动写入数据的时候 我们的main函数应该设置成下面的方式,下面的参数如何理解呢?我这里贴上一段参考----

int main(int argc, char **argv)

argc是命令行中的参数的个数
argv[]对应每一个参数

例如
./a.exe 1 bb
argc将会是3
其中
argv[0]是“./a.exe”
argv[1]是“1”
argv[2]是“bb”


int main(int argc, char **argv)

{

int led_no = 0;

int val ;

int ret;

fd = open("/dev/GPIO_DRV", O_RDWR); //打开设备 并读写权限 上一篇我们后面的参数写的是0 这里需要为O_RDWR,意思是赋予驱动读写权限

if(fd < 0) //成功打开fb返回的肯定是个非零值

{

perror("open device failed");

exit(1);

}

while(1) //每个应用程序都是死循环

{

for(led_no = 0; led_no < 4; led_no++)

{

ioctl(fd, LED_ON, led_no); //点亮一盏灯 这里采用循环点亮方式

sleep(2);

}

led_no = 0;

for(led_no = 0; led_no < 4; led_no++) //又熄灭一个led灯 熄灭2个。。。。。

{

ioctl(fd, LED_OFF, led_no); //熄灭一盏灯

sleep(1);

}

led_no = 0;

if(argc != 2) //如果传入参数不是2个的话 提示信息退出 ./gpio_test on 一定要这种方式

{

printf("Usage :\n");

printf("%s <on|off>\n", argv[0]);

return 0; //退出重新运行应用程序

}

if (strcmp(argv[1], "go") != 0) //如果第二个参数不是go 如果./gpio_test go 则运行流水灯 上面部分

{

if (strcmp(argv[1], "on") == 0) //如果第二个参数为on

{

printf("led app on\n");

val = 1;

}

else if (strcmp(argv[1], "off") == 0) //第二个参数为off

{

printf("led app off\n");

val = 0;

}

ret = write(fd, &val, 4); //写入 fd为文件句柄 传入要写的数据的地址&val 我们就要么1 要么0 所以一个地址就可以了 长度4字节

if(ret < 0) //如果写入成功 返回写入的个数 否则返回-1

{

printf("write failed ret = %d\n", ret);

return 1;

}

return 0;

}

}

//close(fd); //关闭设备

return 0;

}

编写该应用程序的makefile 为了以后开发方便 这里makefile较上篇做了一点改变

CROSS=arm-linux-

DRIVER=gpio_drvtest.c

FILENAME=gpio_test

all: $(FILENAME)

$(FILENAME) : $(DRIVER)

$(CROSS)gcc -g -o $(FILENAME) $(DRIVER)

# $(CROSS)strip FILENAME

clean:

@rm -vf $(FILENAME) *.o *~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: