完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
我使用官方提供的GD32H759bsp移植到GD32H757Z单片机上时发现在finsh命令行输入上下左右键时会卡死,初步调试发现是串口发生了ORE溢出中断,而驱动未对ORE溢出中断清除标志导致卡死在中断里,中断处理代码如下
于是我在中断处理里加入清除ORE标志
这时发现finsh不会卡死,但是出现了新的问题:使用上下键时会出现字母AB 我研究了一下方向键是会下发多个字节,最后一个字节就是字母的ASCII码,如下是上下键下发的数据 这么看来应该是串口数据丢帧导致的,我对串口中断进行修改,打开空闲中断,屏蔽中断处理中rt_hw_serial_isr函数,并定义一个数据接收数据,在空闲中断处理中把接收到数据发送出去
果然接收的数据会丢失一点,如下图 结合溢出中断我觉得可能是串口中断处理的时间太长导致的,为了验证猜想,将波特率从115200改为9600,这时发送上来的数据是完整的,很奇怪,中断中的代码很少,600M的主频不应该会出现这个问题。我又在裸机上试了一下,115200的波特率下并没有出现这个问题。之后我又将设备驱动接口几个函数中的代码注释,在rt_hw_usart_init手动初始化串口
问题依旧。 所以有没有好心的大佬知道什么原因啊 |
|
相关推荐
1个回答
|
|
|
用户的问题是关于在GD32H757Z单片机上移植官方GD32H759的BSP时,使用UART出现数据丢帧,特别是在Finsh命令行输入上下左右键时卡死。用户已经定位到问题是由于串口ORE(Overrun Error)溢出中断未清除标志导致的中断卡死。用户提供了部分中断处理代码,但代码不完整。 首先,我需要理解ORE溢出错误。当接收数据过快,硬件来不及处理时,会发生ORE错误。如果中断服务程序(ISR)没有正确处理并清除ORE标志,中断会持续触发,导致系统卡死。 用户提供的代码片段中,中断处理函数是`GD32_UART_IRQHandler`,其中有一个条件分支处理接收中断(`RESET != usart_flag_get(uart->uart_device, USART_FLAG_RBNE)`),但用户提到没有处理ORE中断。 在GD32的标准外设库中,处理UART中断时,通常需要检查多个中断标志位,包括ORE(溢出错误)、PE(奇偶校验错误)、FE(帧错误)等。特别是ORE标志,必须通过读取USART_STAT寄存器来清除,但根据参考手册,清除ORE标志需要先读取USART_STAT,再读取USART_DATA。然而,在GD32中,通常的做法是:当发生ORE中断时,先读取USART_STAT寄存器(包含ORE标志),然后读取USART_DATA寄存器来清除ORE标志。 但是,在用户提供的代码中,没有看到对ORE中断的处理。因此,应该在中断服务程序中添加对ORE中断标志的检查,并在检测到时清除标志。 参考GD32的标准处理流程,通常会在中断服务函数中检查各种错误标志,包括ORE。例如: ```c if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_ORERR)) { // 清除ORE标志:通过读取USART_STAT和USART_DATA寄存器 usart_flag_clear(uart->uart_device, USART_FLAG_ORERR); // 或者,根据GD32库,可能需要先读取STAT再读取DR // 通常库函数已经处理,调用usart_flag_clear即可 } ``` 但是,在GD32的库函数中,`usart_flag_clear`函数用于清除标志位,但需要注意的是,ORE标志的清除需要先读取USART_STAT寄存器,再读取USART_DATA寄存器。在GD32的库函数中,`usart_flag_clear`函数可能只用于清除需要软件清除的标志(如TC标志),而ORE标志的清除通常是通过读取DR(数据寄存器)来完成的。因此,实际上,处理ORE错误的标准步骤是: 1. 检查USART_STAT的ORERR位是否为1(表示溢出错误)。 2. 读取USART_DATA寄存器(即使没有数据,为了清除ORE标志也需要读取)。 3. 然后可以清除ORE标志(通过先读取STAT再读取DR,ORE标志会自动清除)。 在GD32的库函数中,通常通过调用`usart_flag_get`来检查ORE标志,然后通过读取数据寄存器来清除。但是,在ORE发生时,数据寄存器中的数据可能是无效的,或者已经丢失,但为了清除标志,必须读取数据寄存器。 因此,在中断处理函数中,应该首先处理错误中断,包括ORE。在用户代码中,接收中断(RBNE)的处理在错误处理之前,这可能导致在发生ORE时,先进入接收中断,但ORE没有被处理。所以,通常建议先处理错误标志,再处理数据接收。 修改建议: 1. 在中断处理函数中,首先检查是否有错误中断(包括ORE、PE等),并处理它们(主要是清除标志)。 2. 然后再检查接收中断(RBNE)和发送中断(TC或TBE)。 具体步骤: - 添加对ORE标志的检查,如果发生ORE错误,则清除标志,并执行一些错误处理(如记录错误、丢弃数据等)。 - 清除ORE标志的方法:调用库函数清除,或者按照手册要求读取状态寄存器和数据寄存器。 在GD32的标准外设库中,可以使用`usart_flag_clear`来清除标志位,但需要确认该函数是否支持清除ORE标志。根据GD32的库函数,`usart_flag_clear`函数的参数中有一个是标志位,但ORE标志(`USART_FLAG_ORERR`)的清除方式不同,不能直接通过写0清除,而是通过读取DR寄存器清除。因此,正确的做法是: ```c if (usart_flag_get(uart->uart_device, USART_FLAG_ORERR) != RESET) { // 读取数据寄存器以清除ORE标志,即使数据无效 uint32_t temp = USART_DATA(uart->uart_device); // 或者调用库函数:usart_data_receive(uart->uart_device); // 这样就会清除ORE标志 // 可以选择丢弃该数据 // 同时,也可以清除标志位,但通常读取数据寄存器后标志位会自动清除 } ``` 在GD32的参考手册中,关于ORE的描述:当ORE置位时,表明接收数据寄存器中的数据尚未被读取,又有新的数据到来导致溢出。此时,新数据丢失。清除ORE标志的方法是先读取USART_STAT寄存器,然后读取USART_DATA寄存器。 因此,在代码中,我们可以这样处理: ```c if (usart_flag_get(uart->uart_device, USART_FLAG_ORERR) != RESET) { // 读取USART_STAT寄存器 // 然后读取USART_DATA寄存器 // 使用库函数读取数据 uint16_t data = usart_data_receive(uart->uart_device); // 这样ORE标志会被清除 // 可以记录错误或丢弃数据 } ``` 但是,注意在接收中断中,我们可能已经读取了数据寄存器,所以需要确保在ORE发生时,即使没有RBNE中断,我们也要读取一次数据寄存器来清除ORE标志。因此,应该先处理ORE错误。 另外,为了避免在高速数据接收时频繁溢出,可能需要优化接收处理逻辑,比如使用DMA或者提高中断优先级,但这超出了当前问题的范围。 现在,我们修改用户的中断处理函数,在接收中断处理之前添加对ORE错误的处理。根据用户提供的代码片段,我注意到中断处理函数中已经处理了PE(奇偶错误)和FE(帧错误),但没有ORE。因此,在现有的错误处理部分添加ORE处理。 用户提供的代码片段如下(不完整): ```c static void GD32_UART_IRQHandler(struct rt_serial_device *serial) { struct gd32_uart *uart = (struct gd32_uart *) serial->parent.user_data; RT_ASSERT(uart != RT_NULL); /* UART in mode Receiver -------------------------------------------------*/ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_RBNE)) { // 处理接收数据 } /* If frame error */ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_FERR)) { // 清除帧错误标志 } /* If parity error */ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_PERR)) { // 清除奇偶错误标志 } // ...可能还有其他处理 } ``` 需要添加ORE错误的处理。建议在接收中断之前处理错误,所以将ORE处理放在最前面(在接收中断之前): ```c static void GD32_UART_IRQHandler(struct rt_serial_device *serial) { struct gd32_uart *uart = (struct gd32_uart *) serial->parent.user_data; RT_ASSERT(uart != RT_NULL); /* Handle overflow error */ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_ORERR)) { // 清除ORE标志:读取数据寄存器 // 注意:读取数据寄存器会清除ORE标志 usart_data_receive(uart->uart_device); // 可以记录错误计数或进行其他处理 } /* Handle frame error */ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_FERR)) { // 清除帧错误标志:通过读取USART_STAT和USART_DATA usart_data_receive(uart->uart_device); // 是否必要?根据手册,帧错误清除也是读取数据寄存器? // 实际上,对于帧错误,通常也是读取数据寄存器来清除标志,但需要确认 // 或者调用库函数清除标志:usart_flag_clear(uart->uart_device, USART_FLAG_FERR); // 但根据GD32库,帧错误标志的清除需要读取数据寄存器 } /* Handle parity error */ if (RESET != usart_flag_get(uart->uart_device, USART_FLAG_PERR)) { // 清除奇偶错误标志:同样需要读取数据寄存器 usart_data_receive(uart->uart_device); } /* UART in mode Receiver -------------------------------------------------*/ while (RESET != usart_flag_get(uart->uart_device, USART_FLAG_RBNE)) { // 处理接收数据 rt_uint32_t ch = usart_data_receive(uart->uart_device); rt_serial_rx_indicate(serial, ch); } } ``` 注意:以上代码将接收数据的部分改为了while循环,因为可能在一次中断中有多个数据,这样可以一次性读完。而用户原来的代码是if,可能只读取一个字节。在中断中,如果接收数据寄存器中还有数据,RBNE标志会一直为1,直到被读取。使用while循环可以读空接收缓冲区,避免频繁中断。 另外,错误标志的处理顺序很重要,先处理错误标志,再处理数据接收。对于ORE错误,读取数据寄存器是为了清除标志,读取到的数据可能是无效的,所以直接丢弃。 还需要注意,在GD32库中,清除帧错误和奇偶错误标志也是通过读取数据寄存器来实现的。因此,在错误处理中读取数据寄存器,既清除了错误标志,也清除了数据。所以,在错误处理分支中读取了数据寄存器后,在接收数据分支中就应该不再读取该数据(因为已经被错误处理分支读取了),但这样会导致数据丢失。因此,错误处理分支中读取的数据应该丢弃,因为它是无效的。而接收中断分支只处理没有错误的数据。 但是,由于错误标志和接收中断是同时发生的,所以当发生帧错误或奇偶错误时,RBNE标志也会置位。因此,在错误处理分支中读取数据寄存器后,RBNE标志会被清除,所以接收中断分支就不会再处理该数据。这样是合理的,因为错误的数据不应该被正常接收。 另外,对于ORE错误,当发生ORE时,RBNE标志可能已经置位(因为上一个数据还在寄存器中),所以在处理ORE时读取数据寄存器,会清除RBNE标志,这样就不会再进入接收中断分支。所以,ORE处理分支读取了一个字节(这个字节可能是之前接收到的有效数据,但由于溢出,下一个字节丢失了)。然后,RBNE标志被清除,所以不会再次进入接收处理。这样,ORE处理分支实际上清除了一个数据字节,同时也清除了ORE标志。 因此,上述修改应该能解决ORE中断未清除导致卡死的问题,同时也能处理其他错误。 另外,用户提到是上下左右键时卡死,这些键通常会产生多个字节(如ESC序列),可能数据量较大,容易引起溢出。所以,除了修复中断处理,还需要考虑提高接收效率,比如使用FIFO或DMA,或者优化中断处理速度(使用while循环读空接收寄存器)。 总结修改步骤: 1. 在中断处理函数中,首先检查并处理ORE错误(读取数据寄存器以清除标志)。 2. 然后检查并处理其他错误(如帧错误、奇偶错误),同样读取数据寄存器清除标志。 3. 将接收中断处理改为while循环,确保一次中断中读取所有接收到的数据(直到RBNE为0)。 这样修改后,ORE标志会被清除,不会导致中断卡死,同时也能处理其他错误,并且提高了接收效率,减少了溢出的可能性。 最后,用户需要根据自己使用的GD32库函数调整代码,确保正确使用库函数清除标志。如果库函数提供了专门的错误清除函数,也可以使用,但必须确保按照手册要求清除标志。 针对UART数据丢帧和ORE溢出中断导致卡死的问题,以下是完整的解决方案: 问题原因分析
解决方案(修改中断处理函数)关键修改说明:
补充优化建议:
调试验证方法:
通过上述修改可彻底解决ORE中断卡死问题,同时显著提升UART数据接收可靠性。 |
|
|
|
|
只有小组成员才能发言,加入小组>>
184个成员聚集在这个小组
加入小组rtstudio是否可像keil加载ini解决虚拟串口与mcu串口通讯?
1220 浏览 0 评论
【Vision Board创客营连载体验】基于RA8D1-Vision Board的自动路径规划小车
1780 浏览 1 评论
【Vision Board创客营连载体验】基于Vision Board的垃圾分类
2160 浏览 0 评论
【Vision Board创客营连载体验】使用 Vision Board 做一个 UVC Camera
1786 浏览 0 评论
【Vision Board创客营连载体验】TinyMaix进行手写数字识别
2012 浏览 0 评论
1476浏览 5评论
在RT-Thread Studio中新建的stm32f407-atk-explorer工程运行qemu失败,是什么原因引起的?
1778浏览 3评论
为什么rt_device_read()只能读取到两个字节数据?
374浏览 3评论
连得上热点,但是ping baidu.com出现timeout,请问跟什么有关?
431浏览 3评论
443浏览 2评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-4 12:59 , Processed in 0.656938 second(s), Total 77, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
2529
