1. 程式人生 > >基於μCOSiii的AT指令程式碼和使用方法詳解

基於μCOSiii的AT指令程式碼和使用方法詳解

一、主要思路

專案中需要用到AT指令與模組通訊,前期寫了個不帶作業系統的AT指令程式碼模組。現在需要用μCOSiii作業系統,為了提高程式碼健壯性,對程式碼進行了重構。

在網上看到RT-Thread中實現了AT元件(https://www.rt-thread.org/document/site/submodules/rtthread-manual-doc/zh/1chapters/14-chapter_at/https://www.rt-thread.org/document/site/rtthread-application-note/components/at/an0014-rtthread-system-at-client/

),但該元件和RT-Thread系統核心耦合性太強,無法直接移植到其它作業系統。

於是仔細研讀該程式碼,再加上個人理解,實現了基於μCOSiii的AT指令程式碼,與MCU和模組的耦合性極低,可用於絕大多數AT模組。

下載連結: https://pan.baidu.com/s/1veWZgQxfm8P-iUp11zAo1g    提取碼: sfub 

二、型別定義

        1.  傳送字元陣列函式指標、接收單個字元函式指標

定義這兩個函式指標型別是為了讓使用者在初始化AT時,能直接註冊傳送和接收函式。

定義如下:

typedef void (*SendDataFunc_t)(const char* buf, uint32_t len);

typedef uint8_t (*RecvByteFunc_t)(char* p_data);



static SendDataFunc_t AT_SendDataCallback = NULL;

static RecvCharFunc_t AT_RecvByteCallback = NULL;

        2. 接收緩衝區結構體

此結構體暫存每次接收到的一行字串。

typedef struct

{

    uint16_t len;

    char buf[AT_BUF_SIZE];

}stcRecvBuf;



static stcRecvBuf recvLineBuf;

static stcRecvBuf* pRecvLineBuf = &recvLineBuf;

        3. URC結構體

URC(Unsolicited Result Code),即"非請求結果碼"。一般的AT命令流程都是控制端發出命令,被控端響應結果碼。但當被控端有事件需要通知控制端時,就會主動發出URC,例如有呼叫打入、收到新簡訊息、自動關機等。

比如移遠某物聯網模組,在MQTT應用中,可能會受到+QMTSTAT和+QMTRECV兩種URC,分別代表網路狀態變化和接收到MQTT訊息。

此結構體用於配置每個URC,定義如下:

typedef struct

{

    const char *cmd_prefix; //URC開始字元

    const char *cmd_suffix; //URC結束字元

    void (*handler)(const char *data, uint16_t size);//接收到URC後的處理函式

}stcATUrc;

        4.  AT上下文結構體

此結構體用於儲存AT指令執行的上下文環境,定義如下:

typedef struct

{

    char            resp_buf[AT_BUF_SIZE];//響應緩衝區

    uint8_t         resp_line_cnt;//響應的行數

    AT_RET          resp_status;//響應狀態

    OS_SEM          resp_sem;//響應訊號量,收到期望接收的字串時傳送

    OS_MUTEX        resp_mux;//AT互斥訊號量

    const stcATUrc* urc_table;//urc表格

    uint8_t         urc_table_size;//urc表格大小

}stcATContext;



static stcATContext atContext;

static stcATContext *pATContext = &atContext;

這裡響應緩衝區和第2小節接收緩衝區的區別是:後者每接到一行響應字串後,就將字串新增到響應緩衝區中,直到接收到期望的字串。

        5.  AT配置結構體

此結構體用於配置每個AT指令的引數,在每次執行AT指令前均需配置,定義如下:

typedef struct

{

    const char*     resp_str;     //期望收到的字串

    uint8_t         resp_line_num;//期望收到的行數

    uint16_t        resp_timeout100ms;//傳送後查詢返回資訊的延時,100ms為單位

    uint8_t         max_try_times; //最大重試次數

    uint8_t         max_reset_times; //最大重啟次數

} stcATConfig;



static stcATConfig atConfig;

static stcATConfig *pATConfig = &atConfig;

其中,resp_timeout100ms可配置為AT指令的最大響應時間。

凡是讀寫pATConfig和pATContext的程式碼,均需用pATContext->resp_mux保護起來。

三、全域性函式

        1.  AT指令執行環境初始化函式

此函式用於初始化AT指令的執行環境,串列埠傳送函式、串列埠接收函式和URC表格均需呼叫者在外部按照上面說的結構體的格式定義好,然後呼叫此函式傳遞到AT指令元件中。

/*************************************************************

* 初始化AT指令執行環境,並開啟AT指令處理任務

* @param send_func:串列埠傳送函式

* @param recv_func:串列埠接收函式

* @param urc_table:模組URC表格

* @param urc_table_size:模組URC表格尺寸

*

* @return 無

**************************************************************/

void AT_InitEnv(SendDataFunc_t send_func,RecvCharFunc_t recv_func,stcATUrc* urc_table,uint8_t urc_table_size);

        2.  AT指令配置函式

此函式用於配置AT指令的引數,即結構體pATConfig,函式輸入引數和結構體內容一致。暫存每次接收到的一行字串。

/*************************************************************

* 初始化AT指令配置

* @param resp_str:期望接收到的字串。為NULL時,忽略此配置。不為NULL時,接收到這裡配置的字串就返回OK(line_num配置仍然有效)。

* @param line_num:期望接收到的響應行數。>0時接收到相應行數時證明AT指令執行正常;==0時僅判斷是否接收到OK、ERROR、FAIL等。

* @param timeout100ms:AT指令的響應超時,單位:100ms

* @param max_try_times:指令最大重試次數

* @param max_reset_times:指令重試次數達到後,最大重啟次數

*

* @return 無

**************************************************************/

void AT_InitConfig(const char* resp_str,uint8_t line_num,uint16_t timeout100ms,uint8_t max_try_times,uint8_t max_reset_times);

通過此函式配置AT指令後,返回AT指令執行結果的邏輯是:

  1. 若resp_str==NULL,則忽略resp_str的配置。僅根據line_num判斷:

若line_num==0,則僅判斷接收字串中是否為”OK”、 ”FAIL”,或包含”ERROR”,當接收到”OK”時,返回OK,否則返回ERROR。

若line_num!=0,則當接收到line_num行響應時返回OK,判斷函式時以”\r\n”為分隔符。

  1. 若resp_str!=NULL,則在進行上述第(1)種判斷的同時,還判斷接收到的字串中是否含有resp_str。若接收到resp_str,則直接返回OK,不再進行後續判斷。若未接收到resp_str,則仍然執行第(1)種判斷。比如用移遠某物聯網模組傳送MQTT訊息時,傳送完AT+QMTPUBEX指令後,收到”> ”就可以傳送訊息體了,這個響應和常規的帶”\r\n”的不一樣。
  2. 若達到預設時間timeout100ms後,上述兩種判斷均未接收到響應(只要能返回OK或ERROR都是接收到響應,而不僅僅是指返回OK),將重複執行max_try_times次,若仍失敗,將重啟max_reset_times次,若仍失敗,則返回time_out。

        3.  AT指令執行函式

此函式用於執行每個AT指令,需要呼叫者自己帶上”\r\n”。

/*************************************************************

* 執行AT指令

*

* @param send_str:指令字串,需要呼叫者自己帶上"\r\n"

*

* @return 執行結果,見巨集定義

**************************************************************/

AT_RET AT_ExecCmd(const char *send_str);

        4.  通過AT指令傳送字元陣列函式

此函式用於通過AT指令介面傳送字元陣列,在向模組傳送資料時可能會用到。因陣列中可能包含0(字串的結束符),所以無法跟用函式AT_ExecCmd直接傳送。

/*************************************************************

* 通過AT指令口傳送字元陣列

*

* @param send_buf:傳送的陣列

* @param buf_len:陣列長度

*

* @return 執行結果,見巨集定義

**************************************************************/

AT_RET AT_SendData(const char *send_buf,uint16_t buf_len);

        5.  AT指令響應字串格式化輸出函式

此函式用於從AT響應緩衝區中讀取指定行,並格式化輸出。格式化的語法同sscanf函式。

/*************************************************************

* 通過AT指令口傳送字元陣列

*

* @param send_buf:傳送的陣列

* @param buf_len:陣列長度

*

* @return 執行結果,見巨集定義

**************************************************************/

AT_RET AT_SendData(const char *send_buf,uint16_t buf_len);

 

四、例子

以移遠某物聯網模組為例,使用步驟如下:

        1.  定義串列埠收發函式和URC表格

static void UART_SendData(uint8_t* Buff,uint32_t length)

{

……

}



static void UART_RecvByte(uint8_t* p_data)

{

……

}



static void IOT_RecvMsgHandler(const char *data, uint16_t size)

{

    ……

    return;

}



static stcATUrc urcTable[] = {

    {"+QMTRECV:",        "\r\n",    IOT_RecvMsgHandler},

};

      2.  初始化AT執行環境

AT_InitEnv(UART_SendData, UART_RecvByte,urcTable,1);

      3.  愉快地執行AT命令

比如,執行ATI命令,檢視AT通訊是否執行正常:

static IOT_RET IOT_TestATI(void)

{

    IOT_RET _ret=IOT_OK;

    AT_InitConfig(NULL,0,3,10,0);//responst time:300ms



    _ret = AT_ExecCmd("ATI\r\n");

    if(_ret!=AT_RESP_OK)

    {

        _ret = IOT_ERR_ATI;

    }

    return _ret;

}

 

再比如,執行CSQ命令,查詢網路訊號質量:

static IOT_RET IOT_QuerySigQuality(void)

{

    IOT_RET _ret=IOT_OK;

    int16_t _retVal1 = 0;

    int16_t _retVal2 = 0;

    char _tmpStr[100];



    AT_InitConfig(NULL,4,3,10,0);//response time:300ms

    _ret = AT_ExecCmd("AT+CSQ\r\n");

    if(_ret==AT_RESP_OK)

    {

        AT_ParseRespLineArgs(2,"%[^:]:%d,%d",_tmpStr,&_retVal1,&_retVal2);



        if(_retVal1>0 & _retVal1<=31)

        {

           _ret = IOT_OK;

        }

        else

        {

           _ret = IOT_ERR_CSQ_BAD;

        }

    }

    else

    {

        _ret = IOT_ERR_CSQ_NACK;

    }



    return _ret;

}

 

再再比如,執行QMTPUBEX命令釋出MQTT訊息:

AT_InitConfig("> ",2,3,10,0);//response time:300ms

_ret = AT_ExecCmd(PUB_CMD_INS);

if( _ret== AT_RESP_OK )

{

char ch1,ch2;

        AT_ParseRespLineArgs(2,"%c%*s",&ch1);

        if(ch1 == '>')

        {

           AT_SendData((const char*)(_dataArray),_dataLen);

        }

        ……

}