STM32 UART中断溢出后不起作用

STM32 UART Interrupt does not work after overflow

我目前正在使用带有 STM32F103C8 微控制器的 SIM800C 模块。由于SIM800C是使用AT命令配置的,所以我使用UART进行通信。目前,我在中断模式下将 USART3 配置为 9600 波特(我尝试使用简单接收,但它只接收到第一个字符。我认为这是因为答案太快或 smth)。所以,这是我发送 AT 命令的代码:

static void send_at(char *at_cmd, char *reply, uint16_t timeout)
{

    offset = strlen(at_cmd) + 1; // offset of reply of SIM800
    len = strlen(reply) + 2 + offset; // overall reply length

    rx_buffer = (char *) calloc(len, sizeof(char));
    expected_ans = (char *) calloc(len, sizeof(char));

    sprintf(expected_ans, "%s\r\n", reply);

    HAL_UART_Receive_IT(sim_uart, (uint8_t *) rx_buffer, len); // \r\r\n
    HAL_Delay(10);
    HAL_UART_Transmit(sim_uart, (uint8_t *) at_cmd, strlen(at_cmd), timeout);
}

对于"AT\r\n"命令,SIM800 returns "AT\r\nOK\r\n",因此,偏移量是仅回复开始的指针(在本例中为4,跳过"AT\r\n"部分)。所以,当我收到答案时,我在回调函数中检查它:


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uart)
{
    rx_buffer[len] = 0; // truncate garbage data

    if (!strcmp(rx_buffer + offset, expected_ans))
        ++state;
    else
        state = 0;
}

state是变量,表示发送哪条AT命令(显然,当状态增加时,将执行下一条AT命令)。一切正常,但是...如果回复溢出,一切都结束了。

例如,对于启用 GPRS 的 AT 命令 "AT+SAPBR=1,1\r\n",可以有一个答案 "ERROR" 而不是 "OK"。因为,在 "ER" 中断触发后,我已经调用了 "OK" 长度的 RX 中断。然而,对于此后的所有其他中断调用,中断不会触发,即使答案是 "OK"。基本上,回复 "ERROR" 溢出并且下一个中断被禁用。我不知道是什么问题以及为什么会发生这种情况。有什么办法可以事先知道传入数据的大小或在回调函数中刷新缓冲区吗?

这是我的主要功能:

void sim800_init(UART_HandleTypeDef *uart)
{
    sim_uart = uart;

    while(state < 8)
    {
        switch (state)
        {
            case 0:
                send_at(AT, "OK", 1000);
                break;
            case 1:
                send_at(AT_SAPBR_CONTYPE, "OK", 5000);
                break;
            case 2:
                send_at(AT_SAPBR_SET_APN, "OK", 5000);
                break;
            case 3:
                send_at(AT_ENABLE_GPRS, "OK", 5000);
                break;
            case 4:
                send_at(AT_HTTP_INIT, "OK", 10000);
                break;
            case 5:
                send_at(AT_HTTPPARA_CID, "OK", 10000);
                break;
            case 6:
                send_at(AT_HTTPPARA_URL, "OK", 5000);
                break;
            case 7:
                send_at(AT_HTTPPARA_CONTENT, "OK", 5000);
                break;
        }

        HAL_Delay(1000);
    }
}

简单回答:使用循环缓冲区,一次接收一个字符。您可以在中断中重新加载接收,但它并不是很干净。中断更新循环缓冲区头指针。主要代码只是读取头指针和 use/update 尾指针来进行解析。只需解析您的循环缓冲区并搜索“/r/n”(如果它存在)就在那里拆分,如果需要则解析和交换状态。 请记住,接收应该是连续的,不仅在您期望数据到来时。您迟早也需要处理未经请求的消息。还请记住在中断和主循环上对 written/read 变量使用编译器屏障或至少 "volatile attribute"。

长答案提示:在循环模式下使用dma,idle halffull和full dma中断将帮助您在主循环中触发解析。

针对您的问题:

Is there any way to know the size of incoming data beforehand or to flush buffer in callback function?

不,你不能。 UARTS 有 1 字节的硬件缓冲区。如果你在没有读取的情况下将其填满,它将在下一个字节接收时触发溢出错误,整个读取过程将挂起。