[zz]驱动对象设备对象设备栈乱杂谈

Arduino I2C驱动LCD1604:从并行到串行的硬件精简与通信实践 在嵌入式开发中,串行通信协议是连接微控制器与外围设备的关键技术。I2C(Inter-Integrated Circuit)作为一种两线制同步串行总线,通过SDA(数据线)和SCL(时钟线)实现主从设备间的数据交换,其优势在于硬件连接简洁、支持多设备寻址,极大优化了系统资源占用。从技术价值看,I2C将传统并行通信所需的十几根线精简至四根(含电源),显著提升项目可维护性与扩展性,尤其适用于IO资源有限的开发场景。在Arduino生态中,结合PCF8574等I2C转接芯片驱动LCD显示屏,成为嵌入式显示系统的典型 阅读详情
作者: JIURL

主页: http://jiurl.yeah.net  

用有限的几句话就舒舒服服的建立起对驱动对象和设备对象的概念是不可能的。刚开始是一片模糊,了解的多了,慢慢就清楚。下面的内容会使你对他们了解的清楚些。
  为了后面的叙述方便,首先列出驱动对象和设备对象的结构。
  驱动对象结构 DRIVER_OBJECT ,定义如下
  struct _DRIVER_OBJECT (sizeof=168)
  +00 int16 Type
  +02 int16 Size
  +04 struct _DEVICE_OBJECT *DeviceObject
  +08 uint32 Flags
  +0c void *DriverStart
  +10 uint32 DriverSize
  +14 void *Dri verSe ction
  +18 struct _DRIVER_EXTENSION *DriverExtension
  +1c struct _UNICODE_STRING DriverName
  +1c uint16 Length
  +1e uint16 MaximumLength
  +20 uint16 *Buffer
  +24 struct _UNICODE_STRING *HardwareDatabase
  +28 struct _FAST_IO_DIS Patch  *FastIoDispatch
  +2c function *DriverInit
  +30 function *DriverStartIo
  +34 function *DriverUnload
  +38 function *MajorFunction[28]
  DDK 中有对于一些域的说明
  [00] IRP_MJ_CREATE
  [01] IRP_MJ_CREATE_NAMED_PIPE
  [02] IRP_MJ_CLOSE
  [03] IRP_MJ_READ
  [04] IRP_MJ_WRITE
  [05] IRP_MJ_QUERY_INFORMATION
  [06] IRP_MJ_SET_INFORMATION
  [07] IRP_MJ_QUERY_EA
  [08] IRP_MJ_SET_EA
  [09] IRP_MJ_FLUSH_BUFFERS
  [0a] IRP_MJ_QUERY_VOLUME_INFORMATION
  [0b] IRP_MJ_SET_VOLUME_INFORMATION
  [0c] IRP_MJ_DIRECTORY_CONTROL
  [0d] IRP_MJ_FILE_SYSTEM_CONTROL
  [0e] IRP_MJ_DEVICE_CONTROL
  [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL
  [10] IRP_MJ_SHUTDOWN
  [11] IRP_MJ_LOCK_CONTROL
  [12] IRP_MJ_CLEANUP
  [13] IRP_MJ_CREATE_MAILSLOT
  [14] IRP_MJ_QUERY_SECURITY
  [15] IRP_MJ_SET_SECURITY
  [16] IRP_MJ_POWER
  [17] IRP_MJ_SYSTEM_CONTROL
  [18] IRP_MJ_DEVICE_CHANGE
  [19] IRP_MJ_QUERY_QUOTA
  [1a] IRP_MJ_SET_QUOTA
  [1b] IRP_MJ_PNP
  struct _DRIVER_EXTENSION (sizeof=24)
  +00 struct _DRIVER_OBJECT *DriverObject
  +04 function *AddDevice
  +08 uint32 Count
  +0c struct _UNICODE_STRING ServiceKeyName
  +0c uint16 Length
  +0e uint16 MaximumLength
  +10 uint16 *Buffer
  +14 struct _IO_CLIENT_EXTENSION *ClientDriverExtension
  设备对象结构 DEVICE_OBJECT ,定义如下
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type
  +02 uint16 Size
  +04 int32 ReferenceCount
  +08 struct _DRIVER_OBJECT *DriverObject
  +0c struct _DEVICE_OBJECT *NextDevice
  +10 struct _DEVICE_OBJECT *AttachedDevice
  +14 struct _IRP *CurrentIrp
  +18 struct _IO_TIMER *Timer
  +1c uint32 Flags
  +20 uint32 Characteristics
  +24 struct _VPB *Vpb
  +28 void *DeviceExtension
  +2c uint32 DeviceType
  +30 char StackSize
  +34 union __unnamed62 Queue
  +34 struct _LIST_ENTRY ListEntry
  +34 struct _LIST_ENTRY *Flink
  +38 struct _LIST_ENTRY *Blink
  +34 struct _WAIT_CONTEXT_BLOCK Wcb
  +34 struct _KDEVICE_QUEUE_ENTRY WaitQueueEntry
  +34 struct _LIST_ENTRY DeviceListEntry
  +34 struct _LIST_ENTRY *Flink
  +38 struct _LIST_ENTRY *Blink
  +3c uint32 SortKey
  +40 byte Inserted
  +44 function *DeviceRoutine
  +48 void *DeviceContext
  +4c uint32 NumberOfMapRegisters
  +50 void *DeviceObject
  +54 void *CurrentIrp
  +58 struct _KDPC *BufferChainingDpc
  +5c uint32 AlignmentRequirement
  +60 struct _KDEVICE_QUEUE DeviceQueue
  +60 int16 Type
  +62 int16 Size
  +64 struct _LIST_ENTRY DeviceListHead
  +64 struct _LIST_ENTRY *Flink
  +68 struct _LIST_ENTRY *Blink
  +6c uint32 Lock
  +70 byte Busy
  +74 struct _KDPC Dpc
  +74 int16 Type
  +76 byte Number
  +77 byte Importance
  +78 struct _LIST_ENTRY DpcListEntry
  +78 struct _LIST_ENTRY *Flink
  +7c struct _LIST_ENTRY *Blink
  +80 function *DeferredRoutine
  +84 void *DeferredContext
  +88 void *SystemArgument1
  +8c void *SystemArgument2
  +90 uint32 *Lock
  +94 uint32 ActiveThreadCount
  +98 void *SecurityDescriptor
  +9c struct _KEVENT DeviceLock
  +9c struct _DISPATCHER_HEADER Header
  +9c byte Type
  +9d byte Absolute
  +9e byte Size
  +9f byte Inserted
  +a0 int32 SignalState
  +a4 struct _LIST_ENTRY WaitListHead
  +a4 struct _LIST_ENTRY *Flink
  +a8 struct _LIST_ENTRY *Blink
  +ac uint16 SectorSize
  +ae uint16 Spare1
  +b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension
  +b4 void *Reserved
  DDK 中有对于一些域的说明
  struct _DEVOBJ_EXTENSION (sizeof=36)
  +00 int16 Type
  +02 uint16 Size
  +04 struct _DEVICE_OBJECT *DeviceObject
  +08 uint32 PowerFlags
  +0c *Dope
  +10 uint32 ExtensionFlags
  +14 void *DeviceNode
  +18 struct _DEVICE_OBJECT *AttachedTo
  +1c struct _LIST_ENTRY FileObjectList
  +1c struct _LIST_ENTRY *Flink
  +20 struct _LIST_ENTRY *Blink
  有一个载入内存的驱动程序文件,就会有一个驱动对象(DRIVER_OBJECT)。
  比如,驱动 i8042prt 是在系统初始化阶段,IO 管理器调用函数 nt!IopLoadDriver 载入的。nt!IopLoadDriver 读取注册表,获得驱动程序文件 i8042prt.SYS 的路径,将这个文件载入内存。之后会调用 nt!ObCreateObject 创建一个驱动对象,并初始化这个驱动对象。
  设备对象(DEVICE_OBJECT)由驱动创建。一个驱动可以创建多个设备对象(DEVICE_OBJECT)。通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象(DEVICE_OBJECT)。一个驱动创建的所有设备对象(DEVICE_OBJECT)链成一条链,该驱动的驱动对象(DRIVER_OBJECT)可以找到这个链。一个设备对象(DEVICE_OBJECT)也可以找到创建它的驱动的驱动对象(DRIVER_OBJECT)。下面我们使用  WinDBG  实际观察驱动 i8042prt 的情况,i8042prt 是完成 ps/2 键盘驱动主要功能的驱动程序。
  在这里我们先做个说明,对于同时插有 ps/2 键盘和 ps/2 鼠标的计算机上, i8042prt 会创建两个设备对象,一个用于键盘,一个用于鼠标。
  首先我们从命名对象地址空间,找到 i8042prt 的 DRIVER_OBJECT 的地址
  kd> !object \driver\i8042prt
  Object: fe4f69f0 Type: (fe4ec3e0) Driver
  ObjectHeader: fe4f69d8
  HandleCount: 0 PointerCount: 5
  Directory Object: fe51bf30 Name: i8042prt
  下面我们看 i8042prt 的 DRIVER_OBJECT 中的内容 
  kd> !strct driver_object fe4f69f0
  struct _DRIVER_OBJECT (sizeof=168)
  +00 int16 Type = 0004
  +02 int16 Size = 00a8
  +04 struct _DEVICE_OBJECT *DeviceObject = FE4D3BA0 
  // 注意 DeviceObject 链上的第一个设备对象
  +08 uint32 Flags = 00000012
  +0c void *DriverStart = FE1BD000
  +10 uint32 DriverSize = 00021000
  +14 void *Dri verSe ction = FE4F6CC8
  +18 struct _DRIVER_EXTENSION *DriverExtension = FE4F6A98
  +1c struct _UNICODE_STRING DriverName
  +1c uint16 Length = 0020
  +1e uint16 MaximumLength = 0020
  +20 uint16 *Buffer = E127FE88
  +24 struct _UNICODE_STRING *HardwareDatabase = 80541FC0
  +28 struct _FAST_IO_DISPATCH *FastIoDispatch = 00000000
  +2c function *DriverInit = FE1D8000
  +30 function *DriverStartIo = FE1BF444
  +34 function *DriverUnload = FE1CB920
  +38 function *MajorFunction[28] = FE1CB000
  80425354
  FE1CB14E
  80425354
  80425354
  80425354
  80425354
  80425354
  80425354
  FE1BE558
  80425354
  80425354
  80425354
  80425354
  FE1CB243
  FE1BE667
  80425354
  80425354
  80425354
  80425354
  80425354
  80425354
  FE1D2C63
  FE1D3633
  80425354
  80425354
  80425354
  FE1D1BFB
  我们看到了 DeviceObject 上的第一个设备对象的地址 FE4D3BA0 ,我们使用 WinDbg 的 !devobj 命令,来获得一些信息
  kd> !devobj FE4D3BA0
  Device object (fe4d3ba0) is for:
  \Driver\i8042prt DriverObject fe4f69f0
  Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
  DevExt fe4d3c58 DevObjExt fe4d3ed8 
  ExtensionFlags (0xc0000000) 
  Unknown flags 0xc0000000
  AttachedDevice (Upper) fe4d3a60 \Driver\vmmouse
  AttachedTo (Lower) fe4dd610 \Driver\ACPI
  Device queue is not busy.
  从获得的信息来看,这是用于鼠标的那个设备对象,下面我们看这个设备对象 DEVICE_OBJECT 中的具体内容
  kd> !strct device_object FE4D3BA0
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type = 0003
  +02 uint16 Size = 0338
  +04 int32 ReferenceCount = 00000000
  +08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0 
  // 正是 \Driver\i8042prt 的驱动对象的地址
  +0c struct _DEVICE_OBJECT *NextDevice = FE4F5020
  // DeviceObject 链的下一个
  +10 struct _DEVICE_OBJECT *AttachedDevice = FE4D3A60
  +14 struct _IRP *CurrentIrp = 00000000
  ...
  我们看到了设备对象 +0c struct _DEVICE_OBJECT *NextDevice 处是 DeviceObject 链上的下一个设备对象的地址,值为 FE4F5020 ,我们使用 WinDbg 的 !devobj 命令,来获得一些信息
  kd> !devobj FE4F5020
  Device object (fe4f5020) is for:
  \Driver\i8042prt DriverObject fe4f69f0
  Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
  DevExt fe4f50d8 DevObjExt fe4f5358 
  ExtensionFlags (0xc0000000) 
  Unknown flags 0xc0000000
  AttachedDevice (Upper) fe4f5df0 \Driver\Kbdclass
  AttachedTo (Lower) fe4dd730 \Driver\ACPI
  Device queue is not busy.
  从获得的信息来看,这是用于键盘的那个设备对象,下面我们看这个设备对象 DEVICE_OBJECT 中的具体内容
  kd> !strct device_object FE4F5020
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type = 0003
  +02 uint16 Size = 0338
  +04 int32 ReferenceCount = 00000000
  +08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0
  // 正是 \Driver\i8042prt 的驱动对象的地址
  +0c struct _DEVICE_OBJECT *NextDevice = 00000000
  // 为空,表明链结束了。
  +10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5DF0
  +14 struct _IRP *CurrentIrp = 00000000
  ...
  我们观察初始化时,看到 i8042prt 调用 IoCreateDevice 创建设备对象,并且 IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中。
  现在我们对驱动对象和设备对象之间的联系,做一个总结,
  驱动调用 IoCreateDevice 创建设备对象,一个驱动创建的所有设备对象链成一个链,这个链以空表示结束。驱动对象通过 +04 struct _DEVICE_OBJECT *DeviceObject 可以找到这个链。设备对象通过 +0c struct _DEVICE_OBJECT *NextDevice 链在一起。设备对象通过 +08 struct _DRIVER_OBJECT *DriverObject 可以找到创建它的驱动的驱动对象。
  下面我们来讲讲设备栈。一个物理设备的驱动任务,通常由几个驱动程序一层一层的共同完成。每层一个设备对象,他们联系在一起,组成一个设备栈。一个设备栈上的设备对象,从上自下,联系在一起。从下自上,也联系在一起。我们以 ps/2 键盘驱动为例,使用 WinDbg 来观察它的设备栈,以及他们如何联系在一起。
  ps/2 键盘驱动的设备栈,大概就是这个样子
  kd> !devstack fe4f5020
  !DevObj ! DrvObj  !DevExt ObjectName
  fe4f5df0 \Driver\Kbdclass fe4f5ea8 KeyboardClass0
  > fe4f5020 \Driver\i8042prt fe4f50d8 
  fe4dd730 \Driver\ACPI fe507468 0000000e
  !DevNode fe4fed68 :
  DeviceInst is "ACPI\PNP0303\4&5289e18&0"
  ServiceName is "i8042prt"
  最上面的 DEVICE_OBJECT 地址为 fe4f5df0,属于驱动 \Driver\Kbdclass
  中间的 DEVICE_OBJECT 地址为 fe4f5020,属于驱动 \Driver\i8042prt
  最下面的 DEVICE_OBJECT 地址为 fe4dd730,属于驱动 \Driver\ACPI
  下面我们看这3个设备对象,如何联系在一起。
  最上面一层的 DEVICE_OBJECT
  kd> !devobj fe4f5df0
  Device object (fe4f5df0) is for:
  KeyboardClass0 \Driver\Kbdclass DriverObject fe4f6330
  Current Irp fe43a1c8 RefCount 0 Type 0000000b Flags 00002044
  DevExt fe4f5ea8 DevObjExt fe4f5fd8 
  ExtensionFlags (0000000000) 
  AttachedTo (Lower) fe4f5020 \Driver\i8042prt
  Device queue is busy -- Queue empty.
  看到这个设备对象有名字,叫 KeyboardClass0 ,属于驱动 Kbdclass
  kd> !strct device_object fe4f5df0
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type = 0003
  +02 uint16 Size = 01e8
  +04 int32 ReferenceCount = 00000000
  +08 struct _DRIVER_OBJECT *DriverObject = FE4F6330
  // 正是 kbdclass 的 DRIVER_OBJECT 的地址
  +0c struct _DEVICE_OBJECT *NextDevice = 00000000
  +10 struct _DEVICE_OBJECT *AttachedDevice = 00000000
  // 为空结束,表示上面没有其它设备对象了
  +14 struct _IRP *CurrentIrp = FE43A1C8
  ...
  +ae uint16 Spare1 = 0000
  +b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4F5FD8
  // 它的 DEVOBJ_EXTENSION 在 FE4F5FD8
  +b4 void *Reserved = 00000000
  kd> !strct DEVOBJ_EXTENSION FE4F5FD8
  struct _DEVOBJ_EXTENSION (sizeof=36)
  +00 int16 Type = 000d
  +02 uint16 Size = 0000
  +04 struct _DEVICE_OBJECT *DeviceObject = FE4F5DF0
  +08 uint32 PowerFlags = 00000010
  +0c *Dope = 00000000
  +10 uint32 ExtensionFlags = 00000000
  +14 void *DeviceNode = 00000000
  +18 struct _DEVICE_OBJECT *AttachedTo = FE4F5020
  // 下面一层的设备对象为 FE4F5020
  +1c struct _LIST_ENTRY FileObjectList
  +1c struct _LIST_ENTRY *Flink = 00000000
  +20 struct _LIST_ENTRY *Blink = 00000000
  中间层的 DEVICE_OBJECT
  kd> !devobj fe4f5020
  Device object (fe4f5020) is for:
  \Driver\i8042prt DriverObject fe4f69f0
  Current Irp 00000000 RefCount 0 Type 00000027 Flags 00002004
  DevExt fe4f50d8 DevObjExt fe4f5358 
  ExtensionFlags (0xc0000000) 
  Unknown flags 0xc0000000
  AttachedDevice (Upper) fe4f5df0 \Driver\Kbdclass
  AttachedTo (Lower) fe4dd730 \Driver\ACPI
  Device queue is not busy.
  看到这个设备对象没有名字,属于驱动 i8042prt
  kd> !strct device_object fe4f5020
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type = 0003
  +02 uint16 Size = 0338
  +04 int32 ReferenceCount = 00000000
  +08 struct _DRIVER_OBJECT *DriverObject = FE4F69F0
  // 正是 i8042prt 的 DRIVER_OBJECT 的地址
  +0c struct _DEVICE_OBJECT *NextDevice = 00000000
  +10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5DF0
  // 上面一层的设备对象
  +14 struct _IRP *CurrentIrp = 00000000
  ...
  +ae uint16 Spare1 = 0001
  +b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4F5358
  // 它的 DEVOBJ_EXTENSION 在 FE4F5358
  +b4 void *Reserved = 00000000
  kd> !strct DEVOBJ_EXTENSION FE4F5358
  struct _DEVOBJ_EXTENSION (sizeof=36)
  +00 int16 Type = 000d
  +02 uint16 Size = 0000
  +04 struct _DEVICE_OBJECT *DeviceObject = FE4F5020
  +08 uint32 PowerFlags = 00000000
  +0c *Dope = 00000000
  +10 uint32 ExtensionFlags = c0000000
  +14 void *DeviceNode = 00000000
  +18 struct _DEVICE_OBJECT *AttachedTo = FE4DD730
  // 下面一层的设备对象
  +1c struct _LIST_ENTRY FileObjectList
  +1c struct _LIST_ENTRY *Flink = 00000000
  +20 struct _LIST_ENTRY *Blink = 00000000
  最下面一层的 DEVICE_OBJECT
  kd> !devobj fe4dd730
  Device object (fe4dd730) is for:
  0000000e \Driver\ACPI DriverObject fe4e8ad0
  Current Irp 00000000 RefCount 1 Type 00000032 Flags 00001040
  DevExt fe507468 DevObjExt fe4dd7e8 DevNode fe4fed68 
  ExtensionFlags (0000000000) 
  AttachedDevice (Upper) fe4f5020 \Driver\i8042prt
  Device queue is not busy.
  看到这个设备对象有名字,叫 0000000e ,属于驱动 acpi
  kd> !strct device_object fe4dd730
  struct _DEVICE_OBJECT (sizeof=184)
  +00 int16 Type = 0003
  +02 uint16 Size = 00b8
  +04 int32 ReferenceCount = 00000001
  +08 struct _DRIVER_OBJECT *DriverObject = FE4E8AD0
  // 正是 acpi 的 DRIVER_OBJECT 的地址
  +0c struct _DEVICE_OBJECT *NextDevice = FE4DD850
  +10 struct _DEVICE_OBJECT *AttachedDevice = FE4F5020
  // 上面一层的设备对象
  +14 struct _IRP *CurrentIrp = 00000000
  ...
  +ae uint16 Spare1 = 0001
  +b0 struct _DEVOBJ_EXTENSION *DeviceObjectExtension = FE4DD7E8
  // 它的 DEVOBJ_EXTENSION 在 FE4DD7E8
  +b4 void *Reserved = 00000000
  kd> !strct DEVOBJ_EXTENSION FE4DD7E8
  struct _DEVOBJ_EXTENSION (sizeof=36)
  +00 int16 Type = 000d
  +02 uint16 Size = 0000
  +04 struct _DEVICE_OBJECT *DeviceObject = FE4DD730
  +08 uint32 PowerFlags = 00000010
  +0c *Dope = 00000000
  +10 uint32 ExtensionFlags = 00000000
  +14 void *DeviceNode = FE4FED68
  +18 struct _DEVICE_OBJECT *AttachedTo = 00000000
  // 为空结束,表示下面没有其它设备对象了
  +1c struct _LIST_ENTRY FileObjectList
  +1c struct _LIST_ENTRY *Flink = 00000000
  +20 struct _LIST_ENTRY *Blink = 00000000
  从上自下。\Device\KeyboardClass0(DRIVER_OBJECT 是 kbdclass)可以找到 没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)。没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)可以找到 \Device\0000000e(DRIVER_OBJECT 是 ACPI)。
  从下自上。\Device\0000000e(DRIVER_OBJECT 是 ACPI) 可以找到 没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)。没有名字地址为fe4f5020的DEVICE_OBJECT(DRIVER_OBJECT 是 i8042prt)可以找到 \Device\KeyboardClass0(DRIVER_OBJECT 是 ACPI)。
  现在我们对设备栈上的设备对象之间的联系,做一个总结,
  设备栈上的设备对象,从上自下,通过 DEVICE_OBJECT 的 DEVOBJ_EXTENSION 的 +18 struct _DEVICE_OBJECT *AttachedTo 联系在一起。
  设备栈上的设备对象,从下自上,通过 DEVICE_OBJECT 的 +10 struct _DEVICE_OBJECT *AttachedDevice 联系在一起。
  在观察键盘驱动的初始化过程中,我们看到了设备栈是如何建立起来的。
  驱动 i8042prt ,驱动 kbdclass 分别调用 IoAttachDeviceToDeviceStack ,把自己相应的设备对象加入到键盘设备栈中。IoAttachDeviceToDeviceStack 会设置 DEVICE_OBJECT 的 DEVOBJ_EXTENSION 的 +18 struct _DEVICE_OBJECT *AttachedTo 和 DEVICE_OBJECT 的 +10 struct _DEVICE_OBJECT *AttachedDevice ,把两个对象联系在一起。并设置新加入的设备对象的 +30 char StackSize。
  晕了吧
  欢迎交流,欢迎交朋友,
  欢迎访问
  主页 http://jiurl.yeah.net http://jiurl.nease.net 论坛 http://jiurl.cosoft.org.cn/forum 
  f啊k,不带你们这样的啊,有好事不叫我。
基于I2C协议的OLED显示(利用U82G库) U8g2是GitHub上一款十分优秀的开源图形库(GUI库),其本质是嵌入式设备的单色图形库。在 Github 上超过3.2K Star,2.6K Commit。其开发语言90%为C语言,且代码简洁干练便于移植与后期修改。 阅读详情

相关推荐

从零到一:51单片机智能路灯控制系统的硬件选型与实战避坑指南

本文详细介绍了51单片机智能路灯控制系统的硬件选型与实战优化,涵盖系统架构设计、传感器选型、时钟模块替代方案、电源电路设计及软件优化技巧。通过Proteus仿真和实际案例,帮助开发者避开常见陷阱,提升系统稳定性和效率,特别适合初学者和电子爱好者参考。

weixin_29159127的博客 339

驱动对象设备对象

----------------以下内容摘自《寒江独钓》------------------- 1、驱动对象:   一个驱动对象代表了一个驱动程序。或者说一个内核模块。驱动对象的结构如下(这个结构的定义取自 WDK中的 wdm.h)。下面有一些域用省略号代替。  typedef struct _DRIVER_OBJECT { // 结构的类型和大小。 CSHORT T

吖吖狼 的专栏 2227

ObservableList

普通的List是“数据容器”,而是“数据源 + 广播电台”。添加的功能:监听器、精准变更报告、原子替换、实时过滤/排序视图。本质区别普通List是被动的(你问它才给)。是主动的(它变了就立刻告诉你,并且告诉你“哪里变了、怎么变的”)。正是因为它具备了这种“主动广播”的能力,JavaFX 才能实现数据驱动 UI的响应式编程模型。你之前在ComboBox源码中看到的监听器,正是依赖的机制才得以生效的。

qq_24992377的博客 399

DRIVER_OBJECT & DEVICE_OBJECT

驱动对象结构 DRIVER_OBJECT ,定义如下:(左侧为相对于结构体起始地址的偏移量)  struct _DRIVER_OBJECT (sizeof=168)  +00 int16 Type  +02 int16 Size  +04 struct _DEVICE_OBJECT *DeviceObject  +08 uint32 Flags  +0c void *DriverStart  +10 uint32 DriverSize  +14 void *DriverSection(即为PsLoadedM

Iqian1314的专栏 2665

LCD1602A的创客变形记:从字符显示到自定义字模的艺术

本文详细介绍了如何通过STC89C52单片机驱动LCD1602A显示屏实现自定义字模显示。从硬件连接到CGRAM编程,再到中文字符和动态效果实现,为创客提供了突破LCD1602A字符限制的完整解决方案,让经典显示屏焕发全新艺术生命力。

whisky的博客 1018

【STM32嵌入式入门基础教程之OLED】显示基于(HAL库)移植U8g2的图形库

本次基于 STM32F103C8T6 与 HAL 库的 OLED 显示实践,围绕 “I2C 协议应用 + 开源库移植 + 可视化功能落地” 核心目标,形成了从理论到实践的完整闭环,有效验证了嵌入式显示开发的标准化流程。 实践中,通过 CubeMX 快速生成 HAL 库工程框架,规避了底层寄存器配置的复杂操作;针对 U8g2 库的裁剪移植是关键突破 —— 仅保留 SSD1306 驱动与 I2C 通信相关文件,既减少工程体积,又确保适配 0.96 寸 OLED 屏,成功实现 U8g2 demo 例程验证、个人学

2503_93493602的博客 1049

基于STM32的简易计算器proteus仿真设计(仿真+程序+设计报告+讲解视频)

基于STM32的简易计算器proteus仿真设计(仿真+程序+设计报告+讲解视频)仿真图proteus 8.9程序编译器:keil 5编程语言:C语言设计编号:C0089。

weixin_52733843的博客 6440

温湿度采集与OLED显示

(一)基于STM32移植U8g2图形库——OLED显示(HAL库) 一、U8g2简介 1、 U8g2是什么 U8g2是GitHub上一款十分优秀的开源图形库(GUI库),其本质是嵌入式设备的单色图形库。在 Github 上超过3.2K Star,2.6K Commit。其开发语言90%为C语言,且代码简洁干练便于移植与后期修改。 2、 U8g2支持的显示控制器 U8g2支持单色OLED和LCD,包括以下控制器:SSD1305, SSD1306, SSD1309, SSD1312, SSD1

2401_87430833的博客 900

玩转u8g2 OLED库,一篇就够

1.前言     最近博主听到QQ群里面问得比较多的问题:     “博哥,有玩过OLED吗?”     “博哥,有试过在ESP8266上调成功过SSD1306吗?”     “博哥,OLED上显示天气信息怎么弄?”     ........     诸如此类的问题,在博主看来,无非都是对OLED库用法的不熟悉甚至不了解。Arduino OLED库众多,博主也曾经介绍过一款 《博哥OLED系列》-...

weixin_30510153的博客 2万+

olcd12864的u8g2库_玩转u8g2 OLED库,一篇就够

1.前言????最近博主听到QQ群里面问得比较多的问题:????“博哥,有玩过OLED吗?”????“博哥,有试过在ESP8266上调成功过SSD1306吗?”????“博哥,OLED上显示天气信息怎么弄?”????........????诸如此类的问题,在博主看来,无非都是对OLED库用法的不熟悉甚至不了解。Arduino OLED库众多,博主也曾经介绍过一款 《博哥OLED系列》- 玩转SSD...

weixin_39598584的博客 1370

Java List 接口详解:从基础到实战

List 接口继承自 Collection 接口,位于 java.util 包中。它允许存储重复元素,并且每个元素都有对应的索引位置,可以通过索引精确访问元素。与 Set 不同,List 保持了元素的插入顺序。有序性:元素按照插入顺序排列,可通过索引访问。可重复:允许存储相同的元素。允许 null:大多数实现类允许存储 null 值。索引访问:支持通过下标快速定位元素。List 是 Java 集合框架中最重要的接口之一,掌握它的实现原理、常用方法和遍历方式,是写出高质量 Java 代码的基础。

2601_96129122的博客 240

C++ STL 容器深度剖析:vector 与 list 的模拟实现及对比

vector与list模拟实现

2402_87037360的博客 246

⚡Spring JDBC 完整体系 · 第 9 集 | 封装 list 方法 · 泛型初探

<think>我们只需要从给定内容生成一篇≤150字的摘要。内容是关于Spring JDBC封装list方法、泛型初探的。需要概括核心:项目背景、痛点(每次new RowMapper)、解决方案(BaseDao封装list泛型方法,传入SQL和Class,内部用BeanPropertyRowMapper)。注意字数限制,抓重点。直接输出摘要,不要其他格式。</think>本集针对Spring JDBC中查询需反复手动创建BeanPropertyRowMapper的痛点,设计了一个通用BaseDao并封装泛型

gzz_gzz的博客 445

list(C++)

<think>我们根据要求生成≤150字的摘要。内容是关于std::list的特点、使用和底层实现。注意字数限制,需要精炼。</think>摘要:本文讲解C++ STL中std::list双向链表容器的原理与使用。其优势是任意位置O(1)插入删除,迭代器不易失效;缺点是不支持随机访问、内存开销大。介绍了增删改查、迭代器特性及splice、sort等专属操作,并强调不能使用std::sort。最后简要模拟实现迭代器与核心接口,指导开发者按场景选择合适的容器。

2401_87882972的博客 392

06-04-排序集合-SortedList-TKey-TValue-双数组实现的有序集合

二分查找是 O(log n),后缀搬移是 O(n),所以中间插入总复杂度是 O(n)。随机生成 Add、setter、Remove、Clear 和查询序列,每步检查 Count、键顺序、键值对应和重复键行为。大 O 不告诉转折点。搬移后,原有最后一个有效槽会留下重复引用,实现应在 TKey/TValue 是引用或含引用时将尾槽置为 default,避免已删对象被后备数组继续保活。二分查找使按键查询为 O(log n),连续布局使枚举和已知索引访问紧凑,中间插入/删除则因双数组搬移为 O(n)。

设计 AI 玩游戏🎮 1109

%DUMP LIBRARIES-List Loaded PSL Libraries

%DUMP LIBRARIES-List Loaded PSL Libraries

z19861216的博客 180

C++ list 深度解析:从使用原理到模拟实现

list 是带头结点的双向循环链表,插入删除 O(1),无法随机访问。其迭代器通过 Ref/Ptr 模板区分普通与 const 版本;注意 typename 语法陷阱。因节点零散,排序比 vector 慢,可拷入 vector 排序再拷回优化。splice/merge 仅改指针,效率极高。适合频繁插入删除、无需随机访问的场景。

xxwxx__的博客 355
上一篇: 图解:vmware workstation 8.0.3 安装vmware tools及文件共享
下一篇: device tree的搭建过程
htqlhy
博客等级 码龄19年 5粉丝 0原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值