逻辑分析仪抓取SPI传输波形——外接高速ADC实战

引言

在嵌入式开发中,SPI(Serial Peripheral Interface)协议因其高速、全双工的特性,常被用于与传感器、ADC(模数转换器)等外设通信。然而,SPI的时序调试往往令人头疼——数据是否正确传输?时钟配置是否合理?逻辑分析仪正是解决这类问题的利器。本文以CS553X高速ADC为例,结合STM32驱动代码和逻辑分析仪抓取的波形数据,详解如何通过实战验证SPI通信的正确性。


一、项目背景

CS553X是一款24位Σ-Δ型ADC,支持SPI接口通信,广泛应用于高精度测量场景(如电子秤、温度采集)。本项目通过STM32微控制器驱动CS553X,并借助逻辑分析仪捕获SPI波形,验证以下关键点:

  1. SPI时序是否符合协议要求(时钟相位、片选信号)。
  2. 数据帧内容是否正确(命令、寄存器读写)。
  3. 驱动代码与硬件行为的同步性

二、SPI协议与逻辑分析仪快速入门
1. SPI核心信号线
  • SCLK:时钟信号(主设备控制)。
  • MOSI/SDI:主设备输出,从设备输入。
  • MISO/SDO:从设备输出,主设备输入。
  • CS:片选信号(低电平有效)。
2. 逻辑分析仪的作用
  • 捕获时序波形:精确记录SCLK、MOSI、MISO、CS的跳变。
  • 协议解码:自动解析SPI数据帧内容(如十六进制值)。
  • 时序测量:计算时钟频率、数据建立/保持时间等参数。
    Kingst VIS解析SPI设置
    [Kingst VIS解析SPI设置]

三、驱动代码关键函数解析(CS553X.c)
1. 接口复位:CS553X_ResetInterface
void CS553X_ResetInterface(void) {
    // 发送15字节0xFF(SYNC1) + 1字节0xFE(SYNC0)
    for(int i=0; i<15; i++) tx_data_spi_uint8[i] = 0xFF; 
    tx_data_spi_uint8[15] = 0xFE; 
    SPI_CLR_CS();  // 拉低片选
    HAL_SPI_Transmit(&hspi2, tx_data_spi_uint8, 16, 100); 
    SPI_SET_CS();  // 拉高片选
}

关键点:CS553X要求复位时连续发送至少15个0xFF,最后发送0xFE,以唤醒命令模式。

2. 软件复位:CS553X_SwReset
void CS553X_SwReset(void) {
    tx_data_spi_uint8[0] = 0x03;  // WR_CONFIG命令
    uint32_t regv = 1 << 29;      // RS位(复位标志)
    // 填充4字节配置数据(大端模式)
    tx_data_spi_uint8[1] = (regv >> 24) & 0xFF; 
    ... // 发送并等待20us
}

关键点:复位后需等待至少20μs,再读取配置寄存器确认复位成功。

3. 数据转换启动:CS553X_ConvertStart
void CS553X_ConvertStart(uint8_t cnv_mode, uint8_t setup_chan) {
    uint8_t cmd = 0x80 | (cnv_mode << 4) | setup_chan; // 组合命令字
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100); 
}

关键点:命令字格式为0x8X,其中X包含转换模式和通道号。


四、逻辑分析仪波形分析实战
1. 接口复位波形
  • CS信号:拉低后持续至16字节传输完毕。
  • MOSI数据:15个0xFF + 0xFE,符合预期。
  • 时钟频率:计算SCLK周期为160ns(约6.25MHz),与STM32 SPI配置一致。
    在这里插入图片描述
    复位波形
对应代码
void CS553X_ResetInterface(void){
	uint32_t i=0;
	for(i=0;i<15;i++)
		tx_data_spi_uint8[i]=CS553X_CMD_SYNC1; //at least 15 bytes sync1
	tx_data_spi_uint8[i]=CS553X_CMD_SYNC0;     // one byte sync0
	SPI_CLR_CS();
    HAL_SPI_Transmit(&hspi2, tx_data_spi_uint8, 16, HAL_MAX_DELAY);
	SPI_SET_CS();
}
2. 软件复位
  • 写配置寄存器
  • 发送数据0x03 0x20 0x00 0x00 0x00(RS位置1)。
  • 响应数据:读取配置寄存器时,MISO返回0x00 0x00 0x00 0x00(需确认复位后默认值)。
    在这里插入图片描述
写配置寄存器代码--设置RS位为1,根据数据手册
#define CS553X_CMD_WR_CONFIG    0x03
tx_data_spi_uint8[0]=CS553X_CMD_WR_CONFIG; 
    regv = 1<<29;  //RS set to 1
	tx_data_spi_uint8[1]=(regv>>24)&0XFF; 
	tx_data_spi_uint8[2]=(regv>>16)&0XFF; 
	tx_data_spi_uint8[3]=(regv>>8)&0XFF; 
	tx_data_spi_uint8[4]=(regv>>0)&0XFF; 
	SPI_CLR_CS();
    HAL_SPI_Transmit(&hspi2, tx_data_spi_uint8, 5, HAL_MAX_DELAY);
	SPI_SET_CS();
	lhlx_us_delay(20);  //wait for 20us dut reset
  • 读配置寄存器
  • 发送数据0x0B 0x00 0x00 0x00 0x00
  • 响应数据:读取配置寄存器时,MISO返回0x10 0x00 0x00 0x00
  • 在这里插入图片描述
读配置寄存器代码
#define CS553X_CMD_RD_CONFIG    0x0B
tx_data_spi_uint8[0]=CS553X_CMD_RD_CONFIG; 
	tx_data_spi_uint8[1] = 0x00;
	tx_data_spi_uint8[2] = 0x00;
	tx_data_spi_uint8[3] = 0x00;
	tx_data_spi_uint8[4] = 0x00;
	SPI_CLR_CS();
	HAL_SPI_TransmitReceive(&hspi2, tx_data_spi_uint8, rx_data_spi_uint8, 5,HAL_MAX_DELAY);  
	SPI_SET_CS();

	if((rx_data_spi_uint8[0]&0x10)){ //reset valid
		tx_data_spi_uint8[0]=CS553X_CMD_WR_CONFIG; 
		regv = 0;  //clear RS
		tx_data_spi_uint8[1]=(regv>>24)&0XFF; 
		tx_data_spi_uint8[2]=(regv>>16)&0XFF; 
		tx_data_spi_uint8[3]=(regv>>8)&0XFF; 
		tx_data_spi_uint8[4]=(regv>>0)&0XFF; 
		SPI_CLR_CS();
		HAL_SPI_Transmit(&hspi2, tx_data_spi_uint8, 5, HAL_MAX_DELAY);
		SPI_SET_CS();
	}else{
		while(1);  //reset Failed
	}
3. 其余配置函数
void  CS553X_ReadOffsetAll(CalibChan_Type chan,uint32_t *pBuf);
void  CS553X_ReadGainAll(CalibChan_Type cal_chan,uint32_t *pBuf);
void  CS553X_WriteOffsetAll(SetupChan_Type cal_chan, uint32_t *pBuf);
void  CS553X_WriteGainAll(SetupChan_Type cal_chan, uint32_t *pBuf);

uint32_t  CS553X_ReadSetup(SetupChan_Type chan);
void  CS553X_WriteSetup(SetupChan_Type chan,uint32_t value);
void  CS553X_ReadSetupAll(CalibChan_Type chan,uint32_t *pBuf);
void  CS553X_WriteSetupAll(SetupChan_Type cal_chan,uint32_t *pBuf);

uint32_t CS553X_ReadConfig(void);
void CS553X_WriteConfig(uint32_t value);

void CS553X_ConvertStart(CONV_Type cnv_mode,SetupChan_Type setup_chan);
void CS553X_CalibrateStart(SetupChan_Type setup_chan,CAL_Type calibrate_type);
uint32_t CS553X_ReadData();

uint32_t  CS553X_ReadOffset(CalibChan_Type chan);
void  CS553X_WriteOffset(CalibChan_Type cal_chan, uint32_t value);

uint32_t  CS553X_ReadGain(CalibChan_Type cal_chan);
void  CS553X_WriteGain(CalibChan_Type cal_chan,uint32_t value);
void CS553X_ConvertStop(void);

五、优化建议与调试技巧
  1. 错误处理增强
    if (HAL_SPI_Transmit(..., 100) != HAL_OK) {
        Error_Handler(); // 替换死循环为错误回调
    }
    
  2. 时钟频率优化
    • CS553X支持最高10MHz时钟,可调整STM32 SPI分频系数提升速度。
  3. 自动化测试脚本
    # 使用PySPI自动发送测试命令
    def test_reset():
        send_data([0xFF]*15 + [0xFE])
        assert check_response(0xFE)
    

六、总结

通过逻辑分析仪捕获的SPI波形,我们不仅验证了驱动代码的正确性,还发现了潜在的时序隐患。核心经验如下

  1. 代码与波形必须双向验证:代码逻辑正确≠硬件行为正确。
  2. 逻辑分析仪是调试核心工具:建议搭配协议解码功能(如Saleae Logic)。
  3. 注重细节:CS信号延时、时钟相位(CPOL/CPHA)等参数易被忽视。

附录


相关标签#SPI协议 #逻辑分析仪 #STM32 #ADC驱动开发
讨论互动:你在调试SPI时遇到过哪些“坑”?欢迎评论区分享!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐