编写 GSM 调制解调器驱动程序?

Posted

技术标签:

【中文标题】编写 GSM 调制解调器驱动程序?【英文标题】:Writing a GSM modem driver? 【发布时间】:2013-01-18 06:37:37 【问题描述】:

我一直在开发一个应用程序,它使用 GSM 调制解调器来做两件事之一;通过向服务器发送 GET 请求或向服务器发送数据(使用 UDP),使用内置 HTTP 堆栈检查其状态。我尝试了几种不同的方法来尽可能保证它的可靠性,我终于准备好寻求帮助了。

我的应用程序是为 SIMCOM908 模块和 PIC18 平台编写的(我正在使用 PIC18 Explorer 进行开发)。

所以问题有时是调制解调器忙于做某事,而错过了一个命令。作为人类,我会看到并重新发送命令。为我的 MCU 添加超时和重新发送功能不是问题。

问题是调制解调器在不同事件后发送未经请求的响应。当调制解调器更改注册状态(与手机信号塔)时,它会响应+CGREG: 1, ... 或当 GPS 准备就绪时GPS Ready。这些响应可能随时发生,包括在命令中间(如创建 IP 连接)。

这是一个问题,因为我还没有想到解决这个问题的方法。我的应用程序需要发送一个命令(例如连接到服务器,AT+CIPSTART="UDP","example.com",5000)这个命令将响应'OK',然后当命令完成时'CONNECT OK'。但是,我需要能够对许多其他可能的响应做出反应,而我还没有找到一种方法来做到这一点。我需要用我的代码做什么;等待调制解调器的响应,检查响应,根据该响应执行操作?

我的代码有限(作为一个 8 位微控制器!)并且希望将重复保持在最低限度。如何编写一个响应函数,从 GSM 模块(请求或现在)获取响应,然后让我的程序的其余部分知道发生了什么?

理想情况下,我想对这些响应做点什么。就像保持内部状态(当我听到 GPS Ready 时,我知道我可以为 GPS 等供电。

也许我应该考虑一些事情,或者也许有一个开源项目已经解决了这个问题?

这是我目前所拥有的:

/* Command responses */
enum 
    // Common
    OK = 0,
    ERROR,
    TIMEOUT,
    OTHER,
    // CGREG
    NOT_REGISTERED,
    // CGATT
    NOT_ATTACHED,
    // Network Status
    NO_NETWORK,
    // GPRS status
    NO_ADDRESS,
    // HTTP ACTION
    NETWORK_ERROR,
    // IP Stack State
    IP_INITIAL,
    IP_STATUS,
    IP_CONFIG,
    UDP_CLOSING,
    UDP_CLOSED,
    UDP_CONNECTING
 gsmResponse;

int gsm_sendCommand(const char * cmd) 
    unsigned long timeout = timer_getCurrentTime() + 5000;

    uart_clearb(GSM_UART); // Clear the input buffer
    uart_puts(GSM_UART, cmd); // Send the command to the module
    while (strstr(bf2, "\r") == NULL)  // Keep waiting for a response from the module
        if (timeout < timer_getCurrentTime())  // Check we haven't timed out yet
            printf("Command timed out: %s\r\n", cmd);
            return TIMEOUT;
        
    
    timer_delay(100); // Let the rest of the response be received.

    return OK;


int gsm_simpleCommand(const char * cmd) 
    if (gsm_sendCommand(cmd) == TIMEOUT)
        return TIMEOUT;

    // Getting an ERROR response is quick, so if there is a response, this will be there
    if (strstr(bf2, "ERROR") != NULL)
        return ERROR;

    // Sometimes the OK (meaning the command ran) can take a while
    // As long as there wasn't an error, we can wait for the OK
    while (strstr(bf2, "OK") == NULL);
    return OK;

一个简单的命令是专门寻找OKERROR 作为响应的任何AT 命令。像AT 这样的东西。但是,我也将它用于更高级的命令,例如AT+CPIN?,因为这意味着我将捕获整个响应,并且可以进一步搜索+CPIN: READY。然而,这些都不是对主动回应的回应。事实上,gsm_sendCommand() 函数会在收到主动响应时提前返回。

管理这种复杂的、偶尔不请自来的状态消息有什么好方法?请注意,此应用程序是用 C 语言编写的,并且在 8 位微控制器上运行!

【问题讨论】:

您可能必须实现一个状态机 (FSM):newstate = oldstate [ message ],并且可能还要考虑另一端是状态机。 我认为这将是其中的一部分。但是,在这种情况下,完整的 FSM 似乎并不是最佳选择。我需要跟踪几个不同的主动代码的二进制状态,而不是单个系统的多个状态。我只需要几个全局布尔类型来跟踪。 【参考方案1】:

必须在同一数据流中同时处理未经请求的消息以及对请求的响应是很困难的,因为您需要对传入流进行多路分解并将结果分派给适当的处理程序。这有点像中断处理程序,因为您必须放弃您正在做的事情并处理您不一定期望的其他信息。

一些模块有一个辅助串行端口,也可以用于消息。如果这是可能的,您可能只会在一个串行端口上出现未经请求的消息,而主端口用于您的 AT 命令。这可能是不可能的,并且某些 GSM 模块将不支持辅助端口上的完整命令集。

也许更好的方法是禁用未经请求的消息。大多数命令要请求的所有状态。例如,在等待注册时,无需等待主动注册消息出现,只需轮询模块以了解当前注册状态。这使您始终处于控制之中,您只需处理刚刚发送的命令的响应。如果您正在等待多个事件,您可以在循环中轮询每个项目。这通常会使代码更简单,因为您一次只需要处理一个响应。缺点是您的响应时间受到投票率的限制。

如果您打算继续使用未经请求的消息方法,我建议您为未经请求的消息实施一个小队列。在等待对命令的响应时,如果响应与命令不匹配,只需将响应推送到队列中。然后,当您收到对 AT 命令的响应或超时时,您可以在之后处理主动提供的消息队列。

【讨论】:

这听起来是一个非常好的方法。我知道这个模块辅助串口不适合发送命令。到目前为止,我真的只看到了GPS Ready+CGREG: 1 消息。我知道你可以禁用 CGREG 之一。我的旧代码用于轮询,这很好,因为这意味着如果我错过了主动响应,我的代码将不会仍在等待它。 因此,虽然我不热衷于实现这样的队列(作为一个相当内存有限的应用程序)的想法,但禁用主动响应并非完全可能。我不喜欢将处理能力添加到所有命令函数以考虑被中断的可能性的想法,因此队列更有意义。谢谢你的回答! @dantheman 希望您的队列可以很小。如果您愿意,您可以通过让gsm_sendCommandchar* bufsize_t bufLen 附加几个参数来安排队列在堆栈上。调用gsm_sendCommand 的函数可以在堆栈上分配一个临时缓冲区,并在gsm_sendCommand 返回后处理队列。有很多方法可以做到这一点。 使用我的编译器,我认为声明要使用的缓冲区或使用 malloc 和 free 动态分配缓冲区之间没有任何区别。我的控制器有很多闪存,只是没有多少 RAM。如果 flash 成为问题,我会查看缓冲区(以及用更简单的东西替换 printf!)

以上是关于编写 GSM 调制解调器驱动程序?的主要内容,如果未能解决你的问题,请参考以下文章

Android 手机作为 GSM 调制解调器在 PC 上发送/接收短信?

尝试在 Python 中使用 GSM 调制解调器拨打电话时收到“NO CARRIER”错误

如何通过USB GSM调制解调器从VB应用程序发送短信(华为E1731)

GSM调制解调器的反应太迟了

向 USB GSM 调制解调器发送“AT”命令在 Python 中返回空字符串

SerialPort 类偶尔会挂在 Dispose 上