STM32/GD32——串口中断接收通信异常

一、背景:

最近的工程使用到了串口通信,使用了中断接收和printf发送,中断接收的数据解析之后,将有用的数据,给到屏幕显示,屏幕用的是普通的LCD+LVGL显示。(GD32F303芯片)

1、做了一个界面的显示:背景图+文本+按钮背景图文本字库均是存储在W25Q64(外部flash)内,然后在调试的过程中,我发现在使用屏幕刷新过程中,只要有LVGL在while循环内有刷新文本的操作就影响我串口数据的接收,直接导致了接收数据的缺失了。

2、例如我要接收一串完整的字符串为:<ABC|CDC:123,456|BBC:31.215|PID:1.5,2.3|>

中间总是会缺失几个字符,这就直接影响了我的解析函数,最后导致显示错误。

二、解决:

和很多人一样,我也是百度直接搜索 “串口和lvgl同时使用导致数据的丢失”,但是这样是搜不到有用信息的,于是乎就直接搜索关键词 “串口中断接收数据丢失/异常”,这就能看得一些经验贴了。例如以下的两篇参考:

参考1:GD32 串口接受异常的几个原因 - - 21ic电子技术开发论坛

参考2:串口丢数据的几个常见原因 - 知乎

1、根据参考1,我测量了主板外部8M晶振,确认是正常的8M,内部调试查看到也是8MHz,排除晶振的影响。(排除)

2、使用编译器的调试器,我查看了内部寄存器的ORERR标志位不为1,没有过载,加上串口传输的数据量也不是很大,不存在接收数据溢出的情况,不会过载。(排除)

3、串口线过长导致的数据丢失或延迟,我这边使用的串口线是HDMI,数据RS232,大家都知道RS232主要就是提高抗干扰能力,增大通信距离。可能性不大,我自己我飞线出来接到串口调试助手看过,数据能完全接收过去,所以不可能。(排除)

说到这里就大概能排除串口硬件错误的原因了。

4、再来看一下软件原因:

(1)、查看工程中用到的所有外设中断的等级,看串口中断在接收过程中会不会被打断

(1)、中断分组
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
/* 抢占优先级将分配 4 位,子优先级分配 0 位,即系统中的中断优先级完全由抢占优先级决定,子优先级不再使用。 */
(2)、外设等级设置
/* configure the systick handler priority */
nvic_irq_enable(SysTick_IRQn, 1, 0);	
/* 串口中断等级 */
nvic_irq_enable(DEBUG_UART_IRQn, 0, 0);

nvic_irq_enable(TIMER5_IRQn, 1, 0);

可以看到我的串口中断等级设置为最大,这说明,串口在接收过程中不会被其他中断打断,也不存在接收数据不及时。

所以中断等级不高的问题排除。

(2)、查看一下lvgl内部有没有影响中断的函数,比如说lvgl内部有中断和串口同等级,但是这个我没有找到,应该是没有影响到串口的代码。而且lv_tick_inc(1);函数是被我放在systick中的,基本也可以排除这个的影响。

5、既然中断等级问题被排除了,就考虑是不是整个工程哪里有关闭全局中断的函数了

__disable_irq();      // 关闭全部中断

全局搜索__disable_irq(); ,果然被我发现了在我使用W25Q64读取内部数据的时候使用到了SPI-DMA传输,里面是一个被我内部封装的函数。每次读取的时候都会关闭中断,然后再次开启,这就可以解释出,为什么每次串口中断的数据总是接收不完全。就是中断被关掉了!!!

为什么会一直读取W25Q64的内容?

因为在使用lvgl的绘制页面的时候,我有用到背景图和字库,这些数据就是存储在这个外部flash的,所以每次调用刷新函数的时候,总是会读取数据:

① 

 ②

③  

最后删除掉这个__disable_irq(); 我的问题解决了。完美解决。

三、基础知识:

1、关闭中断和启用中断的函数知识:

Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_disableirq头文件-优快云博客

2、串口配置知识:

GD32F303固件库开发(8)----USART收发配置 - 记帖 - 博客园

3、串口DMA不定长数据接收

GD32F30x的串口不定长DMA接收和DMA发送史诗级例子 - 单片机 - 硬汉嵌入式论坛 - Powered by Discuz!

小熊派gd32f303学习之旅(5)—使用DMA和空闲中断实现串口接收-优快云博客

(PS:这些链接都是网上找的各位博主写的资料分享,感谢)

### GD32 软件模拟 SPI 通信方法 对于GD32系列微控制器,在某些情况下可能需要通过软件来实现SPI通信功能。这通常发生在专用的SPI引脚已被其他外设占用的情况下。 为了在GD32上成功实施软件模拟SPI,需注意几个关键点: - **初始化设置**:确保所有参与SPI通信的引脚都已正确定义为通用I/O口,并配置成推挽输出模式[^4]。 - **时序控制**:由于是纯软件方式生成波形,因此要特别关注SCK(串行时钟)、MOSI(主出从入)以及MISO(主入从出)之间的相对定时关系。可以通过延时函数精确调整每一位传输的时间间隔[^3]。 下面是一个简单的例子展示如何利用C语言编写一段用于发送字节给外部设备的代码片段: ```c void spi_write_byte(uint8_t data){ uint8_t i; for(i=0;i<8;i++){ // 设置 MOSI 输出位 if(data & (1 << (7-i))){ GPIO_SetBits(GPIOB, GPIO_PIN_MOSI); }else{ GPIO_ResetBits(GPIOB, GPIO_PIN_MOSI); } // 产生上升沿触发 GPIO_SetBits(GPIOB, GPIO_PIN_SCLK); delay_us(1); // 下降沿结束当前周期 GPIO_ResetBits(GPIOB, GPIO_PIN_SCLK); delay_us(1); } } ``` 这段代码展示了基本的数据帧构建过程——先设定数据线状态再改变时钟电平从而完成一位的信息传递。整个过程中涉及到的具体延迟时间取决于目标系统的速度需求和连接器件的要求。 ### 使用 STM32 硬件 SPI 实现方案 当选择使用STM32内置的硬件SPI模块来进行高速稳定的通信时,则应充分利用其特性简化编程工作量并提高效率。 以下是基于HAL库的操作流程概述: - 初始化SPI接口参数,包括波特率、相位极性和工作模式等; - 开启相应的中断服务例程以便处理接收缓冲区溢满等情况; - 编写具体的应用层逻辑调用`HAL_SPI_Transmit()`或`HAL_SPI_Receive()`API进行实际的数据交换操作[^2]。 示例代码如下所示: ```c // 假定已经完成了必要的初始化工作... uint8_t tx_data[] = {0xAA}; if(HAL_OK != HAL_SPI_Transmit(&hspi1, tx_data, sizeof(tx_data), HAL_MAX_DELAY)){ Error_Handler(); } while (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_BSY)); ``` 此段程序实现了向指定地址发送单个命令的功能,其中包含了错误检测机制以保障通讯质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值