platform驱动模型编程总结(基于mini2440平台的LED驱动)

发布者:心愿达成最新更新时间:2019-11-19 来源: 51hei关键字:platform  驱动模型  mini2440平台  LED驱动 手机看文章 扫描二维码
随时随地手机看文章

sysfs与platform的相关基础介绍可以参考博文【 sysfs   platform总线 】。
platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(他包含的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动。

在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:1:实现platform驱动 2:实现platform设备,然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很简单,如下所示。




platform驱动模型三个对象:platform总线、platform设备、platform驱动。
platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match()
platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregiste)
platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregiste)


简单介绍下platform驱动的工作过程:设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。




接下来讲解如下实现platform驱动与设备的详细过程。

实现platform驱动的详细过程




1:定义驱动实例 mini2440_led_platform_driver 
static struct platform_driver mini2440_led_platform_driver = {
.probe  = mini2440_led_probe,
.remove = __devexit_p(mini2440_led_remove),
.driver = {
.name = "mini2440_led_platform_device_driver",
.owner = THIS_MODULE,
}
};

2:实现驱动实例成员函数:probe  mini2440_led_probe
probe函数中要实现的功能包括:设备号的申请,字符设备内核对象cdev的定义、初始化、绑定file_operations,注册字符设备内核对象cdev,在/dev下动态创建设备文件。
3:platform模型驱动写字符设备仍要实现字符设备的核心结构file_operations,第一步的cdev要用到
将file_operations结构体中open、write、read等要用到的接口函数实现。
4:实现驱动实例成员函数:remove mini2440_led_remove
remove函数中要实现的功能与probe中的相反,进行相关设备与资源的注销。
【probe与remove函数其实对应前面用非platform机制写驱动的时候所实现的模块加载与卸载函数,而在platform机制中,模块的加载与卸载函数调用的是 设备或驱动的注册函数】
5:实现驱动的加载与卸载函数:
在加载函数中,调用驱动的注册函数,platform_driver_register(...);
在卸载函数中,调用驱动的卸载函数,platform_driver_unregister(...);

实现platform设备的详细过程


platform设备的实现有两种方法:
     1:最笨的一种:直接在内核源代码里面添加相关的资源代码,archarmmach-s3c2440mach-mini2440.c
     2:编写设备模块,用insmod命令加载该设备模块到platform总线上。
当然用第二种了,不过这两种方法的原理与要写的代码都是一样的,但是第一种不用自己注册,因为系统初始化的时候会将mach-mini2440.c中struct platform_device *mini2440_devices[] __initdata 设备数组中包含的所有设备注册到总线上。而第二种手动注册,一个字活。
1:定义设备与资源实例
static struct resource mini2440_led_resource[] = {
        [0] = {
                .start = 0x56000010,
                .end   = 0x56000010 + 12,
                .flags = IORESOURCE_MEM
        },
};
static struct platform_device mini2440_platform_device_led = {
        .name           = " mini2440_led_platform_device_driver ",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(mini2440_led_resource),
        .resource       =  mini2440_led_resource ,
        .dev            = {
              .release  = mini2440_led_platform_device_release,
        },
};

2:实现设备的成员函数release  
static void mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}
3:实现设备的加载与卸载函数:
在加载函数中,调用设备的注册函数,platform_device_register(...);
在卸载函数中,调用设备的卸载函数,platform_device_unregister(...);

多个设备的同时注册:platform_add_devices(struct platform_devices **devs,int num);
struct platform_devices **devs设备数组,num包含的设备数目

驱动与设备是否成功注册,我们都可以在/sys/bus/platform/devices(drivers)/下查看

下面是代码清单:
//////////////////////////////
设备模块:
#include
#include
#include
#include
#include
#include
#include
#include

static void mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}
static struct resource mini2440_led_resource[] = {
        [0] = {
                .start = 0x56000010,
                .end   = 0x56000010 + 12,
                .flags = IORESOURCE_MEM
        },
};
static struct platform_device mini2440_platform_device_led = {
        .name           = "mini2440_led_platform_device_driver",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(mini2440_led_resource),
        .resource       = mini2440_led_resource,
        .dev            = {
              .release  = mini2440_led_platform_device_release,
        },
};
static int __init mini2440_led_platform_device_init(void)
{
    printk("mini2440_led_platform_device add ok!n");
return platform_device_register(&mini2440_platform_device_led);
}
static void __exit mini2440_led_platform_device_exit(void)
{
    printk("mini2440_led_platform_device remove ok!n");
platform_device_unregister(&mini2440_platform_device_led);
}
MODULE_AUTHOR("xxxx");
MODULE_LICENSE("GPL");
module_init(mini2440_led_platform_device_init);
module_exit(mini2440_led_platform_device_exit);
//////////////////////////////
驱动模块:
#include
#include
#include
#include
#include
#include
#include
#include

#define GLOBAL_LED_MAJOR  250

static unsigned int global_led_major = GLOBAL_LED_MAJOR; 
static struct cdev *led_cdev = NULL; 
static struct class *led_class = NULL;

static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat = NULL; 
static volatile unsigned long *gpfup = NULL; 

static int mini2440_led_open(struct inode * inode,struct file * file)
{
printk("mini2440_open[kernel_space]n");
*gpfcon &=~((0x3<<0) | (0x3<<8) |(0x3<<10) |(0x3<<12)|(0x3<<14));
*gpfcon |= (0x1<<0) | (0x1<<8) |(0x1<<10) |(0x1<<12)|(0x1<<14);
return 0;
}
static ssize_t mini2440_led_read(struct file * file,const char __user * in,size_t size,loff_t * off)
{
printk("mini2440_read[kernel_space]n");
return 0;
}
static ssize_t mini2440_led_write(struct file * file,const char __user * in,size_t size,loff_t * off)
{
    int ret;
char ker_buf;
printk("mini2440_write[kernel_space]n");
ret = copy_from_user(&ker_buf,in,size);
printk("ker_buf =%dn",ker_buf);
if(ker_buf)
{
*gpfdat &=~((0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7));
       
*gpfdat |= (0x1<<0);
}
else
{
*gpfdat |=(0x1<<4)|(0x1<<5)|(0x1<<6)|(0x1<<7);
*gpfdat &= ~(0x1<<0);
}
return 0;
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.open  = mini2440_led_open,
.read  = mini2440_led_read,
.write = mini2440_led_write,
};
static int __devinit mini2440_led_probe(struct platform_device *pdev)
{
int ret;
int err;
dev_t devno;
struct resource *pIORESOURCE_MEM;
devno = MKDEV(global_led_major,0);
printk(KERN_ALERT"mini2440_led_probe!n");
if (devno) {
ret = register_chrdev_region(devno,1,"mini2440_led_platfor_driver");
} else {
ret = alloc_chrdev_region(&devno,0,1,"mini2440_led_platfor_driver");
global_led_major = MAJOR(devno);
}
if (ret < 0) {
return ret;
}
led_cdev = cdev_alloc();
cdev_init(led_cdev,&led_fops);
led_cdev->owner = THIS_MODULE;
err = cdev_add(led_cdev,devno,1);
led_class = class_create(THIS_MODULE,"mini2440_led_platfor_driver");
device_create(led_class,NULL,MKDEV(global_led_major,0),NULL,"platfor_driver_for_mini2440_led");
pIORESOURCE_MEM = platform_get_resource(pdev,IORESOURCE_MEM,0);
gpfcon = ioremap(pIORESOURCE_MEM->start,pIORESOURCE_MEM->end - pIORESOURCE_MEM->start);
gpfdat = gpfcon + 1;
gpfup  = gpfcon + 2;
if (err) {
printk(KERN_NOTICE"Error %d adding led_cdev",err);
return -1;
} else {
printk(KERN_NOTICE"platform_driver_for_mini2440_led init ok!n");
return 0;
}
}




static int __devexit mini2440_led_remove(struct platform_device *pdev)
{
    printk("mini2440_led_remove!n");
cdev_del(led_cdev);
iounmap(gpfcon);
unregister_chrdev_region(MKDEV(global_led_major,0),1);
device_destroy(led_class, MKDEV(global_led_major,0));
class_destroy(led_class);
return 0;
}
static struct platform_driver mini2440_led_platform_driver = {
.probe  = mini2440_led_probe,
.remove = __devexit_p(mini2440_led_remove),
.driver = {
.name = "mini2440_led_platform_device_driver",
.owner = THIS_MODULE,
}
};
static int __init mini2440_led_platform_driver_init(void)
{
    printk("platform_driver_for_mini2440_led initn");
return platform_driver_register(&mini2440_led_platform_driver);
}

static void __exit mini2440_led_platform_driver_exit(void)
{
    printk("platform_driver_for_mini2440_led exitn");
platform_driver_unregister(&mini2440_led_platform_driver);
}
MODULE_AUTHOR("xxx");
MODULE_LICENSE("GPL");
module_param(global_led_major,int,S_IRUGO);
module_init(mini2440_led_platform_driver_init);
module_exit(mini2440_led_platform_driver_exit);
///////////////////////
Makefiel

ifneq ($(KERNELRELEASE), )
obj-m  := mini2440_led_platform_driver.o
else 
KDIR := /home/tools/linux-2.6.32.2 
all:
make -C $(KDIR)  M=/linux_prg/platform_driver_device_module/driver_module/  modules  modules
clean:
rm -f *.ko  *.o  *.mod.o  *.mod.c  *.symvers 
endif

[1] [2]
关键字:platform  驱动模型  mini2440平台  LED驱动 引用地址:platform驱动模型编程总结(基于mini2440平台的LED驱动)

上一篇:sysfs platform总线
下一篇:程序的TRACE功能

推荐阅读最新更新时间:2024-11-01 09:00

崧盛电子IPO申请获受理 创业板或增LED驱动电源行业新兵
6月22日,深圳市崧盛电子股份有限公司(简称“崧盛股份”)创业板IPO申请获深交所受理。 据悉,崧盛股份于去年6月在新三板终止其股票挂牌。顶着市场竞争加剧、价格战趋于白热化以及肺炎疫情、中美贸易摩擦等多方问题形成压力,该公司踏上了创业板上市之路。面对这条路上的重重挑战,崧盛股份能否如愿以偿? 据招股书显示,崧盛股份是一家专注于中、大功率LED驱动电源产品的研发、生产和销售业务,是目前国内中、大功率LED驱动电源产品的主要供应商之一。公司产品主要供应下游LED 照明生产厂商用于制造中、大功率LED照明产品,终端产品主要应用于城市路桥、高速公路、隧道、机场等大型户外LED照明设施,以及工业厂房、仓库等LED工业照明设施,同时正向植
[手机便携]
如何利用升压转换器来设计易于编程的LED驱动
  大多数采用白色发光二极管(W LED )背 光显示 器的 便携式 产品同时还需要辅助的 LED照明 。一般需要两个 IC :一个感性升压转换器,使背光LED获得最大效率(》80%);一个电荷泵,允许独立控制各辅助LED。此外,每个IC都需要一个可编程的电流吸收器来进行 亮度 控制或者混色,这会导致成本和复杂度迅速上升。本篇设计技巧介绍如何将单个可编程 LED驱动 器与一个低成本升压转换器结合在一起,实现灵活高效且易于编程的解决方案。图1 显示 使用升压转换器ADP1612(见图 2)和并行LED驱动器ADP8860(见图 3)的实现方案。      图1. 升压转换器ADP1612和LED驱动器ADP8860实现背光和辅助
[电源管理]
如何利用升压转换器来设计易于编程的<font color='red'>LED驱动</font>器
选购LED驱动电源要注意什么
LED驱动 电源是把电源供应转换为特定的电压电流以驱动 LED 发光的电源转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。而LED驱动电源的输出则大多数为可随LED正向压降值变化而改变电压的恒定电流源。   购买驱动电源有两点最重要   一:要认准电容的品牌,其中红宝石(Rubycom日本)特别知名,恒英(HY国产)比较常见,当然价格要比其他不适品牌电容要贵几毛钱。   二:是否带IC控制芯片,因为IC控制芯片具有止短路,过压,过载,过温等保护功能。   只要这两点到位的驱动电源,质量已经非常好了。   网的用电的特点,led特性的要求以及相关 L
[电源管理]
采用原边反馈的LED驱动设计
LED驱动电源目前正朝着高功率因数、高输出电流精度、高效率、高可靠性和低成本、小尺寸方向发展,因此,带PFC(功率因数校正)的原边电流反馈准谐振技术方案已渐渐成为市场主流。现有的照明用LED驱动电源目前标准仍有待统一,但PFC在全电压范围内做到0.95以上、输出电流精度做到 3%以内、效率做到90%以上、启动时间在0.5s以内、输出电压纹波小于5%等,已经成为一些业内领先的芯片供应商设置的技术竞争门槛。 要达到上述这些要求,市场必然要求有一款功能全面、性能优异的芯片,同时,这也对系统设计者提出了更高的要求。本文从芯片和系统两个层面,详细分析了影响上述性能的原因和提高各项性能的手段,并给出了实验波形和数据。无论对于LED驱动芯片设
[电源管理]
采用原边反馈的<font color='red'>LED驱动</font>设计
利用高调光比LED驱动器设计大功率照明方案
  LED照明解决方案广受欢迎的原因之一,是LED能通过简单的电流控制来获得很宽的调光范围,比如汽车仪表盘和飞机驾驶员座舱等环境照度可能非常低的应用场合就需要非常宽的PWM调光范围。凌力尔特公司的LT3478和LT3478-1是单芯片升压型DC/DC转换器,能在很宽的可设置范围内利用恒定电流来驱动高亮度LED。除了可选的10:1模拟调光范围之外,LT3478和LT3478-1还具有3000:1的PWM调光范围,可以保持LED的色彩。   LT3478和LT3478-1的易用性很好,并具有旨在优化性能、可靠性、外形尺寸和总成本的可编程功能。这些器件可工作在升压、降压和降升压型LED驱动器拓扑结构中。它们所能提供的LED电流大小取决
[电源管理]
利用高调光比<font color='red'>LED驱动</font>器设计大功率照明方案
侧光式LED背光技术全面分析 能否成为下一代主流?
  侧光式把 LED 放置于背光的上下两边,布局的调整大大减少了LED的使用,从而减低生产成本及能耗,对厂家及消费者都有莫大好处。但其不可局部调光的弱点,促使了可局部调光矩阵式背光方案的诞生,这是一种侧入与直下相结合的一种背光技术。   回想电视发展历史,家中的传统CRT电视一定是大家第一时间想到的产物。踏入2000年, 等离子电视 开始出现;2003年左右, CCFL (冷荧光灯管光源) LCD TV开始面世及普及;直至2010年, LED背光 技术逐渐取代CCFL。大多数行内外人均应为,LED将于两至三年间可完全取代CCFL作为主要电视的背光源。   大多数巿民都知道LED背光有好几种优势,当中包括电视厚度变薄、能耗降低、色
[电源管理]
侧光式LED背光技术全面分析 能否成为下一代主流?
555定时器取代LED驱动器的uP控制
  本文详细介绍如何使用便宜的555定时器,在一些不需要 LED 驱动器全部功能的应用中,代替微处理器对专用 LED 驱动器实施控制。这样做可让用户在降低总系统成本的同时,维持 LED 驱动器的恒定电流。   专用LED驱动器常常被设计为微处理器控制型,旨在实现诸如模拟或脉宽调制(PWM) LED 电流控制、每个 LED 的独立控制、LED 状态和故障信息读取等特性。对于一些仅要求恒定 LED 电流的应用(例如:LED 照明或者发光)来说,可能不需要这些高级特性。在这些应用中,诸如 TLC555 的 555 定时器可以代替微处理器,从而在实现 LED 电流精确控制的同时降低系统成本,其与输入电压、温度和 LED 正向压降无关。
[电源管理]
555定时器取代<font color='red'>LED驱动</font>器的uP控制
LT3799:隔离型反激式LED驱动
LT®3799 是一款具功率因数校正功能的隔离型反激式控制器,专为驱动 LED 而设计。该控制器采用临界导通模式,因而允许使用一个小的变压器。这款控制器运用一种新颖的电流检测方案,能够向副端输送经过良好调节的电流,而无需采用一个光耦合器。此器件内置了一个强大的栅极驱动器,用于驱动一个外部高电压 MOSFET。LT3799 采用了一个板上乘法器,通常可实现 0.97 的功率因数。FAULT 引脚负责告知开路和短路 LED 情况。 LT3799 运用微功率迟滞启动,以在采用离线输入电压时实现高效运作,并利用第三个绕组来为器件供电。一个内部 LDO 负责为器件的内部电路和栅极驱动器提供经过良好调节的电源。 特点 ●具极少外部组件的
[电源管理]
LT3799:隔离型反激式<font color='red'>LED驱动</font>器
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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