正文
结合之前对Linux内核的platform总线 ,以及对Linux内核的LED设备的驱动框架的分析,本文将编写基于platform总线与/sys接口的LED设备的实例代码并对其进行分析。
platform总线分析,详见Linux platform驱动模型。
字符设备的cdev接口分析,详见Linux字符设备驱动框架(一):Linux内核的LED设备驱动框架。
硬件接口:
CPU:s5pv210;
LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;
使用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
上一篇:linux驱动模型——platform(2)
下一篇:驱动程序实例(四):按键驱动程序(platform + input子系统 + 外部中断方式)
推荐阅读最新更新时间:2026-03-25 12:44
- 用于 7VIN 至 16VIN、1.5V 和 1.2V 输出的 LTM4628EV DC/DC 模块稳压器的典型应用电路
- 使用 Analog Devices 的 LTC3728LIGN 的参考设计
- DER-406 - 适用于 A19 灯的 5.76 W 高 PF 非隔离降压-升压型 TRIAC 调光 LED 驱动器
- ADR5045B 5V 输出精密微功率并联模式电压基准的典型应用
- LT3970EDDB-3.42 2.5V 降压转换器的典型应用
- MC78M08BDTG 8V 电流调节器的典型应用
- LT1021DCN8-5 精密电压基准的典型应用
- DER-282 - 100W, 扁平(11 mm), LLC DC-DC转换器
- REF193 低压差开尔文连接电压基准的典型应用电路
- LT3088EM 线性稳压器用于添加软启动的典型应用



Linux技术手册
智能机械臂
现代雷达系统的信号设计
BFR340T






京公网安备 11010802033920号