GD32 笔记 08:如何使用串口中断接收不定长数据,并用串口中断以非阻塞的方式发送数据

本文详细介绍了在GD32E230C8单片机上实现串口通信的全过程,包括数据接收缓存、初始化、发送函数及中断服务函数的编写。通过接收和发送数据,展示了如何在main函数中处理并利用串口数据进行实时操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

  • 测试过程中使用的 MCU 型号是:GD32E230C8
  • 测试工程下载地址:点击下载

第一步:用于保存数据的数组和变量


#define USART_RECV_BUF_SIZE 32// 串口接收缓存长度
#define USART_SEND_BUF_SIZE 32// 串口发送缓存长度

static uint8_t USART_Rx_Buf[USART_RECV_BUF_SIZE];// 用于保存 RX 接收到的数据
static uint16_t USART_Rx_Len = 0;                // 用于保存 RX 接收到的数据的个数
static uint16_t USART_Rx_Count = 0;              // 用于保存 RX 接收数据时已经接收到的数据个数
static uint8_t USART_Tx_Buf[USART_SEND_BUF_SIZE];// 用于保存 Tx 要发送的数据
static uint16_t USART_Tx_Len = 0;                // 用于保存 Tx 要发送的数据的个数
static uint16_t USART_Tx_Count = 0;              // 用于保存 Tx 要发送的数据时已经发送的数据个数

第二步:定义串口初始化函数

/**
 * @brief 初始化串口
 */
void USART_Init()
{
	rcu_periph_clock_enable(RCU_GPIOA); // 使能外设时钟
	rcu_periph_clock_enable(RCU_USART0);
	
	gpio_af_set            (GPIOA, GPIO_AF_1,     GPIO_PIN_9); // 设置GPIO的备用功能
	gpio_af_set            (GPIOA, GPIO_AF_1,     GPIO_PIN_10);
	gpio_mode_set          (GPIOA, GPIO_MODE_AF,  GPIO_PUPD_PULLUP,  GPIO_PIN_9); // 设置GPIO模式
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9); // 设置GPIO输出模式和速度
	gpio_mode_set          (GPIOA, GPIO_MODE_AF,  GPIO_PUPD_PULLUP,  GPIO_PIN_10);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
	
	usart_deinit                   (USART0);                        // 复位外设USARTx
	usart_word_length_set          (USART0, USART_WL_8BIT);         // 配置USART字长
	usart_stop_bit_set             (USART0, USART_STB_1BIT);        // 配置USART停止位
	usart_parity_config            (USART0, USART_PM_NONE);         // 配置USART奇偶校验
	usart_baudrate_set             (USART0, 115200U);               // 配置USART波特率
	usart_receive_config           (USART0, USART_RECEIVE_ENABLE);  // USART接收器配置
	usart_transmit_config          (USART0, USART_TRANSMIT_ENABLE); // USART发送器配置
	usart_enable                   (USART0);                        // 使能USART
	
	nvic_irq_enable(USART0_IRQn, 0);                // 使能中断,配置中断的优先级
	usart_interrupt_enable(USART0, USART_INT_RBNE); // 使能“读数据缓冲区非空中断和过载错误中断”
	usart_interrupt_enable(USART0, USART_INT_IDLE); // 使能“IDLE线检测中断”
	usart_interrupt_enable(USART0, USART_INT_TBE);  // 使能“发送缓冲区空中断”
}

第三步:定义串口数据发送函数

/**
 * @brief 使用串口发送数据
 */
void USART_Send()
{
	usart_interrupt_enable(USART0, USART_INT_TBE);
}

第四步:重写中断服务函数

/**
 * @brief 串口 0 中断服务函数
 */
void USART0_IRQHandler(void)
{
	// 如果满足以下条件,说明检测到“读数据缓冲区非空中断标志”被置位
	if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))
	{ 
		usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);// 清除标志位
		
		uint8_t ch = (uint8_t)usart_data_receive(USART0);// 从 RX 数据寄存器中读取数据
		
		// 如果满足以下条件,说明缓存区还有空间去存储数据
		if(USART_Rx_Count < USART_RECV_BUF_SIZE)
		{
			USART_Rx_Buf[USART_Rx_Count++] = ch;
		}
	}
	// 如果满足以下条件,说明检测到“IDLE线检测中断标志”被置位
	if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE))
	{
		usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
		
		// 如果满足以下条件,说明已经接收到了一帧数据
		if(USART_Rx_Count > 0)
		{		
			usart_receive_config(USART0, USART_RECEIVE_DISABLE);// 关闭串口接收功能,准备处理数据

			USART_Rx_Len   = USART_Rx_Count;
			USART_Rx_Count = 0;	

			SEGGER_RTT_printf(0,"Receive %d bytes!\n", USART_Rx_Len);	
		}
	}
	// 如果满足以下条件,说明检测到“发送缓冲区空中断标志”被置位
	if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_TBE))
	{
		usart_interrupt_flag_clear(USART0, USART_INT_FLAG_TBE);

		// 如果满足以下条件,说明缓存中有数据需要发送
		if(USART_Tx_Len > 0)
		{
			// 如果满足以下条件,说明缓存中还有数据没发送完
			if(USART_Tx_Count < USART_Tx_Len)
			{
				usart_data_transmit(USART0, USART_Tx_Buf[USART_Tx_Count++]);
			}
			// 反之,说明缓存中的数据都已经发送完毕
			else
			{
				usart_interrupt_disable(USART0, USART_INT_TBE);

				USART_Tx_Len = 0;
				USART_Tx_Count = 0;
			}
		}
		// 反之,说明缓存中没有数据需要发送
		else
		{
			usart_interrupt_disable(USART0, USART_INT_TBE);
		}
	}
}

第五步:在 main 函数中处理接收到的数据并通过串口发送出去

/**
 * @brief 主函数
 */
int main()
{
	systick_config();
	
	USART_Init();
	
	while(1)
	{
		// 如果满足以下条件,说明串口接收到了一帧数据
		if(USART_Rx_Len > 0)
		{
			// 如果满足以下条件,说明串口并没有在发送数据
			if(!(USART_Tx_Len > 0))
			{
				SEGGER_RTT_printf(0,"Start process data!\n");	

				memcpy(USART_Tx_Buf, USART_Rx_Buf, USART_Rx_Len);// 将接收到的数据从 RX 缓存保存到 TX 缓存
				USART_Tx_Len = USART_Rx_Len;
				USART_Rx_Len = 0;

				for(int i=0; i<USART_Tx_Len; i++)
				{
					SEGGER_RTT_printf(0, "%c\n", USART_Tx_Buf[i]);
				}

				USART_Send();// 开启中断发送

				// 处理完数据,重新开启串口接收功能以及相关中断
				usart_receive_config  (USART0, USART_RECEIVE_ENABLE);  
				usart_interrupt_enable(USART0, USART_INT_RBNE);       
				usart_interrupt_enable(USART0, USART_INT_IDLE);       

				SEGGER_RTT_printf(0,"Process data over!\n");
			}	
		}
		
	}
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Simple Man ZHR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值