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传输模式下的缓冲区位对齐逻辑:
- 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,与你观察到的现象完全一致。
- 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
相关产品推荐
相关产品推荐

