LED驱动

通过写博客来巩固自己学到的知识,我觉得是个好方法,在你刚调完程序,对程序还是最熟悉的时候记录下你对程序的学习。今天写led驱动程序。我相信网上一定有无数关于写led驱动的博客了。我觉得我只是希望自己可以通过写博客这种方式来巩固一下自己学到的知识,如果有雷同,说明咱们出自同一个老师,那我就该叫你师兄或师姐了。不好意思。

下面进入正题:

写驱动程序按老师所说可以大致分为两部分内容:

1.将驱动框架搭起来。

2.就是对照原理图,芯片手册一些参考资料来写硬件相关的操作(而与单片机不同的是,单片机的操作是在物理地址,而驱动的操作是在虚拟地址)。

 

其实第一步我在我前面的博客中已经讲到了,所以在这里就不细讲了,其中主要的几点就是

写驱动接口函数如:open,write,read等;

写file_operations型的结构体,并将上面写的open,write,read等函数名对应写入file_operations结构体中。

在入口函数中使用register_chrdev函数注册(告诉内核)上面file_operations结构体。

在出口函数中使用unregister_chrdev函数卸载驱动;

使用module_init修饰入口函数

使用module_exit修饰出口函数

下面我们主要讲第二部分,而第二部分中主要使用了次设备号来实现对不同LED的控制,通过设置和识别不同的次设备号来实现对某一个或者多个LED的控制。

而本驱动使用开发板的GPF4,5,6三个引脚实现对三个LED驱动的控制,下面是在入口函数中对主次设备的创建:

static struct class *ledsdrv_class;   //定义一个ledsdrv的类
static struct class_device *ledsdrv_class_dev[4];  //再定义四个class_device的设备

ledsdrv_class = class_create(THIS_MODULE,"leds");

// 定义主设备为leds 主设备号位auto_major,次设备号为0
ledsdrv_class_dev[0] = class_device_create(ledsdrv_class,NULL,MKDEV(auto_major,0),NULL,"leds");  

for(minor=1;minor<4;minor++){

//定义三个次设备led1,led2,led3,主设备号位auto_major,次设备号分别为1,2,3
ledsdrv_class_dev[minor] = class_device_create(ledsdrv_class,NULL,MKDEV(auto_major,minor),NULL,"led%d",minor); 

}

相应的在出口函数中对主次设备号注销:

for(minor = 0;minor<4;minor++){
class_device_unregister(ledsdrv_class_dev[minor]);
}

class_destroy(ledsdrv_class);

由于在驱动程序中我们使用虚拟地址:所以要在入口函数中使用到ioremap函数将物理地址转化为虚拟地址:

//而在此之前要定义 volatile unsigned long *gpfcon = NULL; //gpf配置寄存器
//                              volatile unsigned long *gpfdat = NULL; //gpf数据寄存器

gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);

相应的在出口函数中去映射:

iounmap(gpfcon);

 

而程序中相应端口的配置在open函数中完成:

/* 配置IO口 */
int minor = MINOR(inode->i_rdev);//MINOR(inode->i_cdev); // 获得次设备号


switch(minor){
case 0:
{
*gpfcon &= ~(0x3<<(4*2));
*gpfcon |= (0x1<<(4*2));


*gpfcon &= ~(0x3<<(5*2));
*gpfcon |= (0x1<<(5*2));


*gpfcon &= ~(0x3<<(6*2));
*gpfcon |= (0x1<<(6*2));


break;
}


case 1:
{
*gpfcon &= ~(0x3<<(4*2));
*gpfcon |= (0x1<<(4*2));


break;
}


case 2:
{


*gpfcon &= ~(0x3<<(5*2));
*gpfcon |= (0x1<<(5*2));


break;
}


case 3:
{


*gpfcon &= ~(0x3<<(6*2));
*gpfcon |= (0x1<<(6*2));


break;
}


}

而程序中相应的端口操作在write函数中:

/* 操作端口 */
int minor = MINOR(file->f_dentry->d_inode->i_rdev);
char val;


copy_from_user(&val,buf,1);

switch(minor){
case 0:
{
if(val&0x1){
*gpfdat &= ~((0x1<<4) | (0x1<<5) | 0x1<<6);
}else{
*gpfdat |= ((0x1<<4) | (0x1<<5) | 0x1<<6);
}


break;
}


case 1:
{
if(val&0x1){
*gpfdat &= ~(0x1<<4);
}else{
*gpfdat |= (0x1<<4);
}


break;
}


case 2:
{
if(val&0x1){
*gpfdat &= ~(0x1<<5);
}else{
*gpfdat |= (0x1<<5);
}


break;
}


case 3:
{
if(val&0x1){
*gpfdat &= ~(0x1<<6);
}else{
*gpfdat |= (0x1<<6);
}


break;
}


}

 

剩下的就是编写测试程序了:

 filename = argv[1];


    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("error, can't open %s\n", filename);
        return 0;
    }


    if (!strcmp("on", argv[2]))
    {
        // 亮灯
        val = 1;
        write(fd, &val, 1);
    }
    else if (!strcmp("off", argv[2]))
    {
        // 灭灯
        val = 0;
        write(fd, &val, 1);
    }
    else
    {
        print_usage(argv[0]);
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值