oFono学习笔记——GATChat(2):发送AT命令
摘要:
本文主要描述了GAtChat如何發送AT命令的全過程
1. GAtChat AT命令發送接口
在GAtChat庫當中,根據AT命令返回結果的不同,GAtChat定義了四種不同的發送接口:一般發送接口,表單發送接口,PDU表單發送接口以及等待提示發送接口。
1 /*一般發送接口*/ 2 guint g_at_chat_send(GAtChat *chat, const char *cmd, 3 const char **prefix_list, GAtResultFunc func, 4 gpointer user_data, GDestroyNotify notify) 5 { 6 return at_chat_send_common(chat->parent, chat->group, 7 cmd, prefix_list, 0, NULL, 8 func, user_data, notify); 9 } 10 11 /*表單發送接口*/ 12 guint g_at_chat_send_listing(GAtChat *chat, const char *cmd, 13 const char **prefix_list, 14 GAtNotifyFunc listing, GAtResultFunc func, 15 gpointer user_data, GDestroyNotify notify) 16 { 17 if (listing == NULL) 18 return 0; 19 20 return at_chat_send_common(chat->parent, chat->group, 21 cmd, prefix_list, 0, 22 listing, func, user_data, notify); 23 } 24 25 /*PDU表單發送接口*/ 26 guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd, 27 const char **prefix_list, 28 GAtNotifyFunc listing, GAtResultFunc func, 29 gpointer user_data, GDestroyNotify notify) 30 { 31 if (listing == NULL) 32 return 0; 33 34 return at_chat_send_common(chat->parent, chat->group, 35 cmd, prefix_list, 36 COMMAND_FLAG_EXPECT_PDU, 37 listing, func, user_data, notify); 38 } 39 40 /*等待提示發送接口*/ 41 guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd, 42 const char **prefix_list, 43 GAtResultFunc func, 44 gpointer user_data, 45 GDestroyNotify notify) 46 { 47 return at_chat_send_common(chat->parent, chat->group, 48 cmd, prefix_list, 49 COMMAND_FLAG_EXPECT_SHORT_PROMPT, 50 NULL, func, user_data, notify); 51 }1.1 一般發送接口
應對絕大部分的AT命令。這類AT命令的特點是命令的回復比較簡單。
比如:ATE0。關閉串口回顯,這種命令只會返回OK或者ERROR。
1.2 表單發送接口
應對返回結果是表單類型的AT命令,這類AT命令的返回結果的特點是,返回的每一條表單結果都有一個固定的prefix(前綴)
比如:用AT+CPBR=1,99來讀取電話本,命令的返回結果為:
AT+CPBR=1,99
+CPBR: 1,"931123456",129,"Ilkka"
+CPBR: 2,"9501234567",129,""
+CPBR: 4,"901234567",129,"Hesari"
OK
1.3 PDU表單發送接口
應對返回結果是表單并帶有PDU數據的AT命令,這類AT命令的特點是返回結果不僅有固定的前綴,還有PDU信息
比如:利用AT+CMGL讀取已經收到的短信,并以PDU方式顯示出來。起返回結果是:
AT+CMGL=4
+CMGL: 1,0,,39
07911326040011F5240B911326880736F40000111081017362401654747A0E4ACF41F4329E0E6A97E7F3F0B90C8A01
+CMGL: 2,0,,39
07911326040011F5240B911326880736F40000111081017323401654747A0E4ACF41F4329E0E6A97E7F3F0B90C9201
OK
1.4 等待提示發送接口
應對的是AT命令發送過程中需要等待GSM/GPRS模塊確認再繼續發送的命令。
比如:利用AT+CMGS命令發送短信,當命令發出后,需要等待模塊返回'>'確認后才能繼續發送短信內容。
AT+CMGS=17
>
0891683108705505f011000b813120882624f700f1ff0361f118
+CMGS: 2
OK
2. at_chat_send_common分析
在上面說到的發送接口中,我們可以看到,四個接口都是對at_chat_send_common進行的一個封裝。可見發送AT命令的核心就在這個函數中:
1 static guint at_chat_send_common(struct at_chat *chat, guint gid, 2 const char *cmd, 3 const char **prefix_list, 4 guint flags, 5 GAtNotifyFunc listing, 6 GAtResultFunc func, 7 gpointer user_data, 8 GDestroyNotify notify) 9 { 10 struct at_command *c; 11 12 if (chat == NULL || chat->command_queue == NULL) 13 return 0; 14 15 /*創建AT命令*/ 16 c = at_command_create(gid, cmd, prefix_list, flags, listing, func, 17 user_data, notify, FALSE); 18 if (c == NULL) 19 return 0; 20 21 c->id = chat->next_cmd_id++; 22 23 /*將創建好的AT命令添加到發送隊尾*/ 24 g_queue_push_tail(chat->command_queue, c); 25 26 /*激活writer將AT命令發送給模塊*/ 27 if (g_queue_get_length(chat->command_queue) == 1) 28 chat_wakeup_writer(chat); 29 30 return c->id; 31 }通過分析這個函數我們可以知道AT命令是如何發送到BP中去的。首先,會創建一個AT命令結構體。如果結構體成功創建了,就把它添加到命令發送隊列的尾部。這個命令發送隊列,就是上一篇文章中提到的command_queue。最后,檢查發送隊列,如果隊列中又且只有一個命令等待發送就激活writer將AT命令發送給BP,否則說明現在writer已經在激活狀態,無需激活。可見command_queue的長度是激活writer的一個關鍵。
2.1 struct at_command
1 struct at_command { 2 char *cmd; 3 char **prefixes; 4 guint flags; 5 guint id; 6 guint gid; 7 GAtResultFunc callback; 8 GAtNotifyFunc listing; 9 gpointer user_data; 10 GDestroyNotify notify; 11 };這里對at_command中的字段做一些說明:
| cmd | 存儲AT命令 |
| prefixes | AT命令返回結果的前綴 |
| flags | AT命令的返回類型,一般情況下為0。當返回結果中包含PDU時,flags=COMMAND_FLAG_EXPECT_PDU。????????????? 當返回包含提示符時, flag=COMMAND_FLAG_EXPECT_SHORT_PROMPT。 |
| id | AT命令的ID(1~(2^16 - 1)),用來唯一標識每一條命令。當id=0時,說明這是一條weakup AT命令。 |
| gid | AT命令的組ID,表明該AT命令屬于那一個GAtChat |
| callback | 一般AT命令返回結果的處理回調函數 |
| listing | 表單AT命令的返回結果處理回調函數 |
| user_data | 用于裝載AT發送/返回的數據 |
| notify | AT命令被GAtChat從隊列中刪除后的通知函數 |
3.chat_wakeup_writer
下面我們來分析writer是如何weakup。代碼比較長,一下代碼只截取重要的部分進行說明
gatchat.c
1 static gboolean can_write_data(gpointer data); 2 3 static void chat_wakeup_writer(struct at_chat *chat) 4 { 5 g_at_io_set_write_handler(chat->io, can_write_data, chat); 6 }gatio.c
1 gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler, 2 gpointer user_data) 3 { 4 /*...*/ 5 io->write_handler = write_handler; 6 io->write_data = user_data; 7 8 if (io->use_write_watch == TRUE) 9 io->write_watch = g_io_add_watch_full(io->channel, 10 G_PRIORITY_HIGH, 11 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, 12 can_write_data, io, 13 write_watcher_destroy_notify); 14 else 15 io->write_watch = g_idle_add(call_blocking_read, io); 16 17 return TRUE; 18 } 19 20 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, 21 gpointer data) 22 { 23 GAtIO *io = data; 24 25 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) 26 return FALSE; 27 28 if (io->write_handler == NULL) 29 return FALSE; 30 31 return io->write_handler(io->write_data); 32 }?當調用chat_wakeup_writer時,GAtChat會將具體的AT命令發送函數通過g_at_io_set_write_handler注冊到GAtIO模塊當中。在g_at_io_set_write_handler之中,gatio會向主事件循環中添加一個寫監控(watch),當指定io口可以寫的時候,系統會自動調用gatio.c中的can_write_data函數,通過這個函數調用,GAtChat所定義的AT命令發送函數。
3.1 can_write_data 分析
通過上面的分析,發送和AT命令發送相關的細節集中在gatchat.c當中的can_write_data之中。所以下面對這個函數的關鍵部分做簡要分析:
1 static gboolean can_write_data(gpointer data) 2 { 3 cmd = g_queue_peek_head(chat->command_queue); 4 5 /*...*/ 6 7 if (chat->wakeup) { 8 if (chat->wakeup_timer == NULL) { 9 wakeup_first = TRUE; 10 chat->wakeup_timer = g_timer_new(); 11 12 } else if (g_timer_elapsed(chat->wakeup_timer, NULL) > 13 chat->inactivity_time) 14 wakeup_first = TRUE; 15 } 16 17 if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) { 18 cmd = at_command_create(0, chat->wakeup, none_prefix, 0, 19 NULL, wakeup_cb, chat, NULL, TRUE); 20 if (cmd == NULL) 21 return FALSE; 22 23 g_queue_push_head(chat->command_queue, cmd); 24 25 len = strlen(chat->wakeup); 26 27 chat->timeout_source = g_timeout_add(chat->wakeup_timeout, 28 wakeup_no_response, chat); 29 } 30 /*...*/ 31 32 bytes_written = g_at_io_write(chat->io, 33 cmd->cmd + chat->cmd_bytes_written, 34 towrite); 35 /*...*/ 36 /* 37 * If we're expecting a short prompt, set the hint for all lines 38 * sent to the modem except the last 39 */ 40 if ((cmd->flags & COMMAND_FLAG_EXPECT_SHORT_PROMPT) && 41 chat->cmd_bytes_written < len && 42 chat->syntax->set_hint) 43 chat->syntax->set_hint(chat->syntax, 44 G_AT_SYNTAX_EXPECT_SHORT_PROMPT); 45 46 /* Full command submitted, update timer */ 47 /*...*/ 48 49 return FALSE; 50 }當發現IO可寫時,Chat會從command_queue中取出AT命令(但不刪除),緊接著檢查模塊是否需要weakup,如果需要weakup操作,就自動生成一個weakup AT命令,然后將這條命令添加到發送隊列的隊頭位置,優先發送weakup命令激活模塊,否則發送一開始選中的AT命令。接下來進入真正的發送部分,將選定的AT命令通過g_at_io_write發送出去。最后,根據AT命令的返回類型,設置相對應的hint,完成AT命令的發送。
4.總結
以上就是AT命令在GATChat中發送的過程,如果發現其中存在錯誤的話,歡迎指正。
?
轉載于:https://www.cnblogs.com/zhx831/p/3166808.html
總結
以上是生活随笔為你收集整理的oFono学习笔记——GATChat(2):发送AT命令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python屏幕的交互(读取输出信息)i
- 下一篇: 配置札记