STM8S UART串口使用中斷收發數據
原來調過STM8L的串口,邏輯簡單,中斷清晰,換成STM8S105K4後,雖然也是用STD庫,
除去函數名、宏名等語言層面的差異以外,中斷處理方面也有些不一樣的地方,特此記之。
和此篇【STM8L USART串口使用】結構相同,也是中斷異步模式,但爲調用方便起見,在調用層面改爲同步。
(STM8S105K的MCU下,RX爲PD6,RX爲PD5。)
使用方面,感覺主要困擾就是中斷名、使用場合和時機不明確、不清晰,這一點不如STM8L的定義清晰。
舉例而言,開關中斷用UART2_IT_RXNE_OR,清中斷則用UART2_IT_RXNE。
不能開關時用UART2_IT_RXNE,也不能清中斷時用UART2_IT_RXNE_OR,
否則STD庫中參數合法assert斷言,分分鐘讓程序掛起。
供大家參考。
以下是示例代碼,爲了和應用層更好的分離和代碼通用化,設置了獨立的UART的讀寫緩衝區,
如果緩衝較大的情況下,請用@near置於分離數據區初始化。
另外雖然是中斷驅動,考慮到絕大多數使用場景是同步的,設置了同步狀態變量,讀寫函數中檢測。
改成中斷的只需將狀態變量的判斷改成在應用面判斷即可。
1. 讀寫緩衝和標識值定義
#define UART_BUF_SIZE 128
/* Read buffer */
uint8_t read_ok = 0; // 讀完成flag
uint8_t read_idx = 0;
uint8_t read_len = 0;
@near uint8_t read_buffer[UART_BUF_SIZE]; // 緩衝區設置較大的時候可用@near放置
/* Write buffer */
uint8_t writ_ok = 0; // 寫完成flag
uint8_t writ_idx = 0;
uint8_t writ_len = 0;
@near uint8_t writ_buffer[UART_BUF_SIZE]; // 緩衝區設置較大的時候可用@near放置
2. 串口初始化
STM8S105K4只有一個串口,即UART2
int8_t uart_init(void)
{
// 串口參數請按需求修改
UART2_DeInit();
UART2_Init((uint32_t)38400,
UART2_WORDLENGTH_8D,
UART2_STOPBITS_1,
UART2_PARITY_NO,
UART2_SYNCMODE_CLOCK_DISABLE,
UART2_MODE_TXRX_ENABLE);
// 顯式關中斷(默認就是關)
// 注意:
// 讀中斷名字是UART2_IT_RXNE_OR,而不是UART2_IT_RXNE
// 寫中斷名字爲UART2_IT_TXE
UART2_ITConfig(UART2_IT_RXNE_OR, DISABLE);
UART2_ITConfig(UART2_IT_TXE, DISABLE);
//串口使能
UART2_Cmd(ENABLE);
return 0;
}
3. 讀寫函數
// 寫多個字節
void uart_send_n_byte(uint8_t* data, uint8_t len)
{
uint16_t count = 0;
UART2_ITConfig(UART2_IT_TXE, DISABLE);
// 準備寫數據緩衝(從用戶數據區複製到串口寫緩衝、初始化索引值等)
memcpy(writ_buffer, data, len);
writ_idx = 0;
writ_len = len;
// 開寫中斷
UART2_ITConfig(UART2_IT_TXE, ENABLE);
while(!writ_ok) { // 等待寫完成(同步處理)
count++;
if( count >= 10000 ) { // 簡單的超時處理,不需要超時可以去除
writ_idx = 0;
writ_len = 0;
break;
}
}
writ_ok = 0; // 寫完成、重置寫完成flag
return;
}
// 讀多個字節
void uart_read_n_byte(uint8_t* data, uint8_t len)
{
// 關讀中斷
UART2_ITConfig(UART2_IT_RXNE_OR, DISABLE);
// 清空讀緩衝(重置讀索引值即可)
read_idx = 0;
read_len = len;
// 開讀中斷
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
while(!read_ok); // 等待讀操作完成(同步化處理)、添加超時處理可參照上述寫操作
read_ok = 0; // 寫完成、重置寫完成flag
memcpy(data, read_buffer, read_len); // 複製數據到用戶緩衝區
return;
}
4. 中斷處理
INTERRUPT_HANDLER(UART2_TX_IRQHandler, 20)
{
// 寫操作自動清中斷,因此可以不用顯式清中斷
//UART2_ClearITPendingBit(UART2_IT_TXE);
// 從寫緩衝中寫出1字節
UART2_SendData8(writ_buffer[writ_idx++]);
// 全部寫完、關寫中斷、置寫完成標誌(同步化處理)
if( writ_idx == writ_len ) {
UART2_ITConfig(UART2_IT_TXE, DISABLE);
writ_ok = 1;
}
}
INTERRUPT_HANDLER(UART2_RX_IRQHandler, 21)
{
// 讀操作自動清中斷,因此可以不用顯式清中斷
// 注意這裏的中斷名是RXNE,不是RXNE_OR
UART2_ClearITPendingBit(UART2_IT_RXNE);
// 讀1字節
read_buffer[read_idx++] = UART2_ReceiveData8();
// 全部讀完,關中斷(UART2_IT_RXNE_OR),置讀完成標誌(同步化處理)
if( read_idx == read_len ) {
UART2_ITConfig(UART2_IT_RXNE_OR, DISABLE);
read_ok = 1;
}
}
5. 使用代碼例
// 寫2字節
uint8_t buf[32];
memset(buf, 0x00, sizeof(buf));
buf[0] = 0xCC;
buf[1] = 0xDD;
uart_send_n_byte(buf, 2);
// 簡單讀(必須讀滿24字節才返回)
memset(buf, 0x00, sizeof(buf));
uart_send_n_byte(buf, 24);