I2C pin映射表
| I2C Peripheral | I2C Serial Data Line(SDA) | I2C Serial Clock line(SCL) |
|---|---|---|
| 12C0 | PA28(35)/PA0(33)/PA10(56) | PA31(39)/PA1(34)/PA11(57) |
| I2C1 | PA3(43)/PA10(56)/PA16(9)/PA18(11)/PA30(37)/PB3(51) | PA4(44)/PA11(57)/PA15(8)/PA16(10)/PA29(36)/PB2(50) |
I2C Controller & I2C Target
| 项目 | Controller | Target |
|---|---|---|
| 角色 | 主动 | 被动 |
| SCL | 产生 | 不产生 |
| START / STOP | 产生 | 识别 |
| 选谁通信 | 自己决定 | 被选 |
| 数据方向 | 控制 | 跟随 |
| 可否多设备 | 可以 | 可以 |
| 固件复杂度 | 中 | 高(状态机) |
| 多主 | 支持(少用) | N/A |
TI Drivers Config
c
/*
* ======== I2C ========
*/
extern const uint_least8_t CONFIG_I2C_CONTROLLER_CONST;
#define CONFIG_I2C_0 0
/*
* ======== I2C ========
*/
extern const uint_least8_t CONFIG_I2C_TARGET_CONST;
#define CONFIG_I2C_TARGET_0 0
#define CONFIG_TI_DRIVERS_I2C_COUNT 1
/* ======== I2C Addresses and Speeds ======== */
#include <ti/drivers/I2C.h>
/* ---- CONFIG_I2C_TARGET I2C bus components ---- */
/* CONFIG_I2C_TARGET max speed (supported by all components) */
#define CONFIG_I2C_TARGET_MAXSPEED (400U) /* kbps */
#define CONFIG_I2C_TARGET_MAXBITRATE ((I2C_BitRate) I2C_400kHz)
/* Defines for I2C0 */
#define I2C0_INST I2C0
#define I2C0_INST_IRQHandler I2C0_IRQHandler
#define I2C0_INST_INT_IRQN I2C0_INT_IRQn
#define GPIO_I2C0_SDA_PIN (28)
#define GPIO_I2C0_IOMUX_SDA (IOMUX_PINCM3)
#define GPIO_I2C0_IOMUX_SDA_FUNC IOMUX_PINCM3_PF_I2C0_SDA
#define GPIO_I2C0_SCL_PIN (31)
#define GPIO_I2C0_IOMUX_SCL (IOMUX_PINCM6)
#define GPIO_I2C0_IOMUX_SCL_FUNC IOMUX_PINCM6_PF_I2C0_SCL
/* Defines for I2C1 */
#define I2C1_INST I2C1
#define I2C1_INST_IRQHandler I2C1_IRQHandler
#define I2C1_INST_INT_IRQN I2C1_INT_IRQn
#define GPIO_I2C1_SDA_PIN (35)
#define GPIO_I2C1_IOMUX_SDA (IOMUX_PINCM16)
#define GPIO_I2C1_IOMUX_SDA_FUNC IOMUX_PINCM16_PF_I2C1_SDA
#define GPIO_I2C1_SCL_PIN (34)
#define GPIO_I2C1_IOMUX_SCL (IOMUX_PINCM15)
#define GPIO_I2C1_IOMUX_SCL_FUNC IOMUX_PINCM15_PF_I2C1_SCL
c
/*
* =============================== I2CTarget ===============================
*/
#include <ti/drivers/I2CTarget.h>
#include <ti/drivers/i2ctarget/I2CTargetMSPM0.h>
#define CONFIG_I2CTARGET_COUNT 1
/*
* ======== i2cObjects ========
*/
I2CTargetMSPM0_Object I2CTargetMSPM0Objects[CONFIG_I2CTARGET_COUNT];
/*
* ======== i2cHWAttrs ========
*/
const I2CTargetMSPM0_HWAttrs I2CTargetMSPM0HWAttrs[CONFIG_I2CTARGET_COUNT] = {
/* CONFIG_I2C_Target */
/* LaunchPad I2C */
{
.i2c = I2C1_INST,
.intNum = I2C1_INST_INT_IRQN,
.intPriority = (~0),
.sdaPincm = GPIO_I2C1_IOMUX_SDA,
.sdaPinIndex = GPIO_I2C1_SDA_PIN,
.sdaPinMux = GPIO_I2C1_IOMUX_SDA_FUNC,
.sclPincm = GPIO_I2C1_IOMUX_SCL,
.sclPinIndex = GPIO_I2C1_SCL_PIN,
.sclPinMux = GPIO_I2C1_IOMUX_SCL_FUNC,
.clockSource = DL_I2C_CLOCK_BUSCLK,
.clockDivider = DL_I2C_CLOCK_DIVIDE_1,
.txIntFifoThr = DL_I2C_TX_FIFO_LEVEL_BYTES_1,
.rxIntFifoThr = DL_I2C_RX_FIFO_LEVEL_BYTES_1,
.isClockStretchingEnabled = true,
.isAnalogGlitchFilterEnabled = false,
},
};
/*
* ======== I2C_config ========
*/
const I2CTarget_Config I2CTarget_config[CONFIG_I2CTARGET_COUNT] = {
/* CONFIG_I2C_TARGET */
/* LaunchPad I2C */
{.object = &I2CTargetMSPM0Objects[CONFIG_I2C_TARGET_0],
.hwAttrs = &I2CTargetMSPM0HWAttrs[CONFIG_I2C_TARGET_0]},
};
const uint_least8_t CONFIG_I2C_0_CONST = CONFIG_I2C_TARGET_0;
const uint_least8_t I2CTarget_count = CONFIG_I2CTARGET_COUNT;
/*
* =============================== I2C Controller ===============================
*/
#include <ti/drivers/I2C.h>
#include <ti/drivers/i2c/I2CMSPM0.h>
#define CONFIG_I2C_COUNT 1
/*
* ======== i2cObjects ========
*/
I2CMSPM0_Object I2CMSPM0Objects[CONFIG_I2C_COUNT];
/*
* ======== i2cHWAttrs ========
*/
const I2CMSPM0_HWAttrs I2CMSPM0HWAttrs[CONFIG_I2C_COUNT] = {
/* CONFIG_I2C_CONTROLLER */
/* LaunchPad I2C */
{
.i2c = I2C0_INST,
.intNum = I2C0_INST_INT_IRQN,
.intPriority = (~0),
.sdaPincm = GPIO_I2C0_IOMUX_SDA,
.sdaPinIndex = GPIO_I2C0_SDA_PIN,
.sdaPinMux = GPIO_I2C0_IOMUX_SDA_FUNC,
.sclPincm = GPIO_I2C0_IOMUX_SCL,
.sclPinIndex = GPIO_I2C0_SCL_PIN,
.sclPinMux = GPIO_I2C0_IOMUX_SCL_FUNC,
.clockSource = DL_I2C_CLOCK_BUSCLK,
.clockDivider = DL_I2C_CLOCK_DIVIDE_1,
.txIntFifoThr = DL_I2C_TX_FIFO_LEVEL_BYTES_1,
.rxIntFifoThr = DL_I2C_RX_FIFO_LEVEL_BYTES_1,
.isClockStretchingEnabled = true,
.i2cClk = I2C_CLOCK_MHZ,
},
};
/*
* ======== I2C_config ========
*/
const I2C_Config I2C_config[CONFIG_I2C_COUNT] = {
/* CONFIG_I2C_CONTROLLER */
/* LaunchPad I2C */
{.object = &I2CMSPM0Objects[CONFIG_I2C_0],
.hwAttrs = &I2CMSPM0HWAttrs[CONFIG_I2C_0]},
};
const uint_least8_t CONFIG_I2C_CONST = CONFIG_I2C_0;
const uint_least8_t I2C_count = CONFIG_I2C_COUNT;
2. 顶层配置常量(句柄索引)
下面这些通常来自 ti_drivers_config.h / SysConfig,用于把"逻辑名"映射成数组下标。
c
/*
* ======== I2C ========
*/
extern const uint_least8_t CONFIG_I2C_CONTROLLER_CONST;
#define CONFIG_I2C_0 0
/*
* ======== I2C ========
*/
extern const uint_least8_t CONFIG_I2C_TARGET_CONST;
#define CONFIG_I2C_TARGET_0 0
#define CONFIG_TI_DRIVERS_I2C_COUNT 1
CONFIG_I2C_0/CONFIG_I2C_TARGET_0:数组索引(0 表示第 1 个实例)。CONFIG_TI_DRIVERS_I2C_COUNT:I2C 实例数量(这里是 1,指 controller 侧的数量;target 侧有自己单独的CONFIG_I2CTARGET_COUNT)。
3. I2C 速率/比特率宏
c
#include <ti/drivers/I2C.h>
/* CONFIG_I2C_TARGET max speed (supported by all components) */
#define CONFIG_I2C_TARGET_MAXSPEED (400U) /* kbps */
#define CONFIG_I2C_TARGET_MAXBITRATE ((I2C_BitRate) I2C_400kHz)
CONFIG_I2C_TARGET_MAXSPEED:更像是"文档级别的能力描述"(单位 kbps)。CONFIG_I2C_TARGET_MAXBITRATE:实际给 TI-Drivers 用的枚举值(这里是 400kHz)。
4. 外设实例、中断号、PinMux(最关键)
这一段把硬件资源"起别名",供后面的 HWAttrs 引用。
4.1 I2C0(Controller)
c
#define I2C0_INST I2C0
#define I2C0_INST_IRQHandler I2C0_IRQHandler
#define I2C0_INST_INT_IRQN I2C0_INT_IRQn
#define GPIO_I2C0_SDA_PIN (28)
#define GPIO_I2C0_IOMUX_SDA (IOMUX_PINCM3)
#define GPIO_I2C0_IOMUX_SDA_FUNC IOMUX_PINCM3_PF_I2C0_SDA
#define GPIO_I2C0_SCL_PIN (31)
#define GPIO_I2C0_IOMUX_SCL (IOMUX_PINCM6)
#define GPIO_I2C0_IOMUX_SCL_FUNC IOMUX_PINCM6_PF_I2C0_SCL
I2C0_INST:指向硬件寄存器基址(MSPM0 的 DriverLib 风格)。I2C0_INST_INT_IRQN:NVIC 中断号。GPIO_I2C0_IOMUX_*:IOMUX 的 PinCM 寄存器(决定某个管脚复用为 I2C SDA/SCL)。GPIO_I2C0_*_PIN:TI GPIO 驱动里的"pin index"(用于 GPIO driver 层)。
4.2 I2C1(Target)
c
#define I2C1_INST I2C1
#define I2C1_INST_IRQHandler I2C1_IRQHandler
#define I2C1_INST_INT_IRQN I2C1_INT_IRQn
#define GPIO_I2C1_SDA_PIN (35)
#define GPIO_I2C1_IOMUX_SDA (IOMUX_PINCM16)
#define GPIO_I2C1_IOMUX_SDA_FUNC IOMUX_PINCM16_PF_I2C1_SDA
#define GPIO_I2C1_SCL_PIN (34)
#define GPIO_I2C1_IOMUX_SCL (IOMUX_PINCM15)
#define GPIO_I2C1_IOMUX_SCL_FUNC IOMUX_PINCM15_PF_I2C1_SCL
同理:这里把 Target 侧 挂在 I2C1,引脚也与 I2C0 不同。
5. I2CTarget(从机)配置
c
#include <ti/drivers/I2CTarget.h>
#include <ti/drivers/i2ctarget/I2CTargetMSPM0.h>
#define CONFIG_I2CTARGET_COUNT 1
I2CTargetMSPM0_Object I2CTargetMSPM0Objects[CONFIG_I2CTARGET_COUNT];
const I2CTargetMSPM0_HWAttrs I2CTargetMSPM0HWAttrs[CONFIG_I2CTARGET_COUNT] = {
{
.i2c = I2C1_INST, /* 选择 I2C 硬件实例:I2C1 */
.intNum = I2C1_INST_INT_IRQN, /* 对应 NVIC IRQn */
.intPriority = (~0), /* 中断优先级:(~0) 通常表示"使用默认/最低/不配置",取决于驱动实现 */
.sdaPincm = GPIO_I2C1_IOMUX_SDA, /* SDA 的 PinCM 寄存器 */
.sdaPinIndex = GPIO_I2C1_SDA_PIN, /* SDA 的 GPIO pin index */
.sdaPinMux = GPIO_I2C1_IOMUX_SDA_FUNC, /* SDA 复用功能选择 */
.sclPincm = GPIO_I2C1_IOMUX_SCL,
.sclPinIndex = GPIO_I2C1_SCL_PIN,
.sclPinMux = GPIO_I2C1_IOMUX_SCL_FUNC,
.clockSource = DL_I2C_CLOCK_BUSCLK, /* I2C 外设时钟源:BUSCLK */
.clockDivider = DL_I2C_CLOCK_DIVIDE_1,/* 时钟分频 */
.txIntFifoThr = DL_I2C_TX_FIFO_LEVEL_BYTES_1, /* TX FIFO 中断阈值 */
.rxIntFifoThr = DL_I2C_RX_FIFO_LEVEL_BYTES_1, /* RX FIFO 中断阈值 */
.isClockStretchingEnabled = true, /* Target 侧常用:允许时钟拉伸,给软件处理时间 */
.isAnalogGlitchFilterEnabled = false /* 模拟毛刺滤波:根据板级/线长/噪声情况决定 */
},
};
const I2CTarget_Config I2CTarget_config[CONFIG_I2CTARGET_COUNT] = {
{
.object = &I2CTargetMSPM0Objects[CONFIG_I2C_TARGET_0],
.hwAttrs = &I2CTargetMSPM0HWAttrs[CONFIG_I2C_TARGET_0]
},
};
const uint_least8_t I2CTarget_count = CONFIG_I2CTARGET_COUNT;
5.1 为什么 .object 和 .hwAttrs 要分开?
.hwAttrs:硬件属性(寄存器基址、引脚、中断等),基本是"只读常量"。.object:运行态对象 (驱动内部状态、锁、缓冲等),由驱动在*_open()时初始化。
这种设计能让一个驱动支持多个实例:每个实例一份 object + 一份 hwAttrs。
5.2 时钟拉伸(Clock Stretching)要不要开?
- Target 侧建议默认开:主机可能连续读/写;如果回调/处理不够快,拉伸 SCL 能避免丢字节。
- 但如果主机不允许拉伸或对拉伸敏感,就要谨慎(看主机 I2C 控制器/驱动是否容忍)。
6. I2C Controller(主机)配置
c
#include <ti/drivers/I2C.h>
#include <ti/drivers/i2c/I2CMSPM0.h>
#define CONFIG_I2C_COUNT 1
I2CMSPM0_Object I2CMSPM0Objects[CONFIG_I2C_COUNT];
const I2CMSPM0_HWAttrs I2CMSPM0HWAttrs[CONFIG_I2C_COUNT] = {
{
.i2c = I2C0_INST, /* 选择 I2C0 作为 controller */
.intNum = I2C0_INST_INT_IRQN,
.intPriority = (~0),
.sdaPincm = GPIO_I2C0_IOMUX_SDA,
.sdaPinIndex = GPIO_I2C0_SDA_PIN,
.sdaPinMux = GPIO_I2C0_IOMUX_SDA_FUNC,
.sclPincm = GPIO_I2C0_IOMUX_SCL,
.sclPinIndex = GPIO_I2C0_SCL_PIN,
.sclPinMux = GPIO_I2C0_IOMUX_SCL_FUNC,
.clockSource = DL_I2C_CLOCK_BUSCLK,
.clockDivider = DL_I2C_CLOCK_DIVIDE_1,
.txIntFifoThr = DL_I2C_TX_FIFO_LEVEL_BYTES_1,
.rxIntFifoThr = DL_I2C_RX_FIFO_LEVEL_BYTES_1,
.isClockStretchingEnabled = true, /* Controller 也可支持拉伸(对端拉伸时能正确等待) */
.i2cClk = I2C_CLOCK_MHZ /* 这里通常是"输入时钟(MHz)"或派生时钟配置常量,供驱动计算时序 */
},
};
const I2C_Config I2C_config[CONFIG_I2C_COUNT] = {
{
.object = &I2CMSPM0Objects[CONFIG_I2C_0],
.hwAttrs = &I2CMSPM0HWAttrs[CONFIG_I2C_0]
},
};
const uint_least8_t I2C_count = CONFIG_I2C_COUNT;
i2cControllerThread
此处连接了IMU
c
/*
* ======== i2cControllerThread ========
*/
void *i2cControllerThread(void *arg0) {
int8_t i;
I2C_Handle i2cHandle;
I2C_Params i2cParams;
I2C_Transaction i2cTransaction;
I2C_Params_init(&i2cParams);
i2cParams.bitRate = I2C_100kHz;
i2cHandle = I2C_open(CONFIG_I2C_0, &i2cParams);
if (i2cHandle == NULL) {
Display_printf(g_display, 0, 0, "Error Initializing I2C\n");
while (1) {
}
} else {
Display_printf(g_display, 0, 0, "I2C Initialized!\n");
}
Display_printf(g_display, 0, 0, "Starting the i2ccontroller example\n");
/* Common I2C transaction setup */
i2cTransaction.writeBuf = txBuffer;
i2cTransaction.writeCount = 1;
i2cTransaction.readBuf = rxBuffer;
i2cTransaction.readCount = 0;
/*
* Determine which I2C sensors are present by querying known I2C
* target addresses.
*/
for (i = TMP_COUNT - 1; i >= 0; i--) {
i2cTransaction.targetAddress = sensors[i].address;
txBuffer[0] = sensors[i].resultReg;
if (I2C_transfer(i2cHandle, &i2cTransaction)) {
targetAddress = sensors[i].address;
Display_printf(g_display, 0, 0,
"Detected TMP%s sensor with target"
" address 0x%x\n",
sensors[i].id, sensors[i].address);
} else {
i2cErrorHandler(&i2cTransaction);
}
}
/* If we never assigned a target address */
if (targetAddress == 0) {
Display_printf(g_display, 0, 0, "Failed to detect a sensor!\n");
I2C_close(i2cHandle);
while (1) {
}
}
Display_printf(g_display, 0, 0, "\nUsing last known sensor for samples.");
i2cTransaction.targetAddress = targetAddress;
while (1) {
sleep(1);
/* If the detected sensor is QMI8658A, read WHO_AM_I and Revision ID */
if (targetAddress == 0x6A) {
i2cTransaction.targetAddress = targetAddress;
/* Read WHO_AM_I (reg 0x00) using write-then-read sequence */
txBuffer[0] = 0x00;
i2cTransaction.writeCount = 1;
i2cTransaction.readCount = 1;
if (!I2C_transfer(i2cHandle, &i2cTransaction)) {
i2cErrorHandler(&i2cTransaction);
} else {
Display_printf(g_display, 0, 0, "QMI8658A WHO_AM_I: 0x%02x\n", rxBuffer[0]);
}
/* Read Revision ID (reg 0x01) using write-then-read sequence */
txBuffer[0] = 0x01;
i2cTransaction.writeCount = 1;
i2cTransaction.readCount = 1;
if (!I2C_transfer(i2cHandle, &i2cTransaction)) {
i2cErrorHandler(&i2cTransaction);
} else {
Display_printf(g_display, 0, 0, "QMI8658A Revision ID: 0x%02x\n", rxBuffer[0]);
}
}
}
}
初始化 I2C(Controller/主机)
- I2C_Params_init(&i2cParams);
- i2cParams.bitRate = I2C_100kHz;:把总线速率设为 100kHz
- I2C_open(CONFIG_I2C_0, &i2cParams);:打开在 SysConfig 里配置的 I2C0(主机侧)。失败就打印并死循环。
准备一个通用的 I2C 事务结构
I2C_Transaction
- i2cTransaction.writeBuf = txBuffer; writeCount = 1;
- i2cTransaction.readBuf = rxBuffer; readCount = 0;
- 这意味着默认事务是"写 1 字节、读 0 字节"(常用于探测:只要地址 ACK 就算设备存在)。
探测传感器是否存在(靠 ACK 判断)
- sensors[] 里目前只有一个:地址 0x6A(标识字符串 "QMI8658A")
- 循环里做:
- i2cTransaction.targetAddress = sensors[i].address;
- txBuffer[0] = sensors[i].resultReg;(这里是 0x00)
- 调用 I2C_transfer(...)
- 因为 readCount = 0,所以这次 transfer 实际上就是"向 0x6A 写 1 个字节(0x00)"。如果对方存在并 ACK,就认为检测到了设备,并把 targetAddress 记下来。
若没探测到任何设备就退出
- targetAddress == 0 表示没找到(因为静态变量默认 0,且只有成功时才赋值为 0x6A)
- 会 I2C_close(i2cHandle); 然后死循环。
主循环:每秒读取两个寄存器(WHO_AM_I 和 Revision)
- while(1) { sleep(1); ... }
- 仅当 targetAddress == 0x6A 才执行读取:
读 WHO_AM_I:
txBuffer[0]=0x00; writeCount=1; readCount=1;
I2C_transfer(...):这是典型的 write-then-read(先写寄存器地址,再重复起始读回 1 字节)
打印 rxBuffer[0] - 读 Revision:
txBuffer[0]=0x01; writeCount=1; readCount=1;
同样 transfer 并打印
错误处理
- 任何 I2C_transfer 失败都会走 i2cErrorHandler(&i2cTransaction);
- 它根据 transaction->status 打印 timeout、NACK、bus busy 等具体原因。