树莓派Pico I2C主从通信异常:主设备无输出问题解决
树莓派Pico I2C主从通信故障修复
需求
- Pico1(主设备)向Pico2(从设备)发送字符'M'并打印「Master Sending: M」;
- Pico2(从设备)接收'M'并打印「Slave Received: M」;
- Pico2(从设备)向Pico1发送字符'S'并打印「Slave Sending: S」;
- Pico1(主设备)接收'S'并打印「Master Received: S」。
当前问题现象
主设备终端无任何输出,从设备终端重复打印「Slave: Sent 'S' back to master」。
原主设备代码
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #define I2C_PORT i2c0 #define SLAVE_ADDRESS 0x42 // I2C从设备地址 #define DATA_LENGTH 1 // 收发数据长度 int main() { // 初始化串口 stdio_init_all(); // 初始化I2C端口,波特率100kHz i2c_init(I2C_PORT, 100 * 1000); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); gpio_pull_up(4); gpio_pull_up(5); char send_data = 'M'; // 要发送给从设备的数据 char receive_data; // 存储从从设备接收的数据的缓冲区 while (true) { // 步骤1:向从设备发送字符'M' i2c_write_blocking(I2C_PORT, SLAVE_ADDRESS, &send_data, DATA_LENGTH, false); printf("Master Sending: %c\n", send_data); // 等待一段时间,给从设备响应时间 sleep_ms(500); // 步骤4:接收从设备发送的字符'S' i2c_read_blocking(I2C_PORT, SLAVE_ADDRESS, &receive_data, DATA_LENGTH, false); printf("Master Received: %c\n", receive_data); // 等待一段时间后重复流程 sleep_ms(1000); } return 0; }
原从设备代码
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #define I2C_PORT i2c0 #define SLAVE_ADDRESS 0x42 // I2C从设备地址 #define DATA_LENGTH 1 // 收发数据长度 int main() { // 初始化串口 stdio_init_all(); // 初始化I2C从模式 i2c_init(I2C_PORT, 100 * 1000); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); gpio_pull_up(4); gpio_pull_up(5); i2c_set_slave_mode(I2C_PORT, true, SLAVE_ADDRESS); char receive_data; // 存储从主设备接收的数据的缓冲区 char send_data = 'S'; // 要发送给主设备的数据 while (true) { // 步骤2:接收主设备发送的字符'M' if (i2c_get_read_available(I2C_PORT) > 0) { i2c_read_raw_blocking(I2C_PORT, &receive_data, DATA_LENGTH); printf("Slave Received: %c\n", receive_data); } // 等待一段时间模拟处理 sleep_ms(500); // 步骤3:向主设备发送字符'S' i2c_write_raw_blocking(I2C_PORT, &send_data, DATA_LENGTH); printf("Slave Sending: %c\n", send_data); // 等待一段时间后重复流程 sleep_ms(1000); } return 0; }
问题分析
- 主设备阻塞:主设备的
i2c_write_blocking会等待从设备的ACK响应,原从设备代码未正确处理主设备的写请求,导致主设备一直卡在写操作步骤,无法执行后续打印和读操作。 - 从设备主动写错误:I2C协议中,从设备不能主动发起写操作,必须由主设备发起读请求时,从设备才能返回数据。原从设备主动调用
i2c_write_raw_blocking不符合规范,是主设备无法接收数据的核心原因。 - 从设备接收逻辑错误:
i2c_get_read_available的判断方式无法准确捕获主设备的写请求,导致从设备无法正确接收'M'。
修复后的代码
修复后主设备代码
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #define I2C_PORT i2c0 #define SLAVE_ADDRESS 0x42 // I2C从设备地址 #define DATA_LENGTH 1 // 收发数据长度 int main() { stdio_init_all(); i2c_init(I2C_PORT, 100 * 1000); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); gpio_pull_up(4); gpio_pull_up(5); char send_data = 'M'; char receive_data; while (true) { // 发送'M'并打印 int write_result = i2c_write_blocking(I2C_PORT, SLAVE_ADDRESS, &send_data, DATA_LENGTH, false); if (write_result >= 0) { printf("Master Sending: M\n"); } sleep_ms(200); // 缩短等待时间,确保从设备处理完成 // 接收'S'并打印 int read_result = i2c_read_blocking(I2C_PORT, SLAVE_ADDRESS, &receive_data, DATA_LENGTH, false); if (read_result >= 0 && receive_data == 'S') { printf("Master Received: S\n"); } sleep_ms(1000); } return 0; }
修复后从设备代码
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #define I2C_PORT i2c0 #define SLAVE_ADDRESS 0x42 #define DATA_LENGTH 1 char received_char = 0; char send_char = 'S'; bool has_received = false; // I2C从设备中断回调函数 void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { switch (event) { case I2C_SLAVE_RECEIVE: // 主设备向从设备写数据 received_char = i2c_read_byte_raw(i2c); has_received = true; break; case I2C_SLAVE_REQUEST: // 主设备请求从设备数据 i2c_write_byte_raw(i2c, send_char); printf("Slave Sending: S\n"); break; case I2C_SLAVE_FINISH: // 传输结束 break; default: break; } } int main() { stdio_init_all(); i2c_init(I2C_PORT, 100 * 1000); gpio_set_function(4, GPIO_FUNC_I2C); gpio_set_function(5, GPIO_FUNC_I2C); gpio_pull_up(4); gpio_pull_up(5); // 设置从设备模式并注册回调 i2c_set_slave_mode(I2C_PORT, true, SLAVE_ADDRESS); i2c_set_slave_event_handler(I2C_PORT, i2c_slave_handler); while (true) { if (has_received) { printf("Slave Received: %c\n", received_char); has_received = false; } sleep_ms(100); // 低功耗轮询 } return 0; }
修复说明
- 从设备改用中断回调:通过
i2c_set_slave_event_handler注册回调函数,在主设备发起写请求时接收数据,发起读请求时返回数据,完全符合I2C协议规范。 - 主设备增加结果判断:添加
i2c_write_blocking和i2c_read_blocking的返回值判断,便于排查通信问题。 - 修正打印内容:调整打印语句匹配需求中的格式。
内容的提问来源于stack exchange,提问作者mahmoud988
相关产品推荐
相关产品推荐

