序列介面相關知識點
並行通訊、序列通訊的概念。
單工、半雙工、全雙工的概念。
非同步序列通訊:通訊雙方在沒有同步時鐘的前提下,將一個字元(包括特定的附加位)按位元進行傳輸的通訊方式。
波特率:每秒鐘傳輸的二進制位數,如9600bps。
TTL電平<—->RS232:MAX3232 SP3232
串列埠<———>USB介面:CH340 CP2012
STM32晶片的串列埠UASRT功能十分強大,但對於日常程式設計而言,使用最多的還是非同步序列通訊。
串列埠1:USART1_TX與PA9複用,USART1_RX與PA10複用。
串列埠2:USART2_TX與PA2複用,USART2_RX與PA3複用。
////查詢方式,阻塞式發送函數(初學者,推薦使用)(就是必須等數據傳送完後微控制器纔去做其他事)
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size, uint32_t Timeout);
參數1:UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
參數2:*pData,待發送數據緩衝區的指針。
參數3:Size,待發送數據的位元組數。
參數4:Timeout,超時時間值。
返回值:HAL_StatusTypeDef,函數執行狀態。
舉例: HAL_UART_Transmit(&huart1, (uint8_t *)data, 3, 0xffff); //串列埠1發送三個位元組數據,最大傳輸時間0xffff
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
----------------------------------------------------------------------------
////中斷方式,非阻塞式發送函數
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart,uint8_t *pData, uint16_t Size);
參數1:UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
參數2:*pData,待發送數據緩衝區的指針。
參數3:Size,待發送數據的位元組數。
返回值:HAL_StatusTypeDef,函數執行狀態。
舉例: HAL_UART_Transmit_IT(&huart1,(uint8_t *)data, 3);//向串列埠1發送三個位元組數的數據
////串列埠發送完畢中斷回撥函數
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串列埠發送中斷回撥函數
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)//串列埠發送一半中斷回撥函數(不常用)
應用舉例:使用非阻塞式的串列埠發送函數,將發送緩陣列dat_Txd中的前5個數據發送到USART1,在數據發送完成後,翻轉PB9引腳的輸出電平。
//使用中斷,非阻塞方式
HAL_UART_Transmit_IT(&huart1, dat_Txd, 5);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart(換成用到的串列埠,例如&huart1))
{
if(huart->Instance == USART1);
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
}
//使用查詢,阻塞方式
HAL_UART_Transmit(&huart1, dat_Txd, 5, 10000);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
/// 查詢方式,阻塞式接受函數
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
參數1:huart,串列埠範例的指針。
參數2:*pData,數據接收據緩衝區的指針。
參數3:Size,待接收數據的位元組數。
參數4:Timeout,超時時間值。
返回值:HAL_StatusTypeDef,函數執行狀態。
----------------------------------------------------------------------------
非阻塞式接收函數(推薦使用)
功能:串列埠中斷接收,以中斷方式接收指定長度數據。
大致過程是,設定數據存放位置,接收數據長度,然後使能串列埠接收中斷。接收到數據時,會觸發串列埠中斷。再然後,串列埠中斷函數處理,直到接收到指定長度數據,而後關閉中斷,進入中斷接收回撥函數,不再觸發接收中斷。(只觸發一次中斷)
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size);
參數1:huart,串列埠範例的指針。
參數2:*pData,數據接收據緩衝區的指針。
參數3:Size,待接收數據的位元組數。
返回值:HAL_StatusTypeDef,函數執行狀態。
HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中斷接收一個字元,儲存到value中
////串列埠接收完畢中斷回撥函數
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串列埠接收中斷回撥函數
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串列埠接收一半回撥函數(不常用)
應用舉例:使用非阻塞式的串列埠接收函數,接收USART1中的一個位元組,將其儲存在dat_Rxd變數中,在數據接收完成後,判斷該位元組,若爲0x5A,則翻轉PB8引腳的輸出電平。
//使用中斷,非阻塞方式
HAL_UART_Receive_IT(&huart1, &dat_Rxd, 1);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart(換成用到的串列埠,例如&huart1))
{
if(huart->Instance == USART1)
{
if(dat_Rxd == 0x5A)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
}
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
參數:UATR的別名 如 : UART_HandleTypeDef huart1; 別名就是huart1
功能:對接收到的數據進行判斷和處理 判斷是發送中斷還是接收中斷,然後進行數據的發送和接收,在中斷服務函數中使用
如果接收數據,則會進行接收中斷處理常式
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果發送數據,則會進行發送中斷處理常式
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
HAL_UART_GetState(); 判斷UART的接收是否結束,或者發送數據是否忙碌
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX) //檢測UART發送結束
串列埠設定的一般步驟可以總結爲如下幾個步驟:
CUBEMX中的基本設定例如時鐘啊之類的和之前的都是一樣的,唯一的不同就是在串列埠這裏,如下圖所示,設定好就行了
功能描述:USART1收到PC機發來的數據後原封不動的返回給PC機顯示,但是C語言中printf函數預設輸出裝置是顯示器,要使用printf輸出到串列埠,需要將fputc裏面的輸出指向串列埠,這一過程就叫重定向。
需要設定的只有USART1,設定過程非常簡單。
第一步:在 stm32f4xx_hal.c中包含#include <stdio.h>,同時宣告外部變數huart1
第二步:在在 stm32f4xx_hal.c 中重寫fget和fput函數
第三步:在main.c中新增
#define RXBUFFRSIZE 256
char RxBuffer[RXBUFFRSIZE];
然後就是測試了:在while(1)中新增
最後得到的效果就這這樣,可以使用printf scanf getchar 函數;
雖然使用printf函數很方便,但是我還是認爲需要深入理解串列埠需要用到HAL庫的底層函數,所以接下來我將使用HAL庫的函數進行學習;
例程1:不使用中斷 使用查詢模式接受和發送數據
用到的函數
HAL_UART_Transmit(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size, uint32_t Timeout);
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
部分需要修改的main.c
#include "main.h"
#include "usart.h"
#include "gpio.h"
char str[] = "USart1 is initializing.....\r\n";
char str1[] = "initialization is OK\r\n";
char data[12];
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
if(HAL_OK == HAL_UART_Transmit(&huart1, (uint8_t*)str, sizeof(str), 0xFFFF)) //顯示初始化是否成功
{
HAL_Delay(1000);
HAL_UART_Transmit(&huart1, (uint8_t*)str1, sizeof(str1), 0xFFFF); //成功標誌
}
while (1)
{
if (HAL_OK == HAL_UART_Receive(&huart1, (uint8_t*)data, 12, 0xFFFF)) //如果成功接收到了data,則將data輸出
{
HAL_UART_Transmit(&huart1, (uint8_t*)data, 12, 0xFFFF);
}
HAL_Delay(100);
}
}
上述程式碼,在while(1),裡如果接受函數沒有接收到函數,則微控制器不會去做其他事,這顯然對專案設計來說是及其不方便的。故介紹第二種中斷方法
例程2:中斷方式,使用中斷更加高效的利用微控制器資源
因爲中斷接收和發送函數只能觸發一次接收中斷,所以我們需要在中斷回撥函數中再呼叫一次中斷接收和發送函數
具體流程:
1、初始化串列埠
2、在main中第一次呼叫接收中斷函數
3、進入接收中斷,接收完數據 進入中斷回撥函數
4、修改HAL_UART_RxCpltCallback中斷回撥函數,處理接收的數據,
5 回撥函數中要呼叫一次HAL_UART_Receive_IT和HAL_UART_Receive_IT函數,使得程式可以重新觸發接收和發送中斷
需要部分修改的main.c
#include "main.h"
#include "usart.h"
#include "gpio.h"
#define LED1_ON() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET)
#define LED1_OFF() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET)
#define LED2_ON() HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET)
#define LED2_OFF() HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET)
#define RXBUFFERSIZE 256 //最大接收位元組數
char RxBuffer[RXBUFFERSIZE]; //接收數據
uint8_t aRxBuffer; //接收中斷緩衝
uint8_t Uart1_Rx_Cnt = 0; //接收緩衝計數
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
/* USER CODE END 2 */c
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function Should not be modified, when the callback is needed,
the HAL_UART_TxCpltCallback could be implemented in the user file
*/
if(Uart1_Rx_Cnt >= 255) //溢位判斷
{
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer));
HAL_UART_Transmit(&huart1, (uint8_t *)"數據溢位", 10,0xFFFF);
}
else
{
RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer; //接收數據轉存
if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判斷結束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //將收到的資訊發送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//檢測UART發送結束
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空陣列
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再開啓接收中斷
}
以上就是中斷模式的串列埠了(程式碼部分參考z小璇大神(https://blog.csdn.net/as480133937/article/details/99073783)的程式碼,小白的程式碼無法做到不定長接受和發送。