驱动程序实例(二):LED设备驱动程序( platform + /sys接口)

发布者:zeta16最新更新时间:2025-01-07 来源: cnblogs关键字:驱动程序  LED设备  platform 手机看文章 扫描二维码
随时随地手机看文章

正文


结合之前对Linux内核的platform总线 ,以及对Linux内核的LED设备的驱动框架的分析,本文将编写基于platform总线与/sys接口的LED设备的实例代码并对其进行分析。


platform总线分析,详见Linux platform驱动模型。


字符设备的cdev接口分析,详见Linux字符设备驱动框架(一):Linux内核的LED设备驱动框架。


硬件接口:


  CPU:s5pv210;


  LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;


  LED的工作方式:低电平亮,高电平灭。


使用Linux内核的LED驱动框架之前,需确保Linux内核支持LED驱动框架。进入Linux内核的配置界面menuconfig进行设置,具体配置如下:


  Device Drivers  --->


    [*] LED Support  --->    


本文将以两种形式编写LED驱动代码:


(1)将led_device和led_driver分别写成两个单独的文件模块;(这种做法适合学习使用)


(2)将led_device集成至Linux内核的/arch/arm/mach-s5pv210/mach-x210.c中,再将led_driver编写为独立模块。(这种做法更接近于实战的驱动程序,更好的使用设备树)


1.第一种写法


1.1 led_device.c

本文将设备信息写成一个模块的形式,需要时加载该模块即可。


#include

#include

#include

#include

#include


//定义并初始化LED设备的相关资源

static struct resource led_resource = 

{

    .start = 0xE0200240,

    .end = 0xE0200240 + 8 - 1,

    .flags = IORESOURCE_MEM,

};


//定义并初始化LED设备信息

static struct platform_device led_dev = 

{

    .name = 'led',             //设备名称

    .id = -1,                  //设备数量,-1表示只有一个设备

    .num_resources = 1,        //资源数量

    .resource = &led_resource, //资源指针

    .dev = 

    {

        .release = led_release,

    },

};


//注册LED设备

static int __init led_device_init(void)

{

    return platform_device_register(&led_dev);

}


//注销LED设备

static void __exit led_device_exit(void)

{

    platform_device_unregister(&led_dev);

}


module_init(led_device_init);

module_exit(led_device_exit);


MODULE_AUTHOR('Lin');

MODULE_DESCRIPTION('led device for x210');

MODULE_LICENSE('GPL');


1.2 led_driver.c

led_driver_init():模块加载函数

  platform_driver_register()将驱动对象模块注册到平台总线

  led_probe()探测函数,提取相应的信息

    platform_get_resource()获取设备资源

    request_mem_region()、ioremap()虚拟内存映射

    readl()、write()初始化硬件设备

    kzalloc()申请led_classdev内存

    led_classdev_register()注册LED设备


#include

#include

#include

#include

#include

#include

#include

#include


//定义GPIO_J0寄存器变量

typedef struct GPJ0REG

{

    volatile unsigned int gpj0con;

    volatile unsigned int gpj0dat;

}gpj0_reg_t;


static gpj0_reg_t *pGPIOREG = NULL;

static struct led_classdev *led_cdev = NULL;


//LED设备实际的硬件操作函数

void led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness)

{

    int reg_value = 0;

    

    //向LED设备属性文件brightness写入0,LED设备灭

    if (brightness == LED_OFF)

    {

        reg_value = readl(&(pGPIOREG->gpj0dat)); 

        reg_value |= (1 << 3) | (1 << 4) | (1 << 5);

        writel(reg_value, &(pGPIOREG->gpj0dat));

    }

    //否则,LED设备亮

    else

    {

        reg_value = readl(&(pGPIOREG->gpj0dat)); 

        reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));

        writel(reg_value, &(pGPIOREG->gpj0dat));

    }    

}


static int led_probe(struct platform_device *pdev)

{

    struct resource *res_led = NULL;

    int ret = -1;

    int reg_value = 0;

    

    /********************************申请资源*********************************/

    //获取资源

    res_led = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (!res_led) 

    {

        return -ENOMEM;

    }

    

    //动态内存映射

    if (!request_mem_region(res_led->start, resource_size(res_led), 'GPIOJ0'))

    {

        return -EBUSY;

    }

    

    pGPIOREG = ioremap(res_led->start, resource_size(res_led));

    if (pGPIOREG ==  NULL) 

    {

        ret = -ENOENT;

        goto ERR_STEP;

    }

    

    /********************************设备初始化********************************/

    //初始化资源,设置GPIO为输出模式

    reg_value = readl(&(pGPIOREG->gpj0dat)); 

    reg_value |= (1 << (3*4)) | (1 << (4*4)) | (1 << (5*4));

    writel(reg_value, &(pGPIOREG->gpj0dat));

    

    /********************************创建接口**********************************/

    //申请led_classdev内存

    led_cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);

    if (led_cdev == NULL)

    {

        ret = -ENOMEM;

        goto ERR_STEP1;

    }

    

    led_cdev->name = pdev->name;

    led_cdev->brightness_set = led_brightness_set;

    

    //注册LED设备

    ret = led_classdev_register(NULL, led_cdev);

    if (ret) 

    {

        goto ERR_STEP2;

    }

    

    return 0;

    

    /******************************倒映式错误处理********************************/

ERR_STEP2:

    kfree(led_cdev);

    

ERR_STEP1:

    iounmap(pGPIOREG);    

    

ERR_STEP:

    release_mem_region(res_led->start, resource_size(res_led));

            

    return ret;

        

}

    

static int led_remove(struct platform_device *pdev)

{

    led_classdev_unregister(led_cdev); //注销LED设备

    iounmap(pGPIOREG);                   //释放内存

    kfree(led_cdev);                   //释放内存

    

    return 0;

}


//定义并初始化LED驱动信息

static struct platform_driver led_drv = 

{

    .driver = 

    {

        .name  = 'led',

        .owner = THIS_MODULE,

    },

    .probe = led_probe,

    .remove = led_remove,

};


static int __init led_driver_init(void)

{

    return platform_driver_register(&led_drv);

}


static void __exit led_driver_exit(void)

{

    platform_driver_unregister(&led_drv);

}


module_init(led_driver_init);

module_exit(led_driver_exit);


MODULE_AUTHOR('Lin');

MODULE_DESCRIPTION('led driver for x210');

MODULE_LICENSE('GPL');


2.第二种写法

2.1 device

在/kernel/arch/arm/mach-s5pv210/include/mach目录下,建立一个leds_gpio.h文件,并填充如下内容。


#ifndef __ASM_ARCH_LEDSGPIO_H

#define __ASM_ARCH_LEDSGPIO_H 'leds-gpio.h'


#define S3C24XX_LEDF_ACTLOW    (1<<0)        /* LED is on when GPIO low */

#define S3C24XX_LEDF_TRISTATE    (1<<1)      /* tristate to turn off */


//定义一个LED设备的数据结构

struct s3c24xx_led_platdata 

{

    unsigned int     gpio;

    unsigned int     flags;

    char            *name;

    char            *def_trigger;

};

#endif


在/kernel/arch/arm/mach-s5pv210/mach-x210.c下,添加如下内容,并添加对leds_gpio.h的包含。


/* LEDS */


static struct s5pv210_led_platdata s5pv210_led1_pdata = {

    .name        = 'xled1',

    .gpio        = S5PV210_GPJ0(3),

    .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,

    .def_trigger = 'heartbeat',

};


static struct s5pv210_led_platdata s5pv210_led2_pdata = {

    .name        = 'xled2',

    .gpio        = S5PV210_GPJ0(4),

    .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,

    .def_trigger = 'heartbeat',

};


static struct s5pv210_led_platdata s5pv210_led3_pdata = {

    .name        = 'xled3',

    .gpio        = S5PV210_GPJ0(5),

    .flags       = S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,

    .def_trigger = 'heartbeat',

};


static struct platform_device s5pv210_led1 = {

    .name      = 's5pv210_led',

    .id        = 1,

    .dev       = {

        .platform_data    = &s5pv210_led1_pdata,

    },

};


static struct platform_device s5pv210_led2 = {

    .name      = 's5pv210_led',

    .id        = 2,

    .dev       = {

        .platform_data    = &s5pv210_led2_pdata,

    },

};


static struct platform_device s5pv210_led3 = {

    .name      = 's5pv210_led',

    .id        = 3,

    .dev       = {

        .platform_data   = &s5pv210_led3_pdata,

    },

};


将LED设备信息集成至smdkc110_devices,内核初始化时smdkc110_devices中的设备将被注册进内核。


static struct platform_device *smdkc110_devices[] __initdata = 

{

    ......

    &s5pv210_led1,

    &s5pv210_led2,

    &s5pv210_led3,

}; 


2.2 driver


#include

#include

#include

#include

#include

#include


#include

#include

#include


#define X210_LED_OFF    1            // X210中LED是正极接电源,负极节GPIO

[1] [2]
关键字:驱动程序  LED设备  platform 引用地址:驱动程序实例(二):LED设备驱动程序( platform + /sys接口)

上一篇:linux驱动模型——platform(2)
下一篇:驱动程序实例(四):按键驱动程序(platform + input子系统 + 外部中断方式)

推荐阅读最新更新时间:2026-03-25 12:44

字符设备驱动程序--LED驱动
编写驱动程序需要编写那些代码: 1、硬件相关的驱动程序 2、Makefile的编译程序 3、还需要编写一个相关的测试程序 比如说:一个摄像头驱动程序 1、驱动程序的编写,需要编写一些硬件相关的操作,编译Makefile 2、安装、运行、卸载驱动程序(insmod ***、。./*** 、remod *** )。 3、使用这个驱动程序:需要一个测试程序,如QQ(测试程序)打开摄像头。 编写驱动程序框架: APP:(测试程序) open read write ......... -----------------------------------------------------
[单片机]
S3C2440 字符设备驱动程序LED驱动程序_操作LED(四)
第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED(四) 写一个点LED的驱动: 一、框架 二、完善、硬件的操作: a.看原理图、确定引脚; b.看2440手册; c.写代码: 单片机:操作物理地址 驱动:用ioremap函数来映射虚拟地址 ★★★ void *ioremap(unsigned long phys_addr, unsigned long size) phys_addr:要映射的物理地址 size:要映射的长度,单位是字节 ioremap是以页为单位进行映射,你的长度写为4,16,32等等,都会映射到4096字节的空间。 ★★★ 下面的代码中:
[单片机]
S3C2440 字符<font color='red'>设备</font><font color='red'>驱动程序</font>之<font color='red'>LED</font><font color='red'>驱动程序</font>_操作<font color='red'>LED</font>(四)
S3C2440 字符设备驱动程序LED驱动程序_编写编译(二)
app: open , read , write 驱动: led_open,led_read,led_write 驱动框架: 一、写出:led_open,led_read 二、怎么告诉内核? a、定义一个file_operations b、把这个结构体告诉内核: register_chrdev(major,name,file_operations) c、谁来调用它 (register_chrdev) 驱动的入口函数 first_drv_init d、修饰:module_init(first_drv_init) (怎么知道是哪个入口函数) module_init:(入口函数)定义了
[单片机]
S3C2440 字符<font color='red'>设备</font><font color='red'>驱动程序</font>之<font color='red'>LED</font><font color='red'>驱动程序</font>_编写编译(二)
09-S3C2440驱动学习(三)嵌入式linux-platform平台总线驱动程序及分离分层构建驱动框架
简介Platform: 内核中有IIC总线、PCI总线、串口总线、SPI总线、can总线、单总线等,一些设备可以挂载在这些总线上,然后通过总线的match进行设备和驱动的匹配。但是有些设备并不属于这些总线,于是引入了虚拟总线,也就是Platform总线,对于的设备叫做Platform设备,对于的驱动叫做Platform驱动。 平台设备框图: Platform总线: struct bus_type platform_bus_type = { .name = platform , .dev_attrs = platform_dev_attrs, .match = platform_match,
[单片机]
09-S3C2440驱动学习(三)嵌入式linux-<font color='red'>platform</font>平台总线<font color='red'>驱动程序</font>及分离分层构建驱动框架
linux下的misc设备led示例
1.开发环境 Cpuinfo: Processor : ARMv7 Processor rev 2 (v7l) BogoMIPS : 998.15 Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 CPU implementer : 0x41 CPU architecture : 7 CPU variant : 0x2 CPU part : 0xc08 CPU revision : 2 Hardware : SMDKV210 Bordinfo:
[单片机]
linux下的misc<font color='red'>设备</font><font color='red'>led</font>示例
LED照明系统在消防照明设备上的应用
LED照明系统在天津爆炸灭火工作中有着不可或缺的重要作用。其实,不仅是这次爆炸事故,消防员经常面临浓烟弥漫的火灾内部现场、夜间伸手不见五指的深山老林等复杂黑暗的环境,而使用高性能、可靠的照明设备是消防员完成灭火救援、登山搜救任务的重要保障。 消防部队照明设备现状 目前消防部队应用的照明设备主要有佩戴式防爆头顶灯、手提式强光照明灯、移动式升降照明灯等,光源基本采用单珠自光LED、卤素灯、氙气灯等。通过对基层调研,现就基层照明设备使用现状总结如下: (1)单珠白光LED光源在火灾浓烟环境下光源穿透力较差,且单珠LED可靠性不高。 (2)卤素灯、氙气灯等,采用黄色光源,因功率大、耗电量高、使用时间短,且该类光源结构抗冲击性
[嵌入式]
<font color='red'>LED</font>照明系统在消防照明<font color='red'>设备</font>上的应用
使用简单的电子设备LED制作无焰电子蜡烛
蜡烛自古以来就非常有用,甚至在爱迪生提出灯泡的想法之前,它就已经在夜间指导人类。今天,从教堂到厨房,蜡烛不仅可以在需要时提供照明,还可以增加美观并提供温暖的感觉。虽然普通蜡烛工作正常,但它们很快就会融化,使这个地方变得很脏,有时如果无人看管,也会导致火灾危险。因此,在本教程中,我们将使用一些简单的电子设备和 LED来制作无焰电子蜡烛。此外,这款智能蜡烛将在夜间或黑暗中自动打开,并在白天自动关闭。 所需材料 LM358集成电路 LDR(光敏电阻) 1M和1K电阻 发光二极管 10K锅 12V 母头直流电源插孔和 12V 适配器 卡片纸和穿孔板 LM358 – 运算放大器比较器 该电路背后的大脑是LM358 IC,它在此特定设计中
[嵌入式]
使用简单的电子<font color='red'>设备</font>和<font color='red'>LED</font>制作无焰电子蜡烛
07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-LED字符设备驱动
一、嵌入式linux字符设备驱动框架 写应用程序的人 不应该去看电路图,但是如何操作硬件呢:调用驱动程序里的open,read,write等来实现。 C库里实现了 open 、read、write上层函数 调用open等:swi val—引发一个异常中断,进入内核异常处理函数。 系统调用接口:根据发生中断的原因,调用处理函数(sys_open,sys_read等sys_open等函数会执行与open相关各种初始化函数,通知调用自己写好的open函数,这里注意应用程序的open不仅仅是调用驱动中的open,其他函数类似,是调用sys_open,sys_open里包含了驱动中实现的open)。 sys_open:根据调
[单片机]
07-S3C2440驱动学习(一)嵌入式linux字符<font color='red'>设备</font>驱动-<font color='red'>LED</font>字符<font color='red'>设备</font>驱动
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

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