RT-Thread论坛
直播中

杨秀英

8年用户 1465经验值
私信 关注
[问答]

硬件spi从机接收为什么会传输失败,但是接收缓冲区又可以完整打印出来?


  • [2] D/NO_TAG: SPI MASTER send successful!
  • [31m[1006] E/drv.spi: SPI transfer error: 3 [0m[31m[1010] E/spi.core: SPI device spi30 transfer failed [0m
  • SPI rx_buf : ???????????????123456787654321


为什么会传输失败,但是接收缓冲区又可以完整打印出来,以下是我的代码:

  • #include "master_slave.h"

  • #define MASTER_BUS_NAME     "spi2"
  • #define MASTER_DEVICE_NAME "spi20"
  • #define MASTER_CS    GET_PIN(B, 12)

  • #define SLAVE_DEVICE_NAME "spi30"
  • #define SLAVE_BUS_NAME     "spi3"
  • #define SLAVE_CS    GET_PIN(A, 4)

  • #define BUFFER_SIZE  32

  • struct rt_spi_device *slave_device;
  • struct rt_spi_device *master_device;

  • void master_send_slave_recv(const char *str)
  • {
  •     rt_size_t len = strlen(str);
  •     rt_uint8_t tx_buf[len + 1],rx_buf[len + 1];
  •     memcpy(tx_buf, str, len);
  •         tx_buf[len ] = '�';

  •     rt_ssize_t result =  rt_device_write( (rt_device_t) master_device, 0, tx_buf, len);
  •         if(result > 0)
  •             LOG_D("SPI MASTER send successful!n");

  •         rt_device_read((rt_device_t) slave_device, 0, rx_buf, len);
  •         rt_kprintf("n SPI rx_buf : %sn",rx_buf);
  • }

  • /* 主从机初始化并配置 */
  • void master_slave_init()
  • {
  •     rt_err_t result;
  • //        __HAL_RCC_GPIOA_CLK_ENABLE();
  • //        __HAL_RCC_GPIOB_CLK_ENABLE();

  •         rt_hw_spi_device_attach(MASTER_BUS_NAME, MASTER_DEVICE_NAME, MASTER_CS);
  •         rt_hw_spi_device_attach(SLAVE_BUS_NAME, SLAVE_DEVICE_NAME, SLAVE_CS);

  •     master_device = (struct rt_spi_device *)rt_device_find(MASTER_DEVICE_NAME);
  •         slave_device = (struct rt_spi_device *)rt_device_find(SLAVE_DEVICE_NAME);
  •     if (master_device == NULL || slave_device == NULL)
  •     {
  •         rt_kprintf("spi failed! can't find %s device!n", MASTER_DEVICE_NAME);
  •     }

  •     {        // 配置SPI2
  •                 master_device->bus->owner = master_device;
  •                 struct rt_spi_configuration cfg;
  •         cfg.data_width = 8;
  •         cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
  •         cfg.max_hz = 1 * 1000 *1000;                           /* 1M */
  •         result = rt_spi_configure(master_device, &cfg);
  •                 if (result != RT_EOK)
  •                     rt_kprintf("SPI2 configuration failed!n");
  •     }

  •     {        // 配置SPI3
  •                 slave_device->bus->owner = slave_device;
  •                 struct rt_spi_configuration cfg;
  •         cfg.data_width = 8;
  •         cfg.mode = RT_SPI_SLAVE | RT_SPI_MODE_0 | RT_SPI_MSB;
  •         cfg.max_hz = 1 * 1000 *1000;                           /* 1M */
  •         result = rt_spi_configure(slave_device, &cfg);
  •                 if (result != RT_EOK)
  •                     rt_kprintf("SPI3 configuration failed!n");
  •     }
  •         rt_device_open( (rt_device_t) master_device,  RT_DEVICE_FLAG_RDWR);
  •         rt_device_open( (rt_device_t) slave_device,  RT_DEVICE_FLAG_RDWR);
  • }

  • char tx_data[LEN] = "123456787654321";


  • int main(void)
  • {
  •         master_slave_init();
  •         master_send_slave_recv(tx_data);
  •         return RT_EOK;
  • }




回帖(2)

张瑜

2025-10-10 16:26:03
     数据对吗? 建议排查一下底层驱动代码
举报

杨平

2025-10-20 17:04:33

您的SPI从机接收出现传输失败(错误码3)但缓冲区数据完整,可能由以下原因导致:


主要原因分析




  1. SPI超时错误 (ETIMEOUT)



    • 错误码3通常表示RT_ETIMEOUT

    • 根本原因:从机处理速度跟不上主机时钟

    • 现象解释

      • 缓冲区数据完整:硬件SPI FIFO在超时前已接收所有数据

      • 传输失败:从机未能在时限内完成状态确认(如总线释放、标志位检查)





  2. 配置不匹配



    • 主从设备参数不一致:

      • 时钟模式(CPOL/CPHA)

      • 数据位宽(8位/16位)

      • 时钟频率(从机最大速率低于主机)

      • 字节序(MSB/LSB)





  3. 硬件问题



    • 物理线路干扰(SCK/MOSI/CS信号噪声)

    • 电压电平不匹配(如3.3V从机连接5V主机)

    • 时序余量不足(高速传输时建立/保持时间违规)




解决方案


1. 调整超时配置(关键)


在从机初始化代码中增加超时时间:


// 修改SPI配置结构体
struct rt_spi_configuration cfg;
cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; // 示例模式
cfg.max_hz = 1 * 1000 * 1000; // 降低从机时钟
cfg.timeout = 500; // ★ 增加超时值(默认通常100ms), 单位: 时钟节拍
rt_spi_configure(spi_dev_slave, &cfg);

2. 检查主从配置一致性


确保主从端匹配:


// 主机配置(示例)
struct rt_spi_configuration master_cfg = {
    .mode = RT_SPI_MODE_0 | RT_SPI_MSB,
    .max_hz = 1 * 1000 * 1000, // 需≥从机能力
    .data_width = 8,
};

// 从机配置(必须相同)
struct rt_spi_configuration slave_cfg = {
    .mode = RT_SPI_MODE_0 | RT_SPI_MSB, // ★ 模式必须匹配
    .max_hz = 1 * 1000 * 1000,         // ★ 从机支持的最大频率
    .data_width = 8,                   // ★ 数据位宽相同
};

3. 优化传输流程


在主机发送后增加延迟:


// 主机发送函数
rt_spi_send(spi_master, tx_buf, len);

// ★ 增加延迟等待从机处理
rt_thread_mdelay(1); // 1ms延迟,根据实际调整

// 释放CS引脚(如果使用手动CS)
rt_pin_write(MASTER_CS, PIN_HIGH);

4. 硬件检查



  • 使用示波器检查信号质量:

    • CS有效期间SCK脉冲是否完整

    • MOSI数据在SCK边沿是否稳定

    • 是否存在振铃/过冲


  • 缩短接线长度,增加上拉电阻(10kΩ)


5. 调试建议


// 打印完整配置信息
rt_kprintf("Slave config: mode=%d, max_hz=%d, timeout=%dn",
           cfg.mode, cfg.max_hz, cfg.timeout);

// 十六进制打印接收缓冲区(避免字符串截断)
for(int i=0; i     rt_kprintf("%02X ", rx_buf[i]);
}

典型错误场景还原


// 主机(发送成功但时序紧张)
rt_spi_send(master, data, 32);  // 高速发送32字节
立即释放CS引脚               //  ✘ 从机尚未处理完毕

// 从机
while(!SPI_READY_FLAG);       //  ✘ 等待标志超时(错误3)
读取缓冲区();                  // 数据实际已存入FIFO


根本解决:调整超时参数 + 主机释放CS前延迟 + 确保时钟匹配。优先尝试增大cfg.timeout值并降低SPI时钟频率验证是否为时序问题。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分