1. 引言

    用過幾款GPRS模塊,也從淘寶上買過多個(gè)GPRS模塊,一般的都會(huì)送一個(gè)驅(qū)動(dòng)程序和使用demo,但是代碼質(zhì)量都較低。

    回頭看了下幾年前使用的GPRS代碼,從今天的角度來看,也就是買模塊贈(zèng)送一個(gè)免費(fèi)demo的那種水平,甚是汗顏。

    GPRS模塊驅(qū)動(dòng)主要是串口驅(qū)動(dòng),其本質(zhì)是字符串處理,本文就從對比下幾種常見的驅(qū)動(dòng)方式。

 

2. 版本1--初學(xué)者的驅(qū)動(dòng)

    思路:

  1. 串口接收使用中斷,收到數(shù)據(jù)放到全局buffer。

  2. 發(fā)送前清空接收buffer。

  3. 拼接字符串,然后從串口發(fā)送出去。

  4. 設(shè)定一個(gè)等待時(shí)間,然后while(1)不停的查看接收buffer里面是否有需要的字符串出現(xiàn),即是否得到需要的響應(yīng)。

  5. 初始化過程使用一個(gè)簡單的狀態(tài)機(jī)輪轉(zhuǎn),一步通過再進(jìn)行下一步。

   下面是一個(gè)我曾經(jīng)用過的例子,問題很明顯:

  1. 難維護(hù),函數(shù)耦合度太高,簡單的堆功能,功能模塊沒有劃分。

  2. 低效,發(fā)送需要CPU停下來一個(gè)一個(gè)字符的發(fā)送,接收還要延時(shí)一段時(shí)間等待GPRS模塊回復(fù)足夠多的數(shù)據(jù)。

  3. 接收buffer只是簡單的共享全局變量,沒有雙buffer切換也沒有讀寫互斥。

     比如每次發(fā)送前清空buffer然后發(fā)送命令,用來判別此次接收都是對本次發(fā)送的命令的響應(yīng)。

  4. 不能精細(xì)控制,AT指令響應(yīng)檢查全部放到一個(gè)函數(shù)里面處理,必然造成有些AT響應(yīng)的回復(fù)無法區(qū)分對應(yīng)哪個(gè)指令。

 

網(wǎng)絡(luò)連接的驅(qū)動(dòng):

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

(gprs_start==)       =uint8_t gprs_connect(uint8_t *= strBuf[, (gprs_mgr.stat == GPRS_GET_CSQ)                 (gprs_get_csq() > =  (gprs_mgr.stat == GPRS_WAIT_REGNET)         (!=  (gprs_mgr.stat == GPRS_CONFIG_PARA)         (!=  (gprs_mgr.stat == GPRS_CONFIG_SOCKET)     (!gprs_config_socket())                    
            gprs_mgr.stat =                                          (num=;num<;num++  (gprs_mgr.stat == GPRS_DATA_RW)            =,len+=

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

發(fā)送函數(shù),串口輸出加上查詢式解析:

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

//---------------------------------------------------------// 函數(shù)名稱:uint8 gprs_send_cmd(char* pcmd)// 函數(shù)功能:gprs命令字發(fā)送函數(shù)// 輸入?yún)?shù): pcmd,要發(fā)送的命令// 返回參數(shù)://           0   ,命令發(fā)送成功//           1   ,命令發(fā)送失敗//---------------------------------------------------------uint8_t gprs_send_cmd(char* pcmd)
{
    uint16_t i;
    uint8_t ret=0, *GSM_ReturnInfo;        
    memset(GSM_info, 0, sizeof(GSM_info));      // 清除串口緩沖區(qū)
    GSM_Info_CNT=0;                             // 清除串口接收計(jì)數(shù)
    debug_print(pcmd);                         //發(fā)送的命令,調(diào)試輸出                   

    while(*pcmd)                              // 發(fā)送命令    {        while(USART_GetFlagStatus(GPRS_USART, USART_FLAG_TXE)==0);
        USART_SendData(GPRS_USART, *pcmd++); 
    }

    delay_ms(1000);    
    GSM_ReturnInfo=GPRS_Get_Info();    for (i = 0; i < 15; i++)                  //15s 等待    {
        delay_ms(500);        if (strstr(GSM_ReturnInfo, "OK"))            // 命令發(fā)送成功        {
            ret = 0;            break;
        }        else if (strstr(GSM_ReturnInfo, "CONNECT"))
        {
            ret = 0;            break;
        }        else if (strstr(GSM_ReturnInfo, "ERROR"))    // 命令發(fā)送失敗        {
            ret = 1;            break;
        }        else
            ret = 1;
        
        delay_ms(500);
    }
    debug_print(GSM_ReturnInfo);                            // 打印調(diào)試信息

    return ret;
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

3. 版本2--有模塊化思想的驅(qū)動(dòng)

  大體流程和第一種差別不大,但是在幾個(gè)關(guān)鍵點(diǎn)上有巨大改進(jìn),比如函數(shù)的模塊化和中斷的使用。

  1. 發(fā)送和接收都用中斷提高效率,不再使用查詢方式。

  2. 拼湊發(fā)送字符串處理和串口數(shù)據(jù)發(fā)送過程分開。

  3. 初步的異常處理,如斷線重連、重啟等。

   比上一版本進(jìn)化很多,但是也有問題:

  1. 模塊已經(jīng)劃分,但是在邏輯層次上區(qū)分不明顯,如下面例子中的發(fā)送的AT指令的響應(yīng)處理,就和發(fā)送函數(shù)混在一起。

  2.全局變量問題,比如記錄GPRS模塊當(dāng)前狀態(tài)標(biāo)識,可以多處進(jìn)行修改。

 

比較獨(dú)立的功能做一定的提取,比如注冊網(wǎng)絡(luò)、SIM卡檢查等功能函數(shù)封裝起來。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

uint8_t gprs_reg_network(void)
{
    uint8_t ret, *uart_buf;

    ret = gprs_send_cmd("AT+CGREG?\r\n");    if (ret == 0)                                //命令發(fā)送成功    {    
        uart_buf = get_gprs_rsp();

        ret = 1;        if (strstr(uart_buf, "+CGREG: 0,5"))    // 已注冊,本地網(wǎng)
            ret = 0;        if (strstr(uart_buf, "+CGREG: 1,5"))    // 已注冊,本地網(wǎng)
            ret = 0;    
        if (strstr(uart_buf, "+CGREG: 0,1"))    // 已注冊,漫游
            ret = 0;        if (strstr(uart_buf, "+CGREG: 1,1"))    // 已注冊,漫游
            ret = 0;        return ret;
    }    else
        return 1;
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

或者接收響應(yīng)的buffer不使用全局變量,而在發(fā)送函數(shù)參數(shù)中直接傳入接收數(shù)組指針。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 gprs_check_sim( err =  rsp_buf[= gprs_send_atcmd(,rsp_buf,(strstr(rsp_buf,)== =(retry++ > =(err !=

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 

4. 版本3--按邏輯層次劃分功能

  分兩個(gè)層次來實(shí)現(xiàn)需求,先是邏輯層次劃分功能,然后在具體是實(shí)現(xiàn)層次按照功能單一原則編碼。

  該方法可以用在產(chǎn)品中,驅(qū)動(dòng)代碼的思路清晰,高效且易維護(hù)。

  1. 使用RTOS來,提升CPU利用率,尤其是等待AT指令回復(fù)的過程中,系統(tǒng)可以執(zhí)行其他任務(wù)。

  2. GPRS操作的本質(zhì)是寫字符串(發(fā)AT指令),然后讀回復(fù)的字符串(讀指令響應(yīng)),那么可以從這個(gè)角度來設(shè)計(jì)驅(qū)動(dòng)。

      3. 屏蔽硬件細(xì)節(jié),在寫GPRS驅(qū)動(dòng)和邏輯處理的過程中,硬件讀寫都抽象成一個(gè)字符處理函數(shù)。

 

例1:查詢SIM卡,發(fā)送AT指令,然后等待接收響應(yīng)字符串。

函數(shù)接口就負(fù)責(zé)填充期待的字符串,如果指定時(shí)間內(nèi)沒等到字符串出現(xiàn)就認(rèn)為出錯(cuò),具體怎么發(fā)出去怎么收到回復(fù)都是更加底層的處理。

具體的響應(yīng)由SIM800_WaitResponse函數(shù)來處理,該函數(shù)自動(dòng)匹配指定字符串,參數(shù)500是超時(shí)時(shí)間,如果匹配成功會(huì)提前退出,否則等待500ms然后回復(fù)超時(shí)。

由于使用的Free RTOS,那么該函數(shù)不是阻塞性的,不會(huì)影響CPU執(zhí)行其他的任務(wù)。如果模塊很快響應(yīng)了指令,那么還可以提前結(jié)束超時(shí)等待。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

/*******************************************************************************
* Function Name  : SIM800_Check_SIM
* Description    : None
* Input          : None
* Output         : None
* Return         : 1-OK, 0-NG
* Attention      : None
*******************************************************************************/
 (SIM800_WaitResponse(,

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

例2:注冊GSM網(wǎng)絡(luò),發(fā)送AT指令,然后等待接收響應(yīng)字符串。

有些指令回復(fù)參數(shù)種類較多,如果寫成上面的形式可能比較臃腫,可以把接收到的數(shù)據(jù)先放到buffer,然后從中搜索需要的字符。

SIM800_ReadResponse完成這個(gè)功能,但該函數(shù)要一直等待直至最大超時(shí)時(shí)間。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

/*******************************************************************************
* Function Name  : SIM800_GsmCheck
* Description    : 檢查是否注冊到GSM網(wǎng)絡(luò)
* Input          : None
* Output         : None
* Return         : 1—OK, 0-NG
* Attention      : None
*******************************************************************************/uint8_t SIM800_GsmCheck(void)
{
    uint8_t rtn = 0;
    SIM800_SendATCmd("AT+CREG?\r\n");
    SIM800_ReadResponse(gprs_rsp_buffer, sizeof(gprs_rsp_buffer), 500);    if (strstr(gprs_rsp_buffer, "+CREG: 0,1") != NULL)
    {
        rtn = 1;    
    }    else if (strstr(gprs_rsp_buffer, "+CREG: 0,5") != NULL)
    {
        rtn = 1;
    }    else rtn = 0;
    
    vTaskDelay(200);    return rtn; 
}

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

例3:AT指令的發(fā)送,不需要等待硬件的響應(yīng),啟動(dòng)硬件發(fā)送標(biāo)識即可,具體發(fā)送由中斷或DMA去操作,代碼更加高效。

剩下的發(fā)送和接收都是CPU硬件操作,在這一層次CPU并不識別數(shù)據(jù)的含義,僅是把數(shù)據(jù)從串口輸出或讀入。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 SIM800_SendATCmd( uint8_t *=  (uint8_t *==

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)

 例4:如果模塊已經(jīng)連接到服務(wù)器,那么需要應(yīng)用數(shù)據(jù)的發(fā)送。

常見的過程分3步:

1)應(yīng)用數(shù)據(jù)預(yù)處理打包;

2)GPRS模塊發(fā)送數(shù)據(jù)可能需要一個(gè)特殊的指令來啟動(dòng),作用是告訴模塊,下面發(fā)過來的是用戶數(shù)據(jù),不是控制字了;

3)啟動(dòng)數(shù)據(jù)包發(fā)送(實(shí)際是初始化發(fā)送過程邏輯控制相關(guān)的標(biāo)志和啟動(dòng)硬件發(fā)送標(biāo)志)。

下面驅(qū)動(dòng)函數(shù)處理了用戶數(shù)據(jù)的發(fā)送,在發(fā)送AT+CIPSEND后,2s內(nèi)收到">" 回復(fù)就可以開始發(fā)送數(shù)據(jù)。

iOS培訓(xùn),Swift培訓(xùn),蘋果開發(fā)培訓(xùn),移動(dòng)開發(fā)培訓(xùn)


http://www.cnblogs.com/pingwen/p/6681955.html