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

STM8L作为SPI从机无法向树莓派主设备通过MISO回传数据求助

问题原因与修复方案

核心问题梳理

你遇到的无法回传数据的问题是由SPI通信时序逻辑错误、代码冗余、主从侧传输逻辑不匹配三个原因共同导致的:

  • SPI是同步串行通信,主设备发起的每一次字节传输过程中,SCK时钟会同时完成发送和接收两个动作:主设备发MOSI数据的同时,会把从机数据寄存器内预存的内容通过MISO移位输出。你在收到RXNE标志(代表本次字节接收完成)后才往DR写数据,已经错过了本次传输的发送窗口,写入的数据只能用于下一次主设备发起的传输。
  • 你配置了SPI为双线全双工模式,额外调用SPI_BiDirectionalLineConfig是无效操作,该接口仅用于单线双向SPI模式。
  • 树莓派侧代码存在数组越界问题:你定义的data数组长度为1,但调用wiringPiSPIDataRW时传入了长度2,同时仅发起1字节的有效传输,从机没有对应的SCK时钟来发送你后写入的返回值。

代码修改示例

STM8侧修改

void main(void)
{
  // GPIO
  GPIO_Init(GPIOA, GPIO_Pin_7, GPIO_Mode_Out_PP_Low_Fast);
  CLK_Config();

  // Set the MOSI and SCK at high level 
  GPIO_ExternalPullUpConfig(GPIOB, GPIO_Pin_6 | GPIO_Pin_5, ENABLE);
  
  SPI_DeInit(SPI1);
  // 若树莓派不改位序,可将此处改为SPI_FirstBit_MSB保持匹配
  SPI_Init(SPI1, SPI_FirstBit_LSB, SPI_BaudRatePrescaler_2, SPI_Mode_Slave,
           SPI_CPOL_Low, SPI_CPHA_2Edge, SPI_Direction_2Lines_FullDuplex,
           SPI_NSS_Hard, (uint8_t)0x07);

  // 删除多余的SPI_BiDirectionalLineConfig调用

  // 使能SPI
  SPI_Cmd(SPI1, ENABLE);

  // 【重要】提前预存第一个要返回的字节,等待主设备第一次传输
  SPI_SendData(SPI1, 0xFA);

  /* Infinite loop */
  while (1)
  {
    // 等待接收完成
    if(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == SET) {
      GPIO_ToggleBits(GPIOA, GPIO_Pin_7);
      // 读DR清RXNE标志
      uint8_t recv_data = SPI_ReceiveData(SPI1);
      // 【重要】立刻写下一次要返回的字节,给下一次传输用
      SPI_SendData(SPI1, recv_data + 1); // 示例:返回接收到的数值+1,可自定义
      GPIO_ResetBits(GPIOA, GPIO_Pin_7);
    }
  }
}

// CLK_Config代码保留原内容即可

树莓派侧修改

#include <iostream>
#include <wiringPi.h>
#include <wiringPiSPI.h>

using namespace std;

int main()
{
    wiringPiSetup();
    // 频率、CPOL/CPHA必须和STM8配置完全匹配,模式1对应CPOL=0, CPHA=1即2Edge
    // 注意:若STM8保留LSB优先配置,需额外设置SPI_LSB_FIRST标志,否则改为MSB优先匹配默认配置
    wiringPiSPISetupMode(0, 50000, 1); 
    // 准备2字节:第一个是发给STM8的0x5A,第二个是占位字节,用于接收STM8预存的返回值
    unsigned char data[] = {0x5A, 0x00};
    // 传输2字节:第一个字节传输时会收到STM8预存的0xFA,第二个字节传输时会收到STM8收到0x5A后写入的0x5B
    wiringPiSPIDataRW(0, data, 2);
    // 输出返回结果
    std::cout << "第一次返回值:" << std::hex << (int)data[0] << std::endl;
    std::cout << "第二次返回值:" << std::hex << (int)data[1] << std::endl;
    return 0;
}

SPI单数据寄存器设计说明

单数据寄存器是SPI外设的标准设计,绝大多数通用MCU的SPI外设都采用该方案:

  • 对外暴露的同一个DR寄存器,内部对应两个独立的缓冲:写操作时写入发送缓冲,读操作时读取接收缓冲,二者完全独立互不干扰。
  • 全双工传输由内部独立的发送移位寄存器、接收移位寄存器配合SCK时钟同步完成,不需要对外暴露两个独立的寄存器。
    你观察到的“读取DR后旧值保留”是正常现象,仅影响读操作的返回值,不会覆盖你写入发送缓冲的内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:24:03