硬汉嵌入式论坛

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

[STM32H7] H7的DMA串口发送库感觉有问题

  [复制链接]

6

主题

234

回帖

252

积分

高级会员

积分
252
发表于 2024-9-19 10:11:23 | 显示全部楼层 |阅读模式
[stm32h7xx_hal_uart.c]文件中

HAL_UART_Transmit_DMA函数
芯片:stm32h750vb
DMA发送不是需要一定的时间么?这个文件中怎么直接调用[HAL_DMA_Start_IT],判断发送完成信号呢?并且没有延时等待


      /* Enable the UART transmit DMA channel */
      if (HAL_DMA_Start_IT(huart->hdmatx, (uint32_t)huart->pTxBuffPtr, (uint32_t)&huart->Instance->TDR, Size) != HAL_OK)
      {
        /* Set error code to DMA */
        huart->ErrorCode = HAL_UART_ERROR_DMA;

        /* Restore huart->gState to ready */
        huart->gState = HAL_UART_STATE_READY;

        return HAL_ERROR;
      }

函数全部代码:
[C] 纯文本查看 复制代码
      /* Enable the UART transmit DMA channel */

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
{
  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    huart->pTxBuffPtr  = pData;
    huart->TxXferSize  = Size;
    huart->TxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    if (huart->hdmatx != NULL)
    {
      /* Set the UART DMA transfer complete callback */
      huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;

      /* Set the UART DMA Half transfer complete callback */
      huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;

      /* Set the DMA error callback */
      huart->hdmatx->XferErrorCallback = UART_DMAError;

      /* Set the DMA abort callback */
      huart->hdmatx->XferAbortCallback = NULL;

      /* Enable the UART transmit DMA channel */
      if (HAL_DMA_Start_IT(huart->hdmatx, (uint32_t)huart->pTxBuffPtr, (uint32_t)&huart->Instance->TDR, Size) != HAL_OK)
      {
        /* Set error code to DMA */
        huart->ErrorCode = HAL_UART_ERROR_DMA;

        /* Restore huart->gState to ready */
        huart->gState = HAL_UART_STATE_READY;

        return HAL_ERROR;
      }
    }
    /* Clear the TC flag in the ICR register */
    __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_TCF);

    /* Enable the DMA transfer for transmit request by setting the DMAT bit
    in the UART CR3 register */
    ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}



我之前一直用的LL库,基本上是自己处理,想着省点事,没想到库用起来不太靠谱.
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-19 10:20:52 | 显示全部楼层
这个库没问题的,这里只是初始化DMA发送参数,并触发DMA传输。如果你要判断是否发送完成,然后再次进行DMA发送,你自己补充相关流程,就好。

我这里有个例子,供参考。
[C] 纯文本查看 复制代码
    /*DMA发送*/
    HAL_UART_Transmit_DMA(&huart6, dmaTxBuffer, len);

    while (1)
    {
        if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_TC))
        {
            __HAL_UART_CLEAR_FLAG(&huart6, UART_FLAG_TC);
            break;
        }
    }

    while (1)
    {
        if (__HAL_DMA_GET_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7)) //等待DMA2_Steam7传输完成
        {
            __HAL_DMA_CLEAR_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
            HAL_UART_AbortTransmit(&huart6);
            break;
        }
    }

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-9-19 10:20:54 | 显示全部楼层
HAL库的DMA和中断方式基本都是非阻塞的,调用完毕后立即返回。

比如里这个DMA,在DMA传输完成回调里面处理即可

/* Set the UART DMA transfer complete callback */
      huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
回复

使用道具 举报

6

主题

234

回帖

252

积分

高级会员

积分
252
 楼主| 发表于 2024-9-19 10:30:23 | 显示全部楼层
morning_enr6U 发表于 2024-9-19 10:20
这个库没问题的,这里只是初始化DMA发送参数,并触发DMA传输。如果你要判断是否发送完成,然后再次进行DMA ...

是的,后边需要自己判断

其实我发现的问题是调用 [HAL_UART_Transmit_DMA]函数后,它的状态变为了HAL_UART_STATE_BUSY_TX,还需要自己清状态,将状态变为HAL_UART_STATE_READY

可能是我自己没开中断的原因,我都是在OS里循环里执行rt_thread_yeid,然后再判断DMA发送完成标志来处理的,
但[HAL_UART_Transmit_DMA]似乎要开中断,在中断里回调来清将标志变为就绪态
[huart4.gState = HAL_UART_STATE_READY;]
回复

使用道具 举报

6

主题

234

回帖

252

积分

高级会员

积分
252
 楼主| 发表于 2024-9-19 10:31:31 | 显示全部楼层
eric2013 发表于 2024-9-19 10:20
HAL库的DMA和中断方式基本都是非阻塞的,调用完毕后立即返回。

比如里这个DMA,在DMA传输完成回调里面处 ...

是的,它的库好像要开中断,我不习惯用中断,在有OS情况下,延时一段时间来判断DMA标志挺方便的.
所以有些操作还要在手工处理,完全用hal_xxx达不到自己想要的效果
回复

使用道具 举报

6

主题

234

回帖

252

积分

高级会员

积分
252
 楼主| 发表于 2024-9-19 10:34:02 | 显示全部楼层
现在自己封装的DMA发送函数
[C] 纯文本查看 复制代码
//DMA发送字节数据,100ms超时
HAL_StatusTypeDef Uart4DMASendData(uint8_t * dat,int len){
	__HAL_DMA_CLEAR_FLAG(&hdma_uart4_tx,DMA_FLAG_TCIF0_4);
	HAL_StatusTypeDef r=HAL_UART_Transmit_DMA(&huart4,(uint8_t *)dat,len);
	if(r!=HAL_OK)
		return r;
	//等待dma传输完成
	uint32_t start=rt_tick_get();
	while(__HAL_DMA_GET_FLAG(&hdma_uart4_tx,DMA_FLAG_TCIF0_4)){
		rt_thread_yield();
		if(rt_tick_get()-start>100){
			r= HAL_TIMEOUT;
      goto exit;
    }
	}
  r=HAL_OK;
  exit:
  huart4.gState = HAL_UART_STATE_READY;
	return r;
}
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-19 10:54:10 | 显示全部楼层
fxyc87 发表于 2024-9-19 10:34
现在自己封装的DMA发送函数
[mw_shl_code=c,true]//DMA发送字节数据,100ms超时
HAL_StatusTypeDef Uart4 ...

我那例子里面的代码有转状态的。就是:HAL_UART_AbortTransmit(&huart6);
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-9-20 08:53:42 | 显示全部楼层
fxyc87 发表于 2024-9-19 10:34
现在自己封装的DMA发送函数
[mw_shl_code=c,true]//DMA发送字节数据,100ms超时
HAL_StatusTypeDef Uart4 ...

问题最终解决没
回复

使用道具 举报

6

主题

234

回帖

252

积分

高级会员

积分
252
 楼主| 发表于 2024-9-20 09:01:45 | 显示全部楼层

HAL库的串口方面搞的不太好用,特别是DMA
现在是HAL库初始化,发送操作用HAL库+寄存器操作,
这样特别好使.
[C] 纯文本查看 复制代码
//DMA发送字节数据,100ms超时
HAL_StatusTypeDef Uart4DMASendData(uint8_t * dat,int len){
	DMA1->LIFCR|=DMA_LIFCR_CTCIF0|DMA_LIFCR_CTEIF0;    //清除DMA发送完成标志
  DMA1_Stream0->M0AR=(uint32_t)dat;
  DMA1_Stream0->NDTR=len;
  DMA1_Stream0->CR|=DMA_SxCR_EN;
	//等待dma传输完成
        HAL_StatusTypeDef r;
	uint32_t start=rt_tick_get();
	while(__HAL_DMA_GET_FLAG(&hdma_uart4_tx,DMA_FLAG_TCIF0_4)){
		rt_thread_yield();
		if(rt_tick_get()-start>100){
			r= HAL_TIMEOUT;
      goto exit;
    }
	}
  r=HAL_OK;
  exit:
  HAL_UART_AbortTransmit(&huart4);
  //huart4.gState = HAL_UART_STATE_READY;
	return r;
}
回复

使用道具 举报

6

主题

234

回帖

252

积分

高级会员

积分
252
 楼主| 发表于 2024-9-20 09:05:10 | 显示全部楼层
[C] 纯文本查看 复制代码
  DMA1->LIFCR|=DMA_LIFCR_CTCIF1|DMA_LIFCR_CTEIF1;    //清除DMA完成标志
  DMA1_Stream1->M0AR=(uint32_t)&Uart4RecBuffer[0];
DMA1_Stream1->NDTR=sizeof(Uart4RecBuffer);
DMA1_Stream1->CR|=DMA_SxCR_EN;
//DMA开启/重置 接收函数
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-20 10:46:21 | 显示全部楼层
fxyc87 发表于 2024-9-20 09:01
HAL库的串口方面搞的不太好用,特别是DMA
现在是HAL库初始化,发送操作用HAL库+寄存器操作,
这样特别好使 ...

回复

使用道具 举报

5

主题

224

回帖

244

积分

高级会员

积分
244
发表于 2024-9-20 10:56:18 | 显示全部楼层
你们这帮人压根不会用HAL,在这里鬼扯
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-9-21 09:33:45 | 显示全部楼层
fxyc87 发表于 2024-9-20 09:01
HAL库的串口方面搞的不太好用,特别是DMA
现在是HAL库初始化,发送操作用HAL库+寄存器操作,
这样特别好使 ...

这个思路不好,你使用了HAL库后,就在HAL的机制下处理即可。

HAL的DMA和中断方式,核心就是回调,传输完毕后,就在回调里面处理即可。
回复

使用道具 举报

0

主题

6

回帖

6

积分

新手上路

积分
6
发表于 2024-9-22 20:38:01 | 显示全部楼层
morning_enr6U 发表于 2024-9-19 10:20
这个库没问题的,这里只是初始化DMA发送参数,并触发DMA传输。如果你要判断是否发送完成,然后再次进行DMA ...

你好,我参照你的写法(如下)。遇到了一个问题:只能检测到发送完成标志位,而检测不到DMA发送完成标志位,请问知道是什么原因吗?
[C] 纯文本查看 复制代码
	
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)txbuf, length);
	while (1)
	{
		if(SET == __HAL_DMA_GET_FLAG(&huart1, DMA_FLAG_TCIF1_5))
		{
			__HAL_DMA_CLEAR_FLAG(&huart1, DMA_FLAG_TCIF1_5);
			break;
		}
	}

	while (1)
	{
		if (SET == __HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC))
		{
			__HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_TCF);
			break;
		}
	}
回复

使用道具 举报

0

主题

6

回帖

6

积分

新手上路

积分
6
发表于 2024-9-22 21:56:29 | 显示全部楼层
morning_enr6U 发表于 2024-9-19 10:20
这个库没问题的,这里只是初始化DMA发送参数,并触发DMA传输。如果你要判断是否发送完成,然后再次进行DMA ...

我也使用了DMA发送,但是不开启中断。用__HAL_DMA_GET_FLAG查询DMA发送完成标志位的时候,一直没置位,不知道怎么回事。发送完成标志位倒是可以查询到。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-9-23 08:40:11 | 显示全部楼层
华府·小书童 发表于 2024-9-22 20:38
你好,我参照你的写法(如下)。遇到了一个问题:只能检测到发送完成标志位,而检测不到DMA发送完成标志 ...

充分利用回调。才能更好的使用HAL库的中断和DMA函数

先看下这个简单的例子。

1、【STM32H743实验例程】实验14:STM32H743串口DMA方式收发,DMA方式问题比较多,要注意数据一致性
https://www.armbbs.cn/forum.php?mod=viewthread&tid=86271

2、然后看这个

STM32H7基于STM32CubeMX的串口DMA+空闲中断接收不定长数据实现(HAL库1.9.0自带函数实现)
https://www.armbbs.cn/forum.php?mod=viewthread&tid=108553



回复

使用道具 举报

0

主题

6

回帖

6

积分

新手上路

积分
6
发表于 2024-9-23 10:46:41 | 显示全部楼层
eric2013 发表于 2024-9-23 08:40
充分利用回调。才能更好的使用HAL库的中断和DMA函数

先看下这个简单的例子。

回调是在触发中断后进行的,而中断是在标志位被置位后触发的。

所以,个人的理解是:即使不开启中断,在使用“HAL_UART_Transmit_DMA(&huart1, (uint8_t *)txbuf, length)”进行DMA发送后,也应该可以检测到发送完成标志位DMA_FLAG_TCIFx和UART_FLAG_TC被置位才对。

而现在的情况是,UART_FLAG_TC置位可以检测到,而DMA_FLAG_TCIFx置位检测不到。

但在开启中断之后,DMA发送完成中断回调函数CpltCallback中打断点,是可以正确进入的。所以,我没有明白DMA_FLAG_TCIFx置位检测不到的原因是什么
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115490
QQ
发表于 2024-9-24 09:30:14 | 显示全部楼层
华府·小书童 发表于 2024-9-23 10:46
回调是在触发中断后进行的,而中断是在标志位被置位后触发的。

所以,个人的理解是:即使不开启中断, ...

这个是在DMA的中断服务程序里面已经处理了DMA_FLAG_TCIFx

你在DMA中断传输完成回调处理的地方看下,直接HAL库的源码里面打断点看。
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-24 09:34:18 | 显示全部楼层
华府·小书童 发表于 2024-9-22 21:56
我也使用了DMA发送,但是不开启中断。用__HAL_DMA_GET_FLAG查询DMA发送完成标志位的时候,一直没置位,不 ...

你写反了,先检测USART的TC,再检测DMA的标志。  另外,你检测DMA的相关通道的标志,要写对!
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-24 09:35:50 | 显示全部楼层
华府·小书童 发表于 2024-9-23 10:46
回调是在触发中断后进行的,而中断是在标志位被置位后触发的。

所以,个人的理解是:即使不开启中断, ...

[C] 纯文本查看 复制代码
/*DMA发送*/
HAL_UART_Transmit_DMA(&huart6, dmaTxBuffer, len);
 
while (1)
{
    if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_TC))
    {
        __HAL_UART_CLEAR_FLAG(&huart6, UART_FLAG_TC);
        break;
    }
}
 
while (1)
{
    if (__HAL_DMA_GET_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7)) //等待DMA2_Steam7传输完成
    {
        __HAL_DMA_CLEAR_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
        HAL_UART_AbortTransmit(&huart6);
        break;
    }
}
回复

使用道具 举报

0

主题

6

回帖

6

积分

新手上路

积分
6
发表于 2024-9-26 13:22:03 | 显示全部楼层
morning_enr6U 发表于 2024-9-24 09:34
你写反了,先检测USART的TC,再检测DMA的标志。  另外,你检测DMA的相关通道的标志,要写对!

1、发送的过程是DMA控制器把数据填到串口的发送数据寄存器,那么应该是DMA的先完成,再到UART的发送完成吧。

2、即使写反,就是我不检测UART的TC,直接检测DMA的标志,也没有问题的呀。

3、DMA通道检测的标志没有写错啊,我UART1的发送使用的是DMA1_STREAM1
回复

使用道具 举报

0

主题

6

回帖

6

积分

新手上路

积分
6
发表于 2024-9-26 13:25:24 | 显示全部楼层
eric2013 发表于 2024-9-24 09:30
这个是在DMA的中断服务程序里面已经处理了DMA_FLAG_TCIFx

你在DMA中断传输完成回调处理的地方看下,直 ...

我现在是不开启中断,调用HAL_UART_Transmit_DMA后,等待检测标志位DMA_FLAG_TCIFx,结果一直没有置位。这种做法不可以的吗?
回复

使用道具 举报

3

主题

295

回帖

304

积分

高级会员

积分
304
发表于 2024-9-26 14:30:58 | 显示全部楼层
华府·小书童 发表于 2024-9-26 13:22
1、发送的过程是DMA控制器把数据填到串口的发送数据寄存器,那么应该是DMA的先完成,再到UART的发送完成 ...

发送使用DMA1_STREAM1,你检测的是DMA_FLAG_TCIF1_5
回复

使用道具 举报

3

主题

295

回帖

304

积分

高级会员

积分
304
发表于 2024-9-26 14:40:28 | 显示全部楼层
华府·小书童 发表于 2024-9-26 13:22
1、发送的过程是DMA控制器把数据填到串口的发送数据寄存器,那么应该是DMA的先完成,再到UART的发送完成 ...

看了下库,没有问题,dma1的stream1发送完成标志确实是DMA_FLAG_TCIF1_5
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-26 15:15:49 | 显示全部楼层
华府·小书童 发表于 2024-9-26 13:25
我现在是不开启中断,调用HAL_UART_Transmit_DMA后,等待检测标志位DMA_FLAG_TCIFx,结果一直没有置位。 ...

[C] 纯文本查看 复制代码
void User_SCI_Send(char *udata, uint16_t len)
{
    


    vx_memcpy(dmaTxBuffer, udata, len);
    /*DMA发送*/
    HAL_UART_Transmit_DMA(&huart6, dmaTxBuffer, len);

    while (1)
    {
        if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_TC))
        {
            __HAL_UART_CLEAR_FLAG(&huart6, UART_FLAG_TC);
            break;
        }
    }

    while (1)
    {
        if (__HAL_DMA_GET_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7)) 
        {
            __HAL_DMA_CLEAR_FLAG(&hdma_usart6_tx, DMA_FLAG_TCIF3_7); 
            
            HAL_UART_AbortTransmit(&huart6);
            break;
        }
    }

}



我的函数一直这么用的,你再找找初始化部分。
回复

使用道具 举报

4

主题

1441

回帖

1453

积分

至尊会员

积分
1453
发表于 2024-9-26 15:17:21 | 显示全部楼层
华府·小书童 发表于 2024-9-26 13:22
1、发送的过程是DMA控制器把数据填到串口的发送数据寄存器,那么应该是DMA的先完成,再到UART的发送完成 ...

[C] 纯文本查看 复制代码
       /**/
        hdma_usart6_tx.Instance = DMA2_Stream7;                              //选择数据流
        hdma_usart6_tx.Init.Channel = DMA_CHANNEL_5;                         //选择通道  通道及数据流选择根据使用外设查表使用
        hdma_usart6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;                //方向:存储器到外设(发送)
        hdma_usart6_tx.Init.PeriphInc = DMA_PINC_DISABLE;                    //外设地址不递增
        hdma_usart6_tx.Init.MemInc = DMA_MINC_ENABLE;                        //存储器地址递增
        hdma_usart6_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;       //外设数据对齐方式-字节
        hdma_usart6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;          //存储器数据对齐方式-字节
        hdma_usart6_tx.Init.Mode = DMA_NORMAL;                               //发送模式选择-正常
        hdma_usart6_tx.Init.Priority = DMA_PRIORITY_HIGH;                    //优先级-高
        hdma_usart6_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;                 // FIFO模式-不使能
        hdma_usart6_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL; // FIFO阈值级别
        hdma_usart6_tx.Init.MemBurst = DMA_MBURST_SINGLE;                    //存储器突发传输模式
        hdma_usart6_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;                 //外设突发传输模式

        /*初始化DMA 并将DMA与外设连接*/
        if (HAL_DMA_Init(&hdma_usart6_tx) != HAL_OK)
        {
            Error_Handler();
        }
        __HAL_LINKDMA(&huart6, hdmatx, hdma_usart6_tx);
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-29 02:00 , Processed in 0.529585 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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