基於μ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/
於是仔細研讀該程式碼,再加上個人理解,實現了基於μ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指令執行結果的邏輯是:
- 若resp_str==NULL,則忽略resp_str的配置。僅根據line_num判斷:
若line_num==0,則僅判斷接收字串中是否為”OK”、 ”FAIL”,或包含”ERROR”,當接收到”OK”時,返回OK,否則返回ERROR。
若line_num!=0,則當接收到line_num行響應時返回OK,判斷函式時以”\r\n”為分隔符。
- 若resp_str!=NULL,則在進行上述第(1)種判斷的同時,還判斷接收到的字串中是否含有resp_str。若接收到resp_str,則直接返回OK,不再進行後續判斷。若未接收到resp_str,則仍然執行第(1)種判斷。比如用移遠某物聯網模組傳送MQTT訊息時,傳送完AT+QMTPUBEX指令後,收到”> ”就可以傳送訊息體了,這個響應和常規的帶”\r\n”的不一樣。
- 若達到預設時間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);
}
……
}