根据您描述的问题,MSP430FR2433 SPI通信失败的两个关键原因是:MOSI数据建立时间不足(时钟跳变时数据不稳定) 和 SPI空闲时MOSI电平状态异常。以下是针对性的解决方案:
根本原因分析
MOSI建立时间不足
MSP430FR2433的eUSCI模块与旧型号USCI存在时序差异。当CPHA=0(模式0)时,数据必须在SCLK上升沿之前半个时钟周期稳定。您的波形显示MOSI与时钟同步跳变,违反了建立时间要求。
SPI空闲时MOSI电平问题
MSP430FR2433在SPI空闲时保持MOSI为上一次发送的最后一位电平(如高电平),而ADXL362默认期望空闲时MOSI为低(模式0)。这会导致通信起始时出现额外的高电平脉冲,破坏数据帧。
解决方案
1. 调整SPI时序相位(关键步骤)
修改时钟相位寄存器 CPHA=1(模式3),确保数据在时钟下降沿采样:
UCB0CTLW0 |= UCMSB; // MSB First
UCB0CTLW0 |= UCMST | UCSYNC; // Master mode, SPI
UCB0CTLW0 |= UCCKPH; // 时钟相位配置 CPHA=1(数据在下降沿采样)
UCB0CTLW0 &= ~UCCKPL; // 时钟极性 CPOL=0(空闲低电平)
原理:
- 模式3(CPHA=1)允许MOSI在SCLK上升沿变化,在下降沿被采样,避开建立时间问题。
- ADXL362支持模式0和模式3(参见其数据手册),兼容性已验证。
2. 强制SPI空闲时MOSI为低电平
在SPI传输完成后,手动拉低MOSI引脚:
void inline SPI_EndFrame(void) {
// 切换MOSI为GPIO模式并强制拉低
P1SEL0 &= ~BIT7; // P1.7(MOSI) 切换为GPIO
P1SEL1 &= ~BIT7;
P1DIR |= BIT7; // 设置为输出
P1OUT &= ~BIT7; // 输出低电平
}
传输开始前重新启用SPI功能:
void inline SPI_BeginFrame(void) {
P1SEL0 |= BIT7; // P1.7(MOSI) 恢复SPI功能
P1SEL1 &= ~BIT7;
}
调用示例:
uint8_t SPI_Transfer(uint8_t data) {
SPI_BeginFrame(); // 启用SPI MOSI功能
UCB0TXBUF = data;
while (!(UCB0IFG & UCRXIFG));
uint8_t res = UCB0RXBUF;
SPI_EndFrame(); // 传输结束强制MOSI=0
return res;
}
3. 优化SPI时钟速度
降低时钟频率(尤其当线路较长时),增加时序裕量:
UCB0BRW = 16; // 设置时钟分频(假设ACLK=16MHz → SPI CLK=1MHz)
4. 关键波形整改点
- SCLK空闲状态:必须为低电平(CPOL=0)。
- CSN片选时序:传输前拉低,结束后拉高并保持至少100ns。
- MOSI建立时间:在SCLK上升沿前稳定(模式0)或下降沿稳定(模式3)。
完整配置代码参考
void SPI_Init(void) {
// 引脚配置
P1SEL0 |= BIT5 | BIT6 | BIT7; // P1.5=SCLK, P1.6=MISO, P1.7=MOSI
P1SEL1 &= ~(BIT5 | BIT6 | BIT7);
// SPI主机模式设置
UCB0CTLW0 = UCSWRST; // 复位SPI
UCB0CTLW0 |= UCMSB // MSB First
| UCMST // 主机模式
| UCSYNC // SPI模式
| UCCKPH // CPHA=1(模式3)
| UCSSEL__SMCLK; // 选择SMCLK时钟源
UCB0BRW = 16; // 分频至1MHz(若SMCLK=16MHz)
UCB0CTLW0 &= ~UCSWRST; // 释放复位
// 初始拉低MOSI
SPI_EndFrame();
}
// 单字节传输函数
uint8_t SPI_Transfer(uint8_t data) {
SPI_BeginFrame(); // 启用MOSI SPI功能
UCB0TXBUF = data;
while (!(UCB0IFG & UCRXIFG)); // 等待接收完成
uint8_t res = UCB0RXBUF;
SPI_EndFrame(); // 强制MOSI=0
return res;
}
验证要点
- 示波器检测:
- 确认SCLK空闲为低(CPOL=0),数据在下降沿稳定(CPHA=1)。
- 检查CSN激活期间MOSI起始位为低电平。
- ADXL362寄存器读取:
尝试读取DEVID_AD寄存器(地址0x00),应返回固定值0xAD:
uint8_t Read_ADXL_Reg(uint8_t addr) {
CSN_LOW(); // 片选使能
SPI_Transfer(0x0B); // 读命令
SPI_Transfer(addr);
uint8_t val = SPI_Transfer(0);
CSN_HIGH(); // 片选释放
return val;
}
通过上述调整,可同时解决建立时间不足和空闲电平问题,确保通信稳定性。务必检查硬件连接(尤其是CSN和VCC电平),必要时增加上拉电阻。