You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM3 MSS UART接收中断重复触发首字节覆盖丢失问题咨询

ARM3 MSS UART中断重复触发、首字符丢失问题

初始问题

为什么我的UART ARM3 MSS函数会被调用两次?
已确认的基础触发原因:当前配置为单字节触发接收中断模式,每收到1字节就会触发一次中断。第一次中断触发时硬件FIFO仅存入1个字符,第二次中断触发时剩余数据才全部进入FIFO。

新异常现象

在plink/putty中输入字符串"test"并按下回车时,出现首字符丢失问题:

  • 第一次触发ISR时,缓冲区中仅存在字符't'
  • 第二次触发中断时,缓冲区中仅存'e','s','t','\n',首个字符't'丢失。同类异常表现为输入"status\n"时仅读取到"tatus\n"
    该现象存在偶发性,少数场景下缓冲区可接收到完整字符串。目前计划调整逻辑为持续接收数据直到检测到'\n'为止,待解决两个核心疑问:
  1. 首个接收到的字符't'丢失到哪里了?
  2. 为何首字符会被覆盖?

现有实现代码

UART初始化

MSS_UART_init(&g_mss_uart0, MSS_UART_115200_BAUD, MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY);

中断回调注册

MSS_UART_set_rx_handler(&g_mss_uart0, uart3_rx_handler, MSS_UART_FIFO_SINGLE_BYTE);

流程差异

中断处理函数负责打印读取到的内容,并将内容传入command和value_str变量供后续处理:

  • 预期流程:中断触发->调用UART处理函数->无后续中断
  • 实际流程:中断触发->调用UART处理函数->再次触发中断->调用UART处理函数->无后续中断

中断处理函数实现

void uart3_rx_handler(mss_uart_instance_t *this_uart)
{
    unsigned int rx_size = MSS_UART_get_rx(this_uart, g_rx_buff, sizeof(g_rx_buff));
    (void)rx_size;
    uart_calls_cnt++;

    uart_handler_flag = 0;

    if (strchr((char *)g_rx_buff, '\n') != NULL) {
        memset(command, 0, 255);
        memset(value_str, 0, 255);
        strcpy(value_str, (char *)g_rx_buff);
        strcpy(command, (char *)g_rx_buff);
        value_str[strcspn(value_str, "\n")] = 0;
        command[strcspn(command, "\n")] = 0;
        memset(&g_rx_buff[0], 0, 128);
        uart_handler_flag = 1;
    } else {
        // 无任何处理逻辑
    }
}

问题根因

两个疑问是同一个逻辑bug导致的:

  • 首字符没有丢失,是被第二次读取UART的操作直接覆盖了。
  • 覆盖原因:第一次中断触发时,硬件FIFO里只有首字符't',调用MSS_UART_get_rx把't'读到了g_rx_buff的起始位置,之后判断缓冲区里没有'\n',直接走else分支无任何操作——既没有把已经读到的't'暂存到累积缓冲区,也没有记录当前已接收的长度偏移。等后续字节到达触发第二次中断时,再次调用MSS_UART_get_rx,仍然把新读到的'e','s','t','\n'从g_rx_buff的起始地址开始写入,直接把之前存在0偏移位置的't'覆盖。
    偶发可以收到完整字符串的场景,本质是多个字节几乎同时到达UART FIFO,第一次中断触发时FIFO里已经攒了全部字符,一次读取完成,没有触发第二次中断,自然不会出现覆盖。
修复方案

不要每次中断都把新读的数据往接收缓冲区的起始地址写入,需要维护一个接收偏移指针:

  1. 定义全局变量记录当前缓冲区已写入的位置,例如uint16_t rx_write_idx = 0;,初始值设为0
  2. 每次进入中断调用MSS_UART_get_rx时,读缓冲区地址传入g_rx_buff + rx_write_idx,不要直接传g_rx_buff首地址
  3. 读取完成后把本次读到的字节数rx_size加到rx_write_idx上,更新下一次写入的位置
  4. 判断缓冲区中是否存在'\n':如果存在,执行命令解析逻辑、置位处理标记,之后把rx_write_idx重置为0准备接收下一包数据;如果不存在'\n',直接退出中断等待后续数据即可,不要清空接收缓冲区
  5. 增加缓冲区长度判断,避免rx_write_idx超过g_rx_buff的大小导致内存越界

内容的提问来源于stack exchange,提问作者pgapga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 23:39:36