电子发烧友网 > 嵌入式技术 > 正文

Linux设备驱动模型之platform总线深入浅出

2019年05月05日 14:58 次阅读

Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 

对于依附在USB、PCI、I2C、SPI等物理总线来 这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。 
相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 
platform总线相关代码:driver\base\platform.c 文件 
相关结构体定义:include\linux\platform_device.h 文件中

platform总线管理下最重要的两个结构体是platform_device和platform_driver 
分别表示设备和驱动 
在Linux中的定义如下 
一:platform_driver

//include\linux\platform_device.h struct platform_driver { int (*probe)(struct platform_device *); //探测函数,在注册平台设备时被调用 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;//设备驱动结构};

1

2

3

4

5

6

7

8

9

10

11

struct device_driver { const char * name; struct bus_type * bus; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module * owner; const char * mod_name; /* used for built-in modules */ struct module_kobject * mkobj; int (*probe) (struct device * dev); int (*remove) (struct device * dev); void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev, pm_message_t state); int (*resume) (struct device * dev);};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

二:platform_device

struct platform_device { const char * name;//设备名称 u32 id;//取-1 struct device dev;//设备结构 u32 num_resources;// resource结构个数 struct resource * resource;//设备资源};

1

2

3

4

5

6

7

resource结构体也是描述platform_device的一个重要结构体 该元素存入了最为重要的设备资源信息

struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child;};

1

2

3

4

5

6

7

我们通常关心start,end,flags。它们分别标明了资源的开始值,结束值和类型,flags可以为IORESOURCE_IO,IORESOURCE_MEM,IORESOURCE_IRQ,IORESOURCE_DMA等,start,end的含义会随着flags变更,如当flags为IORESOURCE_MEM时,start,end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为,IORESOURCE_IRQ时,start,end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,例如某设备占据了两个内存区域,则可以定义两个IORESOURCE_MEM资源。  对resource的定义也通常在BSP的板文件中运行,而在具体的设备驱动中通过platform_get_resource()这样的API来获取,此API的原型为:

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)

1

2

例如在\arch\arm\mach-at91\Board-sam9261ek.c板文件中为DM9000网卡定义了如下的resource

static struct resource at91sam9261_dm9000_resource[] = { [0] = { .start = AT91_CHIPSELECT_2, .end = AT91_CHIPSELECT_2 + 3, .flags = IORESOURCE_MEM }, [1] = { .start = AT91_CHIPSELECT_2 + 0x44, .end = AT91_CHIPSELECT_2 + 0xFF, .flags = IORESOURCE_MEM }, [2] = { .start = AT91_PIN_PC11, .end = AT91_PIN_PC11, .flags = IORESOURCE_IRQ }};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

在DM9000网卡驱动中则是通过如下办法拿到这3份资源

db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

1

2

3

对于irq而言platform_get_resource()还有一个进行了封装的变体platform_get_irq(),  api原型是

int platform_get_irq(struct platform_device *dev, unsigned int num){ struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); return r ? r->start : -ENXIO;}

1

2

3

4

5

实际上这个函数也是调用platform_get_resource(dev, IORESOURCE_IRQ, num)来获取资源

设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存等标准资源以外,可能还会有一些配置信息,这些配置信息也依赖于板,不适宜直接放置在设备驱动上,因此,platform也提供了platform_data的支持,例如对于dm9000

staTIc struct dm9000_plat_data dm9000_platdata = { .flags = DM9000_PLATF_16BITONLY,};staTIc struct platform_device at91sam9261_dm9000_device = { .name = "dm9000", .id = 0, .num_resources = ARRAY_SIZE(at91sam9261_dm9000_resource), .resource = at91sam9261_dm9000_resource, .dev = { .platform_data = &dm9000_platdata, }};

1

2

3

4

5

6

7

8

9

10

11

12

13

获取platform_data的API是dev_get_platdata()

struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev);

1

三:platform总线  系统为platform总线定义了一个bus_type的实例platform_bus_type,其定义位于drivers/base/platform.c下

struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops,};

1

2

3

4

5

6

7

最重要的是match函数,真是此成员函数确定了platform_device和platform_driver之间如何匹配的

staTIc int platform_match(struct device *dev, struct device_driver *drv){ struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0);}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

可知有3中情况下platform_device和platform_driver匹配  1.基于设备树风格的匹配  2.匹配ID表(即platform_device设备名是否出现在platform_driver的id表内)  3.匹配platform_device设备名和驱动的名字  在4.0还有一种情况是基于ACPI风格的匹配

对于设备驱动的开发  其设计顺序为定义 platform_device -> 注册 platform_device-> 定义 platform_driver-> 注册 platform_driver 。  这里选用的例子是q40kbd,在/drivers/input/serio目录里。这是一个键盘控制器驱动  这里直接分析初始化函数

staTIc int __init q40kbd_init(void){ int error; if (!MACH_IS_Q40) return -EIO; /* platform总线驱动的注册 */ error = platform_driver_register(&q40kbd_driver); if (error) return error; /* 分配一个platform设备 */ q40kbd_device = platform_device_alloc("q40kbd", -1); if (!q40kbd_device) goto err_unregister_driver; /* platform设备注册 */ error = platform_device_add(q40kbd_device); if (error) goto err_free_device; return 0; err_free_device: platform_device_put(q40kbd_device); err_unregister_driver: platform_driver_unregister(&q40kbd_driver); return error;}

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

驱动注册函数是 platform_driver_register()函数

int platform_driver_register(struct platform_driver *drv){ //把驱动的总线设置为platform总线 drv->driver.bus = &platform_bus_type; //依次设置驱动的各个函数指针 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; if (drv->suspend) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; //调用driver_register注册驱动 return driver_register(&drv->driver);}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/* 初始化后调用bus_add_driver函数执行注册过程 */int driver_register(struct device_driver * drv){ if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) { printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); return bus_add_driver(drv);}

1

2

3

4

5

6

7

8

9

10

11

12

为总线增加一个驱动

int bus_add_driver(struct device_driver *drv){ struct bus_type * bus = get_bus(drv->bus); int error = 0; if (!bus) return -EINVAL; pr_debug("bus %s: add driver %s\n", bus->name, drv->name); /* 为kobject结构设置名字 */ error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; /* 为sysfs文件系统创建设备的相关文件 */ if ((error = kobject_register(&drv->kobj))) goto out_put_bus; if (drv->bus->drivers_autoprobe) { /* 驱动加入总线的驱动列表 */ error = driver_attach(drv); if (error) goto out_unregister; } /* 在sysfs创建驱动的属性文件和模块 */ klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __FUNCTION__, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __FUNCTION__, drv->name); } return error;out_unregister: kobject_unregister(&drv->kobj);out_put_bus: put_bus(bus); return error;}

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

执行驱动加载

int driver_attach(struct device_driver * drv){ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *)){ struct klist_iter i; struct device * dev; int error = 0; if (!bus) return -EINVAL; /* 初始化一个klist_iter结构 目的是在双向链表中定位一个成员 */ klist_iter_init_node(&bus->klist_devices, &i, (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) //对每个设备都调用fn函数指针 fn就是传入的函数指针__driver_attach error = fn(dev, data); klist_iter_exit(&i); return error;}

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

static int __driver_attach(struct device * dev, void * data){ struct device_driver * drv = data; /* * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); /* 获取设备的 */ down(&dev->sem); if (!dev->driver) driver_probe_device(drv, dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); return 0;}

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

int driver_probe_device(struct device_driver * drv, struct device * dev){ int ret = 0; if (!device_is_registered(dev)) return -ENODEV; /* 调用总线配置的match函数 */ if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); /* 调用really_probe函数 */ ret = really_probe(dev, drv);done: return ret;}static int really_probe(struct device *dev, struct device_driver *drv){ int ret = 0; atomic_inc(&probe_count); pr_debug("%s: Probing driver %s with device %s\n", drv->bus->name, drv->name, dev->bus_id); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __FUNCTION__, dev->bus_id); goto probe_failed; } /* 调用总线的probe函数 */ if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; /* 如果驱动提供了Probe函数,则调用驱动的probe函数 */ } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } /* 设备发现了驱动 通过sysfs创建一些文件 和设备做符号链接 */ driver_bound(dev); ret = 1; pr_debug("%s: Bound Device %s to Driver %s\n", drv->bus->name, dev->bus_id, drv->name); goto done;probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev->bus_id, ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret;}

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

driver_probe_device() 这个函数实际上做了两件事  1.调用总线提供的match函数。如果检查通过 说明设备和驱动是匹配的 设备所指的驱动指针要赋值为当前驱动  2.探测(probe) 首先调用总线提供的probe函数,如果驱动有自己的Probe函数,还要调用驱动的probe函数

platform总线的match函数在上文有介绍 三种情况均可匹配  platform总线的probe函数就是platform_drv_probe() 这个函数是个封装函数 它只是简单的调用了驱动的Probe函数 驱动的Probe函数就是q40kbd_probe

static int __devinit q40kbd_probe(struct platform_device *dev){ q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!q40kbd_port) return -ENOMEM; q40kbd_port->id.type = SERIO_8042; q40kbd_port->open = q40kbd_open; q40kbd_port->close = q40kbd_close; q40kbd_port->dev.parent = &dev->dev; strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); //注册serio结构变量 serio_register_port(q40kbd_port); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0;}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

综上所述,platform总线驱动注册就是遍历各个设备 检查是否和驱动匹配

接下来分析platform设备的注册

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; /* 设置设备的总线类型为platform_bus_type */ pdev->dev.bus = &platform_bus_type; 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); /* 把设备IO端口和IO内存资源注册到系统 */ 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;}

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

int device_add(struct device *dev){ struct device *parent = NULL; char *class_name = NULL; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) goto Error; pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); /* 获得父设备 */ parent = get_device(dev->parent); error = setup_parent(dev, parent); if (error) goto Error; /* first, register with generic layer. */ /* 这是kobject的名字 */ kobject_set_name(&dev->kobj, "%s", dev->bus_id); /* 在sys目录生成设备目录 */ error = kobject_add(&dev->kobj); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /* notify clients of device entry (new way) */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); /* 设置uevent属性 */ dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; if (dev->driver) dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; dev->uevent_attr.show = show_uevent; error = device_create_file(dev, &dev->uevent_attr); if (error) goto attrError; /* 设备的属性文件 */ if (MAJOR(dev->devt)) { struct device_attribute *attr; attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) { error = -ENOMEM; goto ueventattrError; } attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; if (dev->driver) attr->attr.owner = dev->driver->owner; attr->show = show_dev; error = device_create_file(dev, attr); if (error) { kfree(attr); goto ueventattrError; } dev->devt_attr = attr; } /* 创建设备类的符号链接 */ if (dev->class) { sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, "subsystem"); /* If this is not a "fake" compatible device, then create the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kobj) sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, dev->bus_id); if (parent) { sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");#ifdef CONFIG_SYSFS_DEPRECATED class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);#endif } } /* 设备的能源管理 */ if ((error = device_add_attrs(dev))) goto AttrsError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); /* 设备加入父设备的链表 */ if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { down(&dev->class->sem); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: kfree(class_name); put_device(dev); return error; BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { device_remove_file(dev, dev->devt_attr); kfree(dev->devt_attr); } if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); /* If this is not a "fake" compatible device, remove the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kobj) sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); if (parent) {#ifdef CONFIG_SYSFS_DEPRECATED char *class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name);#endif sysfs_remove_link(&dev->kobj, "device"); } } ueventattrError: device_remove_file(dev, &dev->uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: if (parent) put_device(parent); goto Done;}

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

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

遍历驱动 为设备找到合适的驱动

int device_attach(struct device * dev){ int ret = 0; down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); } up(&dev->sem); return ret;}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

但是在Linux加入了设备树的时候 就不再需要大量的板级信息了 譬如/arch/arm/plat-xxx和arch/arm/mach-xxx中platform的修改部分  原先的

static struct resource xxx_resource[] = { [0] = { .start = ..., .end = ..., .flags = ..., }, [1] = { .start = ..., .end = ..., .flags = ..., }, [2] = { .start =..., .end = ..., .flags = ..., }};static struct platform_device xxx_device = { .name = "xxx", .id = -1, .num_resources = ARRAY_SIZE(xxx_resource), .resource = xxx_resource, .dev = { .platform_data = &xxx_platdata, }};

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

之类的注册platform_device,绑定resource,即内存,irq等板级信息 现在都不再需要 其中platform_device会由内核自动展开。而这些resource实际来源于.dts中设备节点的reg,interrups属性

再者就是platform_data这些平台数据属性化  比如\linux-3.4.2\arch\arm\mach-at91\Board-sam9263ek.c下用如下代码注册gpio_keys设备 通过gpio_keys_platform_data来定义platform_data

static struct gpio_keys_button ek_buttons[] = { { /* BP1, "leftclic" */ .code = BTN_LEFT, .gpio = AT91_PIN_PC5, .active_low = 1, .desc = "left_click", .wakeup = 1, }, { /* BP2, "rightclic" */ .code = BTN_RIGHT, .gpio = AT91_PIN_PC4, .active_low = 1, .desc = "right_click", .wakeup = 1, }};static struct gpio_keys_platform_data ek_button_data = { .buttons = ek_buttons, .nbuttons = ARRAY_SIZE(ek_buttons),};static struct platform_device ek_button_device = { .name = "gpio-keys", .id = -1, .num_resources = 0, .dev = { .platform_data = &ek_button_data, }};

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

设备驱动drivers/input/keyboard/gpio_keys.c通过以下方法取得这个platform_data

struct device *dev = &pdev->dev;const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);

1

2

转移到设备树后 platform_data就不再arch/arm/mach-xxx中 它需要从设备树中获取,比如一个电路板上有gpio_keys 则只需要在设备树中添加类似arch/arm/boot/dts/exyons4210-origen.dts的代码

gpio_keys { compatible = "gpio-keys"; #address-cells = <1>; #size-cells = <0>; up { label = "Up"; gpios = <&gpx2 0 0 0 2>; linux,code = <103>; }; down { label = "Down"; gpios = <&gpx2 1 0 0 2>; linux,code = <108>; };

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

而在驱动中通过of_开头的读属性的API来读取这些信息 并组织处gpio_keys_platform_data结构体

gpio_keys_get_devtree_pdata(struct device *dev){ struct gpio_keys_platform_data *pdata; struct gpio_keys_button *button; struct fwnode_handle *child; int nbuttons; nbuttons = device_get_child_node_count(dev); if (nbuttons == 0) return ERR_PTR(-ENODEV); pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); button = (struct gpio_keys_button *)(pdata + 1); pdata->buttons = button; pdata->nbuttons = nbuttons; pdata->rep = device_property_read_bool(dev, "autorepeat"); device_property_read_string(dev, "label", &pdata->name); device_for_each_child_node(dev, child) { if (is_of_node(child)) button->irq = irq_of_parse_and_map(to_of_node(child), 0); if (fwnode_property_read_u32(child, "linux,code", &button->code)) { dev_err(dev, "Button without keycode\n"); fwnode_handle_put(child); return ERR_PTR(-EINVAL); } fwnode_property_read_string(child, "label", &button->desc); if (fwnode_property_read_u32(child, "linux,input-type", &button->type)) button->type = EV_KEY; button->wakeup = fwnode_property_read_bool(child, "wakeup-source") || /* legacy name */ fwnode_property_read_bool(child, "gpio-key,wakeup"); button->can_disable = fwnode_property_read_bool(child, "linux,can-disable"); if (fwnode_property_read_u32(child, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; button++; } return pdata;}
 

下载发烧友APP

打造属于您的人脉电子圈

关注电子发烧友微信

有趣有料的资讯及技术干货

关注发烧友课堂

锁定最新课程活动及技术直播

电子发烧友观察

一线报道 · 深度观察 · 最新资讯
收藏 人收藏
分享:

评论

相关推荐

轻松学Linux系列课程之Linux基础

课程从0开始,讲到云计算结束,按阶段授课,从入门到跑路,适合初学者,也适合定制,需要哪个模块学哪个模块2,包含初级运维工

发烧友学院 发表于 2018-07-24 00:00 15809次阅读
轻松学Linux系列课程之Linux基础

你没听过的10个免费的Linux生产力应用程序

Tomboy 是一个简单的便签应用。它不仅仅适用于 Linux,你也可以在 Unix、Windows...

发表于 2019-05-05 14:51 0次阅读
你没听过的10个免费的Linux生产力应用程序

在嵌入式Linux上实现JFFS文件系统

近年来,随着电子技术的不断进步,嵌入式系统开发已成为热点,而Linux作为一个自由软件 ,也得到了极...

发表于 2019-05-05 14:48 3次阅读
在嵌入式Linux上实现JFFS文件系统

需要了解嵌入式Linux的实时化技术

Linux支持PowerPC、MIPS、ARM、DSP等多种嵌入式处理器,逐渐被用于多种关键性场合。...

发表于 2019-05-05 14:45 5次阅读
需要了解嵌入式Linux的实时化技术

linux驱动的入口函数module_init的...

几乎每个linux驱动都有个module_init(与module_exit的定义在Init.h (...

发表于 2019-05-05 14:43 8次阅读
linux驱动的入口函数module_init的...

你是否了解Linux中的Ramdisk与Init...

Ramdisk与硬盘分区有其不同的地方,例如RAM disk不适合作为长期保存文件的介质,掉电后Ra...

发表于 2019-05-05 14:35 8次阅读
你是否了解Linux中的Ramdisk与Init...

基于Linux与Busybox的Reboot命令...

busybox是如何运行这个命令,同时又是如何调用到Linux内核中的mach_reset中的arc...

发表于 2019-05-05 14:31 10次阅读
基于Linux与Busybox的Reboot命令...

嵌入式linux下u盘自动挂载的实现

一、方法一 由于我调试的系统是基于LSDK的,所以没法直接使用openwrt中的hotplug完成此...

发表于 2019-05-05 14:26 12次阅读
嵌入式linux下u盘自动挂载的实现

Linux设备树关键之一:根节点

从软件的层面讲model属性仅仅表示一个名字而已,没有更多的作用。compatible属性则不同,该...

发表于 2019-05-05 14:20 5次阅读
Linux设备树关键之一:根节点

Linux系统上竟可以模拟系统负载

系统管理员通常需要探索在不同负载对应用性能的影响。这意味着必须要重复地人为创造负载。当然,你可以通过...

发表于 2019-05-05 14:13 7次阅读
Linux系统上竟可以模拟系统负载

Linux系统日志文件中的JFS文件系统

嵌入式linux中文站向大家介绍一下JFS文件系统。Linux系统日志文件中的JFS系统, JFS ...

发表于 2019-05-05 14:10 5次阅读
Linux系统日志文件中的JFS文件系统

你了解Linux下的tmpfs文件系统?

Linux内核的虚拟内存(virtual memory ,VM)资源同时来源于您的RAM(物理内存)...

发表于 2019-05-05 14:07 7次阅读
你了解Linux下的tmpfs文件系统?

学会使用tmpfs文件系统来提速你的linux系...

大家已经知道linux的虚拟内存(VM),包括ram和swap两部分,ram就是你的物理内存,swa...

发表于 2019-05-05 14:03 5次阅读
学会使用tmpfs文件系统来提速你的linux系...

如何在Linux中查找文件?

当系统的磁盘空间不足时,您可能会使用 df、du 或 ncdu 命令进行检查,但这些命令只会显示当前...

发表于 2019-05-05 13:58 8次阅读
如何在Linux中查找文件?

学会处理Linux内核访问外设I/O资源的方式

Linux内核访问外设I/O内存资源的方式有两种:动态映射(ioremap)和静态映射(map_de...

发表于 2019-05-05 13:54 7次阅读
学会处理Linux内核访问外设I/O资源的方式

Linux系统中LVM逻辑卷的工作原理是怎样的

其实在Linux操作系统中,磁盘管理机制和windows上的差不多,绝大多数都是使用MBR(Mast...

发表于 2019-05-03 12:26 149次阅读
Linux系统中LVM逻辑卷的工作原理是怎样的

三种Linux中的常用多线程同步方式浅析

嵌入式linux中文站给大家介绍三种Linux中的常用多线程同步方式:互斥量,条件变量,信号量。

发表于 2019-05-02 14:49 42次阅读
三种Linux中的常用多线程同步方式浅析

Linux系统中的信号使用方式概述

本文主要介绍Linux信号系统和如何使用POSIX API来响应信号。本文中的示例适用于Linux系...

发表于 2019-05-02 14:40 48次阅读
Linux系统中的信号使用方式概述

UCloud基于Linux内核新特性的下一代外网...

Nftables是一种新的数据包分类框架,旨在替代现存的{ip,ip6,arp,eb}_tables...

发表于 2019-05-01 11:33 253次阅读
UCloud基于Linux内核新特性的下一代外网...

变频器与plc连接方式一般有几种方式?

PLC的开关量信号控制变频器PLC可以通过程序控制变频器的启动、停止、复位;也可以控制变频器高速、中...

发表于 2019-05-01 10:12 245次阅读
变频器与plc连接方式一般有几种方式?

简单介绍一下RS485,RS485和其它总线网络...

在RS485组网过程中另一个需要主意的问题是终端负载电阻问题,在设备少距离短的情况下不加终端负载电阻...

发表于 2019-05-01 10:10 212次阅读
简单介绍一下RS485,RS485和其它总线网络...

iTOP4412开发板-使用buildroot搭建最简单的linux

发表于 2019-04-30 11:12 462次阅读
iTOP4412开发板-使用buildroot搭建最简单的linux

2016.3文档导航器错误

发表于 2019-04-30 10:20 21次阅读
2016.3文档导航器错误

请问可以通过Samba文件共享服务代替cuteftp传输工具?

发表于 2019-04-30 07:45 117次阅读
请问可以通过Samba文件共享服务代替cuteftp传输工具?

请问为什么编译嵌入式软件要在Linux环境下?

发表于 2019-04-30 04:39 135次阅读
请问为什么编译嵌入式软件要在Linux环境下?

请问001_linux_basic文件在哪里?

发表于 2019-04-30 04:00 148次阅读
请问001_linux_basic文件在哪里?

Linux创建软链接和查看进程

发表于 2019-04-29 16:37 21次阅读
Linux创建软链接和查看进程

简单又快速看懂linux的磁盘划分

一个服务器是以虚拟机的形式提供的,通过df -h看服务器磁盘空间只有30多个G,但通过fdisk -...

发表于 2019-04-28 15:53 122次阅读
简单又快速看懂linux的磁盘划分

如何拯救被 rm -r的数据

天雷滚滚。天雷滚滚。天雷滚滚。作为一个Linux程序员,你能碰到的最伤心的事情,莫过于:编译了一整天...

发表于 2019-04-28 14:59 121次阅读
如何拯救被 rm -r的数据

采用嵌入式Linux系统实现触摸屏驱动开发

发表于 2019-04-28 09:57 49次阅读
采用嵌入式Linux系统实现触摸屏驱动开发

请问在Ubuntu中怎么安装arm-linux-gcc?

发表于 2019-04-28 08:16 65次阅读
请问在Ubuntu中怎么安装arm-linux-gcc?

两小时轻松快速入门Docker

docker是linux容器的一种封装,提供简单易用的容器使用接口。它是最流行的Linux容器解决方...

发表于 2019-04-26 10:51 271次阅读
两小时轻松快速入门Docker

使用互联网链接和DocNav时出错

发表于 2019-04-26 06:25 31次阅读
使用互联网链接和DocNav时出错

如何采用51单片机设计出总线编址电路

带总线扩展接口的单片机系统,包括外部32kRAM扩展、LCDl602接口、输入输出口。带编址扩展的单...

发表于 2019-04-25 17:29 68次阅读
如何采用51单片机设计出总线编址电路

报文的传输原理你了解吗

CAN总线通讯是我们每天都会使用的工业通讯总线,工程师更多的是关注报文是否能够正常接收,解析结果是否...

发表于 2019-04-25 14:50 129次阅读
报文的传输原理你了解吗

概述在Linux上创建或扩展交换分区的三种方法

有时候在你安装后摇升级 RAM 的时候需要增加一点交换分区的空间,比如你要将你的系统的 RAM 从 ...

发表于 2019-04-25 14:38 17次阅读
概述在Linux上创建或扩展交换分区的三种方法

怎样成为优秀的嵌入式linux工程师

目前,Linux作为嵌入式系统的主力军,广泛应用于消费类电子、工业控制、军工电子、电信/网络/通讯、...

发表于 2019-04-24 09:47 335次阅读
怎样成为优秀的嵌入式linux工程师

在哪里可以获得bulkloopapp?

发表于 2019-04-24 08:22 77次阅读
在哪里可以获得bulkloopapp?

Linux软件开发工程师的岗位职责及职位要求

参与系统的需求调研和需求分析,详设概设,撰写相关技术文档;

发表于 2019-04-23 16:54 211次阅读
Linux软件开发工程师的岗位职责及职位要求

拼技术硬核,ADI打造创新音频总线走入全球90%...

考虑到电动汽车的行车安全,去年欧盟下令要求所有电动汽车在2021年前加装能够发出虚拟引擎声的装置,以...

发表于 2019-04-23 15:51 229次阅读
拼技术硬核,ADI打造创新音频总线走入全球90%...

Linux登录进程的层次关系浅析

当打开Linux系统,LILO(LInux LOader)找到Linux内核把它加载到内存。它初始化...

发表于 2019-04-22 14:53 19次阅读
Linux登录进程的层次关系浅析

Linux运维工程师是做什么的 发展前景又怎么样

如果你想要进入运维管理领域这一行,首先你应该了解linux运维工程师是干什么的。他主要是对Linux...

发表于 2019-04-21 11:47 373次阅读
Linux运维工程师是做什么的 发展前景又怎么样

楼宇对讲系统的全数字技术应用发展

2018年年底,国家质量监督检验检疫总局、国家标准化管理委员会批准发布了2018年第17号中国国家标...

发表于 2019-04-21 08:25 72次阅读
楼宇对讲系统的全数字技术应用发展

电脑卡慢惹人烦 这五个妙招可以让Linux飞起来

玩儿电脑最怕的就是卡慢,那么电脑卡慢应该怎么解决呢?对于windows系统来说,你可能有各种免费的杀...

发表于 2019-04-18 15:26 28次阅读
电脑卡慢惹人烦 这五个妙招可以让Linux飞起来

能够利用现有的存储空间去备份的Linux系统的替...

当转为使用 Linux 时,你可能会注意到你所使用的版本会带有一个默认的备份工具。然而,可能该工具并...

发表于 2019-04-18 15:20 26次阅读
能够利用现有的存储空间去备份的Linux系统的替...

基于Linux系统上的OBS Studio主播软...

也许没有那么多铁杆的游戏玩家使用 Linux,但肯定有很多 Linux 用户喜欢玩游戏。如果你是其中...

发表于 2019-04-16 16:04 43次阅读
基于Linux系统上的OBS Studio主播软...

概述Linux工作站加固的6个方法

正如我之前说过,安全好比是在公路上开车――比你开得慢的人都是白痴,比你开得快的人都是疯子。本文介绍的...

发表于 2019-04-16 15:59 37次阅读
概述Linux工作站加固的6个方法

Docker和传统虚拟化方式的不同之处

当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机...

发表于 2019-04-16 13:40 327次阅读
Docker和传统虚拟化方式的不同之处

盘点一下这些可以通过Wine在Linux上玩的游...

上古卷轴 5 已经不是款新游戏了,但它的 mod 社区依旧活跃。如果你的 Linux 系统有足够资源...

发表于 2019-04-16 12:41 299次阅读
盘点一下这些可以通过Wine在Linux上玩的游...

如何在 Linux 中不使用 CD 命令进入目录...

众所周知,如果没有 cd 命令,我们无法 Linux 中切换目录。这个没错,但我们有一个名为 sho...

发表于 2019-04-16 11:40 333次阅读
如何在 Linux 中不使用 CD 命令进入目录...

Linux 开发者 Christopher He...

在诉讼请求被法院一次次驳回后,最近原告著名 Linux 开发者 Christopher Helwig...

发表于 2019-04-15 16:56 329次阅读
Linux 开发者 Christopher He...

概述linux异步信号handle

程序的二进制代码在内存中都有着确定的执行流程,为什么收到异步信号以后,程序会被“中断”,然后跳转到这...

发表于 2019-04-15 14:58 47次阅读
概述linux异步信号handle

Linux操作系统的用户空间和内核空间开发需要谨...

作为中央核心处理单元的CPU,除了生产工艺的不断革新进步外,在处理数据和响应速度方面也需要有权衡。稍...

发表于 2019-04-15 14:48 40次阅读
Linux操作系统的用户空间和内核空间开发需要谨...

从内核中NVMe IO框架来看其中存在的问题

IO响应过程中最主要问题是中断的balance,由于默认linux中并没有对NVMe的中断进行有效的...

发表于 2019-04-15 13:59 271次阅读
从内核中NVMe IO框架来看其中存在的问题

微软把Linux看作危险的异类

这是一个美妙而且疯狂的时代,瞬息万变,一切皆有可能。 曾经一度,微软把Linux看作危险的异类

发表于 2019-04-12 16:44 446次阅读
微软把Linux看作危险的异类

Linux相对于windows具有以下的优点

Linux系统让我们懂得了共享、开放、自由可以让人类生活的更加美好,开源精神是一种让每个从事Linu...

发表于 2019-04-12 15:33 45次阅读
Linux相对于windows具有以下的优点

概述在Linux下编译安装OpenCV的步骤

OpenCV是一个跨平台的计算机视觉库,可以运行在Windows、Linux、MacOS等操作系统上...

发表于 2019-04-12 15:30 71次阅读
概述在Linux下编译安装OpenCV的步骤

Linux中的关机命令浅析

在本篇中,我们会向你解释 shutdown、poweroff、halt 以及 reboot 命令。我...

发表于 2019-04-11 09:00 68次阅读
Linux中的关机命令浅析

概述学习Linux系统中命令的简单方法

如果说如何快速学习、了解 Linux 的话,我的答案是学命令、背命令!为何呢?对于一名新手来说,去学...

发表于 2019-04-11 08:57 80次阅读
概述学习Linux系统中命令的简单方法

与传统的嵌入式系统相比 PocketIX采用标准...

IT业,如果要问当今最热门的话题是什么,从事硬件开发的人会毫不犹豫地回答:信息家电;从事软件开发的人...

发表于 2019-04-09 15:39 83次阅读
与传统的嵌入式系统相比 PocketIX采用标准...

Linux社区关于解决兼容性和效率的这两个问题浅...

自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:...

发表于 2019-04-09 15:28 65次阅读
Linux社区关于解决兼容性和效率的这两个问题浅...

Valve即将推出的新头显Index将支持Lin...

据报道,Valve回应GamingOnLinux“对Linux的支持是肯定的”,同时确定了将于5月1...

发表于 2019-04-09 10:10 304次阅读
Valve即将推出的新头显Index将支持Lin...

几种常用的经常出现于现有嵌入式应用中的内存映射I...

Linux 暴风雨般占领了嵌入式系统市场。分析家指出,大约有1/3到1/2的32/64位新的嵌入式系...

发表于 2019-04-08 08:52 96次阅读
几种常用的经常出现于现有嵌入式应用中的内存映射I...

Win7生命线将终止 会转向macOS或Linu...

Windows 7将要到达其生命线的终点,市场数据表明,Win7操作系统(OS)的用户数量开始减少。...

发表于 2019-04-06 16:24 819次阅读
Win7生命线将终止 会转向macOS或Linu...

几种有代表性的嵌入式Linux浅析

由于 Linux 所具备的开源、稳定、高效、易裁剪、硬件支持广泛等优点,使得它在嵌入式系统领域最近十...

发表于 2019-04-05 14:58 335次阅读
几种有代表性的嵌入式Linux浅析

Linux应用于嵌入式系统的开发优点浅析

按 照电气工程师协会的一个定义:嵌入式系统是用来控制或监视机器、装置或工厂等的大规模系统的设备。具体...

发表于 2019-04-05 14:51 112次阅读
Linux应用于嵌入式系统的开发优点浅析

汇总一下能够帮助树莓派初学者入门的一些资源

树莓派运行的是Linux系统,因此需要对Linux的命令和操作进行熟悉,我个人的体会Linux的命令...

发表于 2019-04-04 14:43 883次阅读
汇总一下能够帮助树莓派初学者入门的一些资源

我们是如何提供安全的Linux操作系统解决方案的

我们的解决方案是基于Oracle Linux(OL,以前称为Oracle Enterprise Li...

发表于 2019-04-03 15:27 485次阅读
我们是如何提供安全的Linux操作系统解决方案的

关于嵌入式Linux中进程调度的内容浅析

处理机(CPU)是整个计算机系统的核心资源,在多进程的操作系统中,进程数往往多于处理机数,这将导致各...

发表于 2019-04-03 09:02 107次阅读
关于嵌入式Linux中进程调度的内容浅析

linux内核对进程的管理分为两个方面

众所周知,现在的分时操作系统能够在一个CPU上运行多个程序,让这些程序表面上看起来是在同时运行的。l...

发表于 2019-04-03 08:58 138次阅读
linux内核对进程的管理分为两个方面

关于嵌入式Linux系统中的BSP浅析

嵌入式系统由硬件环境、嵌入式操作系统和应用程序组成,硬件环境是操作系统和应用程序运行的硬件平台,它随...

发表于 2019-04-02 14:42 129次阅读
关于嵌入式Linux系统中的BSP浅析

SN74LVC646A 具有三态输出的八路总线收...

SN54LVC646A八路总线收发器和寄存器设计用于2.7 V至3.6 VVCC操作,SN74LVC646A八路总线收发器和寄存器设计用于1.65V至3.6VVCC操作。 这些器件由总线收发器电路,D型触发器和控制电路组成用于直接从输入总线或内部寄存器多路传输数据。 A或B总线上的数据在适当时钟(CLKAB或CLKBA)输入的低到高转换时被输入寄存器。图1显示了使用?? LVC646A器件执行的四种基本总线管理功能。 输出使能(OE)和方向控制(DIR)输入控制收发器功能。在收发器模式下,高阻抗端口的数据存储在任一寄存器或两者中。 选择控制(SAB和SBA)输入可以复用存储的和实时(透明模式)数据。当OE \低时,DIR确定哪个总线接收数据。在隔离模式(OE \ high)中,A数据存储在一个寄存器中,B数据可以存储在另一个寄存器中。 当禁用输出功能时,输入功能仍然有效,并且可以用于存储和传输数据。一次只能驱动两条总线A或B中的一条。 输入可以从3.3 V或5 V器件驱动。此功能允许在混合的3.3 V /5 V系统环境中将这些器件用作转换器。 这些器件完全指定用于部分断...

发表于 2018-10-09 17:08 46次阅读
SN74LVC646A 具有三态输出的八路总线收...

SN74HCT652 具有三态输出的八路总线收发...

 HCT652器件由总线收发器电路,D型触发器和控制电路组成,用于直接从数据中复用数据传输总线或从内部存储寄存器。提供输出使能(OEAB和OEBA \)输入以控制收发器功能。提供选择控制(SAB和SBA)输入以选择实时或存储的数据传输。低输入电平选择实时数据;高输入级别选择存储的数据。图1显示了可以使用这些器件执行的四种基本总线管理功能。 A或B数据总线上的数据或两者都可以存储在内部D型触发器中无论选择控制端子还是输出控制端子,在适当的时钟(CLKAB或CLKBA)端子上都会发生从低到高的跳变。当SAB和SBA处于实时传输模式时,通过同时启用OEAB和OEBA \,可以在不使用内部D型触发器的情况下存储数据。在此配置中,每个输出都会增强其输入。当两组总线的所有其他数据源都处于高阻态时,每组总线保持最后状态。 为确保上电或断电期间的高阻态,OEBA \应通过上拉电阻连接到VCC,OEAB应通过下拉电阻连接到GND;电阻的最小值由驱动器的电流吸收/电流源能力决定。 特性 工作电压范围4.5 V至5.5 V 低功耗,80-μA...

发表于 2018-10-09 17:06 26次阅读
SN74HCT652 具有三态输出的八路总线收发...

AM4379 AM437x ARM Cortex...

TI AM437x高性能处理器基于ARM Cortex-A9内核。 这些处理器通过3D图形加速得到增强,可实现丰富的图形用户界面,还配备了协处理器,用于进行确定性实时处理(包括EtherCAT,PROFIBUS,EnDat等工业通信协议)。该器件支持高级操作系统(HLOS)。基于Linux的® 可从TI免费获取。其它HLOS可从TI的设计网络和生态系统合作伙伴处获取。 这些器件支持对采用较低性能ARM内核的系统升级,并提供更新外设,包括QSPI-NOR和LPDDR2等存储器选项。 这些处理器包含功能方框图中显示的子系统,并且后跟相应的“说明”中添加了更多信息说明。 处理器子系统基于ARM Cortex-A9内核,PowerVR SGX™图形加速器子系统提供3D图形加速功能以支持显示和高级用户界面。 可编程实时单元子系统和工业通信子系统(PRU-ICSS与ARM内核分离,允许单独操作和计时,以实现更高的效率和灵活性.PRU-ICSS支持更多外设接口和EtherCAT,PROFINET,EtherNet /IP,PROFIBUS,以太网Powerlink,Sercos,EnDat等...

发表于 2018-09-25 11:51 70次阅读
AM4379 AM437x ARM Cortex...