STM32使用DMA接收和发送

平台:正点原子,F103ZET
本文阅读前提知识:非零基础教程,需要会cubeMX,有点灯基础,有printf重定向基础,有ITM基础。
设计思路:
1、UART中断:开启TC(传输完成)中断和IDLE(总线空闲)中断;
2、DMA中断:收发中断均不开启。
接收端设计思路
初始化完成后,就开启DMA_RX,开启IDLE中断;
开启后CPU不用理会,DMA会在搬运串口数据到指定内存。
当收到一帧数据后(以停止位为标记),进入idle中断,在中断函数内部,清掉中断位,停止DMA收,重启DMA收。

HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
开启后,从串口搬运数据到pData,一个byte一个byte的搬运,累计搬运Size后不工作。
由于我们是被动接受,远端数据会有多少个不知道,那么则按照一帧数据来处理,即IDLE中断处理。

HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
搬运内存思路和接收一样。
由于我们是主动发送,知道有多少个数据,那么就用TC中断,即发送完成中断来处理最合适。

HAL_UART_DMAStop(huart)
如果收发都在进行(未达到size),则一旦调用这个函数,收发都会被停止。
因为我不建议修改库函数,所以建议对其进行自定义修改;把停止收和停止发分开。
所以我这里把他改写成void StopDMA(UART_HandleTypeDef *huart,uart_pin_t uart_pin) ;
这样使得我们可以控制停止TX或者RX。

下面是部分代码,我用cubemx生成后,就只修改了部分内容,其余部分保留原始。

在初始化的地方,开启中断,开启接收。
 MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  //如果这里不清,程序一启动就会进入一次中断。
  __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
  __HAL_UART_ENABLE_IT(&huart1,UART_IT_TC);
  if(HAL_UART_Receive_DMA(&huart1,RX_buff,sizeof(RX_buff)))
  {
    printf("DMA FAIL!\n");
  }
在中断中,设置跳转到回调函数
void USART1_IRQHandler(void)
{
      HAL_UART_RxCpltCallback(&huart1);
      return;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  int i=0;
  int RX_count = sizeof(RX_buff)-(int)__HAL_DMA_GET_COUNTER(huart->hdmarx);

    /*用于接收IDLE中断*/
    if(__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
    {
        //__HAL_UART_CLEAR_IDLEFLAG(huart);
        __HAL_UART_CLEAR_FLAG(huart,UART_FLAG_IDLE);
        StopDMA(huart,uart_pin_rx);
       // HAL_UART_DMAStop(huart);
      
      printf("DMA RECEICED DATA : ");
      for(i=0;iInstance->CR3, USART_CR3_DMAT);
    if ((huart->gState == HAL_UART_STATE_BUSY_TX) && dmarequest)
    {
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);

      /* Abort the UART DMA Tx channel */
      if (huart->hdmatx != NULL)
      {
        HAL_DMA_Abort(huart->hdmatx);
      }
      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_TXEIE | USART_CR1_TCIE));
      /* At end of Tx process, restore huart->gState to Ready */
      huart->gState = HAL_UART_STATE_READY;
    }
  }
  if(uart_pin_rx==uart_pin)
  {
    dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
    if ((huart->RxState == HAL_UART_STATE_BUSY_RX) && dmarequest)
    {
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

      /* Abort the UART DMA Rx channel */
      if (huart->hdmarx != NULL)
      {
        HAL_DMA_Abort(huart->hdmarx);
      }
      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
      /* At end of Rx process, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;
    }
  }

}

完整代码我传到码云了,有兴趣可以瞅瞅,提交信息是我自己看的,请忽略:
https://gitee.com/xixihaha_is_forbiden/Mycode/tree/master/STM32_YJP_DEMO/8_UART_DMA_RX-IDLE_TX-TC/cubeMX_TEST

注意写代码一定要看手册,HAL库封装的实在太多,导致你不知道HAL到底在干什么
一定要:看到HAL的底层,动了什么寄存器,这些寄存器这样设置有什么用!

比如IDLE在手册中的描述:

IDLE:监测到总线空闲 (IDLE line detected) 位4
当检测到总线空闲时,该位被硬件置位。如果USART_CR1中的IDLEIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后读USART_DR)。
0:没有检测到空闲总线; 1:检测到空闲总线。 注意:IDLE位不会再次被置高直到RXNE位被置起(即又检测到一次空闲总线)

那么我们要做的:
清掉上一次的SR数据
__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);
设置CR1中的enable
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);


你可能感兴趣的:(通信技术)