STM32CubeMX之USART应用


使用usart3实现printf

1 在CubeMX中配置USART

  1. Mode:USART可选模式(Mode)中有八种,具体每种模式可以参考《STM32F10XXX参考手册》,这里选用Asynchronous(异步通信)
    在这里插入图片描述
  2. Hardware Flow Control(RS232) 硬件流控制,这里选择Disable,不使能硬件流控制。
    nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当USART接收器准备好接收新数据时就会将 nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。
    nCTS:清除以发送(Clear To Send),n表示低电平有效。如果使能 CTS流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。
    在这里插入图片描述
  3. 基本的参数设定
    在这里插入图片描述
    1)Baud Rate:即每秒传输的位数;
    2)Word Length:数据帧字长,可选8位或9位。它设定USART_CR1寄存器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位;
    3)Parity:奇 偶 校 验 控 制 选 择,这里选择无校验 , 可 选 USART_Parity_No( 无 校 验 ) 、USART_Parity_Even( 偶 校 验 ) 以 及 USART_Parity_Odd( 奇 校 验 ) , 它 设 定USART_CR1寄存器的 PCE位和 PS位的值;
    奇校验:奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为:01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位。
    偶校验:偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。
    如果启用奇偶校验,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存器的 PE 位置 1,并可以产生奇偶校验中断。使能了奇偶校验控制后,每个字符帧的格式将变成:起始位+数据帧+校验位+停止位
    4)Stop Bits:停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,它设定USART_CR2寄存器的 STOP[1:0]位的值,一般我们选择 1 个停止位。2 个停止位适用于正常USART 模式、单线模式和调制解调器模式。0.5个和 1.5 个停止位用于智能卡模式。
    5)Data Direction:数据传输方向,选择接收和发送

2 在CubeMX中配置NVIC

在这里插入图片描述
在生成代码中 HAL_Init() 函数默认系统优先级为 NVIC_PRIORITYGROUP_4;
在这里插入图片描述
MAL_MspInit(void)函数里根据CubeMX中设置优先级分组
在这里插入图片描述

3 在工程中实现相关功能

3.1 USART数据常规发送与接收

usart.c文件中定义接收数据数组

uint8_t Uart1RxBuff[USART1_RX_LEN];

usart.h文件中定义接收数据数组长度及声明接收数组

#define USART1_RX_LEN    10
extern uint8_t Uart1RxBuff[USART1_RX_LEN];

3.1.1 USART数据阻塞模式(blocking mode)发送与接收

阻塞模式(blocking mode)即USART数据发送或接收都会阻塞,阻塞时间可自定义,时间单位为ms
在main.h文件中开启测试

#define USART_BLOCKING_MODE_TEST           1   /*USART 阻塞模式下测试数据收发*/

在main.c文件中实现如下代码

HAL_StatusTypeDef user_hal_stauts;
#if (USART_BLOCKING_MODE_TEST == 1)
	/*阻塞模式(blocking mode)下的USART数据接收,会停留在该函数,直到接收到USART1_RX_LEN长度的数据或者直到5000ms时间溢出*/
	 user_hal_stauts = 	HAL_UART_Receive(&huart1,Uart1RxBuff,USART1_RX_LEN,5000);
	if(user_hal_stauts == HAL_OK)
	{
		/*阻塞模式(blocking mode)下的USART数据发送,发送完USART1_RX_LEN长度的数据或者直到5000ms时间溢出*/
		HAL_UART_Transmit(&huart1,Uart1RxBuff,USART1_RX_LEN,5000);
	}
	else
	{
	  printf("HAL_StatusTypeDef,%d \r\n",user_hal_stauts); 
	}
#endif

以上程序实现了阻塞模式(blocking mode)下的数据自发自收,如下图所示:
在这里插入图片描述

3.1.2 USART数据非阻塞模式(non blocking mode)发送与接收

非阻塞模式即将数据的发送接收放在中断处理

  1. main函数中使能接收中断
  if(HAL_UART_Receive_IT(&huart1, Uart1RxBuff, USART1_RX_LEN) != HAL_OK)
  {
	   Error_Handler();
  }

在 **HAL_UART_Receive_IT ()**函数检查USART是否正忙,如果不忙则配置参数、使能中断等操作,可自行查看。

  1. stm32f1xx_it.c中的USART1_IRQHandler() 函数下实现函数HAL_UART_Receive_IT();
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  HAL_UART_Receive_IT(&huart1, Uart1RxBuff, USART1_RX_LEN);
  /* USER CODE END USART1_IRQn 1 */
}

上电初始化中调用一次HAL_UART_Receive_IT(); 函数,即第一次开启接收中断使能;而每次中断接收完数据之后又要调用一次HAL_UART_Receive_IT(); 函数,是因为每次接收中断执行HAL_UART_IRQHandler函数之后,接收中断就会被关闭;进入 HAL_UART_IRQHandler 函数会找到 UART_Receive_IT 函数,该函数如下:

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t *tmp;

  /*省略了部分代码*/
  /* Check that a Rx process is ongoing */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
    *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF); /*读取具体数据*/
    if (--huart->RxXferCount == 0U)  /*接收完成*/
    {
      /* Disable the UART Data Register not empty Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);  /*接收完成后,失能接收中断*/

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;    /*改变USART状态*/

      /*Call legacy weak Rx complete callback*/
      HAL_UART_RxCpltCallback(huart);          /*数据接收完成后,调用回调函数*/
    }
  }
}

而在 HAL_UART_Receive_IT 函数中,如下:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);  /*使能接收中断*/
}
  1. usart.c中实现接收回调函数HAL_UART_RxCpltCallback,该函数在stm32f1xx_it.c中的**USART1_IRQHandler() —>HAL_UART_IRQHandler —> UART_Receive_IT ** 函数中调用, 每次接收到USART1_RX_LEN个字节后调用一次该回调函数,数据保存在Uart1RxBuff中
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		HAL_UART_Transmit_IT(&huart1,Uart1RxBuff,USART1_RX_LEN);
	}	
}

以上程序也实现了数据的自发自收

3.1.3 函数深入理解

常用数据发射函数

/*Send an amount of data in blocking mode,该函数发射数据处于阻塞模式,即MCU会一直处在该发射函数中直到数据发射完,才执行往下的程序*/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/*Send an amount of data in non blocking mode,该函数发射数据处于非阻塞模式,即数据在中断中发射,在数据发射不需要MCU干预过程中,MCU会执行往下的程序,但是USART发射状态会处于忙,不能立即又调用函数发射数据*/
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

举例详解:

  1. HAL_UART_Transmit函数
uint8_t TX_Buff[] = "i am a test code ! \r\n";
/*这里的参数Timeout(100),即该函数可执行的最大时间,如果超过该时间数据还没有发射完,也会被强行退出*/
HAL_UART_Transmit(&huart1,TX_Buff,sizeof(TX_Buff),100);
  1. HAL_UART_Transmit_IT函数
    该函数数据发射是在中断中完成的,不会被阻塞
uint8_t TX_Buff[] = "i am a test code ! \r\n";
HAL_UART_Transmit_IT(&huart1,TX_Buff,sizeof(TX_Buff));

对该函数发射深入代码详解
1)将发射数据、数据大小存储到 huart 结构体中,并将USART标记为忙;
2)使能发射中断;
3)在中断中发射数据;
4)计数发射数据是否发射完(若数据没有发射完会一直产生中断),发射完就失能发射中断,并将USART标记为准备;

按照 注释1~注释N 来理解

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    /*省略了部分代码*/
    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pTxBuffPtr = pData;   /*注释1、要发射的数据*/
    huart->TxXferSize = Size;    /*注释2、要发射的数据大小*/
    huart->TxXferCount = Size;   /*注释3、要发射的数据计数*/

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX; /*注释4、标记发射状态忙*/

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Transmit data register empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_TXE);  /*注释5、使能发射中断,并产生中断,开始执行USART中断函数 HAL_UART_IRQHandler */

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->SR);  /*注释6、获取 SR 寄存器的值*/
  uint32_t cr1its     = READ_REG(huart->Instance->CR1); /*注释7、获取 CR1 寄存器的值*/

   /*省略了部分代码*/

  /* UART in mode Transmitter ------------------------------------------------*/
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) /*注释8、这里的USART_SR_TXE中断状态是在(注释5)中使能的,*/
  {
    UART_Transmit_IT(huart);  /*注释9、实际的数据发射函数*/
    return;
  }
  /* UART in mode Transmitter end --------------------------------------------*/
  if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);/*注释15、数据发射完之后,这里将USART状态从忙变为准备*/
    return;
  }
}

static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
  uint16_t *tmp;

  /* Check that a Tx process is ongoing */
  if (huart->gState == HAL_UART_STATE_BUSY_TX) /*注释10、在(注释4)中被标记为忙*/
  {
    if (huart->Init.WordLength == UART_WORDLENGTH_9B)
    {
     /*省略了部分代码*/
    }
    else
    {
      huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);/*注释11、发射数据,一个一个字节发送*/
    }
    if (--huart->TxXferCount == 0U)/*注释12、计数发射的数据,没有发射完会一直使能中断*/
    {
      /* Disable the UART Transmit Complete Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_TXE);        /*注释13、数据发送完,失能发射中断,就不会再产生中断了;如果数据还没有发射完,就会一直使能中断,直到数据发射完*/

      /* Enable the UART Transmit Complete Interrupt */
      __HAL_UART_ENABLE_IT(huart, UART_IT_TC);         /*注释14、这里产生的是,数据发射完中断*/
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

3.1.4 疑问与解答

 /*这里printf并没有打印数据出来,是HAL_UART_Transmit_IT为非阻塞模式,执行完该函数后立即返回,而数据正在中断中同步发射,此时USART正在被占用,所以printf打印不出数据,延时一定时间后,数据发射完了,所以又能打印数据了*/
		ACK = HAL_UART_Transmit_IT(&huart1,TX_Buff,sizeof(TX_Buff));
		printf("ACK: %d \r\n",ACK); /*不能打印数据*/
	
		ACK = HAL_UART_Transmit_IT(&huart1,TX_Buff,sizeof(TX_Buff));
		HAL_Delay(5);
		printf("ACK: %d \r\n",ACK); /*可以打印数据*/

3.2 USART数据使用DMA发送与接收

在STM32CubeMX配置DMA,并生成工程
在这里插入图片描述

3.2.1 USART数据的DMA发送

DMA发送步骤如下:

  1. 在函数HAL_UART_Transmit_DMA中先封装数据、回调函数,调用HAL_DMA_Start_IT配置外设源地址、目标地址等,再开启USART的DMA传输;
  2. HAL_DMA_Start_IT函数中修改DMA状态,配置DMA通道、数据源地址、目标地址、数据长度,使能数据传输一半、传输完成、传输出错中断;
  3. 数据传输完成一半,产生DMA中断,在中断函数中调用UART_DMATxHalfCplt函数,该函数里包含了HAL_UART_TxHalfCpltCallback回调函数,用户代码就写在该函数里;
  4. 数据传输完成,产生DMA中断,在中断函数中调用UART_DMATransmitCplt函数,该函数里会根据DMA配置的正常模式或循环模式做相应的处理,正常模式下,会产生USART中断,并执行HAL_UART_TxCpltCallback回调函数;循环模式下,会直接调用HAL_UART_TxCpltCallback回调函数,用户代码就写在该函数里。

在main函数实现如下函数:

uint8_t DMA_TX_Buff[] = "i am a DMA test code ! \r\n";
HAL_UART_Transmit_DMA(&huart1,DMA_TX_Buff,sizeof(DMA_TX_Buff));

在usart.c中实现如下代码:

/*数据发送完成一半时产生的中断*/
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		  printf("huart1 send half \r\n"); 
	}	
}
/*数据发送完成全部时产生的中断*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		  printf("huart1 send end \r\n"); 
	}	
}

串口调试助手显示如下:
在这里插入图片描述

注意一些问题:
如果DMA传输和printf使用同一串口(如都是用USART1),则调试过程中,不能用 printf 在HAL_UART_TxHalfCpltCallback函数中打印信息,因为数据只传输了一半,USART通道还在被占用,所以不能将信息被打印出来

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
//		  printf("xxx"); /*这里不能用 printf 函数来查看是否执行到这里,因为数据只传输了一半,USART通道还在被占用*/
			HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);	
	}	
}

3.2.2 USART数据的DMA接收

DMA模式为循环模式下,只要在main函数中打开USART的DMA接收就可以

uint8_t DMA_RX_Buff[10]
int main(void)
{
	HAL_UART_Receive_DMA(&huart1,USART_DMA_RX_Buff,USART1_RX_LEN);
  while (1){}
}

其接收过程与DMA发射类似;
数据接收到一半和接收完成都会产生中断,如下函数:

void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
			printf("huart1 dma receive  half \r\n"); 
	}	
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
			printf("huart1 dma receive  end \r\n"); 	
		  /*将收到的数据重新发送*/
			HAL_UART_Transmit_DMA(&huart1,USART_DMA_TX_Buff,USART1_RX_LEN);
	}	
}

接收数据大于定义的数据大小(USART1_RX_LEN)一半时才会产生中断并调用就会调用HAL_UART_RxHalfCpltCallback函数;
接收数据大于定义的数据大小(USART1_RX_LEN)时才会产生中断并调用就会调用HAL_UART_RxCpltCallback函数。

3.2.3 代码分析

  1. USART数据的DMA发送过程分析
/*该函数完成了外设源地址、目标地址、数据大小的配置*/
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  uint32_t *tmp;
  /*省略了部分代码*/
  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pTxBuffPtr = pData;  /*注释1:将需要发射的数据存储到 huart 结构体中*/
    huart->TxXferSize = Size;   /*注释2:数据大小*/
    huart->TxXferCount = Size;  /*注释3:数据计数*/
    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;  /*注释4:修改USART状态*/
    /* Set the UART DMA transfer complete callback */
    huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;  /*注释5:注册数据传输完成回调函数,数据传输完成后产生中断,会执行该函数*/
    /* Set the UART DMA Half transfer complete callback */
    huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt; /*注释6:注册数据传输一半回调函数,数据传输一半产生中断,会执行该函数*/
    /* 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 */
    tmp = (uint32_t *)&pData;
    HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size);  /*注释7:启动DMA传输*/
    /* Clear the TC flag in the SR register by writing 0 to it */
    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC); /*注释8:清零USART发射完成标志*/
    /* Process Unlocked */
    __HAL_UNLOCK(huart);
    /* Enable the DMA transfer for transmit request by setting the DMAT bit
       in the UART CR3 register */
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAT); /*注释9:开启USART的DMA传输*/
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
/*****************************************************************************************************************************/
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{
  uint32_t flag_it = hdma->DmaBaseAddress->ISR;
  uint32_t source_it = hdma->Instance->CCR;
  
  /* Half Transfer Complete Interrupt management ******************************/
  if (((flag_it & (DMA_FLAG_HT1 << hdma->ChannelIndex)) != RESET) && ((source_it & DMA_IT_HT) != RESET)) /*注释6.1:数据传输完成一半时产生中断*/
  {
    /* Disable the half transfer interrupt if the DMA mode is not CIRCULAR */
    if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
    {
      /* Disable the half transfer interrupt */
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_HT);   /*注释6.2:失能数据传输一半的中断*/
    }
    /* Clear the half transfer complete flag */
    __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_HT_FLAG_INDEX(hdma));
    /* DMA peripheral state is not updated in Half Transfer */
    /* but in Transfer Complete case */
    if(hdma->XferHalfCpltCallback != NULL)
    {
      /* Half transfer callback */
      hdma->XferHalfCpltCallback(hdma);  /*注释6.3:调用回调函数 UART_DMATxHalfCplt*/
    }
  }
  /* Transfer Complete Interrupt management ***********************************/
  else if (((flag_it & (DMA_FLAG_TC1 << hdma->ChannelIndex)) != RESET) && ((source_it & DMA_IT_TC) != RESET))  /*注释6.4:数据传输完成产生中断*/
  {
    if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
    {
      /* Disable the transfer complete and error interrupt */
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);  
      /* Change the DMA state */
      hdma->State = HAL_DMA_STATE_READY;
    }
    /* Clear the transfer complete flag */
      __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));

    /* Process Unlocked */
    __HAL_UNLOCK(hdma);
    if(hdma->XferCpltCallback != NULL)
    {
      /* Transfer complete callback */
      hdma->XferCpltCallback(hdma);  /*注释6.5:调用回调函数 UART_DMATransmitCplt*/
    }
  }
  /* Transfer Error Interrupt management **************************************/
  else if (( RESET != (flag_it & (DMA_FLAG_TE1 << hdma->ChannelIndex))) && (RESET != (source_it & DMA_IT_TE)))
  {
    /*省略了部分代码*/
  }
  return;
}

/*****************************************************************************************************************************/
/*该函数位于DMA中断处理函数HAL_DMA_IRQHandler中被调用,在HAL_UART_Transmit_DMA函数中赋值*/
static void UART_DMATxHalfCplt(DMA_HandleTypeDef *hdma)
{
  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  /*Call registered Tx complete callback*/
  huart->TxHalfCpltCallback(huart);
#else
  /*Call legacy weak Tx complete callback*/
  HAL_UART_TxHalfCpltCallback(huart); /*注释6.3.1:调用数据传输一半的回调函数,该函数由用户实现*/
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}

/*该函数位于DMA中断处理函数HAL_DMA_IRQHandler中被调用,在HAL_UART_Transmit_DMA函数中赋值*/
/*这里对DMA的正常模式和循环模式做了区分,正常模式下产生USART中断;循环模式下直接调用回调函数*/
static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
  /* DMA Normal mode*/
  if ((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
  {
    huart->TxXferCount = 0x00U;
    /* Disable the DMA transfer for transmit request by setting the DMAT bit
       in the UART CR3 register */
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);  /*注释6.5.1:关闭DMA传输*/
    /* Enable the UART Transmit Complete Interrupt */
    SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);   /*注释6.5.2:触发UART数据传输完成中断*/
  }
  /* DMA Circular mode */
  else
  {
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    /*Call registered Tx complete callback*/
    huart->TxCpltCallback(huart);
#else
    /*Call legacy weak Tx complete callback*/
    HAL_UART_TxCpltCallback(huart);        /*注释6.5.3:循环模式下,数据传输完成直接执行该回调函数*/
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  }
}

/*****************************************************************************************************************************/
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
  HAL_StatusTypeDef status = HAL_OK;
   /*省略了部分代码*/
  /* Process locked */
  __HAL_LOCK(hdma);
  if(HAL_DMA_STATE_READY == hdma->State)
  {
    /* Change DMA peripheral state */
    hdma->State = HAL_DMA_STATE_BUSY;      /*注释7.1:修改DMA状态*/
    hdma->ErrorCode = HAL_DMA_ERROR_NONE;  
    /* Disable the peripheral */
    __HAL_DMA_DISABLE(hdma);        /*注释7.2:失能DMA,为了配置参数*/    
    /* Configure the source, destination address and the data length & clear flags*/
    DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);  /*注释7.3:配置DMA通道、数据源地址、目标地址、数据长度*/    
    /* Enable the transfer complete interrupt */
    /* Enable the transfer Error interrupt */
    if(NULL != hdma->XferHalfCpltCallback) /*注释7.4:*/
    {
      /* Enable the Half transfer complete interrupt as well */
      __HAL_DMA_ENABLE_IT(hdma, (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE)); /*注释7.5:使能数据传输完成、数据传输一半、数据传输出错中断*/
    }
    else
    {
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_HT); /*注释7.6:不开启数据传输一半中断*/
      __HAL_DMA_ENABLE_IT(hdma, (DMA_IT_TC | DMA_IT_TE)); /*注释7.7:使能数据传输完成、数据传输出错中断*/
    }
    /* Enable the Peripheral */
    __HAL_DMA_ENABLE(hdma); /*注释7.8:参数配置完成后,使能DMA*/
  }
  else
  {      
    /* Process Unlocked */
    __HAL_UNLOCK(hdma); 

    /* Remain BUSY */
    status = HAL_BUSY;
  }    
  return status;
}

/*****************************************************************************************************************************/
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if (errorflags == RESET)
  {
    /* UART in mode Receiver -------------------------------------------------*/
    if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }
  /*省略了部分代码*/

  /* UART in mode Transmitter end --------------------------------------------*/
  if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);   /*注释5.2.1:USART的DMA数据传输完成*/
    return;
  }
}

/*****************************************************************************************************************************/
static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart)
{
  /* Disable the UART Transmit Complete Interrupt */
  __HAL_UART_DISABLE_IT(huart, UART_IT_TC);
  /* Tx process is ended, restore huart->gState to Ready */
  huart->gState = HAL_UART_STATE_READY;  /*注释5.2.1.1:对应(注释7.1),修改USART状态*/
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  /*Call registered Tx complete callback*/
  huart->TxCpltCallback(huart);
#else
  /*Call legacy weak Tx complete callback*/
  HAL_UART_TxCpltCallback(huart); /*注释5.2.1.1:执行数据传输完成回调函数*/
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  return HAL_OK;
}

4 工程链接

gitee平台: STM32F10xxx Learn

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值