Linux驱动之LED驱动编写

发布时间:2024-08-20  

1、查看原理图,确定需要控制的IO端口

打开原理图,确定需要控制的IO端口为GPF4、GPF5、GPF6。

2、查看芯片手册,确定IO端口的寄存器地址,可以看到它的基地址为0x56000050

 

3、编写驱动代码,编写驱动代码的步骤如下:

 1)、编写出口、入口函数。

  a、首先利用register_chrdev函数如果第一个参数为0的话那么会自动分配一个主设备号为Firstmajor ;第二个参数firstled_drv会是这个字符设备的名称可以利用命令cat /proc/devices看到;第三个参数是它的first_drv_fops结构体,这个结构体是字符设备中最主要的,后面再说明。

  b、接着利用class_create函数创建一个firt_drv_class类。它的第一个参数指向这个模块,第二个参数为类的名称。再利用class_device_create创建四个设备节点,第一个参数为类、第三个参数为设备号,第五个参数为设备节点的名称,第六个参数为次设备号。这样的话会在加载驱动之后自动在/dev目录下创建四个设备文件。

  c、ioremap函数重映射函数,将物理地址转换成虚拟地址

  d、a-c为驱动入口函数,在驱动出口函数会将a-c创建的东西全部删除。

  e、module_init与module_exit表示在insmod与rmmod的时候内核会调用first_ledsdrv_init与first_ledsdrv_exit


/*

 * 执行insmod命令时就会调用这个函数 

 */

static int __init first_ledsdrv_init(void)

{

    int minor;//次设备号

    Firstmajor = register_chrdev(0, 'firstled_drv', &first_drv_fops);//注册first_drv_fops结构体到字符设备驱动表,0表示自动分配主设备号

    if(Firstmajor<0)

    {

              printk(' first_drv can't register major numbern');

              return Firstmajor;

        }


    firt_drv_class = class_create(THIS_MODULE, 'leds');//创建类 

    

    firt_drv_class_dev[0] = class_device_create(firt_drv_class, NULL, MKDEV(Firstmajor, 0), NULL, 'leds');//创建设备节点

    if (unlikely(IS_ERR(firt_drv_class_dev[0])))

            return PTR_ERR(firt_drv_class_dev[0]);


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

    {

        firt_drv_class_dev[minor] = class_device_create(firt_drv_class, NULL, MKDEV(Firstmajor, minor), NULL, 'led%d',minor);//创建设备节点

        if (unlikely(IS_ERR(firt_drv_class_dev[minor])))

            return PTR_ERR(firt_drv_class_dev[minor]);

    }


    gpfcon = ioremap(0x56000050 , 16);//重映射,将物理地址变换为虚拟地址

    gpfdat = gpfcon + 1;

    

    printk('firstdrv module insmodedn');

    return 0;

}


/*

 * 执行rmmod命令时就会调用这个函数 

 */

static void __exit first_ledsdrv_exit(void)

{

    int i;

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

        class_device_unregister(firt_drv_class_dev[i]);//删除设备节点

        

    class_destroy(firt_drv_class);//删除类


    iounmap(gpfcon);//删除重映射分配的地址

    

    unregister_chrdev(Firstmajor, 'firstled_drv');//将rst_drv_fops结构体从字符设备驱动表中删除

    printk('firstdrv module rmmodn');

}


/* 这两行指定驱动程序的初始化函数和卸载函数 */

module_init(first_ledsdrv_init);

module_exit(first_ledsdrv_exit);


 2)、添加file_operations 结构体,这个是字符设备驱动的核心结构,所有的应用层调用的函数最终都会调用这个结构下面定义的函数。


static struct file_operations first_drv_fops = {

    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open   =   first_ledsdrv_open,     

    .write    =    first_ledsdrv_write,       

};

其中THIS_MODULE在linux/module.h中定义,它执向__this_module的地址


84    extern struct module __this_module;

85    #define THIS_MODULE (&__this_module)

而__this_module这个变量是在编译的时候由modpost程序生成的,它的结构如下:


struct module __this_module

__attribute__((section('.gnu.linkonce.this_module'))) = {

 .name = KBUILD_MODNAME,

 .init = init_module,

#ifdef CONFIG_MODULE_UNLOAD

 .exit = cleanup_module,

#endif

};


3)、分别编写file_operations 结构体下的open、wrtie函数。当应用程序调用系统调用led设备的open与write时最终内核会定位到驱动层的open与write函数。


其中open函数的功能是根据打开的设备文件初始化相应的io口为输出口


static int first_ledsdrv_open(struct inode *inode, struct file *file)

{

    int minor = MINOR(inode->i_rdev);//取得次设备号,根据次设备号来配置IO端口


    switch(minor)

        {

            case 0:

                *gpfcon &= ~((3 << 8)  | (3 << 10) | (3 << 12));//先清0 :8,9,10,11,12,13

                *gpfcon |= ((1 << 8)  | (1 << 10) | (1 << 12));//再置1:8,10,12break;

                printk('initialize ledsn');

                break;

            case 1:

                *gpfcon &= ~((3 << 8) );//先清0 :8,9,10,11,12,13

                *gpfcon |= ((1 << 8));//再置1:8,10,12break;

                printk('initialize led1n');

                break;

            case 2:

                *gpfcon &= ~( (3 << 10));//先清0 :8,9,10,11,12,13

                *gpfcon |= ( (1 << 10) );//再置1:8,10,12break;

                printk('initialize led2n');

                break;

            case 3:

                *gpfcon &= ~((3 << 12));//先清0 :8,9,10,11,12,13

                *gpfcon |= ((1 << 12));//再置1:8,10,12break;

                printk('initialize led3n');

                break;

            default:break;

        }

    

    

//    printk('hello this is openn');

    return 0;

}


write函数的功能是根据设备文件以及向设备写入的值来操作相应的IO口做相应的动作


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

{

    char val;

    int ret;

    int minor = MINOR(file->f_dentry->d_inode->i_rdev);//根据文件取出次设备号

    

    ret = copy_from_user(&val, buf, count);//ret返回0表示拷贝成功


    if(!ret)

    {

        switch(minor)

        {

            case 0:

                if(val==1)

                {

                    *gpfdat &= ~((1 << 4) | (1<<5) | (1<<6));//点灯

                     printk('leds onn');

                }

                else if(val == 0)

                {

                    *gpfdat |= ((1 << 4) | (1<<5) | (1<<6));//灭灯

                    printk('leds offn');

                }

                break;

            case 1:

                if(val==1)

                {

                    *gpfdat &= ~((1 << 4));//点灯

                     printk('led1 onn');

                }

                else if(val == 0)

                {

                    *gpfdat |= ((1 << 4));//灭灯

                    printk('led1 offn');

                }

                break;

            case 2:

                if(val==1)

                {

                    *gpfdat &= ~((1<<5));//点灯

                     printk('led2 onn');

                }

                else if(val == 0)

                {

                    *gpfdat |= ((1<<5));//灭灯

                    printk('led2 offn');

                }

                break;

            case 3:

                if(val==1)

                {

                    *gpfdat &= ~((1<<6));//点灯

                     printk('led3 onn');

                }

                else if(val == 0)

                {

                    *gpfdat |= ((1<<6));//灭灯

                    printk('led3 offn');

                }

                break;

            default:break;

        }

    }

    else

        printk('copy from user wrong!!!!%d  %dn',ret,count);

//    printk('hello this is writen');

    return 0;

}

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>