尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32H5 SPI DMA采集ADC数据异常排查与修复

STM32H5 SPI DMA采集ADC数据异常排查与修复 1. 先把现场还原清楚中断版能跑DMA版到底败在哪最近在Nucleo H563ZI上调试一块SPI接口的外置ADC遇到一个非常典型的“换API就翻车”的问题用HAL的SPI中断接口 HAL_SPI_TransmitReceive_IT 读ADC数据完全正常把代码改成 HAL_SPI_TransmitReceive_DMA 之后要么卡死在DMA等待要么读回来一串0xFF偶尔第一帧是对的后面全乱。如果你也在用STM32H5系列或者任何带DMAMUX的STM32芯片接SPI接口ADC时碰到“中断能用、DMA不能用”的怪现象这篇文章基本就是给你写的。我会把当时的硬件配置、排查思路、最终修复代码以及后来在连续采样场景里踩到的其他坑全部摊开讲。1.1 硬件连接和CubeMX参数先说板子配置方便你对照自己的环境项目配置主控Nucleo H563ZISTM32H563ZICortex-M33250MHzSPI外设SPI1Full-Duplex Master8bit字长SPI引脚SCKPA5MISOPA6MOSIPA7片选CSPA4软件NSS控制ADC芯片SPI接口24位Delta-Sigma ADC带寄存器读命令开发环境STM32CubeMX 6.10 STM32CubeH5 1.11CubeMX里SPI1的基本参数很常规帧格式Motorola8bit数据时钟极性CPOL0时钟相位CPHA1第二个边沿采样不少SPI ADC都吃这个参数预分频32SPI时钟约7.8MHzNSSSoftware也就是不使用硬件NSSPA4当普通GPIO用SPI外设本身工作很稳定中断版代码读到的ADC数据和示波器上看到的波形一致说明硬件接线、供电、参考电压这些基础项都没问题。1.2 中断版代码能交出正常数据的基准能跑通的中断版代码非常简单uint8_t adc_tx[3] {0x10, 0x00, 0x00}; // 命令字节 两个Dummy字节 uint8_t adc_rx[3] {0}; void ADC_Read_IT(void) { HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive_IT(hspi1, adc_tx, adc_rx, 3); } void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET); // 在这里把 adc_rx 里的数据拼装、保存 } }这套代码在主循环里每10ms调用一次ADC_Read_IT打印出来的ADC值稳定CS波形也正常。在SPI接口ADC这种场景下中断版是最容易跑通的“基准版”因为它能让CS的每个动作都精确落在CPU可控的时间点上。1.3 DMA版代码看似只换个函数名实际差很远为了降低CPU占用我把函数名从IT改成DMA。CubeMX里也补上了SPI1的DMA请求一个SPI1_TX一个SPI1_RX都配置成Normal模式、Data Width为Byte。uint8_t adc_tx[3] {0x10, 0x00, 0x00}; uint8_t adc_rx[3] {0}; void ADC_Read_DMA(void) { HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive_DMA(hspi1, adc_tx, adc_rx, 3); } void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET); // 处理数据 } }我当时觉得这不过就是换了底层搬运方式SPI时序一样数据应该一样。结果完全不是。1.4
返回列表