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
相关产品推荐
相关产品推荐

