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_read_n_byte(buf, 24);
  • 10
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
下面是一个基本的STM8串口收发程序,使用中断方式: ```c #include <stm8s.h> #define UART_TX_PIN GPIO_PIN_3 #define UART_RX_PIN GPIO_PIN_2 void uart_init(void); void uart_send_byte(uint8_t data); void uart_send_string(char *str); uint8_t uart_receive_byte(void); void main(void) { CLK_CKDIVR = 0x00; // 16MHz uart_init(); enableInterrupts(); while (1) { // 主程序内容 } } void uart_init(void) { // 配置串口引脚 GPIOC->DDR |= UART_TX_PIN; GPIOC->CR1 |= UART_TX_PIN; GPIOC->CR2 |= UART_TX_PIN; GPIOC->DDR &= ~UART_RX_PIN; GPIOC->CR1 |= UART_RX_PIN; GPIOC->CR2 |= UART_RX_PIN; // 配置波特率 UART1_BRR2 = 0x03; UART1_BRR1 = 0x68; // 配置控制寄存器 UART1_CR1 = UART1_CR1_M | UART1_CR1_RXNEIE; UART1_CR2 = 0x00; UART1_CR3 = UART1_CR3_STOP1; } void uart_send_byte(uint8_t data) { while (!(UART1_SR & UART1_SR_TXE)); UART1_DR = data; } void uart_send_string(char *str) { while (*str) { uart_send_byte(*str++); } } uint8_t uart_receive_byte(void) { while (!(UART1_SR & UART1_SR_RXNE)); return UART1_DR; } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { while (1); } #endif // 中断服务程序 #ifdef _COSMIC_ @far @interrupt void UART1_RX_IRQHandler(void) #else /* _RAISONANCE_ */ void UART1_RX_IRQHandler(void) interrupt 18 #endif { uint8_t data = uart_receive_byte(); uart_send_byte(data); } ``` 上述程序中,我们使用了`UART1`模块和`PC2`和`PC3`两个引脚进行串口收发。在`uart_init()`函数中,我们配置了波特率、控制寄存器,并开启了接收中断。在`uart_send_byte()`函数中,我们等待发送寄存器为空,然后将数据写入发送寄存器。在`uart_send_string()`函数中,我们循环调用`uart_send_byte()`函数,将字符串中的每个字符依次发送出去。在`uart_receive_byte()`函数中,我们等待接收寄存器非空,然后读取接收寄存器中的数据。最后,在中断服务程序中,我们读取接收寄存器中的数据,并将其通过串口发送回去。 注意:上述代码仅供参考,具体实现可能需要根据自己的具体需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值