Windows 串口程式設計涉及複雜的 Win32 API (`CreateFile`, `SetCommState`, `WaitCommEvent`, `ReadFile`/`WriteFile` 重疊 I/O) 与多執行緒同步,開發門檻高、易出錯。**MOXA PcommPro** 將這些底層細節封裝為簡單的 `sio_xxx()` 函式庫 (DLL),讓開發者專注於業務邏輯。本文以 C++Builder 3/5/6/10+ 為例,示範從專案建立到完整收發程式的全流程。
| 元件 | Name | Caption/Text | 用途 |
|---|---|---|---|
| Button | btnOpen | 開啟埠 | 觸發 sio_open() |
| Button | btnSend | 發送資料 | 觸發 sio_write() |
| Button | btnClose | 關閉埠 | 觸發 sio_close() |
| Edit | edtSend | (空) | 發送緩衝區 |
| Edit | edtRecv | (空) | 接收顯示區 |
| ComboBox | cbPort | COM1~COM4 | 選擇通訊埠 (可擴充) |
| ComboBox | cbBaud | 2400,9600,19200,38400,57600,115200 | 鮑率 |
class TForm1 : public TForm {
...
private:
int nPort; // 0=COM1, 1=COM2...
AnsiString sSendData; // 發送字串
AnsiString sRecvData; // 接收字串
void __stdcall CntIrq(int port); // 中斷服務函數 (需 __stdcall)
};
void __fastcall TForm1::btnOpenClick(TObject *Sender)
{
nPort = cbPort->ItemIndex; // 0-based
if (sio_open(nPort) != 0) {
ShowMessage("開啟埠失敗!"); return;
}
// 參數對應:B2400=2400, P_NONE=無奇偶, BIT_7=7資料位, STOP_1=1停止位
// 實際應從 cbBaud/cbParity/cbDataBits/cbStopBits 讀取組合
int nBaud = B2400; // 範例固定 2400
int nMode = P_NONE | BIT_7 | STOP_1;
if (sio_ioctl(nPort, nBaud, nMode) != 0) {
ShowMessage("參數設定失敗!"); sio_close(nPort); return;
}
// 掛載中斷回調:當接收緩衝區有資料 (計數 >= 1) 觸發 CntIrq
void (__stdcall *pIrq)(int) = CntIrq;
if (sio_cnt_irq(nPort, pIrq, 1) != 0) {
ShowMessage("中斷掛載失敗!"); sio_close(nPort); return;
}
btnOpen->Enabled = false;
btnClose->Enabled = true;
btnSend->Enabled = true;
}
void __fastcall TForm1::btnSendClick(TObject *Sender)
{
sSendData = edtSend->Text;
if (sSendData.IsEmpty()) return;
// AnsiString → char* (Pcomm 需 char 緩衝區)
char* pBuf = new char[sSendData.Length() + 1];
strcpy(pBuf, sSendData.c_str());
int nSent = sio_write(nPort, pBuf, strlen(pBuf));
if (nSent < 0) ShowMessage("發送失敗!");
else edtSend->Clear();
delete[] pBuf;
}
void __stdcall TForm1::CntIrq(int port)
{
// 此函數在 Pcomm 內部執行緒呼叫,**嚴禁直接操作 VCL 控制項**!
// 僅讀取資料到成員變數,再用 PostMessage/ Synchronize 通知主執行緒更新 UI。
char buf[256];
int nRead = sio_read(port, buf, sizeof(buf) - 1);
if (nRead > 0) {
buf[nRead] = '\0';
sRecvData = AnsiString(buf); // 暫存
// 發送自訂訊息給主視窗,觸發 UI 更新
PostMessage(Handle, WM_USER + 1, 0, 0);
}
}
// 主執行緒訊息處理器 (需在類別宣告加入 MESSAGE_HANDLER)
void __fastcall TForm1::OnRecvData(TMessage &Msg)
{
edtRecv->Text = sRecvData; // 安全更新 UI
}
void __fastcall TForm1::btnCloseClick(TObject *Sender)
{
sio_close(nPort);
btnOpen->Enabled = true;
btnClose->Enabled = false;
btnSend->Enabled = false;
}
| 函式 | 原型 | 說明 |
|---|---|---|
| sio_open | `int sio_open(int port)` | 開啟埠,成功回 0。port: 0=COM1... |
| sio_close | `int sio_close(int port)` | 關閉埠 |
| sio_ioctl | `int sio_ioctl(int port, int baud, int mode)` | 設定參數。baud: B300~B115200。mode: 位元組組合 (P_NONE/P_ODD/P_EVEN \| BIT_5~BIT_8 \| STOP_1/STOP_2) |
| sio_write | `int sio_write(int port, char* buf, int len)` | 發送資料,回傳實際發送位元組數 |
| sio_read | `int sio_read(int port, char* buf, int len)` | 讀取資料,回傳實際讀取位元組數 |
| sio_cnt_irq | `int sio_cnt_irq(int port, void(__stdcall* func)(int), int count)` | 設定接收中斷。當接收緩衝區資料量 ≥ count 觸發 func。func 需 `__stdcall` 慣例。 |
| sio_flush | `int sio_flush(int port, int queue)` | 清空緩衝區。queue: 0=接收, 1=發送, 2=雙向 |
在表單加入 `ComboBox` 對應鮑率、資料位、停止位、奇偶校驗、流控 (RTS/CTS, XON/XOFF),`btnOpenClick` 內依使用者選擇組合 `nBaud` 與 `nMode` 即可。PcommPro 亦支援 `sio_flow_ctrl()` 設定硬體/軟體流控。
PcommPro 以極簡 API (`open/ioctl/write/read/cnt_irq/close`) 隱藏了 Windows 串口程式設計的所有複雜性:DCB 結構、重疊事件、執行緒同步、驅動交互。配合 C++Builder 快速視覺化開發,幾十行代碼即可構建穩定的工業級串口收發程式。掌握 **ISR 執行緒隔離** 與 **字串/二進位轉換** 兩大關鍵,即可應對絕大多數 RS-232/422/485 通訊場景。