You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派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;
}

问题分析

  1. 主设备阻塞:主设备的i2c_write_blocking会等待从设备的ACK响应,原从设备代码未正确处理主设备的写请求,导致主设备一直卡在写操作步骤,无法执行后续打印和读操作。
  2. 从设备主动写错误:I2C协议中,从设备不能主动发起写操作,必须由主设备发起读请求时,从设备才能返回数据。原从设备主动调用i2c_write_raw_blocking不符合规范,是主设备无法接收数据的核心原因。
  3. 从设备接收逻辑错误: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;
}

修复说明

  1. 从设备改用中断回调:通过i2c_set_slave_event_handler注册回调函数,在主设备发起写请求时接收数据,发起读请求时返回数据,完全符合I2C协议规范。
  2. 主设备增加结果判断:添加i2c_write_blocking和i2c_read_blocking的返回值判断,便于排查通信问题。
  3. 修正打印内容:调整打印语句匹配需求中的格式。

内容的提问来源于stack exchange,提问作者mahmoud988

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:42:06