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

STM32向GSM模块发送AT+CSTT命令时TX缓冲区丢字符问题

AT命令发送丢失字符问题(STM32 + GSM模块)

开发STM32搭载GSM模块的系统时,发送AT+CSTT命令返回ERROR,排查发现模块实际收到的是A+CSTT(丢失了AT中的'T')。进一步测试发现:密码长度≤2位时命令发送正常,超过2位就出现丢字符问题;DEBUG打印显示发送前命令格式完全正确,推测问题出在串口收发的数据处理环节。


发送AT命令的代码

void sendAT(char command[], char answer[], int waitForAnswer) {
    char ATcommand[500];
    uint8_t buffer[500] = { 0 };
    uint8_t ATisOK = 0;

    sprintf(ATcommand, "%s\n", command);

    DEBUG_PRINT("Sending AT Command: ");
    DEBUG_PRINT(ATcommand);

    while (!ATisOK) {
        HAL_UART_Transmit(&huart2, (uint8_t*) ATcommand, strlen(ATcommand),
                2000);
        
        
        HAL_UART_Receive(&huart2, buffer, sizeof(buffer) - 1, 300);
        buffer[sizeof(buffer) - 1] = '\0';

        HAL_Delay(500);

        ATisOK = !waitForAnswer;

        if (strstr((char*) buffer, "OK")) {
            ATisOK = 1;
        }

        DEBUG_PRINT("GSM: ");
        DEBUG_PRINT((char* ) buffer);
        HAL_Delay(1000);
        memset(buffer, 0, sizeof(buffer));
    }
}

char* sendATWithReturn(char command[]) {
    char ATcommand[500];
    uint8_t buffer[300] = { 0 };
    char *returnBuffer = malloc(300 * sizeof(char));

    if (returnBuffer == NULL) {
        return NULL;
    }

    sprintf(ATcommand, "%s\n", command);
    HAL_UART_Transmit(&huart2, (uint8_t*) ATcommand, strlen(ATcommand), 1000);
    HAL_UART_Receive(&huart2, buffer, 300, 100);
    HAL_Delay(500);

    DEBUG_PRINT("GSM: ");
    DEBUG_PRINT((char* ) buffer);

    strncpy(returnBuffer, (char*) buffer, 300);

    return returnBuffer;
}

模块初始化代码

void modemStart(char *apn, char *user, char *pwd) {
    sendATWithReturn("AT");

    HAL_Delay(1000);
    sendATWithReturn("AT+CPIN?");

    HAL_Delay(1000);
    sendATWithReturn("AT+CREG?");

    HAL_Delay(1000);
    sendATWithReturn("AT+CGATT?");

    HAL_Delay(1000);
    sendATWithReturn("AT+CSQ");

    HAL_Delay(1000);
    char command[500];
    snprintf(command, sizeof(command), "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    sendAT(command, "OK", 1);
    memset(command, 0, sizeof(command));

    HAL_Delay(1000);
    sendAT("AT+CGATT=1", "OK", 0);
    HAL_Delay(1000);
    sendATWithReturn("AT+CSTT?");

    HAL_Delay(1000);
    snprintf(command, sizeof(command), "AT+CSTT=\"%s\",\"%s\",\"%s\"", apn, user, pwd);
    sendAT(command, "OK", 1);
    memset(command, 0, sizeof(command));

    HAL_Delay(1000);
    sendATWithReturn("AT+CIICR");

    HAL_Delay(1000);
    sendATWithReturn("AT+CIFSR");

    HAL_Delay(1000);
    sendATWithReturn("AT+CIPMUX=1");

    /*
     AT+CPIN?: Verifica o status do PIN do SIM.
     AT+CREG?: Verifica o status de registro na rede.

     Sequência de Inicialização:

     AT+NETCLOSE: Fecha qualquer conexão de rede existente.
     AT+CGATT=0: Desativa o GPRS.
     AT+CGATT=1: Ativa o GPRS.
     AT+CGDCONT: Define o contexto PDP.
     AT+CSTT: Define o APN, usuário e senha.
     AT+CIICR: Inicia a conexão sem fio.
     AT+CIFSR: Obtém o endereço IP.
     AT+CIPMUX=1: Habilita o modo de múltiplas conexões.
    */
}

日志信息

19:37:54.671 -> Serial2: Send AT:
19:37:54.671 -> Sending AT Command: AT
19:37:54.671 -> GSM: AT

19:37:54.671 -> OK
19:37:54.671 -> 
19:37:56.282 -> Serial2: GSM: AT

19:37:56.282 -> OK
19:37:56.282 -> 
19:37:57.878 -> Serial2: GSM: AT+CPIN?

19:37:57.878 -> +CPIN: READY
19:37:57.878 -> 
19:37:57.878 -> OK
19:37:57.878 -> 
19:37:59.455 -> Serial2: GSM: AT+CREG?

19:37:59.455 -> +CREG: 0,1
19:37:59.455 -> 
19:37:59.455 -> OK
19:37:59.455 -> 
19:38:01.060 -> Serial2: GSM: AT+CGATT?

19:38:01.060 -> +CGATT: 1
19:38:01.060 -> 
19:38:01.060 -> OK
19:38:01.060 -> 
19:38:04.242 -> Serial2: GSM: AT+CSQ

19:38:04.242 -> +CSQ: 18,0
19:38:04.242 -> 
19:38:04.242 -> OK
19:38:04.242 -> Sending AT Command: AT+CGDCONT=1,"IP","zap.vivo.com.br"
19:38:04.287 -> GSM: AT+CGDCONT=1,"IP","zap.vivo.com.br"

19:38:04.287 -> OK
19:38:04.287 -> 
19:38:06.839 -> Serial2: Sending AT Command: AT+CGATT=1
19:38:06.839 -> GSM: AT+CGATT=1

19:38:06.885 -> OK
19:38:06.885 -> 
19:38:11.069 -> Serial2: GSM: AT+CSTT?

19:38:11.069 -> ERROR
19:38:11.069 -> Sending AT Command: AT+CSTT="zap.vivo.com.br","vivo","vivo"
19:38:11.069 -> GSM: A+CSTT="zap.vivo.com.br","vivo","vivo"

19:38:11.069 -> ERROR
19:38:11.069 -> 
19:38:12.670 -> Serial2: GSM: A+CSTT="zap.vivo.com.br","vivo","vivo"

19:38:12.670 -> ERROR

排查与修复建议

1. 调整串口发送超时参数

当前HAL_UART_Transmit的超时设置(2000ms/1000ms)可能不足以完成长命令的发送,导致数据截断。建议:

  • 将超时参数改为HAL_MAX_DELAY,确保数据完全发送完成后再退出函数;
  • 确认STM32与GSM模块的波特率配置完全一致,波特率不匹配是丢字符的常见原因。

2. 替换sprintf为snprintf

sprintf存在缓冲区溢出风险,可能破坏内存数据间接影响发送。建议:

// 替换原sprintf语句
snprintf(ATcommand, sizeof(ATcommand), "%s\n", command);

同时检查snprintf的返回值,确保命令没有超出缓冲区长度:

int cmdLen = snprintf(command, sizeof(command), "AT+CSTT=\"%s\",\"%s\",\"%s\"", apn, user, pwd);
if (cmdLen >= sizeof(command)) {
    DEBUG_PRINT("Command buffer overflow!\n");
    return;
}

3. 优化串口接收逻辑

当前发送后立即调用HAL_UART_Receive且超时仅300ms,可能模块还未返回数据就结束接收,导致后续处理混乱。建议:

  • 发送完成后增加适当延时(如100ms),确保模块有足够时间处理命令;
  • 改用中断方式接收数据,避免阻塞式接收的时序问题;
  • 检查HAL_UART_Receive的返回值,确认是否成功接收数据,避免处理空缓冲区或不完整数据。

4. 硬件层面排查

  • 检查STM32与GSM模块的串口接线,确保TX/RX引脚连接正确,无虚焊、松动;
  • 尝试降低串口波特率(如从115200降至9600),排除高速波特率下的信号干扰问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 07:17:35