首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 串口程式設計實戰:基於西門子 TC35 模組的短訊應用開發

     編輯:關於VC++

前言

在前幾篇連載中,我們詳細探討了串口通訊的硬體原理、DOS 平台控制,以及基於 WIN32 API、ActiveX 控制項與第三方類別的串口程式設計技巧。作為本系列的最後一篇,我們將透過一個典型的實戰案例——西門子短訊服務模組 TC35 的串口控制,將理論知識落地為具體應用。

一、短訊控制終端:原理與硬體

對於頻繁使用簡訊(SMS)的用戶而言,手機鍵盤編輯訊息不僅繁瑣,還容易出錯且難以修改。若能透過電腦直接收發簡訊,將大幅提升效率。值得注意的是,此處所指的「電腦收發簡訊」並非透過網路平台(如早期的「網易短訊王」),而是直接控制運行 GSM 通訊系統的短訊終端,其收發原理與手機完全一致。事實上,大量垃圾簡訊正是利用此類終端設備批量發送的。

西門子 TC35 模組介紹

TC35 是西門子推出的 GSM 模組,集成了 GSM 基帶處理器、電源專用 IC、射頻電路與快閃記憶體等核心部件,負責處理音訊、數據與信令,內建軟體執行應用介面與所有 GSM 協定堆疊功能。其主要規格如下:

  • 支援中文簡訊,工作於 EGSM900/GSM1800 雙頻段
  • 供電範圍 3.3~5.5V,功耗分別為 2W (EGSM900) 與 1W (GSM1800)
  • 透過介面連接器與天線連接器分別連接 SIM 卡讀卡器與天線
  • 資料介面 (CMOS 電平) 透過 AT 指令雙向傳輸指令與數據,波特率 300bps~115.2kbps,自動波特率 1.2k~115.2kbps
  • 支援 Text 與 PDU 兩種格式,可透過 AT 指令或關機信號實現重啟與故障恢復

TC35 終端電路圖

核心 AT 指令集

TC35 的控制主要包含以下幾類指令:

1. 初始化指令

設定簡訊發送格式:AT+CMGF=1<CR>(1 代表 PDU 模式,<CR> 為回車符 0x0D)。指令正確時模組返回 <CRLF>OK<CRLF>。

2. 設定/讀取簡訊中心號碼

簡訊中心號碼由移動運營商提供。設定指令格式:AT+CSCA="+8613800531500"<CR>;讀取指令:AT+CSCA=?<CR>,模組返回 <CRLF>+CSCA:"8613800531500"<CRLF>。

3. 設定簡訊到達自動提示

指令格式:AT+CNMI=1,1,0,0,1<CR>。設定成功後,簡訊到達時模組會主動向串口發送:<CRLF>+CMTI:"SM",INDEX<CRLF>(INDEX 為訊息存儲位置)。

4. 發送簡訊流程 (PDU 模式)

  1. PC 端先發送指令 AT+CMGS=<length><CR>,其中 length 為 PDU 數據長度(不含 SMSC 資訊)。
  2. 等待模組返回 ASCII 字符 > (提示符)。
  3. 輸入 PDU 數據,以 <Z> (0x1A, Ctrl+Z) 作為結束符。
  4. 發送成功模組返回 <CRLF>OK<CRLF>。

5. 接收簡訊流程

  1. 簡訊到達時,串口收到 <CRLF>+CMTI:"SM",INDEX<CRLF>。
  2. PC 端發送讀取指令 AT+CMGR=INDEX<CRLF> 讀取單條 PDU 簡訊,或用 AT+CMGL<CRLF> 一次性讀取所有簡訊。

6. 刪除簡訊

為防止 SIM 卡存儲飽和,處理完畢後應刪除:AT+CMGD=INDEX<CR>,成功返回 <CRLF>OK<CRLF>。

二、程式實例:簡訊收發軟體實作

本範例專注於串口通訊核心邏輯,界面簡潔但功能完整,包含發送、接收、清空等操作。界面資源定義如下:

簡訊控制軟體界面

BEGIN
 EDITTEXT IDC_SMSCONTENT_EDIT,39,61,242,38,ES_AUTOHSCROLL
 PUSHBUTTON "發送 ",IDC_SEND_BUTTON,316,80,45,18
 GROUPBOX "接收短消息 ",IDC_STATIC,28,124,361,167
 LTEXT "對方手機號 ",IDC_STATIC,41,35,42,11
 EDITTEXT IDC_PHONENUM_EDIT,88,30,192,17,ES_AUTOHSCROLL
 PUSHBUTTON "清除 ",IDC_CLEAR_BUTTON,316,30,45,18
 GROUPBOX "發送短消息 ",IDC_STATIC,29,19,361,95
 LISTBOX IDC_RECVSMS_LIST,43,137,331,127,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
 PUSHBUTTON "接收 ",IDC_RECV_BUTTON,77,269,55,16
 PUSHBUTTON "清空 ",IDC_DELETEALL_BUTTON,273,268,45,14
END

核心資料結構

// 用戶資料編碼方式
#define GSM_7BIT 0
#define GSM_8BIT 4
#define GSM_UCS2 8

// 簡訊參數結構 (編碼/解碼共用)
typedef struct {
    char SCA[16];     // 簡訊服務中心號碼
    char TPA[16];     // 目標號碼或回覆號碼
    char TP_PID;      // 協定識別碼
    char TP_DCS;      // 資料編碼方案
    char TP_SCTS[16]; // 服務中心時間戳 (接收時用)
    char TP_UD[161];  // 用戶資料 (編碼前/解碼後)
    char index;       // 簡訊序號 (讀取時用)
} SM_PARAM;

發送簡訊核心函數 gsmSendMessage

該函數完整實現了 PDU 模式下的發送流程:

BOOL gsmSendMessage(const SM_PARAM *pSrc) {
    int nPduLength, nLength;
    unsigned char nSmscLength;
    char cmd[16], pdu[512], ans[128];

    nPduLength = gsmEncodePdu(pSrc, pdu);  // 編碼 PDU 串
    strcat(pdu, "\x1A");                    // Ctrl-Z 結束符
    gsmString2Bytes(pdu, &nSmscLength, 2); // 取 SMSC 長度
    nSmscLength++;                          // 加上長度位元組本身

    // 發送 AT+CMGS 指令 (長度不含 SMSC)
    sprintf(cmd, "AT+CMGS=%d\r", nPduLength / 2 - nSmscLength);
    WriteComm(cmd, strlen(cmd));
    nLength = ReadComm(ans, 128);

    // 檢查是否收到 ">" 提示符
    if (nLength == 4 && strncmp(ans, "\r\n> ", 4) == 0) {
        WriteComm(pdu, strlen(pdu)); // 發送 PDU 數據
        nLength = ReadComm(ans, 128);
        // 檢查是否有錯誤回應
        if (nLength > 0 && strncmp(ans, "+CMS ERROR", 10) != 0) {
            return TRUE;
        }
    }
    return FALSE;
}

接收簡訊核心函數 gsmReadMessage

使用 AT+CMGL 一次性讀取所有簡訊,並逐條解碼:

int gsmReadMessage(SM_PARAM* pMsg) {
    int nLength, nMsg = 0;
    char *ptr, cmd[16], ans[1024];

    sprintf(cmd, "AT+CMGL\r");
    WriteComm(cmd, strlen(cmd));
    nLength = ReadComm(ans, 1024);

    if (nLength > 0 && strncmp(ans, "+CMS ERROR", 10) != 0) {
        ptr = ans;
        while ((ptr = strstr(ptr, "+CMGL:")) != NULL) {
            ptr += 6; // 跳過 "+CMGL:"
            sscanf(ptr, "%d", &pMsg->index); // 讀取序號
            ptr = strstr(ptr, "\r\n");       // 找下一行
            ptr += 2;                        // 跳過 "\r\n"
            gsmDecodePdu(ptr, pMsg);         // PDU 解碼
            pMsg++; nMsg++;
        }
    }
    return nMsg;
}

刪除簡訊函數 gsmDeleteMessage

BOOL gsmDeleteMessage(const int index) {
    int nLength;
    char cmd[16], ans[128];
    sprintf(cmd, "AT+CMGD=%d\r", index);
    WriteComm(cmd, strlen(cmd));
    nLength = ReadComm(ans, 128);
    return (nLength > 0 && strncmp(ans, "+CMS ERROR", 10) != 0);
}

串口底層操作 (WIN32 API)

應用程式啟動/退出及收發流程中廣泛使用的串口函數:

HANDLE hComm;

BOOL OpenComm(const char *pPort, int nBaudRate, int nParity, int nByteSize, int nStopBits) {
    DCB dcb;
    COMMTIMEOUTS timeouts = {100, 0, 0, 0, 0}; // 讀字元間隔超時 100ms
    // ... 具體實現略 ...
}

PDU 編解碼輔助函數原型

受限篇幅,僅列出關鍵函數原型,完整實現請參考 GSM 標準文檔:

int gsmEncodeUcs2(const char *pSrc, unsigned char *pDst, int nSrcLength);
int gsmDecodeUcs2(const unsigned char *pSrc, char *pDst, int nSrcLength);
int gsmString2Bytes(const char *pSrc, unsigned char *pDst, int nSrcLength);
int gsmBytes2String(const unsigned char *pSrc, char *pDst, int nSrcLength);

三、總結

串口程式設計的核心在於通訊控制邏輯(發送、接收、握手)的掌握,具體應用領域反而是次要的。一旦掌握了底層原理,即可靈活應用於各類領域,本文的「短訊控制終端」僅是冰山一角。希望這個完整的實戰案例能幫助讀者將理論轉化為生產力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved