前言

  • 本笔记主要传达一种设备驱动拆分的概念和实现。
  • 使得写好一个驱动框架后,随意添加相应设备,提高开发效率。
  • 使用到以空间换时间的方法,即是数组管理设备,使得时间复杂度为 O(1)。(数组直接定位)。
  • 本笔记的框架支持 N个设备 绑定 X个驱动

笔录草稿

概要

  • 触发想法

    • 有时候,在写驱动时,发现多个设备使用同一个驱动逻辑,只是部分内容不一样(如引脚),此时就可以想如何写出一个驱动逻辑支持多个不同设备。
  • 例子:IIC
    • 一个 IIC 逻辑
    • 多个设备绑定 IIC
    • 目标效果:
      • 只需要执行以下步骤即可: 注册 IIC 驱动 --> 注册实际设备A并绑定 IIC --> 初始化该 IIC
      • 只需要执行以下步骤即可: 注册 IIC 驱动 --> 注册实际设备B并绑定 IIC --> 初始化该 IIC

原理及实现方法

  • ID 为数组下标,可以根据 ID 获得 驱动或设备 句柄。(LiteOS 里任务ID和任务句柄也类似噢)

  • 数组为 驱动数组或设备数组或其它需要统一管理的数组等等。主要为实体开辟空间,直接定位使用。

    • 使用数组管理是明显的 空间换时间的方法,时间复杂度达到O(1)
    • 当然也可以使用链表,但是时间复杂度可能达不到 O(1)。
  • 实现 驱动部分

    1. 创建两个驱动文件:bsp_xx.cbsp_x.h
    2. 创建 xx 驱动名字列表
      • 名字列表也就是 ID,用于下标、校验和操作

        • 下标:数组下标,用于直接定位,获得驱动句柄
        • 校验:下标对应的驱动里面也有保存 驱动 ID 的,在使用时,通过对比操作带来的ID与结构体里面的ID是否相等即可检查到是否获得准确的驱动实体
        • 操作:通过 ID 获得驱动句柄,便可进行操作
    3. 组建 xx 驱动结构体
      • xx 驱动结构体里面

        1. 必须包含 驱动 ID
        2. 其他业务成员
    4. 编写 注册 xx 驱动函数
      • 注册 xx 驱动函数 其实就是一个初始化,初始化 驱动ID 对应驱动数组下标的实体驱动
      • 必须给对应实体驱动里的 驱动ID 赋 当前 ID 值,这样使用时便可以校验
    5. 创建 xx 驱动数组
      • xx 驱动数组 就是所有驱动实体的空间,不同下标对应不同的实体驱动
      • 使用到数组,即是静态申请空间。当然也可以自己实现动态申请,如用链表的方法或者动态申请内存空间。
    6. 编写驱动逻辑
      • 一个驱动,支持多个设备
      • 驱动逻辑,多个设备的驱动逻辑相似,不同点可以通过 驱动结构体 中的成员区别开来。
  • 实现 设备部分

    1. 创建两个设备文件:lss_yy.clss_yy.h
    2. 创建 yy 设备名字列表
      • 名字列表也就是 ID,用于下标、校验和操作

        • 下标:数组下标,用于直接定位,获得驱动句柄
        • 校验:下标对应的设备里面也有保存 设备 ID 的,在使用时,通过对比操作带来的ID与结构体里面的ID是否相等即可检查到是否获得准确的设备实体
        • 操作:通过 ID 获得设备句柄,便可进行操作
    3. 组建 xx 设备结构体
      • xx 设备结构体里面

        1. 必须包含 设备 ID : 用于标识本结构体为哪一个设备
        2. 必须包含 驱动 ID : 就是绑定的 驱动 ID
        3. 其他业务成员
    4. 编写 注册 xx 设备函数
      • 注册 xx 设备函数 其实就是一个初始化,初始化 设备ID 对应驱动数组下标的实体设备
      • 必须给对应实体驱动里的 驱动ID 赋 当前 ID 值,这样使用时便可以校验
    5. 创建 xx 设备数组
      • xx 设备数组 就是所有设备实体的空间,不同下标对应不同的实体设备
      • 使用到数组,即是静态申请空间。当然也可以自己实现动态申请,如用链表的方法或者动态申请内存空间。
    6. 编写设备逻辑
      • 在设备逻辑中,通过 设备ID和设备数组 获得设备实体,再在设备实体中找到驱动ID,把设备ID传给驱动逻辑函数即可。
    7. 实现设备初始化函数 **
      • 简要步骤(必须遵循前三个步骤的顺序

        1. 先注册 xx 驱动
        2. 注册 yy 设备,并绑定对应的 xx 驱动
        3. 初始化 xx 引脚
        4. 执行自己的驱动业务

IIC 例子实战-驱动

  • 通过实现一下步骤,我们便实现了 设备驱动框架的驱动部分
  • 简要步骤
    1. 创建两个文件:bsp_i2c.cbsp_i2c.h
    2. 创建 I2C 驱动名字列表
    3. 组建 I2C 驱动结构体
    4. 编写 注册 I2C 驱动函数
    5. 创建 I2C 驱动数组
    6. 编写驱动逻辑
      1. static uint32_t selectClkByGpio(const uint32_t addr) 选择时钟信号函数
      2. void i2cGpioInit(eI2C_ID id) I2C 引脚初始化函数
      3. void i2cStart(eI2C_ID id) I2C Start 函数
      4. void i2cStop(eI2C_ID id) I2C Stop 函数
      5. uint8_t i2cSendByte(eI2C_ID id, uint16_t TxData) I2C SendByte 函数
      6. uint8_t i2cReceiveByte(eI2C_ID id) I2C ReceiveByte 函数
      7. void i2cAck(eI2C_ID id, uint8_t Ack) I2C Ack 函数
      8. uint8_t i2cWaitAck(eI2C_ID id) I2C WaitAck 函数

1. 创建文件

  • 创建两个文件:bsp_i2c.cbsp_i2c.h

2. 创建 I2C 驱动名字列表

  • 本驱动列表需要根据实际设备修改
  • 驱动名字其实就是对应驱动数组下标,用于直接定位
  • 注意:
    • 第一个驱动名必须从 0 开始
    • ei2cDEVICE_COUNT 是和 i2cI2C_DEVICE_COUNT 一样的大小,在实际工程中,二选一即可。
  • 源码例子如下,驱动名字按照自己的命名风格命名即可。
/*
*********************************************************************************************************
* CONFIG
*********************************************************************************************************
*/
// [注][I2C] 根据实际设备修改
// i2c 驱动数量
#define i2cI2C_DRIVER_COUNT 3
/**
* @brief i2c id
* @author lzm
*/
typedef enum
{
ei2cEEPROM_1 = 0, // 第一个 EEPROM 设备驱动
ei2cEEPROM_2, // 第二个 EEPROM 设备驱动
ei2cMPU6050, // MPU6050设备驱动 ei2cDEVICE_COUNT; // 驱动数量
}eI2C_ID;

3. 组建 I2C 驱动结构体

  • I2C 驱动结构体必须包含

    1. I2C ID : 就是一个实体 I2C 的 ID驱动数组下标
    2. SCL 及 SDA 引脚数据。
  • 结构体中的延时数据,主要是为了 IIC 速度可控。
/*
*********************************************************************************************************
* BASIC
*********************************************************************************************************
*/
/**
* @brief i2c struct
* @author lzm
*/
struct I2C_T{
/* id */
eI2C_ID ID; /* delay */
// cnt
unsigned char delayUsCnt;
// delay function
void ( *delayUsFun )(int cnt); /* pin */
GPIO_TypeDef * sclGpiox;
uint16_t sclPin;
GPIO_TypeDef * sdaGpiox;
uint16_t sdaPin;
};
typedef struct I2C_T i2c_t;

4. 编写-注册 I2C 驱动函数

  • 注册 I2C 驱动函数 其实就是初始化对应驱动的参数,如绑定 SCL 和 SDA 引脚。
  • 在开发中,实际设备绑定及使用 I2C 之前必须先注册对应 I2C 驱动。
  • 一些参数解析
    • @param delayuscnt : 延时多少个 微妙
    • @param fun : 微妙延时函数
    • @param sclgpio : SCL 引脚 port
    • @param sclpin : SCL 引脚 pin
    • @param sdagpio : SDA 引脚 port
    • @param sdapin : SDA 引脚 pin
/*
*********************************************************************************************************
* DEFINE [API] FUNCTION
*********************************************************************************************************
*/
/**
* @brief 注册IIC设备
* i2cDeviceElem[i2cID].id = i2cID; // 保持下标与ID相等,查找时可以直接定位,实现时间复杂度为O(1);
* @param
* @retval none
* @author lzm
*/
#define REGISTER_I2C_DRI(i2cID, delayuscnt, fun, sclgpio, sclpin, sdagpio, sdapin) \
{ \
i2cDeviceElem[i2cID].id = i2cID; \
i2cDeviceElem[i2cID].delayUsCnt = delayuscnt; \
i2cDeviceElem[i2cID].delayUsFun = fun; \
i2cDeviceElem[i2cID].sclGpiox = sclgpio; \
i2cDeviceElem[i2cID].sclPin = sclpin; \
i2cDeviceElem[i2cID].sdaGpiox = sdagpio; \
i2cDeviceElem[i2cID].sdaPin = sdapin; \
}

5. 创建 I2C 驱动数组

  • i2cI2C_DRIVER_COUNT 表示有 i2cI2C_DRIVER_COUNT 个 I2C 驱动
  • 创建 I2C 驱动数组是提前为可能需要用到 I2C 驱动的设备提前申请空间(静态),当然也可以动态申请。
/*
*********************************************************************************************************
* DEFINE
*********************************************************************************************************
*/
// i2c 驱动元素(设备表)
i2c_t i2cDriverElem[i2cI2C_DRIVER_COUNT];

6. 编写驱动逻辑

static uint32_t selectClkByGpio(const uint32_t addr) 选择时钟信号函数
  • 本函数主要用于根据引脚端口来选择时钟,当然也可以选择把 时钟变量 放到 I2C 驱动结构体里面
  • 形参: const uint32_t addr 需要初始化引脚对应的 port
  • 返回:返回时钟值 或 NULL
/**
* @brief 选出时钟信号线
* @param addr : 引脚对应 port
* @retval 返回时钟值 或 NULL
* @author lzm
*/
static uint32_t selectClkByGpio(const uint32_t addr)
{
switch(addr)
{
case GPIOA_BASE:
return RCC_APB2Periph_GPIOA;
case GPIOB_BASE:
return RCC_APB2Periph_GPIOB;
case GPIOC_BASE:
return RCC_APB2Periph_GPIOC;
case GPIOD_BASE:
return RCC_APB2Periph_GPIOD;
case GPIOE_BASE:
return RCC_APB2Periph_GPIOE;
case GPIOF_BASE:
return RCC_APB2Periph_GPIOF;
case GPIOG_BASE:
return RCC_APB2Periph_GPIOG;
}
return NULL;
}
void i2cGpioInit(eI2C_ID id) 初始化I2C引脚
  • 本函数主要用于初始化 I2C 需要的引脚:SCL 和 SDA
  • 形参: eI2C_ID id 为 I2C 驱动 ID,可以理解为需要初始化哪一个 I2C 驱动,从 I2C 驱动命名表中选出。
  • 返回:无
  • 分析
    • 原理:I2C 驱动 ID 即是 I2C 驱动数组下标,对应一个 I2C 驱动,通过 ID 可以获取 I2C 数据,然后做出处理。
    • 步骤:
      1. 获取需要初始化的时钟值 sclGpioClksdaGpioClk
      2. 初始化需要的时钟
      3. 配置初始化引脚结构体并初始化
      4. 拉高 SCL 和 SDA引脚。
/**
* @brief 初始化I2C引脚
* @param id : I2C 驱动 ID
* @retval none
* @author lzm
*/
void i2cGpioInit(eI2C_ID id)
{
GPIO_InitTypeDef G_GPIO_IniStruct; //定义结构体
uint32_t sclGpioClk;
uint32_t sdaGpioClk;
const i2c_t * i2c = &i2cDeviceElem[id]; sclGpioClk = selectClkByGpio((uint32_t)(i2c->sclGpiox));
sdaGpioClk = selectClkByGpio((uint32_t)(i2c->sdaGpiox)); RCC_APB2PeriphClockCmd(sclGpioClk | sdaGpioClk, ENABLE); //打开时钟 G_GPIO_IniStruct.GPIO_Pin = i2c->sclPin; //配置端口及引脚(指定方向)
G_GPIO_IniStruct.GPIO_Mode = GPIO_Mode_Out_OD;
G_GPIO_IniStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(i2c->sclGpiox, &G_GPIO_IniStruct); //初始化端口(开往指定方向) G_GPIO_IniStruct.GPIO_Pin = i2c->sdaPin; //配置端口及引脚(指定方向)
G_GPIO_IniStruct.GPIO_Mode = GPIO_Mode_Out_OD;
G_GPIO_IniStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(i2c->sdaGpiox, &G_GPIO_IniStruct); //初始化端口(开往指定方向) // 初始化完以后先拉高
iicOutHi(i2c->sclGpiox, i2c->sclPin);
iicOutHi(i2c->sdaGpiox, i2c->sdaPin);
}
void i2cStart(eI2C_ID id) I2C Start函数
  • 本函数为 I2C 逻辑函数 Start 部分
  • 形参: eI2C_ID id 为 I2C 驱动 ID,可以理解为需要初始化哪一个 I2C 驱动,从 I2C 驱动命名表中选出。
  • 返回:无
  • 分析
    • 原理:I2C 驱动 ID 即是 I2C 驱动数组下标,对应一个 I2C 驱动,通过 ID 可以获取 I2C 数据,然后做出处理。
    • 步骤:
      1. 从驱动表中获取一个驱动的句柄进行操作,i2c_t * i2c = &i2cDeviceElem[id];
      2. 通过句柄获取该 I2C 驱动数据,实现逻辑
/**
* @brief IIC START
* @param id : I2C 驱动 ID
* @retval none
* @author lzm
*/
void i2cStart(eI2C_ID id)
{
i2c_t * i2c = &i2cDeviceElem[id]; iicSdaOutHi(i2c);
iicSclOutHi(i2c);
i2c->delayUsFun(i2c->delayUsCnt);
iicSdaOutLo(i2c);
i2c->delayUsFun(i2c->delayUsCnt);
}
其余 I2C 逻辑函数
  • 其余 I2C 逻辑函数原理和 void i2cStart(eI2C_ID id) 函数原理一样,只是实现的逻辑不一样而已,完整源码可以参考我的gitee上的 LiteOS 源码工程。

IIC 例子实战-设备

  • 本笔记选用 eeprom 设备做例子
  • 通过实现一下步骤,我们便实现了 设备驱动框架的设备部分
  • 简要步骤
    1. 创建设备文件:lss_eeprom.clss_eeprom.h
    2. 创建设备名字列表
    3. 组键设备结构体
    4. 编写注册设备函数
    5. 创建设备数组
    6. 实现设备驱动逻辑
    7. 实现设备初始化函数

1. 创建设备文件

  • 直接创建 lss_eeprom.clss_eeprom.h 文件即可。

2. 创建设备名字列表

  • 本设备列表需要根据实际设备修改
  • 设备名字其实就是对应驱动数组下标,用于直接定位
  • 注意:
    • 第一个设备名必须从 0 开始
    • ei2cDEVICE_COUNT 是和 i2cI2C_DEVICE_COUNT 一样的大小,在实际工程中,二选一即可。
  • 源码例子如下,驱动名字按照自己的命名风格命名即可。
/*
*********************************************************************************************************
* CONFIG API
*********************************************************************************************************
*/
/* [注][eeprom]实时修改 */
// eeprom 设备数量
#define eeEEPROM_DEVICE_COUNT 2
/* delay API */
#define eeDelayMs(cnt) vTaskDelay(cnt) /* 调度式延时 */
#define eeEEPROM_WRITE_COUNT 5 /* 写页时等待时间 */ /* fpga id. */
typedef enum
{
eAT24C08_1 = 0,
eAT24C08_2, eeeprom_COUNT,
}eEEPROM_ID;

3. 组键设备结构体

  • 设备结构体必须包含

    1. eEEPROM_ID ID : 就是一个实体 EEPROM 的 ID设备数组下标
    2. eI2C ID : 就是一个实体 I2C 的 ID驱动数组下标
  • 除了以上两个必须的成员外,其他成员可以根据业务自行添加。
  • 以上两个 ID 是 eEEPROM_ID ID 绑定 eI2C ID ,设备结构体只需要知道它对应哪一个 I2C 实体即可,即是只需要知道一个 I2C ID即可。
/*
*********************************************************************************************************
* BASIC
*********************************************************************************************************
*/
/* eeprom struct */
struct EEPROM_T{
/* id */
eEEPROM_ID ID;
/* i2c id */
eI2C_ID i2cID;
};

4. 编写注册设备函数

  • 注册设备函数 其实就是初始化一些数据,如绑定 I2C,绑定 SPI,绑定一些数据等等。
  • 在开发中,实际设备绑定及使用 I2C 之前必须先注册对应 I2C 驱动,然后注册 I2C 设备。
  • 一些参数解析
    • @param eeid : EEPROM ID,用于直接定位,也可以同时用于定位校验。
    • @param i2cid : 设备绑定的 I2C 驱动 ID。
/*
*********************************************************************************************************
* DEFINE [API] FUNCTION
*********************************************************************************************************
*/
/**
* @brief 注册IIC设备
* @param eeid : EEPROM ID,用于直接定位,也可以同时用于定位校验。
* @param i2cid : 设备绑定的 I2C 驱动 ID。
* @retval none
* @author lzm
*/
#define REGISTER_EEPROM_DEV(eeid, i2cid) \
{ \
eepromDeviceElem[eeid].ID = eeid; \
eepromDeviceElem[eeid].i2cID = i2cid; \
}

5. 创建 EEPROM 设备数组

  • eeEEPROM_DEVICE_COUNT 表示有 eeEEPROM_DEVICE_COUNT 个 EEPROM 设备
  • 创建 I2C 驱动数组是提前为可能需要用到 I2C 驱动的设备提前申请空间(静态),当然也可以动态申请。
/*
*********************************************************************************************************
* DEFINE
*********************************************************************************************************
*/
// eeprom 设备元素(设备表)
eeprom_t eepromDeviceElem[eeEEPROM_DEVICE_COUNT];

6. 实现设备驱动逻辑

  • 原理:通过 eI2C_ID i2cid = eepromDeviceElem[id].i2cID; 获取对应的 I2C 驱动实体
  • 例子如下,该函数只需要用设备 ID eEEPROM_ID 管理即可,APP 用户不需接触到 I2C 驱动名字的操作,只需要自己操作的设备的设备名字即可。
eeprom 其中一个逻辑函数
  • 其余逻辑函数自己可以实现,只需要寻址问题即可。
/**
* @brief read [size] bytes from pReadBuf
* @param pReadBuf : store data form addr
* addr : start addr
* size : the size of need read
* @retval 1 : normal
* 0 : abnormal
* @author lzm
*/
uint8_t __eeReadBytes(eEEPROM_ID id, uint16_t addr, uint8_t *pReadBuf, uint16_t lenght)
{
uint16_t i;
uint8_t active = 0x0A;
eI2C_ID i2cid = eepromDeviceElem[id].i2cID; while( active-- )
{
i2cStart(i2cid); if (i2cSendByte(i2cid, eeEEPROM_DEVICE_ADDR + ((addr>>8)<<1)))
{
i2cStop(i2cid);
continue; /* EEPROM器件无应答 */
}
#if 0 // [注][eeprom] AT24C32 及以上的 eeprom才启用
/* High 8 bits address. */
if(LSS_I2C_SendByte(addr>>8))
{
LSS_I2C_Stop();continue;
}
#endif
if (i2cSendByte(i2cid, (uint8_t)(addr)))
{
i2cStop(i2cid);
continue; /* EEPROM器件无应答 */
} i2cStart(i2cid); if (i2cSendByte(i2cid, eeEEPROM_DEVICE_ADDR | eeEEPROM_I2C_RD))
{
i2cStop(i2cid);
continue; /* EEPROM器件无应答 */
} for (i = 0; i < lenght; i++)
{
pReadBuf[i] = i2cReceiveByte(i2cid); if(i == lenght-1)
i2cAck(i2cid,1); //No ACK
else
i2cAck(i2cid,0); //ACK
} i2cStop(i2cid);
return 0; /* 执行成功 */
}
return 1;
}

7. 实现设备初始化函数 **

  • 简要步骤

    1. 先注册 I2C 驱动
    2. 注册 EEPROM 设备,并绑定对应的 I2C 驱动
    3. 初始化 I2C 引脚
    4. 执行自己的驱动业务
/**
* @brief 所有EEPROM设备初始化
* @param
* @retval
* @author lzm
*/
void eepromInit(void)
{
uint8_t eepromID; // 先注册 I2C 驱动
REGISTER_I2C_DRI(ei2cEEPROM_1, 5, dwtDelayUs, EEP_SCL_PORT, EEP_SCL_PIN, EEP_SDA_PORT, EEP_SDA_PIN);
REGISTER_I2C_DRI(ei2cEEPROM_2, 5, dwtDelayUs, EEP_SCL_PORT, EEP_SCL_PIN, EEP_SDA_PORT, EEP_SDA_PIN);
// 注册 EEPROM 设备并绑定 i2c 驱动
REGISTER_EEPROM_DEV(eAT24C08_1, ei2cEEPROM_1);
REGISTER_EEPROM_DEV(eAT24C08_2, ei2cEEPROM_2); for (eepromID = 0; eepromID < eeEEPROM_DEVICE_COUNT; eepromID++)
{
// 初始化 I2C
i2cGpioInit( (eI2C_ID)(ei2cEEPROM + eepromID) );
// 业务 [待写]
}
// 业务 [待写]
}

重要后语(小小鸡汤)

  • 自己写 MCU 驱动时想出上述这种框架,感觉很清晰,很精简,开发效率很高,后面才发现和 linux 的设备驱动框架相识。
  • 不过,想出这个框架还是收获满满的。
    • 要学会 偷懒

      • 这里的 偷懒 是提高效率的意思,这不是一件简单的事,还得学会思考。
      • 搭建好一个优秀的框架,后期开发效率高。如上述中添加一个 I2C 设备,直接在设备列表中添加一个枚举,再在设备初始化代码段中注册、绑定即可。
    • 多出去走走
      • 这里也不是让你经常去游山玩水,而是多逛逛一些优秀的论坛、多看看牛人的博客、多研究一下优秀的源码、多了解一下常用的算法、框架等等

        • 本人圈子小,有优秀的学习源,跪求推荐给我哈哈,好东西不怕多

          • 包括技术、理财、外语(英语、日语)、二次元
        • 本人圈子小,有好的学习源,跪求推荐给我哈哈,好东西不怕多
          • 包括技术、理财、外语(英语、日语)、二次元
        • 本人圈子小,有好的学习源,跪求推荐给我哈哈,好东西不怕多
          • 包括技术、理财、外语(英语、日语)、二次元

框架-设备与驱动的拆分及实现-I2C的更多相关文章

  1. 《连载 | 物联网框架ServerSuperIO教程》- 11.实现设备(驱动)与设备(驱动)交互和级联控制。注:设备驱动模拟金三与普京的对话

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  2. 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架

    misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型.本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动 ...

  3. Linux驱动框架之framebuffer驱动框架

    1.什么是framebuffer? (1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备.帧缓冲 ...

  4. linux设备驱动归纳总结(八):2.总线、设备和驱动的关系【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110295.html linux设备驱动归纳总结(八):2.总线.设备和驱动的关系 xxxxxxxxx ...

  5. Minifilter微过滤框架:框架介绍以及驱动层和应用层的通讯

    minifilter是sfilter后微软推出的过滤驱动框架.相比于sfilter,他更容易使用,需要程序员做的编码更简洁. 系统为minifilter专门制作了一个过滤管理器,这个管理器本身其实是一 ...

  6. 从需求的角度去理解Linux系列:总线、设备和驱动

    笔者成为博客专家后整理以前原创的嵌入式Linux系列博文,现推出以让更多的读者受益. <从需求的角度去理解linux系列:总线.设备和驱动>是一篇有关如何学习嵌入式Linux系统的方法论文 ...

  7. SPI设备的驱动

    主要包括两个SPI设备步骤:register_chrdevspi_register_driver关键点1:spi_board_info可以去已经运行的板子下面找例子:/sys/bus/spi/driv ...

  8. I2C总线、设备、驱动

    I2C总线.设备.驱动 框架 I2C驱动框架可分为3个部分,分别是:I2C核心层.I2C总线驱动层(适配器层)以及I2C设备驱动层: I2C核心层 提供了统一的I2C操作函数,主要有两套函数smbus ...

  9. 【Linux开发】linux设备驱动归纳总结(八):2.总线、设备和驱动的关系

    linux设备驱动归纳总结(八):2.总线.设备和驱动的关系 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. JVM内存溢出与内存泄漏

    内存溢出与内存泄漏 内存溢出相对于内存泄漏来说,尽管更容易被理解,但是同样的,内存溢出也是引发程序崩溃的罪魁祸首之一. 由于GC一直在发展,所有一般情况下,除非应用程序占用的内存增长速度非常快,造成垃 ...

  2. java之死锁

    转载自 https://www.cnblogs.com/xiaoxi/p/8311034.html 一.死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力.然而,并发执行也带来 ...

  3. zookeeper服务端

    服务端启动时,就启动线程通过NIO监听网络端口.每个连接都会有一个上下文环境对象,当接收到请求后,会在上下文环境对象中进行处理. 服务端启动线程,监听网络端口,(NIOServerCnxn.Facto ...

  4. JDK8在windows系统下安装

    一.下载 下载地址:https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK8 目前大部分公司内部使用的还是jdk ...

  5. C# NX二次开发环境搭建

    在网上看到一篇C#二次开发环境搭建的文章:NX二次开发-使用NXOPEN C#手工搭建开发环境配置 ,写得非常好.我按照文章操作,过程中遇到几个问题,把问题分享给大家,希望对各位有帮助. 注意三点: ...

  6. 针对于Java的35 个代码性能优化总结

    针对于Java的35 个代码性能优化总结前言代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的 ...

  7. CCNP:重发布及实验

    重发布(又:重分布.重分发):一台设备同时运行于两个协议或两个进程,默认从两端学习到的路由条目不共享:重发布技术就是人为的进行共享. 一  满足: 1.必须存在ASBR --- 自治系统边界路由器-- ...

  8. 【Processing-日常3】等待动画1

    之前在CSDN上发表过: https://blog.csdn.net/fddxsyf123/article/details/79755976

  9. 面试可能遇到的关联式容器(map、set等)相关问题

    >>>. map与set的区别是什么,各有哪些优势?   map中存储的数据是以键值对(key - value)形式并且通过排序(比较key,默认以 '<' 方式排序)存在的( ...

  10. 微信小程序结合微信公众号进行消息发送

    微信小程序结合微信公众号进行消息发送 由于小程序的模板消息已经废弃了,官方让使用订阅消息功能.而订阅消息的使用限制比较大,用户必须得订阅.需要获取用户同意接收消息的权限.用户必须得和小程序有交互的时候 ...