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

dsPIC33CK256MP506 SPI自定义字长仅传输前6位问题求助

dsPIC33CK256MP506 SPI从机模式同步UART发送故障排查

问题背景

我正在将dsPIC33CK256MP506的SPI配置为从机模式的同步UART(仅发送)。由于SPI从最高位开始传输,我在8个数据位前添加1个起始位,之后添加1个停止位和1个空闲位,总长度为11位。

我将SPI3的SPI3CON2Lbits.WLENGTH设置为10(对应11位),硬件确实能在外部主时钟下移出11位,但仅SPI3BUFL中加载的前6位输出正确,其余5位始终为0。

空闲电平为低,因此起始位为1,停止位+空闲位为00。

初始化代码

SPI3CON1Lbits.SPIEN  =  0;  // Disable SPI
RPINR29bits.SCK3R    = 64; // RP64 - SCLK IN
RPOR22bits.RP76R     = 11; // RP76 - Dout

SPI3CON1Lbits.DISSDI  =  1; // Disable Data IN
SPI3CON1bits.MSTEN    =  0; // Set SPI to Slave mode
SPI3CON1Lbits.CKP     =  0; // CKP is the same as CPOL
SPI3CON1Lbits.CKE     =  1; // CKE is the inverse of CPHA
SPI3CON1Lbits.SMP     =  0; // 0 when in slave mode
SPI3CON1Lbits.MODE    =  0; // Shall be zero if WLENGTH > 0
SPI3CON2Lbits.WLENGTH = 10; // 1 start + 8 data + 1 stop + 1 Idle bit.

(void volatile)SPI3BUFL; // Clear buffer
(void volatile)SPI3BUFH; // Clear buffer

SPI3IMSKLbits.SPITBEN = 1; // Enable TX Buffer Empty
IFS3bits.SPI3TXIF     = 0; // Clear Tx interrupt flag
IEC3bits.SPI3TXIE     = 1; // Enable TX interrupts
IEC8bits.SPI3IE       = 1; // Enable SPI3 interrupts
SPI3CON1Lbits.SPIEN   = 1; // Enable SPI

中断服务程序(ISR)

void __attribute__((interrupt, auto_psv)) _SPI3TXInterrupt(void) {    

    if(SPI3STATLbits.SPITBE == 1) { // Check if TX buffer is empty
        SPI3BUFL = (uint16_t)0b11111111100;
    }

    IFS3bits.SPI3TXIF = 0;
}

测试现象

测试时设置1个起始位、8个全1数据位、1个停止位和1个空闲位,预期输出为11111111100,但示波器显示实际输出为11111100000。仅能修改前6位的值,后5位始终为0。


故障原因分析

问题核心在于多比特SPI传输模式下的缓冲区位对齐逻辑:

  1. SPI缓冲区的位映射规则
    当WLENGTH > 0(非8位传输模式)时,dsPIC33CK的SPI模块会将SPI3BUFH(高8位)和SPI3BUFL(低8位)组合成16位缓冲区,并且从缓冲区的最高有效位(MSB)开始依次移出数据。

你当前的操作存在两个关键错误:

  • SPI3BUFL是8位寄存器,写入16位值0b11111111100时会自动截断为低8位0b11111100,高3位0b111直接丢失;
  • SPI3BUFH未被赋值,保持初始的全0状态。

最终16位缓冲区的内容为0b0000000011111100,当WLENGTH=10(11位传输)时,硬件会从缓冲区的最高位开始取11位,即0b00000000111。由于SPI是MSB优先传输,实际输出的位序列为0、0、0、0、0、0、0、0、1、1、1,若示波器以LSB在前的方式显示,就会呈现为11111100000,与你观察到的现象完全一致。

  1. WLENGTH配置验证
    WLENGTH的取值规则为:传输位数 = WLENGTH + 1,因此设置为10对应11位传输是正确的,这部分无问题。

解决方法

需要将11位目标数据加载到16位SPI缓冲区的高11位,可以通过以下两种方式实现:

方法1:直接操作16位SPIBUF寄存器

void __attribute__((interrupt, auto_psv)) _SPI3TXInterrupt(void) {    
    if(SPI3STATLbits.SPITBE == 1) { // Check if TX buffer is empty
        // 将11位目标数据左移5位,放到16位缓冲区的高11位
        uint16_t tx_data = 0b11111111100 << 5;
        SPI3BUF = tx_data; // 自动分配到SPI3BUFH和SPI3BUFL
    }

    IFS3bits.SPI3TXIF = 0;
}

方法2:手动拆分到高低8位寄存器

void __attribute__((interrupt, auto_psv)) _SPI3TXInterrupt(void) {    
    if(SPI3STATLbits.SPITBE == 1) { // Check if TX buffer is empty
        uint16_t tx_data = 0b11111111100 << 5;
        SPI3BUFH = (tx_data >> 8) & 0xFF; // 写入高8位
        SPI3BUFL = tx_data & 0xFF;        // 写入低8位
    }

    IFS3bits.SPI3TXIF = 0;
}

额外注意事项

  • 初始化时清除缓冲区的操作是正确的,也可以直接写入0来确保初始状态干净;
  • 从机模式下SMP=0、MODE=0的配置符合手册要求,无需修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:42:05