QNX----I2C驱动框架

本文详细介绍了QNX操作系统中的I2C驱动框架,包括其硬件接口、资源管理器层、共享库接口和资源管理器设计。深入探讨了I2C总线的常见应用及I2C主设备实现方式,解析了I2C硬件接口函数的各个功能,以及硬件访问接口和资源管理器的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                         QNX----I2C驱动框架

   I2C (interintegrated Circuit)是一种简单的串行协议,它以主从关系连接多个设备。多个主设备可以共享一个总线。在不同的事务中,同一设备可以同时充当主从设备。I2C规范定义了这些传输速度范围:

  • ≤ 100 Kbit/s
  • ≤ 400 Kbit/s
  • ≤ 3.4 Mbit/s

I2C框架由以下部分组成:

 

hardware/i2c/*   硬件接口

lib/i2c   资源管理器层

<hw/i2c.h> 定义硬件和应用程序接口的公共头文件

 

I2C总线最常见的应用是对从设备寄存器的低带宽、低速率访问,例如:编写音频编解码器、编程一个RTC程序、读取温度传感器数据等等。 通常,总线上只交换几个字节。可以将I2C主机实现为单线程资源管理器或专用应用程序。资源管理器接口的主要优点是:

1、它为应用程序开发人员提供了一个清晰、易于理解的思路。

2、它作为一个中介,在多个应用程序对一个或多个从设备之间进行访问,强制不同I2C接口之间的一致性。

3、对于专用的i2c总线应用程序,硬件访问库更有效;硬件接口定义了这个库的接口,有助于维护和代码可移植性。

 

1、硬件管理接口

typedef struct {
    size_t size;    /* size of this structure */
    int (*version_info)(i2c_libversion_t *version);
    void *(*init)(int argc, char *argv[]);
    void (*fini)(void *hdl);
    i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    int (*abort)(void *hdl, int rcvid);
    int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
    int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
    int (*driver_info)(void *hdl, i2c_driver_info_t *info);
    int (*ctl)(void *hdl, int cmd, void *msg, int msglen, 
               int *nbytes, int *info);
} i2c_master_funcs_t;

这是整个硬件接口函数,提供了10个接口函数,分别对10个接口函数进行介绍:

 

1、version_info函数来获取关于库版本的信息。这个函数的原型是:

   int version_info( i2c_libversion_t *version );

version参数是指向这个函数必须填充的i2c_libversion_t结构的指针。该结构定义如下:

typedef struct {
    unsigned char   major;
    unsigned char   minor;
    unsigned char   revision;
} i2c_libversion_t;

这个函数应该设置这个结构的成员如下:

version->major = I2CLIB_VERSION_MAJOR;
version->minor = I2CLIB_VERSION_MINOR;
version->revision = I2CLIB_REVISION;

 

2、init函数初始化主接口。该函数的原型是:

void *init( int argc, char *argv[]);

参数是在命令行上传递的。函数返回传递给所有其他函数的句柄,如果发生错误则返回NULL。这个函数主要根据命令行解析来进行I2C初始化。

 

3、fini函数清理驱动程序并释放与给定句柄关联的所有内存。该函数的原型是:

void fini(void *hdl);  

hdl init函数返回的句柄。

 

4、发送函数启动主发送。通信完成时将发送一个可选事件(可以释放数据缓冲区)。如果此函数失败,则未启动任何通信。

i2c_status_t send(
               void *hdl,
               void *buf,
               unsigned int len,
               unsigned int stop );

hdl :init函数返回的句柄;

buf:指向要发送的数据缓冲区的指针;

len:要发送的数据的长度(以字节为单位);

stop:如果这是非零的,函数将在发送完成时设置停止条件;

返回状态为:

I2C_STATUS_DONE:传输完成,并且没有错误。

I2C_STATUS_ERROR:传输错误

I2C_STATUS_NACK:没有ACK

I2C_STATUS_ARBL:失去了仲裁。

I2C_STATUS_BUSY:传输超时

I2C_STATUS_ABORT:传输终止

 

5、recv函数启动主接收。传输完成时将发送一个可选事件(可以使用数据缓冲区)。如果此函数失败,则未启动任何传输。

该函数的原型是:

i2c_status_t recv(
               void *hdl,
               void *buf,
               unsigned int len,
               unsigned int stop );

hdl :init函数返回的句柄;

buf:指向要接收的数据缓冲区的指针;

len:要接收的数据的长度(以字节为单位);

stop:如果这是非零的,函数将在发送完成时设置停止条件;

返回状态为:

I2C_STATUS_DONE:传输完成,并且没有错误。

I2C_STATUS_ERROR:传输错误

I2C_STATUS_NACK:没有ACK

I2C_STATUS_ARBL:失去了仲裁。

I2C_STATUS_BUSY:传输超时

I2C_STATUS_ABORT:传输终止

 

6、中止功能迫使主程序释放总线。当停止条件被发送时,它返回。该函数的原型是:

int abort(
      void *hdl,
      int rcvid );

hdl:  init函数返回的句柄。

rcvid:客户端的接收ID。

 

7、set_slave_addr函数指定目标从地址。该函数的原型是:

int set_slave_addr(
      void *hdl,
      unsigned int addr,
      i2c_addrfmt_t fmt );

hdl:  init函数返回的句柄。

addr: 目标从地址。

fmt:地址的格式; I2C_ADDRFMT_7BIT 和I2C_ADDRFMT_10BIT

 

 8、set_bus_speed函数指定总线速度。如果请求的总线速度无效,该函数将返回一个失败,并保持总线速度不变。该函数的原型是:

int set_bus_speed(
      void *hdl,
      unsigned int speed,
      unsigned int *ospeed );

hdl

 init函数返回的句柄。

speed

总线的速度。

ospeed

NULL或指向函数应该存储实际总线速度的位置的指针。

 

9、driver_info函数返回有关驱动程序的信息。该函数的原型是:

int driver_info(
      void *hdl,
      i2c_driver_info_t *info );

hdl

init函数返回的句柄

info

一个指向i2c_driver_info_t结构的指针,函数应该在该结构中存储信息:

typedef struct {

    uint32_t    speed_mode;

    uint32_t    addr_mode;

    uint32_t    reserved[2];

} i2c_driver_info_t;

 

10.ctl函数处理一个驱动程序特定的devctl()命令。该函数的原型是:

int ctl(

      void *hdl,

      int cmd,

      void *msg,

      int msglen,

      int *nbytes,

      int *info );

 

hdlinit函数返回的句柄;

cmd:设备命令;

msg一个指向消息缓冲区的指针。该函数可以更改缓冲区的内容;

msglen:消息缓冲区的长度,以字节为单位;

nbytes:返回的字节数。这个不能大于msglen;

Info指向函数可以存储devctl()返回状态信息的位置指针;

 

2、硬件访问接口

资源管理器接口使用此函数访问特定于硬件的函数。它必须得到执行;

int i2c_master_getfuncs(i2c_master_funcs_t *funcs, int tabsize);

硬件访问接口流程如下:

    #include <hw/i2c.h>

 
    i2c_master_funcs_t  masterf;
    i2c_libversion_t    version;
    i2c_status_t        status;
    void *hdl;

 
    i2c_master_getfuncs(&masterf, sizeof(masterf));//初始化硬件接口

 
    masterf.version_info(&version);
    if ((version.major != I2CLIB_VERSION_MAJOR) ||
        (version.minor > I2CLIB_VERSION_MINOR))
    {
        /* error */
        ...
    }

 
    hdl = masterf.init(...); //调用初始化函数

 
    masterf.set_bus_speed(hdl, ...); //设置总线速度
    masterf.set_slave_addr(hdl, ...); //设置从地址

 
    status = masterf.send(hdl, ...);//发送数据
    if (status != I2C_STATUS_DONE) {
        /* error */
        if (!(status & I2C_STATUS_DONE))
            masterf.abort(hdl);
    }

 
    status = masterf.recv(hdl, ...);//接收数据
    if (status != I2C_STATUS_DONE) {
        /* error */
        ...
    }

 
    masterf.fini(hdl);//完成I2C传输后,清理驱动程序并释放与给定句柄关联的所有内存

3、共享库的接口

当I2C主设备专用于单个应用程序使用时,可以将硬件接口代码编译为库并将其链接到应用程序。

为了提高代码的可移植性,应用程序应该调用i2c_master_getfuncs()来访问特定于硬件的函数。通过功能表访问硬件库,应用程序更容易加载和管理多个硬件库。

资源管理器接口以类似的方式使用硬件库。

4、资源管理器设计

资源管理器层实现为静态链接到硬件库的库,提供了一个单线程管理器,libi2c-master。

在启动时,resmgr层执行以下操作:

i2c_master_getfuncs(&masterf)
masterf.init()
masterf.set_bus_speed()

然后,资源管理器让自己在后台运行,下面是资源管理器处理这些devctl()命令:

  • DCMD_I2C_DRIVER_INFO calls masterf.driver_info().
  • DCMD_I2C_SET_BUS_SPEED and DCMD_I2C_SET_SLAVE_ADDR only update the state of the current connection.
  • DCMD_I2C_SENDDCMD_I2C_RECVDCMD_I2C_SENDRECVDCMD_I2C_MASTER_SEND, and DCMD_I2C_MASTER_RECV result in the following:
  • if (bus_speed has changed)
  •     masterf.set_bus_speed()
  • masterf.set_slave_address()
  • masterf.send() or masterf.recv()

资源管理器线程一直占用,直到传输完成并回复客户端,可以通过向资源管理器发送SIGTERM来终止它。

5、实例程序分析

1、首先在startup 对I2C进行硬件初始化,包括基地址、中断向量、时钟等等,这部分和硬件相关。

 

2、首先驱动从build 文件里进行加载,在dev生成设备节点,/dev/i2c1 等等

 

 

3、编写I2C驱动,其中init.c解析build加载参数,如I2C基地址地址、 中断号等等,并在lib.c 对所有的I2C驱动进行封装,调用i2c_master_getfuncs函数实现。

<think>首先,用户的问题是:“我想了解或配置I2C_TIMINGR寄存器的相关信息 请问I2C_TIMINGR register configuration and usage”。这涉及到I2C通信中的寄存器配置。 从系统指令中,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考提供的引用内容。 提供的引用内容: - [^1]:描述Linux I2C驱动架构。 - [^2]:讨论I2C信号探测和软件模拟。 - [^3]:关于使用I2C框架驱动硬件,如MPU6050。 - [^4]:QNX下的工具i2cdbgr和i2ctool。 - [^5]:讨论错误处理函数I2C_IsErrorOccurred。 用户的问题集中在I2C_TIMINGR寄存器的配置和使用。这似乎是针对特定硬件(如STM32微控制器)的,因为I2C_TIMINGR是STM32 HAL库中的一个寄存器,用于设置I2C时序参数。 我需要构建一个清晰的回答: 1. **介绍I2C_TIMINGR寄存器**:解释它是什么,为什么重要。 2. **配置步骤**:如何设置寄存器的值。 3. **使用示例**:提供代码片段或配置说明。 4. **注意事项**:如时序计算、错误处理。 5. **生成相关问题**:在末尾添加相关问题。 参考引用: - [^2] 提到I2C信号探测,这与时序相关。 - [^3] 提到硬件平台和I2C总线驱动- [^5] 提到错误处理,可能涉及时序问题。 所以,我可以自然地引用这些内容。 结构: - **开头**:简要介绍I2C_TIMINGR。 - **主体**: - 解释寄存器的功能。 - 如何计算和设置值。 - 提供配置示例。 - 讨论常见问题和错误处理。 - **结尾**:生成相关问题。 使用中文回答。 确保LaTeX格式正确:如果有数学表达式,如时序计算,使用$...$或$$...$$。 例如,I2C时序参数可能涉及频率计算,所以可能需要数学表达式。 最后,添加相关问题。</think>### I2C_TIMINGR寄存器配置与使用详解 I2C_TIMINGR寄存器是I2C(Inter-Integrated Circuit)通信中的一个关键寄存器,主要用于配置I2C总线的时序参数,如时钟频率、数据保持时间和建立时间等。在STM32等微控制器中,该寄存器属于I2C外设的一部分,直接影响数据传输的稳定性和效率。正确配置I2C_TIMINGR能确保设备在标准模式(100 kHz)、快速模式(400 kHz)或高速模式(1 MHz或更高)下可靠工作。下面我将逐步解释其配置和使用方法,帮助您解决实际问题。 #### 1. I2C_TIMINGR寄存器的功能与结构 I2C_TIMINGR寄存器通常是一个32位寄存器,包含多个字段,用于设置I2C时钟的精确参数。主要字段包括: - **PRESC**(Prescaler):时钟预分频因子,用于调整I2C时钟源的分频比例。 - **SCLDEL**(SCL Delay):SCL时钟线的下降沿延时,确保数据稳定。 - **SDADEL**(SDA Delay):SDA数据线的建立时间,防止信号冲突。 - **SCLH**(SCL High Period):SCL高电平持续时间。 - **SCLL**(SCL Low Period):SCL低电平持续时间。 这些参数共同定义了I2C总线的时序特性。例如,在标准模式下,总线频率为100 kHz,时序计算需满足: $$ f_{I2C} = \frac{f_{PCLK}}{(PRESC + 1) \times [(SCLDEL + SDADEL + 1) + (SCLH + 1) + (SCLL + 1)]} $$ 其中,$f_{PCLK}$是微控制器的外设时钟频率(如STM32中的APB时钟)。配置时需确保计算结果符合I2C规范(如NXP的I2C标准)。 #### 2. 配置I2C_TIMINGR寄存器的步骤 配置I2C_TIMINGR涉及计算和设置寄存器值。以下是基于STM32 HAL库的通用步骤(其他平台类似): **步骤1: 确定系统时钟和I2C模式** - 获取微控制器的$f_{PCLK}$值(例如,通过RCC配置)。 - 选择I2C模式:标准模式(100 kHz)、快速模式(400 kHz)或高速模式(1 MHz)。模式决定了时序参数的约束范围。 **步骤2: 计算时序参数** 使用STM32CubeMX或手动计算参数值。STM32CubeMX提供自动计算工具,但手动计算更灵活。公式如下: - 计算PRESC:根据$f_{PCLK}$和目标频率$f_{I2C}$,$PRESC = \frac{f_{PCLK}}{f_{I2C} \times K} - 1$,其中K是固定系数(通常为2-5)。 - 其他参数如SCLDEL、SDADEL、SCLH、SCLL需参考芯片手册的推荐值。例如,在快速模式下: - SCLDEL ≥ 数据保持时间 / $t_{PCLK}$。 - SDADEL ≥ 数据建立时间 / $t_{PCLK}$。 **步骤3: 设置寄存器值** 在代码中直接写入I2C_TIMINGR寄存器或使用HAL库函数。以下是STM32 HAL的示例代码(C语言): ```c #include "stm32f4xx_hal.h" I2C_HandleTypeDef hi2c1; // I2C句柄 void I2C_Config(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 设置目标频率为400 kHz (快速模式) hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比,通常为2:1 hi2c1.Init.Timing = 0x2000090E; // 手动计算的TIMINGR值,示例值 // 其他参数... if (HAL_I2C_Init(&hi2c1) != HAL_OK) { // 错误处理 Error_Handler(); } } ``` 这里,`hi2c1.Init.Timing` 直接设置I2C_TIMINGR的值。您可以使用STM32CubeMX生成此值,或通过公式计算。例如,对于$f_{PCLK} = 48 MHz$和400 kHz模式,典型值为0x2000090E。 **步骤4: 验证和调试** - 使用示波器或逻辑分析仪检查SCL/SDA信号,确保时序符合规范。 - 在Linux或QNX系统中,可利用工具如i2cdbgr或i2ctool监控I2C总线状态[^4]。例如,在QNX下运行: ```bash i2cdbgr -D /dev/i2c0 -s 0x68 -r -b 1 -o 0x00 -v ``` 此命令读取从设备地址0x68的寄存器0x00,帮助验证时序配置是否导致通信错误。 - 如果遇到错误(如ACK缺失),检查I2C_TIMINGR值是否过紧或过松。错误处理函数如I2C_IsErrorOccurred()可检测超时或信号问题[^5]。 #### 3. 注意事项和常见问题 - **时序计算工具**:推荐使用STM32CubeMX自动生成TIMINGR值,避免手动错误。工具基于$f_{PCLK}$和目标频率自动计算所有字段。 - **信号稳定性**:在长总线或噪声环境中,增加SCLDEL和SDADEL可提高抗干扰能力。软件模拟I2C时(如无硬件模块的MCU),需更频繁采样SDA线[^2]。 - **错误处理**:配置不当可能导致通信失败(如NACK错误)。在HAL库中,启用错误中断并处理HAL_I2C_ERROR_AF等标志。 - **平台差异**:不同微控制器(如STM32F4 vs STM32H7)的I2C_TIMINGR结构可能略有不同。务必参考具体芯片的参考手册(如STM32 Reference Manual RM0090)。 - **性能优化**:高速模式下,减小PRESC可提高速度,但需平衡信号完整性。在Linux驱动中,I2C总线驱动(如i2c-mv64xxx.c)通常处理底层时序[^3],用户只需在设备树中配置时钟频率。 #### 4. 总结 配置I2C_TIMINGR寄存器是I2C通信的基础,需结合系统时钟和模式精确计算参数值。通过工具辅助和代码设置,可确保数据传输可靠。在实际应用中,如驱动MPU6050等传感器[^3],正确时序能避免超时或错误响应。如果您有具体硬件平台(如STM32型号),提供更多细节可给出针对性建议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值