Linux驱动之LED驱动编写

发布者:VelvetSoul最新更新时间:2024-08-20 来源: cnblogs关键字:Linux驱动  LED  驱动编写 手机看文章 扫描二维码
随时随地手机看文章

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;

}

[1] [2] [3]
关键字:Linux驱动  LED  驱动编写 引用地址:Linux驱动之LED驱动编写

上一篇:Linux驱动之按键驱动编写(查询方式)
下一篇:Linux驱动之建立一个hello模块

推荐阅读最新更新时间:2024-11-10 10:27

[单片机框架][bsp层][AT32F415][bsp_led] LED配置和使用
重新封装一层函数,用于bsp层函数,方便调用。 void bsp_led_init(void); void bsp_led_deinit(void); void bsp_led_on(bsp_led_t ch); void bsp_led_off(bsp_led_t ch); void bsp_led_toggle(bsp_led_t ch); /******************************************************************************** * @file bsp_led.c * @author jianqiang.xue * @version V1.0.
[单片机]
名不副实,上汽互联网汽车将让我们大失所望?
由上汽和阿里联合打造的“全球首款量产 互联网汽车 ”将于4月18日正式亮相北京。刚看到这个概念的时候,羊铁君只想说“城会玩”,互联网车到底是个什么鬼。 据介绍,该车通过“数字赋能,汽车互联,高效动力总成”,实现人车网的高度统一。原来就是主打个上汽擅长的车载互联网技术而已,这名号打得,也是没sei了。 另外,该车还是国内唯一同时采用全 LED 大灯和泊车照明模式的自主品牌车型。 其“矩阵式全LED大灯”采用24颗LED芯片作为光源,“如意形日间行车灯”采用光导技术,寓意吉祥如意。真能掰扯,到底“如意”不“如意”,大家自己看图吧。  
[嵌入式]
LED点阵_单片机_普中
1 介绍 LED点阵屏由LED发光二极管组成,通过控制LED亮灭来显示文字、图片、动画、视频等,被广泛应用于公共场合做信息展示,如广告屏、公告牌等。 工作原理:每个LED放置在行线和列线的交叉点上,当对应的某一行电平拉高,某一列电拉低,则对应交叉点的LED就会点亮。 要实现显示图像或字体,只需考虑其显示方式。通过编程控制各显示点对应LED阳极和阴极端的电平,就可以有效的控制各显示点的亮灭。 2 功能要求:在点阵屏上点亮一个点(如左上第一个点),再显示字母和图形。 3 硬件设计 3.1 系统框图 3.2 LED点阵和74HC595原理图 4 软件设计 4.1 点亮指定的LED等 1 #include
[单片机]
<font color='red'>LED</font>点阵_单片机_普中
LED显示屏故障排除
1、电阻检测法,将万用表调到电阻档,检测一块正常的电路板的某点的到地电阻值,再检测另一块相同的电路板的同一个点测试与正常的电阻值是否有不同,若不同则就确定了问题的范围。 2、电压检测法,将万用表调到电压档,检测怀疑有问题的电路的某个点的到地电压,比较是否与正常值相似,否则确定了问题的范围。
[测试测量]
LED电子显示屏技术解决及全彩品质提升
 大屏显示在生活中的应用越来越广,对于大屏显示的技术也各有提高,目前,液晶显示凭其出色的显示效果被十分看好,但是在大屏显示中的拼接技术还没有达到无缝的水平,而LED小间距成功弥补了这一缺点,成功兴起。在液晶大屏无缝拼接技术成熟的时期,LED显示一跃而起,强占大屏显示市场。    LED电子显示屏技术问题解决   首先是高光效:对于led电子显示屏的光效可以说是节能效果重要指标,目前我国在光效效果上还有待加强,要真正要做到高光效,要从产业链各个环节上解决相关的技术问题,那么如何实现高光效呢?本文将具体争对外延、芯片,封装,灯具等几个环节要解决的技术问题探讨。   1.提高内量子效率和外量子效率。   2.提高封装出光效率及降低结温
[电源管理]
LED的四种特殊应用电路图解分析
众多电源工程师都不难熟悉,发光 二极管 是 LED 电路设计中经常用到的器件,这里我们介绍几种它的特殊用法,这对于条件有限的业余电子爱好者在电子制作中有时会很有帮助(见附图)。 下面我们以4个电路图来示意这几种特殊的应用方法。下图中图(a)是用发光二极管LED监视 电池 电压的应用电路。电路中稳压管DW的稳压值应选电池额定电压的70%左右。图(b)是LED用在220V、50Hz市电中,作为电器具工作指示或 保险丝 熔断指示。图(c)是将两只LED反向并联,用在移相式 可控硅 调压电路中代替双向触发二极管。图(d)是用在固定输出三端稳压电路中用以提升输出电压。并可兼作电源指示。 电路示意图
[电源管理]
<font color='red'>LED</font>的四种特殊应用电路图解分析
MSP430F149单片机的内部定时器A定时1秒钟实现LED闪烁
#include msp430x14x.h //********************函数声明****************** void InitClock(); //********************主函数******************** void main(void) { WDTCTL = WDTPW + WDTHOLD; // 关看门狗 InitClock(); // 初始化时钟 P1DIR |= BIT0; // 设P1.0为输出 CCTL0 = CCIE;
[单片机]
LED背光在汽车显示设计中的应用
引言   根据 Strategies Unlimited 公司的数据 (参见图 1),到 2012 年,高亮度 (HB) LED 的市场规模预计将达到 120 亿美元,到 2015 年,该市场将增长到 202 亿美元,年复合增长率为 30.6%。目前,用来给显示器进行背光照明的 LED 是这种前所未有的增长的主要驱动力。应用包括高清电视机(HDTV)、汽车显示器和大量手持式设备。为了保持这种令人惊叹的增长率,LED 不仅必须提供增强的可靠性、更低的功耗和更小 / 更扁平的外形尺寸,而且必须实现对比度、画面清晰度和色彩准确的明显改进。此外,汽车、航空电子设备和航海电子产品的显示器必须最大限度地实现所有这些改进,同时能接受从明亮
[电源管理]
<font color='red'>LED</font>背光在汽车显示设计中的应用
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved