嗨,亲爱的工程师、学生和爱好者们,我来啦!我是你们的探索导游,今天我要带你们走进一个神秘而有趣的世界——IIC(Inter-Integrated Circuit)总线,以及它在小安派-Eyes-S1微控制器上的应用。这里的一切都那么有趣、高科技,且小巧玲珑,就像一个迷你的电子王国。但别以为IIC总线只擅长“短跑”,它的传输距离也很短。而且,嘿,任意时刻只能有一个主机哦!这就像是一场没有并列第一的比赛,只能有一个冠军。这不是你常听说的爱情故事里的“I-I-Can't”,而是电子世界里的“I-I-Control”IIC以其独特的特性,在主控制器和从器件间的通信中独占鳌头。然而,对于这种看似复杂的技术,深入了解其背后的原理和细节是必不可少的。在这篇文章中,我将分享我在深入了解IIC总线,特别是在Eyes-S1微控制器上的应用中的体验。希望您能对小安派-Eyes-S1的I2C操作有全面的掌握。但是别紧张,这不是那种会让你头大的编程问题,而是像在家里看电影一样的简单有趣!
image.png
IIC(Inter-Integrated Circuit)总线,哈哈哈,这名字听起来就很高科技,就像是两条线把整个集成电路都串在一起一样。就是我们常说的两线式串行总线,是那种让微控制器和外围设备手拉手一起走的总线。它在主控制器和从器件间建立起了沟通的桥梁,尤其在小数据量场合,就像给微控制器发微信一样常用。是微控制器和它的外围设备之间的“小秘密通道”。不过呢,它有个特点,就是传输距离短,而且同一时间只能有一个主机发言。这种总线主要就是用来连接微控制器和外围设备的,就像是把微控制器和各种小设备用一条线连起来,让他们可以互相聊天。它只需要两根线(时钟线SCL和数据线SDA)即可在连接于总线上的器件之间实现双向数据传输。
image.png
IIC 总线一般用于小数据量、短距离的通信,比如主控制器和从器件之间的对话。而且,它有个特点,就是同一时刻只能有一个主机在说话,这就好比一个大家庭里,一次只能有一个人说话一样。但是它的传输速度很快哦,一般在 400kbps 以上,这速度可比我们平常说话快多了!I2C总线的通信速率可以根据不同的模式进行调节。标准模式下的传输速率为100kbit/s,快速模式下的传输速率为400kbit/s,高速模式下的传输速率为3.4Mbit/s。另外,还有超快速模式,其传输速率可以达到5Mbit/s。


请注意,实际使用中,通信速率还会受到其他因素的影响,如总线上连接的器件数量、传输距离、线缆的品质等。因此,需要根据实际情况选择合适的通信速率。


接下来咱们来聊聊这个小安派-Eyes-S1 的 I2C 吧!这可是小安派-Eyes-S1的“小耳朵”,它能听到微控制器和被控 IC 之间的悄悄话。
image.png
1. **struct bflb_i2c_msg_s**:这个结构体是用来存储 I2C 传输时需要的信息的。
        * **addr**:设备地址,可以是 7 位或 10 位。就像我们打电话,要先知道对方的电话号码才能打过去。就像你在学校时,老师会告诉你哪个班级的哪个学生有问题一样,这个地址就是那个学生的名字。
        * **flags**:传输时的标志。这个标志可以是很多种,比如读标志、10 位地址标志、DMA 标志等等。就像我们发微信时可以选择“发送”或者“撤回”,这里的标志就是这些选项。就像交通信号灯,告诉你下一步该做什么。
        * **buffer**:数据区。这里是要发送或接收的数据。
        * **length**:数据长度。告诉你要发送或接收多少数据。
        * **flag** 可以是这些参数:
                + **I2C_M_READ**:0x0001,读标志。
                + **I2C_M_TEN**:0x0002,10 位地址标志。
                + **I2C_M_DMA**:0x0004,DMA 标志。
                + **I2C_M_NOSTOP**:0x0040,传输完数据后不停机。
                + **I2C_M_NOSTART**:0x0080,不发送起始信号。备注:flag也可以设为 0,表示写入数据。在使用 transfer 函数操作时,一般设置两个结构体数组,其中第一位操作的从机地址和寄存器地址,且 Flags 设置为 NOSTOP,而后一个结构体数组为写入的数据,Flags 设置为 0,也就是 write。哈哈,这里就像是设置微信的发送方式一样,可以选择语音、文字或者视频。
2. **bflb_i2c_init**:这个函数是用来初始化 I2C 并配置频率的。就像是我们新买了一个手机,要先设置一下网络、铃声等基本配置一样。
这个函数就像是 I2C 的“保姆”。bflb_i2c_init 负责照顾 I2C 的起居饮食,设置其“作息时间”(频率);而下面的 bflb_i2c_deinit 则负责让 I2C 好好休息。
        * **dev**:设备句柄。就像是手机的唯一识别码一样。
        * **frequency**:配置频率。这个频率范围是 305 Hz 到 400 KHZ,就像是我们的手机网络信号一样,可以选择不同的网络频段。
3. **bflb_i2c_deinit**:这个函数是用来反初始化 I2C 的。就像是我们把手机恢复出厂设置一样。
        * **dev**:设备句柄。
4. **bflb_i2c_link_txdma**:这个函数是用来设置 I2C 发送是否使用 DMA 的。DMA 就是直接内存访问,可以让数据直接从内存传输到设备或者反之,而不需要经过 CPU 中间处理一下。这可以大大提高传输效率哦!
        * **dev**:设备句柄。
        * **enable**:是否使能 DMA。如果选择使能 DMA,那么数据传输的速度就会更快哦!
5. **bflb_i2c_link_rxdma**:这个函数是用来设置 I2C 接收是否使用 DMA 的。同样地,DMA 可以提高数据接收的速度哦!bflb_i2c_link_txdma 和 bflb_i2c_link_rxdma这两个函数是 I2C 的“按摩师”。当 I2C 需要发送或接收大量数据时,这两个函数可以为其开启dma模式,让数据传输更加流畅。
        * **dev**:设备句柄。
        * **enable**:是否使能 DMA。使能 DMA 后,数据接收得更快哦!
image.png
6. **bflb_i2c_transfer**:这个函数是用来进行 I2C 消息传输的。就像是我们用手机发送消息一样,输入内容然后点击发送就 OK 了!
        * **dev**:设备句柄。
        * **msgs**:消息指针。这里是要发送的消息内容哦!
        * **count**:消息个数。告诉你要发送多少条消息哦!哈哈,就像是我们发微信一样,可以选择一次性发多条消息哦!
又像是 I2C 的“司机”。它负责驾驶数据从一地传到另一地。
7. 我们需要知道这个i2c传输时需要填充哪些信息。这里有一个结构体:
image.png
struct bflb_i2c_msg_s {
uint16_t addr;          //设备地址(7bit 或 10bit)
uint16_t flags;          //传输时附带标志
uint8_t *buffer;          //数据区
uint16_t length;          //数据长度
};


这个结构体告诉我们:传送给哪个设备(地址),用什么方式传(标志),传什么内容(数据区),以及传多少内容(长度)。


这里还有一些标志的定义:


#define I2C_M_READ    0x0001       //读取数据
#define I2C_M_TEN     0x0002       //10位地址模式
#define I2C_M_DMA     0x0004       //使用DMA传输
#define I2C_M_NOSTOP  0x0040       //传输完数据后不停机
#define I2C_M_NOSTART 0x0080       //不发送起始信号
备注:flagS 也可以设为 0,表示写入数据。在使用 tansfer 函数操作时,一般设置两个结构体数组,其中第一位操作的从机地址和寄存器地址,且 Flags 设置为 NOSTOP,而后一个结构体数组为写入的数据,Flags 设置为 0,也就是 write。
image.png
想要了解如何使用这些 API 函数进行操作吗?以下是一个简化的 I2C 操作流程,并手把手给您 API 函数的详细说明:


1. **初始化 I2C**:




```c
bflb_i2c_init(i2c0, 400000);  // 初始化 I2C,频率为 400kHz
```
2. **配置 GPIO 作为 SDA 和 SCL**:
这通常涉及到设置 GPIO 的模式和输出值。根据您的示例,您已经知道如何设置它们。
3. **发送数据**:
假设您要向地址为 `I2C_SLAVE_ADDR` 的设备写入数据:




```c
msgs[0].addr = I2C_SLAVE_ADDR;     // 从机地址
msgs[0].flags = I2C_M_NOSTOP;      // 不停止传输
msgs[0].buffer = (uint8_t*)data;   // 写入的数据
msgs[0].length = length;          // 数据长度


msgs[1].addr = I2C_SLAVE_ADDR;     // 从机地址
msgs[1].flags = 0;                // 写入数据,不使用特殊标志
msgs[1].buffer = (uint8_t*)data;   // 写入的数据
msgs[1].length = length;          // 数据长度


bflb_i2c_transfer(i2c0, msgs, 2);  // 发送两个消息
```
4. **反初始化 I2C**:
完成所有 I2C 通信后,为了节省资源,建议反初始化 I2C:




```c
bflb_i2c_deinit(i2c0);
```
5. **其他操作**:
根据需要,您还可以使用其他 API 函数,例如 `bflb_i2c_link_txdma` 和 `bflb_i2c_link_rxdma` 来启用 DMA 传输。
6. **错误处理**:
当使用 I2C 时,总是需要处理可能发生的错误。确保检查每个 API 函数的返回值并进行适当的错误处理。
7. **文档和示例**:
为了更好地理解这些 API 和它们的使用方式,建议查看相关硬件供应商提供的文档和示例代码。这些资源通常提供有关如何配置和使用特定 API 的详细信息和示例。
image.png
8. **注意事项**:
* 在使用 I2C 时,确保遵循时序要求,特别是对于特定的数据速率。这可能会影响传输的成功与否。
* 对于多字节的数据传输,您可能需要构建一个循环来发送所有字节,而不是只发送一个消息。
* 如果遇到通信问题,请检查硬件连接、I2C 总线的连接以及任何相关的配置设置。


手把手玩转实例:
使用 I2C 总线与 Rd-04 雷达进行通信。首先你需要准备一块Rd-04模块,并将其板载的MCU拆除。然后你需要查阅Rd-04的寄存器手册。将Rd-04与S1进行连接,将GPIO_0设置为SDA,GPIO_1设置为SCL。注意,Rd-04有一个I2C的使能引脚,在配置时需要将I2C_EN拉高,可以将其连接到3V3。
**初始化 I2C 总线**:


```c
i2c0 = ...; // 假设这是您获取的 I2C 外设句柄
bflb_i2c_init(i2c0, 400000); // 初始化 I2C,频率为 400kHz
```


接下来,你需要进行以下步骤来驱动 Rd-04 雷达:


1. **配置 GPIO 作为 SDA 和 SCL**:
首先,确保你已经正确地配置了 GPIOs,使 GPIO_0 为 SDA 和 GPIO_1 为 SCL。
```c
// 将 GPIO_0 设置为 SDA,GPIO_1 设置为 SCL
bflb_gpio_set_mode(GPIO_0, BFLB_GPIO_MODE_ Func);
bflb_gpio_set_mode(GPIO_1, BFLB_GPIO_MODE_ Func);
```


2. **设置 I2C 使能引脚**:
你需要将 I2C 使能引脚连接到 3V3,确保 I2C 总线是激活的。
这可以通过 GPIO 控制实现:
```c
// 将 I2C 使能引脚拉高
bflb_gpio_write(I2C_EN, 1);
```
image.png
3. **发送 I2C 消息**:
使用你提供的 `bflb_i2c_transfer` 函数来发送 I2C 消息。你需要填充 `msgs` 数组,其中包含目标设备的地址(I2C_SLAVE_ADDR)、寄存器地址(如果需要的话)、数据缓冲区以及数据长度。


例如:
```c
msgs[0].addr = I2C_SLAVE_ADDR; // 从机地址
msgs[0].flags = I2C_M_READ; // 读取标志
msgs[0].buffer = buffer; // 数据缓冲区
msgs[0].length = sizeof(buffer); // 数据长度


msgs[1].addr = I2C_SLAVE_ADDR; // 从机地址
msgs[1].flags = 0; // 写入标志,不停止传输
msgs[1].buffer = write_data; // 写入的数据缓冲区
msgs[1].length = sizeof(write_data); // 数据长度
```
然后使用 `bflb_i2c_transfer` 函数发送消息:




```c
int result = bflb_i2c_transfer(i2c0, msgs, 2); // 发送两个消息
if (result != BFLB_I2C_XFER_OK) {
    // 处理错误
}
```
4. **处理响应**:
根据你的需要,你可以处理从 I2C 传输中接收到的数据。这通常涉及到检查返回值或读取特定的寄存器值。


5. **反初始化 I2C**:
完成所有操作后,确保反初始化 I2C 外设以释放资源:
```c
bflb_i2c_deinit(i2c0);
```


6. **错误处理**:
确保添加适当的错误处理代码,以便在出现问题时能够正确响应。
`bflb_i2c_transfer` 函数会返回操作的结果。您应该检查这个返回值,确保通信成功。例如:




```c
if (result < 0) {
    // 处理错误情况,如打印错误信息或采取其他措施。
} else {
    // 通信成功,处理收到的数据或执行其他任务。
}
```
7. **查阅文档**:
确保你仔细阅读了 Rd-04 的寄存器手册,以了解如何正确地与它进行通信。手册应该提供了关于如何设置寄存器、读取数据等的详细信息。
8. **测试和调试**:
image.png
在连接 Rd-04 后,进行实际的测试和调试以确保一切正常工作。你可能需要多次尝试和调整代码以获得正确的结果。
希望这个讲解能帮助你更好地理解 IIC 总线和与 Rd-04 雷达的交互!这就像是要和一位高冷的雷达先生打交道。首先,你需要准备一块 Rd-04 雷达板,然后拆除它的“心脏”(MCU)。接着查阅它的“日记”(寄存器手册)以了解它的习惯和喜好。然后你要与 S1 进行“相亲”,确保 GPIO_0 是 SDA,GPIO_1 是 SCL。别忘了,这位雷达先生有一个“任性”的 I2C 使能引脚,所以在约会时记得把它放在3V3上哄开心了。Main部分是整个约会的“主持人”。它负责介绍所有参与的“嘉宾”(硬件模块),并确保一切按照计划进行。其中 I2C_SLAVE_ADDR 是雷达先生的“身份证号码”,用来确保你找的是正确的先生。而 i2c0 是与雷达先生交流的“使者”,负责传递信息。I2C它在微控制器与外围设备间牵线搭桥,让它们能甜蜜地交流。这种总线擅长在主控制器和从器件间进行主从通信,特别适合在那些不需要太多数据、距离也较短的情况下使用。而且,它只允许一个“媒人”在任何时刻出现,确保了通信的秩序。要成为I2C总线上的“红娘”,首先得了解一些基本规则。微控制器和外围设备得遵循一些特定的“礼节”,才能确保交流的顺畅。比如,它们得知道如何正确地发送和接收信息,以及如何处理可能出现的冲突。这就像真实的相亲一样,双方都得有一定的了解和准备,才能确保相亲成功。
说了这么多,是不是觉得I2C总线这位“红娘”很神奇呢?其实,这只是冰山一角。在实际应用中,还有很多技巧和注意事项需要我们去学习和掌握。只有这样,我们才能更好地利用I2C总线,为微控制器和外围设备搭建一座坚固的桥梁,让它们能够更好地交流和合作。你是否曾经想过,微控制器和外围设备之间的交流就像一场复杂的舞蹈?他们用特定的步伐和节奏相互配合,完成各种任务。在这场舞蹈中,IIC总线就像是一位经验丰富的指挥家,确保每一个音符都准确无误。这就是我与IIC总线的故事。希望你们也能找到自己的电子舞伴,享受那美妙的电子舞蹈!

这次探索之旅真是收获满满!我们不仅深入了解了IIC总线的原理和应用方法,还通过实践验证了理论知识在实际操作中的可行性。同时,也欢迎大家加入我们的探索之旅,将不断探索和学习新技术,一起探索科技的无限可能!
希望以上心得对您会有所帮助!
谢谢!
我在本论坛内的试读经验 :

《Proteus实战攻略》+7 第五章双足机器人仿真实例
还没吃饭中
2024年1月10日