4G LTE 數據機狀態機分析器
本頁提供 `modem_main()` 函式的核心流程控制邏輯解析、相關輔助函式的功能查詢,以及關鍵資料結構的詳細說明。
modem_main()
流程控制邏輯
modem_main()
函式主要透過一個大的 switch
語句來根據 info->session
(當前數據機會話狀態) 執行不同的邏輯。每個 case
代表一個特定的數據機操作階段。
case _AT_SESSION_RESET:
(數據機重置階段)
►
目的: 等待數據機完成硬體重置後的初始化延遲。
邏輯:
- 檢查自
at_cmd_reset_tick
以來的時間是否超過AT_RESET_INITIAL_TIMEOUT_MS
(5 秒)。 - 如果超過,表示重置延遲已滿足,則將會話切換到
_AT_SESSION_INIT
(初始化階段)。 - 同時初始化
at_cmd_polling_tick
和at_cmd_poll_gps_tick
,為後續的命令輪詢和 GPS 數據讀取計時。
case _AT_SESSION_INIT:
(數據機初始化階段)
►
目的: 執行一系列初始化的 AT 命令,包括基本設定、讀取 UUID、檢查訊號品質、設定 RTC 時間、以及 MQTT 相關參數載入和訂閱主題等。
邏輯:
- 呼叫
cmd_processing(info)
:這是處理單個 AT 命令的核心函式,負責發送命令、等待響應、解析響應並更新命令狀態。 if(info->status == STATUS_CMD_OK)
:- 重置
info->status
為STATUS_CMD_NONE
。 - 檢查訊號品質 (
chk_cmd_response()==CMD_STATUS_BAD_SQ
): 如果當前命令響應指示訊號品質不佳 (例如 AT+CSQ 命令返回的值低於預設閾值),則:- 增加
_modem.chk_sq_cnt
。 - 根據當前會話是否為
_AT_SESSION_CHK_SQ
,決定是直接切換到_AT_SESSION_WAIT_TO_CHK_SQ
還是保留原會話並切換到_AT_SESSION_WAIT_TO_CHK_SQ
(使用switchATSessionK
保留原會話)。 - 更新
at_cmd_reset_tick
。
- 增加
- 檢查是否為命令集的末尾 (
commands[info->index+1].mode == _CMD_MODE_END
): 如果當前命令是會話中的最後一個命令:- 增加
info->index
。 - 根據不同的會話類型執行相應的收尾工作:
_AT_SESSION_CHK_SQ
: 恢復到之前的會話 (restoreToOrgSession()
)。_AT_SESSION_TX
: 將_modem.lte_4G_TX_flag
清零,表示數據已發送,然後切換到_AT_SESSION_FREE_RUN
。_AT_SESSION_POLLING
: 更新at_cmd_polling_tick
,然後切換到_AT_SESSION_FREE_RUN
。_AT_SESSION_GET_GPS
: 更新at_cmd_poll_gps_tick
,然後切換到_AT_SESSION_FREE_RUN
。_AT_SESSION_INIT / _AT_SESSION_LOG_OUT_IN
: 設定_send_ping_data
為 1 (表示需要發送 PING 數據)、設定_connected
為 1 (表示已連接 MQTT)、清除_relog_request
旗標。default
: 切換到info->cmdSet[info->index].sleeps
所指定的下一個會話。
- 增加
- 重置
else if (info->status == STATUS_CMD_ERROR)
:- 重置
info->status
為STATUS_CMD_NONE
。 - 如果
_modem.error_count
超過 20 次,則執行modem_reset()
(硬體重置數據機)。
- 重置
else if (info->status == STATUS_CMD_TIMEOUT)
:- 與
STATUS_CMD_ERROR
處理方式類似,如果超時次數過多,也執行modem_reset()
。
- 與
case _AT_SESSION_WAIT_TO_CHK_SQ:
(等待檢查訊號品質階段)
►
目的: 在訊號品質不佳時,暫停其他操作,等待一段時間後重新檢查訊號。
邏輯:
- 如果
_modem.chk_sq_cnt
小於 5 次 (表示還在初始的快速重試階段),且自at_cmd_reset_tick
以來超過 5 秒,則切換到_AT_SESSION_CHK_SQ
。 - 如果
_modem.chk_sq_cnt
大於等於 5 次 (表示已經進行多次重試,需要更長的等待時間),且自at_cmd_reset_tick
以來超過 30 秒,則切換到_AT_SESSION_CHK_SQ
。
case _AT_SESSION_FREE_RUN:
(自由運行階段)
►
目的: 釋放數據機物件的控制權 (semaphore),並切換到下一個自由運行階段。
邏輯:
- 呼叫
releaseSemaObj(_OWNER_MODEM)
釋放數據機的互斥鎖。 - 切換到
_AT_SESSION_FREE_RUN_1
。 - 更新
at_cmd_reset_tick
。
case _AT_SESSION_FREE_RUN_1:
(自由運行子階段)
►
目的: 提供一個短暫的窗口,允許其他模組 (例如 CAN) 處理數據,然後再進入數據機的輪詢階段。
邏輯:
- 如果自
at_cmd_reset_tick
以來超過 1 秒,則切換到_AT_SESSION_WAIT_TO_GET_GPS
。
case _AT_SESSION_WAIT_TO_GET_GPS:
(等待獲取 GPS 或輪詢數據階段)
►
目的: 這是數據機在穩定連接後的主要運行階段,負責根據不同的條件觸發數據傳輸、重新登錄或 GPS/訊息輪詢。
邏輯:
- 優先處理數據發送 (
_modem.lte_4G_TX_flag==1
): 如果有待發送的數據:- 嘗試獲取數據機的互斥鎖 (
acquireSemaObj(_OWNER_MODEM)
)。 - 如果成功,則切換到
_AT_SESSION_TX
。
- 嘗試獲取數據機的互斥鎖 (
- 處理重新登錄請求 (
chk_relog_request()
): 如果有 MQTT 重新登錄請求:- 嘗試獲取數據機的互斥鎖。
- 如果成功,則切換到
_AT_SESSION_LOG_OUT_IN
,並將_connected
設為 0。
- 平衡 GPS 數據讀取和訊息輪詢: 這部分邏輯比較複雜,旨在交替執行 GPS 請求和輪詢接收到的數據,以避免阻塞:
- 高頻率同時觸發 (
chk_timeup_ms(timer1ms,at_cmd_poll_gps_tick, _modem.at_gps_read_time) && chk_timeup_ms(timer1ms, at_cmd_polling_tick, 5*1000)
): 如果 GPS 讀取時間和輪詢時間都到期:- 嘗試獲取互斥鎖。
- 根據
_prev_action_gps
的值,決定是切換到_AT_SESSION_POLLING
(輪詢) 還是_AT_SESSION_GET_GPS
(獲取 GPS),並更新_prev_action_gps
。
- 僅輪詢時間到期 (
chk_timeup_ms(timer1ms, at_cmd_polling_tick, 5*1000)
): 如果只有輪詢時間到期:- 嘗試獲取互斥鎖。
- 切換到
_AT_SESSION_POLLING
,並將_prev_action_gps
設為 0。
- 僅 GPS 時間到期 (
chk_timeup_ms(timer1ms,at_cmd_poll_gps_tick, _modem.at_gps_read_time)
): 如果只有 GPS 時間到期:- 嘗試獲取互斥鎖。
- 切換到
_AT_SESSION_GET_GPS
,並將_prev_action_gps
設為 1。
- 高頻率同時觸發 (
cmd_processing(COMMAND_STATE_INFO *info)
(輔助函式但與主邏輯緊密相關)
►
功能: 這是執行單個 AT 命令的核心。
邏輯: 根據 info->state
(當前命令狀態) 進行處理:
_AT_CMD_TO_SEND
: 準備並發送 AT 命令到 UART 0。如果命令類型是_CMD_MODE_PUB
,_CMD_MODE_DOWN
,_CMD_MODE_UP
,則需要處理 UUID 或將 IoT 數據轉換為十六進制格式發送。_AT_CMD_WAIT_TO_RECEIVE
: 等待 UART 0 接收到足夠的數據 (通過判斷 \r\n 的數量)。如果超時,則將info->status
設為STATUS_CMD_TIMEOUT
。_AT_CMD_TO_RECEIVE
: 檢查接收到的數據是否包含 "OK" 或 "ERROR"。- 如果包含 "OK",表示命令成功,清除錯誤計數,複製數據到
platformrxbuffer
,並將狀態切換到_AT_CMD_WAIT_TO_SEND
。 - 如果包含 "ERROR",增加錯誤計數,如果錯誤是 MQTT 登錄相關且不影響後續流程,則切換到
_AT_CMD_WAIT_TO_SEND
;否則切換到_AT_CMD_ERROR_RESEND
。 - 如果超時,則將
info->status
設為STATUS_CMD_TIMEOUT
。
- 如果包含 "OK",表示命令成功,清除錯誤計數,複製數據到
_AT_CMD_WAIT_TO_SEND
: 等待一段時間後,將狀態切換到_AT_CMD_TO_SEND
並增加命令索引,準備發送下一個命令。_AT_CMD_ERROR_RESEND
: 等待一段時間後,將狀態切換到_AT_CMD_TO_SEND
(重發當前命令)。
chk_cmd_response()
(輔助函式但與主邏輯緊密相關)
►
功能: 解析 platformrxbuffer
中接收到的 AT 命令響應,並根據響應內容更新 _modem
結構中的相關資訊 (如 UUID, 訊號品質, RTC 時間, GPS 數據)。
邏輯: 根據解析結果返回 CMD_STATUS_OK
, CMD_STATUS_BAD_SQ
或 CMD_STATUS_BAD_GPS
。
switchATSessionK()
, switchATSession()
和 restoreToOrgSession()
(會話切換輔助函式)
►
switchATSessionK(AT_SESSION_STATES session)
:切換到新的 AT 會話,並保留當前會話的狀態資訊,以便後續恢復。switchATSession(AT_SESSION_STATES session)
:切換到新的 AT 會話,不保留當前會話的狀態資訊。restoreToOrgSession()
:恢復到之前保存的 AT 會話狀態。
modem_reset()
(數據機硬體重置輔助函式)
►
功能: 執行數據機的硬體重置,包括 GPIO 控制電源和重置引腳,並初始化 _modem
結構中的所有相關變數。
acquireSemaObj()
和 releaseSemaObj()
(互斥鎖輔助函式)
►
功能: 用於實現對數據機操作的互斥鎖,確保在同一時間只有一個模組可以控制數據機,避免競爭條件。
輔助函式與資料結構查詢
請從左側列表選擇一個項目,或在上方搜尋。
核心資料結構定義
以下是 4G LTE 數據機狀態機中使用的核心資料結構定義,您可以展開查看詳細內容。
typedef enum COMMAND_STATUS
►
功能: 定義 AT 命令的執行狀態。
typedef enum
{
STATUS_CMD_NONE=0,
STATUS_CMD_OK,
STATUS_CMD_ERROR,
STATUS_CMD_TIMEOUT,
STATUS_CMD_IN_PROCESS,
} COMMAND_STATUS;
STATUS_CMD_NONE
: 無狀態,或初始狀態。STATUS_CMD_OK
: 命令執行成功。STATUS_CMD_ERROR
: 命令執行失敗或返回錯誤。STATUS_CMD_TIMEOUT
: 命令執行超時。STATUS_CMD_IN_PROCESS
: 命令正在處理中。
typedef enum AT_SESSION_STATES
►
功能: 定義數據機狀態機的各個會話階段。
typedef enum
{
_AT_SESSION_INIT =0,
_AT_SESSION_RESET, //1
_AT_SESSION_CHK_SQ, //2 check signal quality when error or timeout happens.
_AT_SESSION_WAIT_TO_CHK_SQ, //3
_AT_SESSION_FREE_RUN, //4
_AT_SESSION_TX, //5 MQTT up send json message
_AT_SESSION_WAIT_TO_GET_GPS, //6
_AT_SESSION_GET_GPS, //7
_AT_SESSION_LOG_OUT_IN, //8
_AT_SESSION_POLLING, //9
_AT_SESSION_FREE_RUN_1, //10
}AT_SESSION_STATES;
_AT_SESSION_INIT
: 初始化會話。_AT_SESSION_RESET
: 數據機重置後等待階段。_AT_SESSION_CHK_SQ
: 檢查訊號品質會話。_AT_SESSION_WAIT_TO_CHK_SQ
: 等待檢查訊號品質的延遲階段。_AT_SESSION_FREE_RUN
: 自由運行階段 (釋放鎖)。_AT_SESSION_TX
: 數據上傳 (MQTT) 會話。_AT_SESSION_WAIT_TO_GET_GPS
: 等待獲取 GPS 或輪詢數據的決策階段。_AT_SESSION_GET_GPS
: 獲取 GPS 數據會話。_AT_SESSION_LOG_OUT_IN
: MQTT 登出再登入會話。_AT_SESSION_POLLING
: 輪詢接收數據會話。_AT_SESSION_FREE_RUN_1
: 自由運行子階段 (短暫延遲)。
typedef enum AT_CMD_STATES
►
功能: 定義單個 AT 命令的執行狀態,用於 cmd_processing()
函式。
typedef enum
{
_AT_CMD_NONE = 0,
_AT_CMD_TO_SEND,
_AT_CMD_TO_RECEIVE,
_AT_CMD_TO_RECEIVE1,
_AT_CMD_WAIT_TO_SEND,
_AT_CMD_ERROR_RESEND,
_AT_CMD_WAIT_TO_RECEIVE,
}AT_CMD_STATES;
_AT_CMD_NONE
: 無命令狀態。_AT_CMD_TO_SEND
: 準備發送命令。_AT_CMD_TO_RECEIVE
: 準備接收響應。_AT_CMD_TO_RECEIVE1
: 另一種接收響應狀態。_AT_CMD_WAIT_TO_SEND
: 等待下次發送命令。_AT_CMD_ERROR_RESEND
: 錯誤後等待重發。_AT_CMD_WAIT_TO_RECEIVE
: 等待接收響應數據。
typedef enum _CMD_MODES
►
功能: 定義 AT 命令的處理模式,影響命令的發送和響應解析方式。
typedef enum {
_CMD_MODE_0 = 0, //normal command, just send it to modem
_CMD_MODE_PUB,
_CMD_MODE_DOWN,
_CMD_MODE_UP,
_CMD_MODE_P0,
_CMD_MODE_CSQ,
_CMD_MODE_TM,
_CMD_MODE_E, //ignore the error
_CMD_MODE_TEST,
_CMD_MODE_GPS,
_CMD_MODE_TX,
_CMD_MODE_POLL, //poll reading the data of Link Commands from uBlox catched buffer.
_CMD_MODE_MQTTLOGIN, //=== 20241111 handel check mqtt login === "+UUMQTTC: 1 ,1"
_CMD_MODE_END,
}_CMD_MODES;
_CMD_MODE_0
: 普通命令,直接發送。_CMD_MODE_PUB
: MQTT 發布相關命令。_CMD_MODE_DOWN
: MQTT 訂閱下載相關命令。_CMD_MODE_UP
: MQTT 上傳相關命令。_CMD_MODE_P0
: 可能用於特定參數設定或檔案操作。_CMD_MODE_CSQ
: 查詢訊號品質命令。_CMD_MODE_TM
: 查詢時間命令。_CMD_MODE_E
: 忽略錯誤的命令模式。_CMD_MODE_TEST
: 測試模式命令。_CMD_MODE_GPS
: GPS 相關命令。_CMD_MODE_TX
: 數據傳輸命令。_CMD_MODE_POLL
: 輪詢 Link Command 數據命令。_CMD_MODE_MQTTLOGIN
: MQTT 登錄檢查命令。_CMD_MODE_END
: 命令集的結束標誌。
typedef struct AT_COMMANDS
►
功能: 定義單個 AT 命令的結構,包括其模式、延遲時間和命令字串。
typedef struct{
_CMD_MODES mode;
uint8_t sleeps;
char *command;
} AT_COMMANDS;
mode
: 命令的處理模式 (參考_CMD_MODES
)。sleeps
: 命令發送後的延遲時間或切換到的下一個會話狀態。command
: AT 命令字串。
typedef struct COMMAND_STATE_INFO
►
功能: 儲存數據機狀態機的當前運行資訊。
typedef struct{
AT_SESSION_STATES session;
AT_SESSION_STATES prev_session;
AT_CMD_STATES state;
uint8_t index;
uint8_t prev_index;
COMMAND_STATUS status;
AT_COMMANDS* cmdSet;
uint8_t signal_bad; //the quality of 4G signal, 0: good, 1:bad
}COMMAND_STATE_INFO;
session
: 當前會話狀態。prev_session
: 上一個會話狀態 (用於恢復)。state
: 當前命令執行狀態。index
: 當前命令在命令集中的索引。prev_index
: 上一個命令的索引。status
: 當前命令的執行結果狀態。cmdSet
: 指向當前使用的 AT 命令集陣列。signal_bad
: 4G 訊號品質標誌 (0: 好, 1: 差)。
typedef struct GPS_LOCATION
►
功能: 儲存 GPS 定位資訊。
typedef struct {
char utcTime[16];
char utcDate[16];
char latitude[32];
char longitude[32];
} GPS_LOCATION;
utcTime
: UTC 時間字串。utcDate
: UTC 日期字串。latitude
: 緯度字串。longitude
: 經度字串。
typedef struct MODEM_DATA
►
功能: 數據機的整體數據和狀態儲存結構,包含了數據機運行所需的所有關鍵資訊。
typedef struct MODEM_DATA
{
uint8_t modem_uuid_md5[33];
uint8_t lte_4G_TX_flag;
uint8_t lte_4G_TX_data[UART_TX_RX_SIZE*2+100];
uint8_t lte_4G_RX_index1;
uint8_t lte_4G_RX_index2;
uint8_t lte_4G_RX_data[LTE_4G_RX_DATA_COUNT][UART_TX_RX_SIZE];
uint8_t error_count;
uint8_t relog_request;
uint8_t chk_sq_cnt;
COMMAND_STATE_INFO at_cmd_info;
uint8_t lte_4G_gps_first_flag;
uint8_t lte_4G_gps_read_flag;
uint32_t lte_4G_gps_tick;
GPS_LOCATION gpsLoc;
uint32_t at_gps_read_time;
UTC_TIME rtc_time;
uint8_t chk_mqtt_login;
uint8_t lte_4G_csqval;
} MODEM_DATA;
modem_uuid_md5
: 數據機的 UUID (MD5 格式)。lte_4G_TX_flag
: 4G 數據發送標誌。lte_4G_TX_data
: 用於發送 IoT 數據的緩衝區。lte_4G_RX_index1
,lte_4G_RX_index2
: 接收數據緩衝區的索引。lte_4G_RX_data
: 接收到的訊息列表。error_count
: 錯誤計數。relog_request
: 重新登錄請求標誌。chk_sq_cnt
: 檢查訊號品質的計數。at_cmd_info
: 數據機狀態機的當前資訊。lte_4G_gps_first_flag
,lte_4G_gps_read_flag
,lte_4G_gps_tick
: GPS 讀取相關的標誌和計時。gpsLoc
: GPS 定位資訊。at_gps_read_time
: GPS 讀取時間間隔。rtc_time
: RTC 時間資訊。chk_mqtt_login
: MQTT 登錄狀態 (1: 登錄, 0: 登出)。lte_4G_csqval
: 4G 訊號品質 (CSQ 值)。
typedef enum CMD_STATUS
(不同於 COMMAND_STATUS
的狀態)
►
功能: 定義命令響應的狀態,特別用於 chk_cmd_response()
函式的返回結果。
typedef enum {
CMD_STATUS_OK = 0,
CMD_STATUS_BAD_SQ,
CMD_STATUS_BAD_GPS,
}CMD_STATUS;
CMD_STATUS_OK
: 命令響應正常。CMD_STATUS_BAD_SQ
: 訊號品質不佳。CMD_STATUS_BAD_GPS
: GPS 數據不佳。
以下是數據機初始化、檢查訊號、獲取 GPS、發送數據、登出登入和輪詢等不同會話所使用的 AT 命令集定義。
這些陣列包含了 _CMD_MODES
、sleeps
(延遲或下一會話) 和實際的 command
字串。
例如:
static AT_COMMANDS atCmdInit[]={
// ...
{_CMD_MODE_CSQ ,2 ,"AT+CSQ\r\n"},
{_CMD_MODE_TM ,2 ,"AT+CCLK?\r\n"},
// ...
{_CMD_MODE_END ,_AT_SESSION_FREE_RUN ,""}
};
static AT_COMMANDS atCmdChkSQ[] = {
{_CMD_MODE_CSQ ,1 ,"AT+CSQ\r\n"},
{_CMD_MODE_END ,99 ,""} //back to previous session
};
static AT_COMMANDS atCmdGetGPS[] = {
{_CMD_MODE_TM ,1 ,"AT+CCLK?\r\n"},
{_CMD_MODE_GPS ,1 ,"AT+UGRMC?\r\n"},
{_CMD_MODE_END ,_AT_SESSION_FREE_RUN ,""}
};
static AT_COMMANDS atTxData[] = {
{_CMD_MODE_TM ,1 ,"AT+CCLK?\r\n"},
{_CMD_MODE_UP ,1 ,"AT+UMQTTC=2,0,0,1,\"EMOTO/DEV/"},
{_CMD_MODE_END ,_AT_SESSION_FREE_RUN ,""}
};
static AT_COMMANDS atCmdLogOutIn[] = {
{_CMD_MODE_0 ,2 ,"AT+UMQTTC=0\r\n"},
{_CMD_MODE_0 ,2 ,"AT+UMQTTC=1\r\n"},
{_CMD_MODE_DOWN ,2 ,"AT+UMQTTC=4,0,\"EMOTO/DEV/"},
{_CMD_MODE_PUB ,2 ,"AT+UMQTTC=4,0,\"EMOTO/DEV/"},
{_CMD_MODE_END ,_AT_SESSION_FREE_RUN ,""}
};
static AT_COMMANDS atCmdPolling[] = {
{_CMD_MODE_TM ,1 ,"AT+CCLK?\r\n"},
{_CMD_MODE_POLL ,1 ,"AT+UMQTTC=6\r\n"},
{_CMD_MODE_END ,_AT_SESSION_FREE_RUN ,""}
};
static void* atCommands[] = {
atCmdInit, //_AT_SESSION_INIT
0, //_AT_SESSION_RESET
atCmdChkSQ, //_AT_SESSION_CHK_SQ
0, //_AT_SESSION_WAIT_TO_CHK_SQ
0, //_AT_SESSION_FREE_RUN
atTxData, //_AT_SESSION_TX
0, //_AT_SESSION_WAIT_TO_GET_GPS
atCmdGetGPS, //_AT_SESSION_GET_GPS
atCmdLogOutIn, //_AT_SESSION_LOG_OUT_IN
atCmdPolling, //_AT_SESSION_POLLING
0 //_AT_SESSION_FREE_RUN1
};
沒有留言:
張貼留言