硬汉嵌入式论坛

 找回密码
 立即注册
查看: 865|回复: 31
收起左侧

[DMA] SPI + DMA + TIM 采集外部 ADC

  [复制链接]

4

主题

90

回帖

102

积分

初级会员

积分
102
发表于 2025-6-10 14:40:14 | 显示全部楼层 |阅读模式
本帖最后由 Penguins 于 2025-6-10 15:14 编辑

我使用 H743 采集外部 ADC,通过 DMAMUX 的 SYNC 配合 SPI 以 1 MSPS 接收单路 16-bit 转换结果。但当 HAL_SPI_Receive_DMA(...) 的数量在 15 个及以上时,接收结果错误且仅有前 8 个正确,数量在 14 个及以下时都正确。此处仅进行软件测试,即 SPIx_SCK 悬空,SPIx_MISO 接固定高电平,接收结果应均为 0xFFFF。关闭来自 TIM12_TRGO 的 SYNC 的同步功能,连续接收 16 个 16-bit 数据是正确的;可以排除 DMAMUX 的 Overrun,因为 SPI 时钟为 60 MHz;引脚速度已调整至 Very High,也尝试过使能内部上拉电阻;且相同的 SYNC 配置(仅降低 TIM12 频率至 1 Hz)在串口发送 64 个字符(每次传输 2 个)无异常。经测试发现,接收错误时未能进入 HAL_SPI_RxCpltCallback(...) 接收完成回调函数。请问还有什么检查思路或尝试方案吗?以下为配置代码和测试程序。

  • main.c
[C] 纯文本查看 复制代码
__ZI_AXI_SRAM char gUART1_TxMsg[UART_BUF_SIZE];
__ZI_AXI_SRAM uint16_t gSPI2_RxMsg[32];

__IO uint8_t gSPI2_RxCplt = 0;

myUART_HandleTypeDef myUART = {
  .huart = &huart1,
  .pTxMsg = gUART1_TxMsg,
  .pRxMsg = gUART1_RxMsg,
};

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
  if (hspi == &hspi2)
  {
    gSPI2_RxCplt = 1;
  }
}

int main(void)
{
  SCB_EnableICache();
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_TIM14_Init();
  MX_TIM12_Init();
  MX_SPI2_Init();
  
  /* USER CODE BEGIN 2 */
  HAL_SPI_Receive_DMA(&hspi2, (uint8_t *)gSPI2_RxMsg, 16);
  HAL_TIM_Base_Start(&htim12);
  
  uint32_t tickstart;
  tickstart = HAL_GetTick();
  while (gSPI2_RxCplt == 0 && HAL_GetTick() - tickstart < 0x3FF);
  HAL_TIM_Base_Stop(&htim12);
  
  myUART_Transmit_DMA(&myUART, "status = %s\n", gSPI2_RxCplt ? "SUCCESS!" : "FAILED!");
  myUART_Transmit_DMA(&myUART, "rx0 = %x %x %x %x\n", gSPI2_RxMsg[0], gSPI2_RxMsg[1], gSPI2_RxMsg[2], gSPI2_RxMsg[3]);
  myUART_Transmit_DMA(&myUART, "rx1 = %x %x %x %x\n", gSPI2_RxMsg[4], gSPI2_RxMsg[5], gSPI2_RxMsg[6], gSPI2_RxMsg[7]);
  myUART_Transmit_DMA(&myUART, "rx2 = %x %x %x %x\n", gSPI2_RxMsg[8], gSPI2_RxMsg[9], gSPI2_RxMsg[10], gSPI2_RxMsg[11]);
  myUART_Transmit_DMA(&myUART, "rx3 = %x %x %x %x\n", gSPI2_RxMsg[12], gSPI2_RxMsg[13], gSPI2_RxMsg[14], gSPI2_RxMsg[15]);
  while (1)
  {
  }
}


  • spi.c
[C] 纯文本查看 复制代码
void MX_SPI2_Init(void)
{
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
  hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 0x0;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  hspi2.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi2.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi2.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi2.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi2.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi2.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi2.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  HAL_DMA_MuxSyncConfigTypeDef pSyncConfig= {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(spiHandle->Instance==SPI2)
  {
  /** Initializes the peripherals clock */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI2;
    PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    */
    GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* SPI2 DMA Init */
    /* SPI2_RX Init */
    hdma_spi2_rx.Instance = DMA2_Stream0;
    hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;
    hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_rx.Init.Mode = DMA_NORMAL;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_TIM12_TRGO;
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_FALLING;
    pSyncConfig.SyncEnable = ENABLE;
    pSyncConfig.EventEnable = DISABLE;
    pSyncConfig.RequestNumber = 1;
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi2_rx, &pSyncConfig) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi2_rx);

    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);
  }
}


  • tim.c

[C] 纯文本查看 复制代码
void MX_TIM12_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim12.Instance = TIM12;
  htim12.Init.Prescaler = 23999;
  htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim12.Init.Period = 9999;
  htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim12, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}


  • 【测试结果1】HAL_SPI_Receive_DMA(...) 接收 14 个 16-bit 时的串口数据
[C] 纯文本查看 复制代码
status = SUCCESS!
rx0 = ffff ffff ffff ffff
rx1 = ffff ffff ffff ffff
rx2 = ffff ffff ffff ffff
rx3 = ffff ffff 0 0


  • 【测试结果2】HAL_SPI_Receive_DMA(...) 接收 15 个 16-bit 时的串口数据
[C] 纯文本查看 复制代码
status = FAILED!
rx0 = ffff ffff ffff ffff
rx1 = ffff ffff ffff ffff
rx2 = 0 0 0 0
rx3 = 0 0 0 0


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117530
QQ
发表于 2025-6-11 07:28:33 | 显示全部楼层
myUART_Transmit_DMA代码是不是没贴
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-11 12:22:22 | 显示全部楼层
串口发送应该和这个关系不大吧,代码也附上
[C] 纯文本查看 复制代码
void myUART_Transmit_DMA(myUART_HandleTypeDef *myhuart, const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	while (myhuart->huart->gState != HAL_UART_STATE_READY || myhuart->huart->hdmatx->State != HAL_DMA_STATE_READY);
	vsnprintf(myhuart->pTxMsg, UART_BUF_SIZE, format, ap);
	va_end(ap);
	HAL_UART_Transmit_DMA(myhuart->huart, (uint8_t *)myhuart->pTxMsg, strlen(myhuart->pTxMsg));
}
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-11 17:30:59 | 显示全部楼层
顶顶顶顶顶
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117530
QQ
发表于 2025-6-12 07:55:17 | 显示全部楼层
Penguins 发表于 2025-6-11 12:22
串口发送应该和这个关系不大吧,代码也附上
[mw_shl_code=c,true]void myUART_Transmit_DMA(myUART_Handle ...

这个等待使用传输完成回调里面做标志,效果更好。
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-12 11:05:34 | 显示全部楼层
eric2013 发表于 2025-6-12 07:55
这个等待使用传输完成回调里面做标志,效果更好。

但是还是不能解决 SPI 的同步问题啊,不用 DMAMUX 的 SYNC 是正常的,加了 TIM12_TRGO 就不行了
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117530
QQ
发表于 2025-6-13 06:42:36 | 显示全部楼层
当前的定时器触发速度最快能做到多少不出错,100KHz能行吗
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-18 22:12:57 | 显示全部楼层
本帖最后由 Penguins 于 2025-6-20 13:59 编辑
eric2013 发表于 2025-6-13 06:42
当前的定时器触发速度最快能做到多少不出错,100KHz能行吗

今天重新测试了若干方案,都是 SPI2 DMA 接收 16-bit 数据,结果如下:

序号MCUSPI 时钟频率触发源触发频率测试结果
1H743500 kHzTIM12 TRGO (Update)10 Hz8 个及以下:成功
9 个及以上:失败
2H74360 MHzTIM12 TRGO (Update)1 MHz8 个及以下:成功
9 个及以上:失败
3H74360 MHzEXTI0 (TIM13 在 PA6 生成 PWM,短接至 PB0)1 MHz任意数量:失败
4G47442.5 MHzEXTI10 (TIM15 在 PF9 生成 PWM,短接至 PF10)1 MHz任意数量:成功

  • 所有测试条件下,SPI 引脚配置为 GPIO_SPEED_FREQ_VERY_HIGH(否则接收任意数量均失败)
  • H743 测试条件下,未开启 I-Cache、D-Cache,未配置 MPU,缓存数组放在 AXI SRAM
  • H743 测试条件下,更换开发板运行结果相同


目前还是对 H743 没有什么头绪,但是 G474 倒是可以用了。


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117530
QQ
发表于 2025-6-19 09:39:47 | 显示全部楼层
Penguins 发表于 2025-6-18 22:12
今天重新测试了若干方案,都是 SPI2 DMA 接收 16-bit 数据,结果如下:

这个测试很详细,后面我也试试
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-19 19:48:15 | 显示全部楼层
Penguins 发表于 2025-6-18 22:12
今天重新测试了若干方案,都是 SPI2 DMA 接收 16-bit 数据,结果如下:

忘记说了,8 楼所有测试条件下,每次触发时 DMA 放行数量均为 1,即寄存器 DMAMUX_CxCR 的 NBREQ[4:0] = 0。


今天继续测试又发现,即使是 G474,在每次触发时 DMA 放行数量为 2 时,依然出现传输失败问题。
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-19 23:07:05 | 显示全部楼层
Penguins 发表于 2025-6-19 19:48
忘记说了,8 楼所有测试条件下,每次触发时 DMA 放行数量均为 1,即寄存器 DMAMUX_CxCR 的 NBREQ[4:0] =  ...

已查出是 HAL 库函数错误,修复后可正常使用。明日详述并投稿至 ST 论坛。
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-20 01:52:54 | 显示全部楼层
对 G4 系列的 DMA2 通道,在 DMAMUX 每次触发放行数量超过 1 时,现版本 HAL 库 (V1.2.5) 因错误计算 hdma->DMAmuxChannelStatusMask,在进入 HAL_DMAEx_MUX_IRQHandler(DMA_HandleTypeDef *hdma) 后无法正确判断并清除同步溢出中断 (synchronization overrun interrupt)。对本例,该现象将导致无法正常进入 SPI 接收完成回调函数 HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi),而换用 DMA1 通道即可正常使用。

此错误已提交至 ST 社区,有关代码可参阅此链接。

H7 系列现版本 HAL 库 (V1.11.5) 未现此错误,已正确处理 DMA2 流的偏移问题。

至此,G4 系列已成功实现基于 DMAMUX 的 SPI 主机仅接收 (Receive Only Master) 模式下定频定量采集外部 ADC 数据,但 H7 系列仍待研究。

回复

使用道具 举报

25

主题

232

回帖

307

积分

高级会员

积分
307
QQ
发表于 2025-6-20 08:49:10 | 显示全部楼层
mark DMAMUX HAL 库 ChannelStatus 问题
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-20 14:21:04 | 显示全部楼层
Penguins 发表于 2025-6-18 22:12
今天重新测试了若干方案,都是 SPI2 DMA 接收 16-bit 数据,结果如下:

对序号 2 的 H743 由 TIM12 TRGO 触发方案,若在 HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) 前若关闭 DMAMUX 同步触发配置,即清除 DMAMUX_CxCR_SE 位条件下,任意长度均未现异常;而使用同步触发仍仅在总数不超过 8 时正常,超过时仅触发一次 DMA 传输,表现为对应 DMA 流的 DMA_SxNDTR 锁死于设定值减 DMAMUX1_CxCR 寄存器的 NBREQ[4:0]

对比 G4 与 H7 参考手册,DMAMUX 章节而未现功能区别,而 DMA 章节差异较大,如 H7 支持 FIFO 模式(但本例并未使用)。后续考虑由 DMA 章节入手排查。
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-24 11:22:46 | 显示全部楼层
Penguins 发表于 2025-6-20 14:21
对序号 2 的 H743 由 TIM12 TRGO 触发方案,若在 HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t ...

博主,您好!我目前也在处理TIM12触发SPI DMA进行外部ADC数据通讯。目前和您SPI及TIM配置相同,目前我的函数只有
uint8_t data[2] = {0};

HAL_TIM_Base_Start(&htim12);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_SPI_Receive_DMA(&hspi1, data, 2);
但是在debug时发现data的值实际并未变化。
且不会进入HAL_SPI_RxCpltCallback回调函数中,请问是什么原因?
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-24 13:29:10 | 显示全部楼层
[C] 纯文本查看 复制代码
void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 0x0;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  HAL_DMA_MuxSyncConfigTypeDef pSyncConfig= {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
  /* USER CODE BEGIN SPI1_MspInit 0 */

  /* USER CODE END SPI1_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
    PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    */
    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* SPI1 DMA Init */
    /* SPI1_RX Init */
    hdma_spi1_rx.Instance = DMA1_Stream0;
    hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;
    hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi1_rx.Init.Mode = DMA_CIRCULAR;
    hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_TIM12_TRGO;
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_FALLING;
    pSyncConfig.SyncEnable = ENABLE;
    pSyncConfig.EventEnable = DISABLE;
    pSyncConfig.RequestNumber = 1;
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi1_rx, &pSyncConfig) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(spiHandle,hdmarx,hdma_spi1_rx);

    /* SPI1 interrupt Init */
    HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
  /* USER CODE BEGIN SPI1_MspInit 1 */

  /* USER CODE END SPI1_MspInit 1 */
  }
}

[C] 纯文本查看 复制代码
void MX_TIM12_Init(void)
{

  /* USER CODE BEGIN TIM12_Init 0 */

  /* USER CODE END TIM12_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM12_Init 1 */

  /* USER CODE END TIM12_Init 1 */
  htim12.Instance = TIM12;
  htim12.Init.Prescaler = 25-1;
  htim12.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim12.Init.Period = 25-1;
  htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim12, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim12, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM12_Init 2 */

  /* USER CODE END TIM12_Init 2 */

}

[C] 纯文本查看 复制代码
	HAL_TIM_Base_Start(&htim12);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
	HAL_SPI_Receive_DMA(&hspi1, data, 1);

数组未改变,且无法跳转到RXCALLBACK中,求大佬们指点为什么,困扰好多天了
回复

使用道具 举报

6

主题

60

回帖

78

积分

初级会员

积分
78
发表于 2025-6-25 09:03:01 | 显示全部楼层
陈清 发表于 2025-6-24 11:22
博主,您好!我目前也在处理TIM12触发SPI DMA进行外部ADC数据通讯。目前和您SPI及TIM配置相同,目前我的 ...

请问一下,TIM12触发SPI DMA是什么意思啊?就是用定时器周期性拉低SPI的CS吗?
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-25 22:17:25 | 显示全部楼层
陈清 发表于 2025-6-24 13:29
[mw_shl_code=c,true]void MX_SPI1_Init(void)
{

我的 DMA 是 Normal 而非 Circular,你试试是不是这个原因?
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-25 22:24:13 | 显示全部楼层
红烧鱼头 发表于 2025-6-25 09:03
请问一下,TIM12触发SPI DMA是什么意思啊?就是用定时器周期性拉低SPI的CS吗?

根据 H743 参考手册的 DMAMUX 框图,DMAMUX 支持 同步输入(Synchronization inputs),用以放行 DMA 请求,表 125 指出 TIM12 TRGO 可作为该同步输入信号,即周期性放行 SPI 在指定位数(由 SPI 配置)下传输指定次数(由 DMAMUX 配置)。

SPI 的硬件 NSS 有诸多限制,我一般不使用硬件片选,DMAMUX 也与此无关。
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 09:04:45 | 显示全部楼层
红烧鱼头 发表于 2025-6-25 09:03
请问一下,TIM12触发SPI DMA是什么意思啊?就是用定时器周期性拉低SPI的CS吗?

高版本的CUBEMX(我用的6.13)配置SPI的DMA时有同步信号选择,可以选择外部信号触发,当选择TIM12作为TRGO时可以选择其边沿(上升、下降、双边沿)触发。这样在程序内部配置好之后就可以脱手了,但实际使用时发现不完全能去掉CPU的开销,依然会有消耗,速度有一定的提升。
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 09:06:13 | 显示全部楼层
Penguins 发表于 2025-6-25 22:17
我的 DMA 是 Normal 而非 Circular,你试试是不是这个原因?

感谢回复!昨天尝试了一下,将DMA的宽度从BYTE改成HALF WORD就可以了。
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 09:40:18 | 显示全部楼层
陈清 发表于 2025-6-26 09:04
高版本的CUBEMX(我用的6.13)配置SPI的DMA时有同步信号选择,可以选择外部信号触发,当选择TIM12作为TRG ...

TIM12 TRGO 可以配置为 Reset、Enable、Update、OC1、OC1REF、OC2REF,DMAMUX 可以配置上述信号的出发极性,不过对于前 4 种短脉冲意义不大,后 2 种则是与 PWM 边沿对应。
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 09:42:02 | 显示全部楼层
陈清 发表于 2025-6-26 09:06
感谢回复!昨天尝试了一下,将DMA的宽度从BYTE改成HALF WORD就可以了。

好的,CubeMX 在 G4 配置 SPI 为 16-bit 时会强制 DMA 为 Half-word,而 H7 保留 Byte,可能是因为 H7 有 FIFO 功能,不要求等宽。
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 09:42:54 | 显示全部楼层
Penguins 发表于 2025-6-26 09:40
TIM12 TRGO 可以配置为 Reset、Enable、Update、OC1、OC1REF、OC2REF,DMAMUX 可以配置上述信号的出发极 ...

这个我就不太清楚了,只是最近做麦克风才遇到的,三个边沿是我基于CUBEMX配置里面看到的,剩下的还需要了解,感谢硬汉
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 09:43:46 | 显示全部楼层
Penguins 发表于 2025-6-26 09:42
好的,CubeMX 在 G4 配置 SPI 为 16-bit 时会强制 DMA 为 Half-word,而 H7 保留 Byte,可能是因为 H7 有 ...

感谢分享,原来还会有这种区别
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 09:48:28 | 显示全部楼层
陈清 发表于 2025-6-26 09:06
感谢回复!昨天尝试了一下,将DMA的宽度从BYTE改成HALF WORD就可以了。

不过,你是如何测试可以的?我昨天又发现新问题:

程序中先开启 SPI(即调用 HAL_SPI_Receive_DMA),再开启定时器(即调用 HAL_TIM_Base_Start),出现 SPI 时钟线在开启 SPI 后立即产生时钟信号,没有等待定时器的同步信号;且如果不开启定时器,即完全没有同步信号,理论上应完全不开始通信,但测量时钟引脚发现在持续发出时钟信号,以致 ADC 时序完全混乱。

日前测试时,我仅将 SPI 数据线置固定电平,采样数据是正常的,但没想到时钟线有问题。

回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 09:52:06 | 显示全部楼层
Penguins 发表于 2025-6-26 09:48
不过,你是如何测试可以的?我昨天又发现新问题:

程序中先开启 SPI(即调用 HAL_SPI_Receive_DMA), ...

我是先开启时钟再开启 SPI的,且我的SPI是TXRX一起的,先发两包空数据然后收两包数据。目前测试数据也是比较正常的,只是我的USB不能用,无法录音观察其是否确实正常
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 10:21:50 | 显示全部楼层
陈清 发表于 2025-6-26 09:52
我是先开启时钟再开启 SPI的,且我的SPI是TXRX一起的,先发两包空数据然后收两包数据。目前测试数据也是 ...

你的 ADC 时序图能贴一份吗?我因为需要定时器除了一路向 DMAMUX 发送同步信号,还要输出一路 PWM 给 ADC 用作转换开启,所以得先开 SPI 再开 TIM。
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 10:23:36 | 显示全部楼层
Penguins 发表于 2025-6-26 10:21
你的 ADC 时序图能贴一份吗?我因为需要定时器除了一路向 DMAMUX 发送同步信号,还要输出一路 PWM 给 ADC ...

抱歉,我没有通过TIM驱动ADC,我的ADC完全是由SPI触发的
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 11:47:26 | 显示全部楼层
陈清 发表于 2025-6-26 10:23
抱歉,我没有通过TIM驱动ADC,我的ADC完全是由SPI触发的

了解,但是还是想看看你们的时序是如何的
回复

使用道具 举报

7

主题

81

回帖

102

积分

初级会员

积分
102
发表于 2025-6-26 13:05:46 | 显示全部楼层
Penguins 发表于 2025-6-26 11:47
了解,但是还是想看看你们的时序是如何的

硬件部分不是我负责的,示波器暂时也不在手里,除了软件配置其余的没法提供帮助,抱歉
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
 楼主| 发表于 2025-6-26 19:10:11 | 显示全部楼层
陈清 发表于 2025-6-26 13:05
硬件部分不是我负责的,示波器暂时也不在手里,除了软件配置其余的没法提供帮助,抱歉

好吧,辛苦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-13 02:57 , Processed in 0.062113 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表