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