原来调过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);