[arm驱动]Platform设备驱动

简介:

Tip:红色字体为简要部分

《[arm驱动]Platform设备驱动》涉及内核驱动函数五个,内核结构体三个,分析了内核驱动函数四个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序或内核驱动二个


想了解platform总线管理方式的原理 参考[arm驱动]Platform总线原理


前言扩展
1、sysfs文件系统
设备模型sysfs是2.6内核新引入的特征。设备模型提供了一个独立的机制专门来表示设备,并描述其在系统中的拓扑结构。
在2.4内核中,设备的信息放在/proc中。
而在2.6内核,内核把设备相关的信息归类在新增加sysfs文件系统,并将它挂载到/sys目录中,把设备信息归类的同时,让用户可以通过用户空间访问。
2、sys中的目录:
block:用于管理块设备,系统中的每一个块设备会在该目录下对应一个子目录。
bus:用于管理总线,每注册一条总线,在该目录下有一个对应的子目录。(重要)
其中,每个总线子目录下会有两个子目录:devices和drivers。

devices包含里系统中所有属于该总线的的设备。(重要)
drivers包含里系统中所有属于该总线的的驱动。(这个好像在linux2.6以上版本剔除了)
class:将系统中的设备按功能分类。(重要)
devices:该目录提供了系统中设备拓扑结构图。
dev:该目录已注册的设备节点的视图。
kernel:内核中的相关参数。
module:内核中的模块信息。
fireware:内核中的固件信息。
Fs:描述内核中的文件系统。
3、总线,设备,驱动对应的定义struct bus_type,devices,drivers;
4、总线:总线不是字符设备、块设备和网络设备并列的概念,它是一种"设备"和"驱动"的管理者,总线通过分别遍历"设备(device)"和”驱动(driver)"链表如果发现name是一样的就将调device_bind_driver将name对应的设备(device)和驱动(driver)绑定好。
5、总的来说:对于总线型设备驱动,我们只要关心如何将设备(device)和驱动(driver)挂载到总线上就行了;剩下的工作就交给了内核了:设备(device)如果配备驱动(driver)由内核的总线来管理
6、总线的工作方式案例:以USB为例(u盘、键盘、鼠标设备都算usb)
a)古老的方式:
    先有device,每一个要用的device在计算机启动之前就已经插好了,插放在它应该在的位置上,然后计算机启动,然后操作系统开始初始化,总线开始扫描设备,每找到一个设备,就为其申请一个struct device结构,并且挂入总线中的devices链表中来;然后每一个驱动程序开始初始化,开始注册其struct device_driver结构,然后它去总线的devices链表中去寻找(遍历),去寻找每一个还没有绑定driver的设备,即struct device中的struct device_driver指针仍为空的设备,然后它会去观察这种设备的特征,看是否是他所支持的设备,如果是,那么调用一个叫做device_bind_driver的函数,然后他们就结为了秦晋之好.
b)现在的方式:
   device可以在任何时刻出现,而driver也可以在任何时刻被加载,所以,出现的情况就是,每当一个struct device诞生,它就会去bus的drivers链表中寻找自己的另一半,反之,每当一个一个struct device_driver诞生,它就去bus的devices链表中寻找它的那些设备.如果找到了合适的,那么ok,和之前那种情况一下,调用device_bind_driver绑定好.如果找不到,没有关系,等待
7)platform总线是bus总线的一条子总线,可以理解为platform继承了bus,platform是bus的子类

一、platform设备驱动
platform继承bus,文件位于/sys/bus/platform下;/sys//bus/platform/device文件夹和/sys/bus/platform/driver目录下的文件分别是对应platform总线的"设备(device)“驱动(driver)"。与传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用资源,提高了代码的安全性和可移植性。 

1.一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,

但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。
基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为 platform_driver。

2.注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,
例如,在 S3C6410处理器中,把内部集成的I2C、RTC、SPI、LCD、看门狗等控制器都归纳为platform_device,而它们本身就是字符设备。

二、设备(device)驱动(driver)相关结构体
结构体一)platform设备(device)结构体platform_device

1
2
3
4
5
6
7
struct  platform_device {      //设备结构体信息
         const  char       * name;      //该平台设备的名称
         u32             id;       //设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。
         struct  device   dev;     ///结构体中内嵌的device结构体
         u32             num_resources;     //设备使用的资源的数目
         struct  resource * resource;          //定义平台设备的资源
};


结构体二)device结构体中的resources

1
2
3
4
5
6
7
struct  resource {
         resource_size_t start;       //定义资源的起始地址
         resource_size_t end;        //定义资源的结束地址
         const  char  *name;                    //定义资源的名称
         unsigned  long  flags;            //定义资源的类型,例如MEM, IO ,IRQ, DMA类型
         struct  resource *parent, *sibling, *child;      //资源链表指针
};



结构体三)platform 驱动(driver)结构体

1
2
3
4
5
6
7
8
9
10
11
struct  platform_driver {
         int  (*probe)( struct  platform_device *);     
//driver和device注册之后,会触发平台设备和驱动的匹配函数platform_match,匹配成功,则会调用平台设备驱动的probe()函数
         int  (* remove )( struct  platform_device *);         //删除该设备
         void  (*shutdown)( struct  platform_device *);     //关闭该设备
         int  (*suspend)( struct  platform_device *, pm_message_t state); //电源管理挂起
         int  (*suspend_late)( struct  platform_device *, pm_message_t state); //电源管理挂起
         int  (*resume_early)( struct  platform_device *); //电源管理唤醒
         int  (*resume)( struct  platform_device *); //电源管理唤醒
         struct  device_driver driver;                          //老设备驱动,定义在include/linux/device.h中
};

三、设备(device)驱动(driver)相关定义、注册函数
1设备结构体定义和注册
   1)、设备platform_device变量的定义
   方式a)使用platform_device结构体数组定义
   单个设备结构platform_device体定义

1
2
3
4
static  struct  platform_device platformthird_dev_device = {   //单个设备结构体定义
     .name =  "platformthird_dev" ,
     .id = -1,
};

   多个设备结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
static  struct  platform_device platformthird_dev_device[] = {   //多个设备结构体定义
     [0] = {.name =  "platformthird_dev" ,
     .id = 0,
     },
     [1] = {.name =  "platformthird_dev" ,
     .id = 1,
     },
     [2] = {.name =  "platformthird_dev" ,
     .id = 2,
     },
     //................
};


函数一)b)动态定义方式一个platform_device

1
struct  platform_device * platform_device_alloc( const  char  * name, unsigned  int  id)

   如:

1
2
struct  platform_device *platformthird_dev_device = platform_device_alloc( "platform_led" , -1);
//-1表示只有一个设备



   2)、设备(device)注册函数
函数二)单个设备(device)的注册:platform_device_register(struct platform_device * pdev);
内核源码一)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int  platform_device_register( struct  platform_device *pdev)
{
     device_initialize(&pdev->dev); //dev初始化
     arch_setup_pdev_archdata(pdev);
     return  platform_device_add(pdev); //加入到dev链表
}
//int platform_device_add(struct platform_device *pdev)内核源码
int  platform_device_add( struct  platform_device *pdev)
{
     int  i, ret = 0;
     if  (!pdev)
         return  -EINVAL;
     if  (!pdev->dev.parent)
         pdev->dev.parent = &platform_bus;
     pdev->dev.bus = &platform_bus_type;
         //id = -1时的操作
     if  (pdev->id != -1)
         snprintf(pdev->dev.bus_id, BUS_ID_SIZE,  "%s.%u" , pdev->name, pdev->id);
     else
         strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
     for  (i = 0; i < pdev->num_resources; i++) {
         struct  resource *p, *r = &pdev->resource[i];
         if  (r->name == NULL)
             r->name = pdev->dev.bus_id;
         p = r->parent;
         if  (!p) {
             if  (r->flags & IORESOURCE_MEM)
                 p = &iomem_resource;
             else  if  (r->flags & IORESOURCE_IO)
                 p = &ioport_resource;
         }
         if  (p && insert_resource(p, r)) {
             printk(KERN_ERR
                    "%s: failed to claim resource %d\n" ,
                    pdev->dev.bus_id, i);
             ret = -EBUSY;
             goto  failed;
         }
     }
     pr_debug( "Registering platform device '%s'. Parent at %s\n" ,
          pdev->dev.bus_id, pdev->dev.parent->bus_id);
     ret = device_add(&pdev->dev);
     if  (ret == 0)
         return  ret;
  failed:
     while  (--i >= 0)
         if  (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
             release_resource(&pdev->resource[i]);
     return  ret;
}

函数三)多个设备(device)的注册:        

1
platform_add_devices( struct  platform_device **devs,  int  num)

内核源码三)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//platform_add_devices调用了platform_device_register
int  platform_add_devices( struct  platform_device **devs,  int  num)
{
     int  i, ret = 0;
     for  (i = 0; i < num; i++) {
         ret = platform_device_register(devs[i]);
         if  (ret) {
             while  (--i >= 0)
                 platform_device_unregister(devs[i]);
             break ;
         }
     }
     return  ret;
}

   如:

1
platform_add_devices(platformthird_dev_device,  ARRAY_SIZE(platformthird_dev_device));

2、驱动(driver)结构体定义和注册
   a)platform_driver结构体的定义如:

1
2
3
4
5
6
7
static  struct  platform_driver platformthird_dev_driver = {
     .probe = platformthird_dev_probe,
     . remove  = platformthird_dev_remove,
     .driver = {
     .name =  "platformthird_dev" , //名字要和设备(device)的名字一样
     .owner = THIS_MODULE,
     }

函数四)platform_driver结构体的注册

1
platform_driver_register( struct  platform_driver *drv)

   如:

1
platform_driver_register(&platformthird_dev_driver)

内核源码四)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int  platform_driver_register( struct  platform_driver *drv)
{
       drv->driver.bus =&platform_bus_type;
//总线platform_bus_type就是指platform总线,详细见(五、platform总线管理方式的原理)
/*设置成platform_bus_type这个很重要,因为driver和device是通过bus联系在一起的,
            具体在本例中是通过 platform_bus_type中注册的回调例程和属性来是实现的,
            driver与device的匹配就是通过 platform_bus_type注册的回调例程platform_match ()来完成的。
*/
if (drv->probe)
              drv-> driver.probe = platform_drv_probe;
if (drv-> remove )
              drv->driver. remove  = platform_drv_remove;
if (drv->shutdown)
              drv->driver.shutdown = platform_drv_shutdown; //电源设备
       return  driver_register(&drv->driver);
//注册驱动,将drv的driver添加到总线的driver链表中
}

函数五)int (*probe)(struct platform_device *);//driver被insmod后,会触发平台设备和驱动的匹配函数platform_match匹配成功,则会调用平台设备驱动的probe()函数;详细看五、platform总线管理方式的原理。

driver结构体几个函数的运行顺序

插入driver==>触发平台设备和驱动的匹配函数platform_match匹配,成功则调用probe()==>当device被卸载时就会调用remove

四、实例
实例一)实验一、只添加一个名为name的驱动(driver)到总线上,此时只创建名称为name驱动文件夹/sys/bus/platform/driver/name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
  *本程序只涉及platform的driver(驱动);未涉及device。
  */
     #include <linux/device.h>
     #include <linux/module.h>
     #include <linux/kernel.h>
     #include <linux/init.h>
     #include <linux/string.h>
     #include <linux/platform_device.h>
     MODULE_LICENSE( "GPL" );
                                                                                                                                                                                                                                                                                                                    
     static  int  test_probe( struct  platform_device *dev) //
     {
         printk( "driver say the driver found the device\n" );
         return  0;
     }
     static  int  test_remove( struct  platform_device *dev)
     {
         printk( "driver say the device is polled out\n" );
         return  0;
     }
//platform 总线相关文件挂载/sys/bus/platform/路径下
     static  struct  platform_driver test_driver = { //driver是驱动的意思,相关文件在/sys/bus/platform/drivers
         .probe = test_probe, //注册时要执行的函数
         . remove  = test_remove, //注销时会执行的函数
         .driver = {
             .owner = THIS_MODULE,
             .name =  "platform_dev" , //会在"/sys/bus/platform/drivers"下创建platform_dev文件夹
         },
     };
     static  int  __init test_driver_init( void )
     {
             /*注册平台驱动*/
         return  platform_driver_register(&test_driver);
     }
     static  void  test_driver_exit( void )
     {
         platform_driver_unregister(&test_driver);
     }
     module_init(test_driver_init);
     module_exit(test_driver_exit);


实例二)实验二、只添加一个名为names(driver)到总线上,此时只创建名称为name驱动文件夹/sys/bus/platform/drivers/name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/device.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
static  void  platformthird_dev_release( struct  device * dev){
     printk( "device say the device is release\n" );
return ;
}
static  struct  platform_device platformthird_dev_device = {   //添加设备结构体
     .name =  "platform_dev" ,
     .id = -1,
     .dev = {
     .release = platformthird_dev_release, //解决"Device 'platform_dev' does not have a release() function“问题
     }
};
static  int   __init platformthird_dev_init( void ){
     platform_device_register(&platformthird_dev_device);   //注册设备到内核
     return  0;
}
static  void  platformthird_dev_exit( void ){
      platform_device_unregister(&platformthird_dev_device);   //卸载设备
     printk(KERN_ALERT  "good bye\n" );
}
module_init(platformthird_dev_init);
module_exit(platformthird_dev_exit);
MODULE_LICENSE( "GPL" );

   实例一和实例二的Makefile如下


1
2
3
4
5
6
7
8
9
10
11
12
KERN_DIR = /workspacearm/linux-2.6.2.6
#platform_device_test.ko
all:
     make -C $(KERN_DIR) M=`pwd` modules
     cp platform_device_test.ko /opt/fsmini/
     cp platform_driver_test.ko /opt/fsmini/
clean:
     make -C $(KERN_DIR) M=`pwd` modules clean
     rm -rf modules.order
     rm -rf Module.symvers
obj-m   += platform_device_test.o
obj-m   += platform_driver_test.o

   Make一下,在开发板上加载驱动,结果如下输出

1
2
3
4
5
6
7
8
9
10
# insmod platform_driver_test.ko                                            
# insmod platform_device_test.ko                                            
driver say the driver found the device                                      
# rmmod platform_device_test.ko                                             
driver say the device is polled out                                         
device say the device is release                                            
good bye                                                                    
                                                                                                                                                                                                                  
# rmmod platform_driver_test.ko                                             
#

实验结论:从前面三句可以看的出来,当driver没有device时,会一直等候device的接入,类似热插拔,当有对应name的device时执行probe;从第四句可以知道当设备被拔出时候,driver执行remove。验证了:插入driver==>触发平台设备和驱动的匹配函数platform_match匹配,成功则调用probe()

实例三)实验三、创建名称为name的/sys/bus/platform/drivers/name和/sys/bus/platform/devices/name文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
  *本程序实现将字符设备挂载在总线上,只是在原来的的基础上加上一层platform_driver的外壳。
  *将驱动设备device挂载在总线上,这样的好处是:总线将设备(device)和驱动(driver)绑定,当有事件发生时就会调用相应驱动函数来完成,如智能电源管理(挂起,休眠,关机唤醒等等),热插拔,即插即用的支持
  *
  */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/device.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
#define VIRTUALDISK_MAJOR 250
int  VirtualDisk_major = VIRTUALDISK_MAJOR;
static  int   platformthird_dev_probe( struct  platform_device *pdev){
     return  0;
}
static  int  platformthird_dev_remove( struct  platform_device *pdev){
     return  0;
}
static  struct  platform_device platformthird_dev_device = {   //添加设备结构体
     .name =  "platformthird_dev" ,
     .id = -1,
};
static  struct  platform_driver platformthird_dev_driver = {
     .probe = platformthird_dev_probe,
     . remove  = platformthird_dev_remove,
     .driver = {
     .name =  "platformthird_dev" ,
     .owner = THIS_MODULE,
     }
};
static  int   __init platformthird_dev_init( void ){
     int  ret = 0;
     platform_device_register(&platformthird_dev_device);   //注册设备到内核总线
     ret = platform_driver_register(&platformthird_dev_driver);   //注册驱动到内核总线
     if  (ret){
         printk(KERN_ERR  "failed to register\n" );
     }
     return  ret;
}
static  void  platformthird_dev_exit( void ){
      platform_device_unregister(&platformthird_dev_device);   //卸载设备
      platform_driver_unregister(&platformthird_dev_driver);   //卸载驱动
     printk(KERN_ALERT  "good bye\n" );
}
module_init(platformthird_dev_init);
module_exit(platformthird_dev_exit);
MODULE_LICENSE( "GPL" );

实例四)实验四,给以前写的一般驱动代码加platform壳,原来只是创建/dev/***,注册class,class_device(对应/sys/class文件夹下****文件夹)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*
  *本程序实现将字符设备挂载在总线上,只是在原来的的基础上加上一层platform_driver的外壳。
  *将驱动设备device挂载在总线上,这样的好处是:总线将设备(device)和驱动(driver)绑定,当有事件发生时就会调用相应驱动函数来完成,如智能电源管理(挂起,休眠,关机唤醒等等),热插拔,即插即用的支持
  *
  */
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/device.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
#define VIRTUALDISK_MAJOR 250
int  VirtualDisk_major = VIRTUALDISK_MAJOR;
struct  cdev cdev;
static  struct  class  *platform_class;
static  struct  class_device  *platform_class_dev;
static  int  platform_drv_open( struct  inode *inode,  struct  file *file)
{
     printk( "platform_dev read\n" );
     return  0;
}
static  int  platform_drv_release( struct  inode *inode,  struct  file *file)
{
     printk( "platform_dev release\n" ); 
     return  0;
}
static  struct  file_operations platform_drv_fops = {
     .owner  =   THIS_MODULE,     /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
     .release = platform_drv_release,
     .open = platform_drv_open,
};
static  int   platform_dev_probe( struct  platform_device *pdev){
     int  result;
     int  err;
     dev_t devno = MKDEV(VirtualDisk_major, 0);
     if (VirtualDisk_major){
     result = register_chrdev_region(devno, 1,  "platform_dev" );
     } else {
     result = alloc_chrdev_region(&devno, 0, 1,  "platform_dev" );
     VirtualDisk_major = MAJOR(devno);
    
     if (result < 0 ){
     return  result;
     }
     cdev_init(&cdev, &platform_drv_fops);
     cdev.owner = THIS_MODULE;
     err = cdev_add(&cdev, devno, 1);
     if (err){
     printk( "error %d cdev file added\n" , err);
     }
     platform_class = class_create(THIS_MODULE,  "platform_dev" );
     if  (IS_ERR(platform_class))
         return  PTR_ERR(platform_class);
     platform_class_dev = class_device_create(platform_class, NULL, MKDEV(VirtualDisk_major, 0), NULL,  "platform_dev" );  /* /dev/xyz */
     if  (IS_ERR(platform_class_dev))
         return  PTR_ERR(platform_class_dev);
                                                       
     return  0;
}
static  int  platform_dev_remove( struct  platform_device *pdev){
     cdev_del(&cdev);
     unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
     class_device_unregister(platform_class_dev);
     class_destroy(platform_class);
     return  0;
}
static  void  platform_device_release( struct  device * dev){
return ;
}
static  struct  platform_device platform_dev_device = {   //添加设备结构体
     .name =  "platform_dev" ,
     .id = -1,
     .dev = {
      .release = platform_device_release, //解决"Device 'platform_dev' does not have a release() function“问题
     }
};
static  struct  platform_driver platform_dev_driver = {
     .probe = platform_dev_probe,
     . remove  = platform_dev_remove,
     .driver = {
     .name =  "platform_dev" ,
     .owner = THIS_MODULE,
     }
};
static  int   __init platform_dev_init( void ){
     int  ret = 0;
     platform_device_register(&platform_dev_device);   //注册设备到内核
     ret = platform_driver_register(&platform_dev_driver);   //注册驱动到内核
     if  (ret){
         printk(KERN_ERR  "failed to register\n" );
     }
                                                          
     return  ret;
}
static  void  platform_dev_exit( void ){
      platform_device_unregister(&platform_dev_device);   //卸载设备
      platform_driver_unregister(&platform_dev_driver);   //卸载驱动
                                                       
     printk(KERN_ALERT  "good bye\n" );
}
module_init(platform_dev_init);
module_exit(platform_dev_exit);
MODULE_LICENSE( "GPL" );



本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1396319,如需转载请自行联系原作者

相关文章
|
4月前
|
传感器 物联网 网络架构
ENS、IoT设备、X86、ARM
ENS(Enterprise Name Service)是企业名称服务,是一种为物联网设备提供命名和寻址服务的技术。ENS通过为物联网设备分配唯一的名称和地址,使得物联网设备可以被网络中的其他设备和服务所识别和访问。 IoT设备(Internet of Things device)是连接到互联网的物理设备,可以收集和共享数据,用于监测、控制和优化各种业务流程和操作。IoT设备包括各种传感器、执行器、网关和路由器等。
40 2
|
7月前
|
缓存 C++
基于ARM-contexA9-蜂鸣器pwm驱动开发
基于ARM-contexA9-蜂鸣器pwm驱动开发
50 0
|
7月前
基于ARM-contexA9蜂鸣器驱动开发
基于ARM-contexA9蜂鸣器驱动开发
43 0
|
11月前
|
iOS开发
iOS设备支持的ARM平台
iOS设备支持的ARM平台
65 0
|
Linux
ARM6818开发板输入设备(触摸屏)操作(开发五子棋游戏)
ARM6818开发板输入设备(触摸屏)操作(开发五子棋游戏)
308 0
|
传感器 网络协议 Linux
ARM嵌入式学习笔记——《设备驱动基础》(三)
ARM嵌入式学习笔记——《设备驱动基础》
163 0
|
Linux C++
ARM嵌入式学习笔记——《设备驱动基础》(二)
ARM嵌入式学习笔记——《设备驱动基础》
|
存储 Web App开发 Unix
ARM嵌入式学习笔记——《设备驱动基础》(一)
ARM嵌入式学习笔记——《设备驱动基础》
108 0
|
缓存 Linux 开发工具
ARM嵌入式学习笔记——Linux字符设备驱动程序设计(三)
ARM嵌入式学习笔记——Linux字符设备驱动程序设计
166 0
|
缓存 Linux
ARM嵌入式学习笔记——Linux字符设备驱动程序设计(二)
ARM嵌入式学习笔记——Linux字符设备驱动程序设计
102 0