12-HAL庫串口通訊總結

1.定義了三種傳輸方式:阻塞傳輸,中斷傳輸、DMA傳輸數組

HAL_UART_Transmit;  HAL_UART_Receive函數

HAL_UART_Transmit_IT;    HAL_UART_Receive_ITui

HAL_UART_Transmit_DMA;    HAL_UART_Receive_DMAthis

此外還定義了兩個中斷回調函數,供中斷和DMA使用,分別在數據傳輸一半和完成時使用指針

voidHAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);調試

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef*huart);rest

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);code

voidHAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);ip

2.阻塞傳輸input

阻塞傳輸是調用這個函數並在等待時間內一直等待操做完成。

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<LENTH;i++)
			{
				RxBuff[i]=0;
			}//清空數組
			flag=0;
			count=0;
			HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//從新開啓接收中斷
		}
	}
}


void USART1_IRQHandler(void)
{
	HAL_UART_IRQHandler(&huart1);
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
	if(count<LENTH-1)
	{
		if(aRxBuffer!=0x0d)//輸入非回車
		{
			flag=0;
	    RxBuff[count]=aRxBuffer;
		  count++;
			HAL_UART_Transmit(&huart1,&aRxBuffer,count,0x20);
			printf("\n");	
	    HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//從新開啓接收中斷	
		}
		else flag=1;
	}
	else
		flag=1;
}
//接收中斷回調函數

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<LENTH));  //將接收到的字節存進數組,直到元素爲0或溢出爲止。
		flag=1;
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{	
	
}
//接收中斷回調函數

void Buffer_reset(void){
	for(uint8_t i=0;i<LENTH;i++)
	{
		RxBuff[i]=0;
		aRxBuffer[i]=0;
	}//清空數組
}

此代碼暫時有問題,調試中。

 

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<LENTH;i++)
	{
		RxBuff[i]=0;
		aRxBuff[i]=0;
	}
}