12-HAL库串口通信总结

1.定义了三种传输方式:阻塞传输,中断传输、DMA传输

HAL_UART_Transmit;  HAL_UART_Receive

HAL_UART_Transmit_IT;    HAL_UART_Receive_IT

HAL_UART_Transmit_DMA;    HAL_UART_Receive_DMA

此外还定义了两个中断回调函数,供中断和DMA使用,分别在数据传输一半和完成时使用

voidHAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef*huart);

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

voidHAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);

2.阻塞传输

阻塞传输是调用这个函数并在等待时间内一直等待操作完成。

uint8_t aTxbuffer[]="enter 10 characters:\n";
uint8_t aRxBuffer;			
uint8_t Usart1_RxBuff[10];		
uint8_t Usart1_Rx_Cnt = 0;		
int main(void)    
{
  HAL_Init();
  Sysclk_config();
	USART1_UART_Init(19200);
	printf("input your string:\n");
	HAL_UART_Transmit(&huart1 ,(uint8_t*)aTxbuffer,sizeof(aTxbuffer),0xFFF); 
	HAL_UART_Receive(&huart1,(uint8_t*)Usart1_RxBuff,10,10);
	HAL_UART_Transmit(&huart1 ,(uint8_t*)Usart1_RxBuff,10,10); 
}

可以添加循环语句,循环输入输出。

3.中断传输

配置串口,开启中断,在中断处理函数中进行输入语句的输出。

通过查看源代码,可以看到HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)这个函数只是用来开启中断用的,并不能真正接收数据。开启中断后,在中断处理函数HAL_UART_IRQHandler(&huart1)中,会先调用UART_Receive_IT(huart)函数进行数据输入的接收,此为静态全局函数,代码如下:

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t* tmp;
  uint16_t uhMask = huart->Mask;

  /* Check that a Rx process is ongoing */
  if(huart->RxState == HAL_UART_STATE_BUSY_RX)
  {

    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {
      tmp = (uint16_t*) huart->pRxBuffPtr ;
      *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
      huart->pRxBuffPtr +=2;
    }
    else
    {
      *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
    }

    if(--huart->RxXferCount == 0)
    {
      /* Disable the UART Parity Error Interrupt and RXNE interrupt*/
      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));

      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;

      HAL_UART_RxCpltCallback(huart);

      return HAL_OK;
    }

    return HAL_OK;
  }
  else
  {
    /* Clear RXNE interrupt flag */
    __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);

    return HAL_BUSY;
  }
}

可以看到该函数的作用是将接收到的数据存入结构体huart内的pRxBuffPtr指针中,当待传输数据长度RxXferCount为0时,便调用回调函数HAL_UART_RxCpltCallback(huart);因此可以考虑更改此函数的源代码,当待传输数据的字符为空格或回车时,判断为输入结束,将RxXferCount置为0,然后调用回调函数进行数据处理。

a.单字节循环接收数据

HAL_UART_Receive_IT通过设置接收缓冲区和需要接收的数据个数。当数据接收达到设定个数后引发一次中断调用回调函数HAL_UART_RxCpltCallback。由于只引发一次中断,如果需要连续接收,则需要在HAL_UART_RxCpltCallback再调用HAL_UART_Receive_IT。这种定长的接收可能并不是想要的,往往传输的数据都是不定长的,我想这需要将HAL_UART_Receive_IT长度设置为1,然后自己根据接收的数据判断。此外由于回调函数没有指明是哪个串口引发的中断,因此有必要在回调函数中做判断,如if(huart==&huart1){ }。

int main(void)    
{
  HAL_Init();
  Sysclk_config();
	USART1_UART_Init(19200);
	//HAL_UART_Transmit(&huart1,aTxBuffer,sizeof(aTxBuffer),0xFFF);
  HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//开启接收中断
	while(1)
	{
		if(flag==1)
		{
			HAL_UART_Transmit(&huart1,RxBuff,count,0xFF);
			printf("\n");
			for(uint8_t i=0;i

b.长字节数据循环输入

设置输入缓冲数组(长度可设置100),利用单次中断,将接收到的数据元素轮流存入数组中,数据元素为0或回车时判断数据输入完成,进行数据输入完成标志位置位。

void Buffer_reset(void);
int fputc(int ch, FILE *f);


uint8_t aTxBuffer[]="this is a test message!\n";
uint16_t flag=0;
uint8_t RxBuff[LENTH];		//接收缓冲数组
uint16_t count = 0;		//接收缓冲计数
uint8_t aRxBuffer[LENTH]={0};		//USART接收Buffer

int main(void)    
{
  HAL_Init();
  Sysclk_config();
	Buffer_reset();
	USART1_UART_Init(19200);
  HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//开启接收中断
	while(1)
	{
		if(flag==1)
		{
			HAL_UART_Transmit(&huart1,RxBuff,count,0xFF);
			printf("\n");
			Buffer_reset();
			flag=0;
			count=0;
			HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//重新开启接收中断
		}
	}
}


void USART1_IRQHandler(void)
{
	HAL_UART_IRQHandler(&huart1);
	do
		{
			RxBuff[count]=aRxBuffer[count];
			printf("aRxBuffer[%d]=%d\n",count,aRxBuffer[count]);
			count++;
		}
		while((aRxBuffer[count]!=0)&&(count

此代码暂时有问题,调试中。

 

4.DMA传输

DMA可以解放CPU,同时可以利用DMA+空闲中断实现任意字节的串口输入输出。

空闲中断:接收到一条完整的数据,就会产生空闲中断,同时空闲标志位置位。

串口接收中断:每接收到一个字符,就会产生一个串口接收中断。

原理:利用DMA配置,将串口读入的数据存储在DMA缓冲区的接收数组中。当检测到一帧数据(即数据输入完成)时,产生空闲中断,此时可以将所接收的数据进行处理或输出。

步骤:

a.串口配置(时钟使能,引脚配置,串口配置,中断配置,使能空闲中断,串口全局中断,开启DMA接收数据)

b.DMA配置(时钟使能,usart_tx和usart_rx通道配置,中断配置,关联usart和DMA通道)

c.重写串口中断函数(检测到空闲中断时,清除空闲中断标志位,停止DMA传输,获取输入数据的长度,置位输入完成标志位)

d.主函数处理(检测到输入完成标志位时,进行数据处理或输入,然后清空数组,清除数据长度和输入完成标志位)

DMA配置:

void usart_dma_init(void){
	
	 __HAL_RCC_DMA2_CLK_ENABLE();
	
	huart1_dma_rx.Instance=DMA2_Stream2;
	huart1_dma_rx.Init.Channel=DMA_CHANNEL_4;
	huart1_dma_rx.Init.Direction=DMA_PERIPH_TO_MEMORY;
	huart1_dma_rx.Init.PeriphInc=DMA_PINC_DISABLE;
	huart1_dma_rx.Init.MemInc=DMA_MINC_ENABLE;
	huart1_dma_rx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE;
	huart1_dma_rx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
	huart1_dma_rx.Init.Mode=DMA_NORMAL;
	huart1_dma_rx.Init.Priority=DMA_PRIORITY_LOW;
	huart1_dma_rx.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
	HAL_DMA_Init(&huart1_dma_rx);
	//RX_DMA_config
	
	huart1_dma_tx.Instance=DMA2_Stream7;
	huart1_dma_tx.Init.Channel=DMA_CHANNEL_4;
	huart1_dma_tx.Init.Direction=DMA_MEMORY_TO_PERIPH;
	huart1_dma_tx.Init.PeriphInc=DMA_PINC_DISABLE;
	huart1_dma_tx.Init.MemInc=DMA_MINC_ENABLE;
	huart1_dma_tx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE;
	huart1_dma_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;
	huart1_dma_tx.Init.Mode=DMA_NORMAL;
	huart1_dma_tx.Init.Priority=DMA_PRIORITY_HIGH;
	huart1_dma_tx.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
	HAL_DMA_Init(&huart1_dma_rx);
	//TX_DMA_config
	
    __HAL_LINKDMA(&huart1, hdmarx, huart1_dma_rx);
	__HAL_LINKDMA(&huart1, hdmatx, huart1_dma_tx);
	//关联USART1和DMA
	
	HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);

    HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
	//配置DMA通道的中断
}

中断处理函数:

void USART1_IRQHandler(void)
{
	uint32_t tmp_flag = 0;
  uint32_t temp;
	tmp_flag= __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
	if(tmp_flag==1)//当产生空闲中断时(及接收到一帧数据)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除空闲中断标志位
    HAL_UART_DMAStop(&huart1); //停止串口的DMA传输
    temp = __HAL_DMA_GET_COUNTER(&huart1_dma_rx);// 获取DMA中未传输的数据个数        
    rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,即得到已经接收的数据个数
		flag=1;
	}
		HAL_UART_IRQHandler(&huart1);
}

5.利用定时器实现串口的不定长字节输入

当接收到第一个字符时,打开定时器。经过延时后,进入定时器中断回调函数,在该回调函数中进行数据的处理和输出。

int main(void)    
{
  HAL_Init();
  Sysclk_config();
	USART1_UART_Init(19200);
	Basic_Tim_Config();
	printf("input strings:\n");
	num_reset();
	HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF);
	if(RxBuff[0])
	{
		HAL_TIM_Base_Start_IT(&Basic_Tim6);//有数据输入的时候就开启定时器
  }
	while(1)
	{
		if(flag)
		{
			printf("input strings again:\n");
			flag=0;
			count=0;
			num_reset();
	    HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF);  //开启串口输入
	   if(RxBuff[0])
	   {
		    HAL_TIM_Base_Start_IT(&Basic_Tim6);//重新开启定时器
     }
		}
	}
}

void TIM6_DAC_IRQHandler(TIM_HandleTypeDef *htim) 
{
	HAL_TIM_IRQHandler(&Basic_Tim6);
}


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_TIM_Base_Stop(&Basic_Tim6);
	huart1.RxState = HAL_UART_STATE_READY;
	huart1.Lock=HAL_UNLOCKED;
	printf("your input:");
	do
	{
		aRxBuff[count]=RxBuff[count];
		count++;
	}
	while(RxBuff[count]);	
	if((HAL_UART_Transmit(&huart1,aRxBuff,count+1,0xFFF))==HAL_OK)//串口输出
	{
		flag=1;
	}
}

void num_reset(void)
{
	for(uint8_t i=0;i

 

 

 

 

 

你可能感兴趣的:(STM32F7之HAL库学习)