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; } }