news 2026/4/18 9:21:34

汇编语言全接触-71.在 Win32 下 Serial Port 的通訊

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汇编语言全接触-71.在 Win32 下 Serial Port 的通訊

以下的東西是我在閱讀 "Communications Programming for Windows 95" 時做的一點筆記, 所以在 topic 上大致上都和這書上的第三章一樣. 一些 structure 和 API 的宣告都是 copy from VC 5.0 的 on-line help.

這些東西主要談的是如何在 Win32 的平台下對 serial port 的通訊. 可能可以帶來幫助的是那些了解 serial port 的通訊, 但是不清楚在 Win32 平台究竟有那些 API 可用的 programer (就像我? :p) 我只是做做整理罷了, 更清楚的內容可能要再翻翻 on-line help 或是書本. 如果你要嘗試在 Win32 下做像是對 modem 做控制(例如撥號, 通訊)的程式, 但是對於 serial port 並不了解, 那你應該再去找一本有講到這些東西的書看看.

開啟一個 Serial Port

利用一般開啟檔案的 CreatFile() 即可開啟 serial port device

HANDLE CreateFile(

LPCTSTR lpFileName, // pointer to name of the file

DWORD dwDesiredAccess, // access (read-write) mode

DWORD dwShareMode, // share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes

DWORD dwCreationDistribution, // how to create

DWORD dwFlagsAndAttributes, // file attributes

HANDLE hTemplateFile // handle to file with attributes to copy

);

用 CreateFile() API.

lpFileName 為 "COM1" 或是 "COM2"

dwDersiredAccess 一般為 GENERIC_READ|GENERIC_WRITE

dwShareMode "必須"為 0, 即不能共享, 但同一個 process 中的不同 thread 在一開啟之後就可以共享.

lpSecurityAttributes 一般為 NULL

dwCreateionDistributon 在這裡"必須"為 OPEN_EXISTING

dwFlagsAndAttributes 定義了開啟的屬性, 若是設成 FILE_FLAG_OVERLAPPED 則可使用非同步的 I/O.

hTemplateFile "必須"為 NULL

傳回檔案 handle

設定 Serial Port 傳送及接收緩衝區的大小

在開啟完 serial port 之後, 可以藉由呼叫 SetupComm() 來進行配置傳送時的緩衝區及接收時的緩衝區. 如果沒有呼叫 SetupComm() 的話, Win95 會配置內定的緩衝區.

BOOL SetupComm(

HANDLE hFile, // handle of communications device

DWORD dwInQueue, // size of input buffer

DWORD dwOutQueue // size of output buffer

);

關閉 Serial Port file

利用一般的 CloseHandle() 即可.

BOOL CloseHandle(

HANDLE hObject // handle to object to close

)

取得 Seial Port 的資訊

在 Win32 裡頭, 將一些通訊時會用到的資訊用 COMMPROP 這個結構來表示. (當然不僅僅是 Serial Port) 可以用 GetCommProperties() 來取得:

BOOL GetCommProperties(

HANDLE hFile, // handle of communications device

LPCOMMPROP lpCommProp // address of communications properties structure

);

COMMPROP 長的像這個樣子:

typedef struct _COMMPROP { // cmmp

WORD wPacketLength; // packet size, in bytes

WORD wPacketVersion; // packet version

DWORD dwServiceMask; // services implemented

DWORD dwReserved1; // reserved

DWORD dwMaxTxQueue; // max Tx bufsize, in bytes

DWORD dwMaxRxQueue; // max Rx bufsize, in bytes

DWORD dwMaxBaud; // max baud rate, in bps

DWORD dwProvSubType; // specific provider type

DWORD dwProvCapabilities; // capabilities supported

DWORD dwSettableParams; // changable parameters

DWORD dwSettableBaud; // allowable baud rates

WORD wSettableData; // allowable byte sizes

WORD wSettableStopParity; // stop bits/parity allowed

DWORD dwCurrentTxQueue; // Tx buffer size, in bytes

DWORD dwCurrentRxQueue; // Rx buffer size, in bytes

DWORD dwProvSpec1; // provider-specific data

DWORD dwProvSpec2; // provider-specific data

WCHAR wcProvChar[1]; // provider-specific data

} COMMPROP;

在這裡, lpCommProp 需要 programmer 自行配置空間. 有趣的問題是, 系統在這個結構之後會需要額外的空間. 但是配置者也就是 programmer 卻不知道系統會需要多少. 很簡單的做法是配置一大塊絕對會夠的空間. 另一個聰明的做法是執行兩次 GetCommProperties() , 第一次只配置 sizeof(COMMPROP) 這麼大的空間, 因為還沒有開始執行一些動作, 所以系統並不會嘗試著在後面填東西, 所以不會出問題. 接著執行第一次的 GetCommProperties(), 得到結果, 取出結構中的 wPacketLength, 這個 member 代表實際上需要的大小, 然後依據這個大小重新配置一個新的. 這樣的話 , 就不會有浪費任何空間的問題了.

至於上述 COMMPROP 結構的成員所代表的意思, on-line help 中應該寫的都滿清楚的 .

設定及取得通訊狀態

你可以利用 COMMPROP 來取得一些狀態, 但是當你想改變目前的設定時你需要兩個 API 來完成:

BOOL GetCommState(

HANDLE hFile, // handle of communications device

LPDCB lpDCB // address of device-control block structure

);

BOOL SetCommState(

HANDLE hFile, // handle of communications device

LPDCB lpDCB // address of device-control block structure

);

你可以用 GetCommState() 來取得目前 Serial Port 的狀態, 也可以用 SetCommState() 來設定 Serial Port 的狀態.

DCB 的結構就請自行翻閱 help 囉.

另外, programmer 最常控制的幾個設定就是 baud rate, parity method, data bits, 還有 stop bit. BuildCommDCB() 提供了對於這幾個常見設定的控制.

BOOL BuildCommDCB(

LPCTSTR lpDef, // pointer to device-control string

LPDCB lpDCB // pointer to device-control block

);

lpDef 長的像這樣: "baud=2400 parity=N data=8 stop=1"

通訊設定對話盒

Win32 API 中提供了一個開啟通訊設定對話盒的 API: CommConfigDialog(), 當呼叫這個 API 時, 會蹦現一個可供設定 Baud Rate, Data Bits, Parity .. 等資訊的對話盒, programmer 可以利用它來讓使用者設定一些資訊, 並且取得結果.

BOOL CommConfigDialog(

LPTSTR lpszName, // pointer to device name string

HWND hWnd, // handle to window

LPCOMMCONFIG lpCC // pointer to comm. configuration structure

);

其中 lpCC 被用來存放設定值的結果.

typedef struct _COMM_CONFIG {

DWORD dwSize;

WORD wVersion;

WORD wReserved;

DCB dcb;

DWORD dwProviderSubType;

DWORD dwProviderOffset;

DWORD dwProviderSize;

WCHAR wcProviderData[1];

} COMMCONFIG, *LPCOMMCONFIG;

在我們呼叫 CommConfigDialog() 之前, dwSize 要設為 sizeof(COMMCONFIG), wVersion 的值在這邊似乎不重要(我不清楚, VC5 的 on-line help 說可以設為 1, 我手中的 book 的範例是設為 0x100), 呼叫完 CommConfigDialog() 之後, 成員 dcb 中的 BaudRate, ByteSize, StopBits, Parity 就是使用者的設定.

Timeout 的機制

因為傳輸時並不會維持一個絕對穩定的速率. 因為傳輸品質的關係, programer 會需要 timeout 的機制來協助他們做一些控制. 在 Win32 通訊 Timeout 的機制中, timeout 的性質共分為兩類, 先來看看 COMMTIMEOUTS 這個結構:

typedef struct _COMMTIMEOUTS { // ctmo

DWORD ReadIntervalTimeout;

DWORD ReadTotalTimeoutMultiplier;

DWORD ReadTotalTimeoutConstant;

DWORD WriteTotalTimeoutMultiplier;

DWORD WriteTotalTimeoutConstant;

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

programmer 可以利用 GetCommTimeouts() 和 SetCommTimeouts() 來讀取或是設定目前的 timeout 值.

BOOL GetCommTimeouts(

HANDLE hFile, // handle of communications device

LPCOMMTIMEOUTS lpCommTimeouts // address of comm. time-outs structure

);

BOOL SetCommTimeouts(

HANDLE hFile, // handle of communications device

LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure

);

第一種 timeout 的機制稱為 interval timeout, 從字面上的意義很容易可以理解這種 timeout 的機制是讀取字元之間的間隔時間的 timeout, 只有讀取字元時才能夠使用interval timeout. 也就是在這個結構中的 ReadIntervalTimeout, 單位為 ms, 當讀取完一個字元後, 超過了 ReadIntervalTimeout 的值, 卻還沒有讀到下一個字元時, timeout 就發生了.

第二種 timeout 的機制稱為 total timeout, 顧名思義即是傳輸的總時間的 timeout . 在這種 timeout 的機制下, Win32 提供了一個具有彈性的方式來設定 total timeout. 以讀取的 total timeout 為例, 利用 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 構成了一個線性的上限值. 什麼意思呢? 實際上的 total timeout 應該是這樣的一個式子:

ReadTotalTimeout = ReadTotalTimeOutMultiplier*BytesToRead+ReadTotalTimeoutConstant

WriteTotalTimeout 用同樣的公式來計算. 這樣的話, 不僅可以用一個固定的值來做為 timeout 值, 也可以用條線來做為 timeout 的值, 而隨著要讀取或是要寫的 bytes 數而變動.

如果不想使用 timeout, 就把 COMMTIMEOUTS 裡頭的資料成員都填為 0.

如果你將 ReadIntervalTimeout 設為 MAXDWORD, 且將 ReadTotalTimeOutMultiplier 和 ReadTotalTimeoutConstant 都設為 0 的話, 那麼讀取時, 如果 receive queue 裡頭並沒有資料, 讀取的動作將會馬上返回, 而不會停滯在讀取的動作.

這裡有一個和 BuildCommDCB() 很像的 API 叫 BuildCommDCBAndTimeouts():

BOOL BuildCommDCBAndTimeouts(

LPCTSTR lpDef, // pointer to the device-control string

LPDCB lpDCB, // pointer to the device-control block

LPCOMMTIMEOUTS lpCommTimeouts // pointer to comm. time-out structure

);

lpDef 一樣是控制字串, 可以給像 BuildCommDCB() 中的 lpDef 那樣格式的字串, 但是多了 "TO=XXX" 這個設定. 如果 "TO=ON", 這個 API 會依據 lpCommTimeouts 裡頭的值來設定讀

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 9:19:01

基于单精度浮点数转换的温控系统设计示例

从ADC到PID:用单精度浮点数打造高精度温控系统你有没有遇到过这样的情况?明明传感器标称分辨率达到0.1C,可实际控制中温度总是在设定值附近来回“抖动”,调参调到怀疑人生,最后发现不是PID不行——而是数据在半路就“丢…

作者头像 李华
网站建设 2026/4/18 8:32:03

计算机毕设Java基于协同过滤算法的个性化智能图书推荐系统 基于Java的协同过滤算法驱动的个性化图书推荐平台开发 Java技术实现的个性化智能图书推荐系统:协同过滤算法的应用

计算机毕设Java基于协同过滤算法的个性化智能图书推荐系统j4vuw9 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着互联网的飞速发展,人们获取信息的渠道日益丰富&…

作者头像 李华
网站建设 2026/4/18 11:59:12

内容审核机制:防止生成涉及盗版或侵权的信息输出

内容审核机制:防止生成涉及盗版或侵权的信息输出 在当前大语言模型(LLM)广泛应用的背景下,如何避免AI系统无意中成为版权内容传播的通道,已成为开发者和平台方必须面对的核心挑战。尤其当模型被用于教育、竞赛辅导或编…

作者头像 李华
网站建设 2026/4/18 10:34:26

【Dify触发器性能优化终极指南】:揭秘高并发场景下响应延迟的5大元凶及解决方案

第一章:Dify触发器性能优化的核心挑战在高并发场景下,Dify触发器的性能表现面临多重技术瓶颈。触发器作为连接用户请求与底层工作流的核心组件,其响应延迟、资源调度效率以及事件堆积处理能力直接影响系统整体可用性。事件处理延迟的根源分析…

作者头像 李华
网站建设 2026/4/18 12:27:32

网盘分类无序?VibeThinker构建智能目录树

网盘分类无序?VibeThinker构建智能目录树 在数字生活日益膨胀的今天,几乎每个人都曾面对过这样的场景:打开网盘,映入眼帘的是上百个命名混乱的文件——“新建文本文档(3).txt”、“IMG_20230412_1532.jpg”、“最终版_v2_final.do…

作者头像 李华
网站建设 2026/4/18 8:27:19

Docker镜像体积大?AI推荐精简layer策略

Docker镜像体积大?AI推荐精简layer策略 在AI模型日益向边缘端和本地化部署演进的今天,一个1.5B参数的小模型竟能在数学竞赛题上击败千亿级大模型——这听起来像天方夜谭,但微博开源的 VibeThinker-1.5B-APP 正在让这种“以小搏大”成为现实。…

作者头像 李华