我的QQ:843013501
一、应用程序:在main()函数之前,自己编写了一个小的延时程序,方便观察流水灯的流动效果。在main()函数里,先用使用open函数打开设备8weiled0,并将打开成功与否的返回值赋给文件描述符fd,接着进行判断:若打开失败,会继续打开设备8weiled,若打开失败,则会将错误输出到标准错误中,同时程序退出。若打开设备文件成功,则程序继续向下执行能实现流水灯效果的无限循环语句。进入循环后,会依次为要操作的led赋予亮或灭的状态,通过ioctl()函数调用驱动函数,完成对led操作,然后调用延时子函数,并依次向后做循环操作,最终实现流水灯的效果。在这个无限循环语句的后面,是关闭文件的close()函数,和最终的退出语句。
#include
#include
#include
#include
static int fd; //led设备文件描述符的变量
int yanshi(unsigned int a) //延时子函数
{
unsigned int ys1,ys2,ys3; //延时参数
for(;a>0;a--){
for(ys1=0;ys1<200;ys1++){
for(ys2=0;ys2<200;ys2++){
for(ys3=0;ys3<200;ys3++);
}
}
}
}
int main(void)
{
int zt; //定义led的状态,0或者1;
int led_no; //定义控制哪个LED,0-7,一共八个
int fd; //LED设备文件描述符
fd = open("/dev/8weiled0", 0); //若成功打开设备8weiled0,则fd值为1,反之为-1。
if (fd < 0) {
fd = open("/dev/8weiled", 0); //若成功打开设备8weiled,则fd值为1,反之为-1。
}
if (fd < 0) {
perror("open device 8weiled"); //将错误输出到标准输出错误里面。
exit(1);
}
for(;;){
//LED流水灯的调用和延时部分,通过ioctl函数完成对
//单个LED的操作,第二个for循环作延时用。
for(led_no=0;led_no<8;led_no++){
zt=1; //led亮
ioctl(fd, zt, led_no);
yanshi(2);
zt=0; //led灭
ioctl(fd, zt, led_no);
yanshi(2);
}
}
close(fd); //关闭led_no
return 0;
}
二、驱动程序:先宏定义驱动程序的设备名为“8weiled”,后定义八个控制led的GPIO口的管脚,并将这些管脚设置为输出状态。接着为应用程序中的ioctl函数调用的驱动程序中的ioctl函数设置属性,包括索引节点、打开文件的路径、定义指令键和控制哪个led的自变量,对应用程序中ioctl函数控制led状态的自变量的识别是通过switch语句来判断的,对要操作的led的编号识别是用if语句加以判断的,如果命令输入正确,则会进一步掉用内核中相应的指令,最终显示出被操作的led状态。后面是这个操作文件的一些属性及设备相关信息,还有内核加载函数的声明、初始化,最后是卸载。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "8weiled" // 定义设备名为'8weiled'
static unsigned long led_table [] = { //定义八个控制led的GPIO口管脚
S3C2410_GPF0,
S3C2410_GPF1,
S3C2410_GPF2,
S3C2410_GPF3,
S3C2410_GPF4,
S3C2410_GPF5,
S3C2410_GPF6,
S3C2410_GPG0,
};
static unsigned int led_cfg_table [] = { //GPIO口的状态为输出
S3C2410_GPF0_OUTP,
S3C2410_GPF1_OUTP,
S3C2410_GPF2_OUTP,
S3C2410_GPF3_OUTP,
S3C2410_GPF4_OUTP,
S3C2410_GPF5_OUTP,
S3C2410_GPF6_OUTP,
S3C2410_GPG0_OUTP,
};
static int sbc2440_leds_ioctl( // I/O口的控制
struct inode *inode, //索引节点,即程序在磁盘中的存放处
struct file *file, //打开文件的路径
unsigned int cmd, //定义指令键
unsigned long arg) //定义自变量
{
switch(cmd) { //led的状态(cmd的值为0,灭;为1,亮)
case 0: //灭
case 1: //亮
if (arg > 8) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
//为gpio口设置引脚(第几个led,此led的亮灭状态)
return 0; //程序正确执行完
default: //否则(即出错处理)
return -EINVAL;
}
}
static struct file_operations dev_fops = { //对文件的操作,{}内为属性
.owner = THIS_MODULE, //此处为一个宏,指向编译模块时自动创建的_ _this_module变量
.ioctl = sbc2440_leds_ioctl,
};
static struct miscdevice misc = { //记录混合设备的一些信息
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void) //内核模块加载函数声明,初始化
{
int ret;
int i;
for (i = 0; i < 8; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //(第几个led,led的配置)
s3c2410_gpio_setpin(led_table[i], 0); //设置引脚的输出电平
}
ret = misc_register(&misc); //注册设备名
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void) //内核模块卸载函数
{
misc_deregister(&misc);
}
module_init(dev_init); //内核模块加载函数,初始化
module_exit(dev_exit); //内核模块卸载
MODULE_LICENSE("GPL"); //遵循的协议
MODULE_AUTHOR("FriendlyARM Inc."); //作者
ch841123 于 2012-01-10 16:03:36发表:
我最头疼的也是写驱动了,不过好多芯片的驱动都有模板!改改就好!
不过还是要认真学习一下才好!