STM8S UART串口使用中斷收發數據

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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章