定讀寫)
簡介這份資源是面向工業(yè)自動化工程師、PLC初學(xué)者及上位機開發(fā)人員的OMRON串口通訊實戰(zhàn)示例圍繞RS-232、RS-485等串行標準演示如何與OMRON PLC建立穩(wěn)定數(shù)據(jù)交互解決協(xié)議選擇、參數(shù)配置與指令收發(fā)等常見問題。壓縮包共38個文件約1.64MB以C#源碼14個cs和窗體資源4個resx為主體輔以可執(zhí)行程序、調(diào)試符號、解決方案與說明文本構(gòu)成一套可直接運行的通訊測試工程。資源中提供了完整的通訊程序框架涵蓋串口參數(shù)設(shè)置、請求發(fā)送、響應(yīng)解析與錯誤處理等關(guān)鍵環(huán)節(jié)并配有通訊實例說明文檔便于讀者對照代碼理解ASCII、Modbus RTU等協(xié)議的實際落地方式。目前已有3657人學(xué)習下載適合希望快速搭建OMRON PLC串口通訊原型、排查通訊異?;蜓a充上位機開發(fā)經(jīng)驗的讀者參考借鑒。1. OMRON串口通訊實例從一根線到穩(wěn)定讀寫中間隔著多少坑車間里一臺老設(shè)備還在跑PLC 是 OMRON 的上位機要拿它的產(chǎn)量和報警數(shù)據(jù)。你翻出通訊手冊看到 Host Link、FINS、無協(xié)議幾種模式心里大概有數(shù)但真把線接上、命令發(fā)出去回過來的卻是一串亂碼或者干脆沒響應(yīng)。OMRON 串口通訊實例這件事難點從來不在協(xié)議本身而在于接線、參數(shù)、時序、校驗這四件事里任何一件對不上整條鏈路就是黑匣子。這篇筆記面向兩類人一類是第一次用 OMRON PLC 做串口采集、想照著跑通最小閉環(huán)的工程師另一類是已經(jīng)能通、但偶爾丟包或超時、想搞清楚邊界在哪的熟手。我會按「先立住原理、再動手復(fù)現(xiàn)、最后講坑」的順序把 Host Link 和 FINS 串口這兩條最常見路徑拆開講參數(shù)怎么設(shè)、命令怎么拼、失敗時看什么都落到可抄作業(yè)的粒度。2. 先把協(xié)議選對Host Link、FINS 串口、無協(xié)議到底用哪個選錯協(xié)議是新手翻車的第一大來源。OMRON 的串口通訊不是只有一種玩法上位機主動發(fā)命令、PLC 被動應(yīng)答這套主從結(jié)構(gòu)下有三條主流路徑適用場景差別很大。先把它們擺清楚再決定你的實例該怎么寫。2.1 三種模式的本質(zhì)區(qū)別與選型依據(jù)Host Link 是 OMRON 最早、最通用的一套上位機命令協(xié)議走 ASCII 文本幀命令形如00RD00000001這種可讀字符串PLC 回00RD00...加數(shù)據(jù)和 FCS 校驗。它的好處是調(diào)試直觀串口助手直接敲命令就能看到回應(yīng)壞處是 ASCII 傳輸效率低單次讀寫點數(shù)有限適合小批量、低頻采集。FINS 串口是把 FINS 命令幀套在串口上跑幀是二進制或 ASCII 可選命令結(jié)構(gòu)比 Host Link 復(fù)雜但尋址能力強能跨網(wǎng)絡(luò)、能訪問 DM/CIO/EM 等各區(qū)域適合數(shù)據(jù)量大、需要結(jié)構(gòu)化訪問的場合。很多中高端機型同時支持 Host Link 和 FINS低端機型可能只給 Host Link。無協(xié)議模式則是 PLC 側(cè)用指令自己收發(fā)原始字節(jié)協(xié)議由你自定義靈活但上位機要自己處理全部幀格式和校驗工作量最大一般只在對接第三方設(shè)備、協(xié)議已經(jīng)定死時才用。模式幀格式調(diào)試難度單次數(shù)據(jù)量典型場景Host LinkASCII 文本低小產(chǎn)量計數(shù)、狀態(tài)讀取FINS 串口二進制/ASCII中大多區(qū)域批量采集無協(xié)議自定義高任意對接既定第三方協(xié)議選型建議很直接能上 Host Link 就先上 Host Link把鏈路跑通、把參數(shù)摸清再考慮換 FINS 提效率。不要一上來就啃 FINS 幀結(jié)構(gòu)那樣連串口通沒通都驗證不了。2.2 物理層與串口參數(shù)9600 8N1 不是隨便寫的OMRON PLC 的串口默認參數(shù)在多數(shù)機型上是 9600bps、8 數(shù)據(jù)位、無校驗、1 停止位也就是常說的 8N1。這個默認值寫在 PLC 的 DM 區(qū)設(shè)置里不同機型對應(yīng)的 DM 地址不一樣常見的是 DM6645 到 DM6648 這一段控制通訊口參數(shù)。如果你改了 PLC 側(cè)參數(shù)上位機必須同步改否則收到的就是一堆錯位字節(jié)。接線是另一個高頻翻車點。RS-232 和 RS-485 的接法完全不同232 是點對點TX/RX 要交叉GND 必須共地485 是差分總線A 接 A、B 接 B終端電阻在長距離時不能省。很多人用 USB 轉(zhuǎn) 232 的線結(jié)果 PLC 那頭是 485 口物理層就對不上軟件怎么調(diào)都沒用。提示先用串口助手以 8N1、9600 手動發(fā)一條 Host Link 讀命令能收到正確回應(yīng)再寫代碼。這一步能排掉八成「代碼沒問題但就是不通」的情況。2.3 最小可復(fù)現(xiàn)的 Host Link 讀命令Host Link 命令幀結(jié)構(gòu)是起始符 單元號2 位 命令碼2 位 數(shù)據(jù)區(qū) FCS 校驗2 位 結(jié)束符* CR。以讀 DM 區(qū)為例讀 DM0000 開始的 1 個字# Host Link 讀 DM 區(qū)最小示例 # 幀: 單元號 RD 起始地址(4位) 點數(shù)(4位) FCS * CR unit 00 # PLC 單元號默認 00 cmd RD # 讀 DM 區(qū) addr 0000 # 起始地址 DM0000 count 0001 # 讀 1 個字 body unit cmd addr count # FCS 計算對 body 所有字節(jié)做異或 fcs 0 for ch in body: fcs ^ ord(ch) frame body format(fcs, 02X) *\r print(repr(frame)) # 輸出: 00RD00000001 FCS *\r邏輯說明body是不含起始符、FCS、結(jié)束符的純命令體FCS 是對它逐字節(jié)異或得到的兩位十六進制。參數(shù)上unit要和 PLC 的單元號設(shè)置一致多臺 PLC 掛在同一 485 總線上時靠它區(qū)分addr是 4 位十六進制地址count是 4 位十六進制點數(shù)讀多個字時改count即可。發(fā)出去后 PLC 回幀格式類似數(shù)據(jù)區(qū)就是你要的值回幀的 FCS 也要校驗一遍不通過說明傳輸有誤。3. 用 Python 把 Host Link 讀寫跑通串口配置與幀收發(fā)原理清楚之后落到代碼。這一章給一個能直接跑的 Python 實例覆蓋串口打開、命令拼裝、發(fā)送、接收、FCS 校驗、超時處理。用 pyserial 這個庫跨平臺Windows 和 Linux 都能用。3.1 串口打開與參數(shù)配置import serial import time # 打開串口參數(shù)必須與 PLC 側(cè)一致 ser serial.Serial( portCOM3, # Windows 用 COMxLinux 用 /dev/ttyUSB0 baudrate9600, # 與 PLC DM 區(qū)設(shè)置一致 bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeout1.0 # 讀超時 1 秒避免死等 ) if not ser.is_open: ser.open()參數(shù)說明timeout是關(guān)鍵設(shè)成 None 會一直阻塞設(shè)太小又會把正常慢響應(yīng)誤判為超時。OMRON PLC 的響應(yīng)時間通常在幾十毫秒量級1 秒足夠?qū)捤?。baudrate如果 PLC 側(cè)改過這里必須同步改完記得重新打開串口熱改波特率在部分 USB 轉(zhuǎn)串口芯片上不生效。3.2 命令拼裝與 FCS 校驗函數(shù)def build_hostlink(unit, cmd, addr, count): 拼裝 Host Link 命令幀 body f{unit}{cmd}{addr}{count} fcs 0 for ch in body: fcs ^ ord(ch) return f{body}{fcs:02X}*\r def check_fcs(resp): 校驗 PLC 回幀的 FCS if not resp.startswith() or * not in resp: return False body resp[1:resp.index(*)] recv_fcs body[-2:] calc 0 for ch in body[:-2]: calc ^ ord(ch) return format(calc, 02X) recv_fcs.upper()邏輯說明build_hostlink把單元號、命令碼、地址、點數(shù)拼成命令體算 FCS 后加上起始符和結(jié)束符。check_fcs從回幀里剝出命令體取最后兩位作為收到的 FCS再對前面部分重算比對。這兩個函數(shù)是整個通訊的核心寫錯一個字節(jié)PLC 要么不回要么回錯誤碼。3.3 發(fā)送、接收與超時處理def read_dm(ser, addr, count): frame build_hostlink(00, RD, addr, count) ser.reset_input_buffer() # 清掉殘留數(shù)據(jù)避免串幀 ser.write(frame.encode(ascii)) time.sleep(0.05) # 給 PLC 一點響應(yīng)時間 resp ser.read(64).decode(ascii, errorsignore) if not resp: raise TimeoutError(PLC 無響應(yīng)檢查接線與參數(shù)) if not check_fcs(resp): raise ValueError(fFCS 校驗失敗: {resp!r}) # 回幀格式: 00RD00 數(shù)據(jù) FCS * data resp[7:-4] return data # 使用 try: val read_dm(ser, 0000, 0001) print(DM0000 , val) except Exception as e: print(讀取失敗:, e)邏輯說明reset_input_buffer在每次發(fā)送前清空接收緩沖這是防止上一幀殘留導(dǎo)致串幀的關(guān)鍵一步很多人丟包就是因為省了這行。time.sleep(0.05)是給 PLC 的處理窗口太短會讀到空太長影響吞吐。回幀的數(shù)據(jù)區(qū)位置取決于命令碼RD 命令下數(shù)據(jù)從第 7 個字符開始到 FCS 前結(jié)束實際項目里建議按命令碼分別解析不要寫死偏移。注意如果連續(xù)讀多個地址不要每條命令都 sleep 固定值改成讀回幀長度判斷更穩(wěn)。固定 sleep 在波特率變化或 PLC 負載高時會失效。3.4 寫命令與讀命令的差異寫 DM 區(qū)用WD命令幀結(jié)構(gòu)多一段寫入數(shù)據(jù)def write_dm(ser, addr, value_hex): 寫單個 DM 字value_hex 為 4 位十六進制 body f00WD{addr}{value_hex} fcs 0 for ch in body: fcs ^ ord(ch) frame f{body}{fcs:02X}*\r ser.reset_input_buffer() ser.write(frame.encode(ascii)) time.sleep(0.05) resp ser.read(32).decode(ascii, errorsignore) # 寫成功回幀以 00WD00 開頭 return resp.startswith(00WD00)寫命令的回幀只表示「收到并執(zhí)行」不返回寫入值所以判斷成功看回幀的命令碼和結(jié)束碼是否為00。寫操作比讀操作更容易受時序影響連續(xù)寫多個地址時建議每條之間留 20 到 50 毫秒太快 PLC 可能丟命令。4. FINS 串口實例什么時候該從 Host Link 換過去Host Link 跑通之后如果發(fā)現(xiàn)單次讀點數(shù)不夠、或者要訪問多個存儲區(qū)、或者要跨網(wǎng)段就該考慮 FINS。FINS 串口不是另起爐灶而是把 FINS 幀套在串口鏈路上理解它的結(jié)構(gòu)能讓你在數(shù)據(jù)量上來之后不慌。4.1 FINS 幀結(jié)構(gòu)與 Host Link 的本質(zhì)差異FINS 幀由 ICF、RSV、GCT、DNA、DA1、DA2、SNA、SA1、SA2、SID 這十個字節(jié)的頭部加上命令碼和參數(shù)區(qū)組成。頭部里 DA1/DA2 是目標節(jié)點地址SA1/SA2 是源節(jié)點地址串口直連時通常都填 00。命令碼是兩字節(jié)比如讀 CIO 區(qū)是0101讀 DM 區(qū)是0101加不同區(qū)域碼。和 Host Link 最大的差異在于Host Link 是 ASCII 文本、命令即語義FINS 是二進制為主、頭部字段多、需要理解節(jié)點尋址。好處是 FINS 一次能讀的字節(jié)數(shù)遠大于 Host Link且能訪問所有存儲區(qū)壞處是調(diào)試時不能靠串口助手直接敲得先算好二進制幀。4.2 用 Python 構(gòu)造 FINS 串口讀幀import struct def build_fins_read_dm(start_addr, count): 構(gòu)造 FINS 讀 DM 區(qū)命令幀串口直連節(jié)點地址全 0 # FINS 頭部 10 字節(jié) icf, rsv, gct 0x80, 0x00, 0x02 dna, da1, da2 0x00, 0x00, 0x00 sna, sa1, sa2 0x00, 0x00, 0x00 sid 0x00 header struct.pack(BBBBBBBBBB, icf, rsv, gct, dna, da1, da2, sna, sa1, sa2, sid) # 命令碼 0101 讀區(qū)域碼 82 DM 區(qū) command struct.pack(HH, 0x0101, 0x0082) # 起始地址 2 字節(jié) 點數(shù) 2 字節(jié) params struct.pack(HH, start_addr, count) return header command params邏輯說明icf0x80表示需要響應(yīng)gct0x02是網(wǎng)關(guān)計數(shù)串口直連時固定值。command里0x0101是內(nèi)存區(qū)讀命令0x0082是 DM 區(qū)區(qū)域碼讀 CIO 區(qū)要換成0x00B0之類。params是起始地址和字數(shù)都是大端兩字節(jié)。整個幀發(fā)出去后PLC 回的幀頭部相同命令碼后跟結(jié)束碼和讀到的數(shù)據(jù)。4.3 FINS 回幀解析與結(jié)束碼判斷def parse_fins_response(resp): 解析 FINS 回幀返回 (結(jié)束碼, 數(shù)據(jù)) if len(resp) 14: raise ValueError(回幀過短) # 頭部 10 字節(jié) 命令碼 2 字節(jié) 結(jié)束碼 2 字節(jié) end_code struct.unpack(H, resp[12:14])[0] data resp[14:] return end_code, data # 結(jié)束碼 0x0000 表示正常非零要查手冊結(jié)束碼是 FINS 排錯的核心0x0000正常0x0101之類是命令不支持0x0201是區(qū)域碼錯誤0x0301是地址越界。拿到非零結(jié)束碼不要瞎猜對照 FINS 命令手冊的結(jié)束碼表逐條查比反復(fù)改代碼快得多。4.4 串口 FINS 與 Host Link 的混用策略實際項目里不必二選一。低頻狀態(tài)量用 Host Link直觀好維護高頻批量數(shù)據(jù)用 FINS效率高。兩條路徑可以共用同一個物理串口只要命令幀不交叉、每次收發(fā)完整PLC 不會混淆。但要注意混用時單元號和節(jié)點地址要理清Host Link 的單元號和 FINS 的 DA1 不是一回事別把兩套尋址搞混。5. 避坑與排查OMRON 串口通訊最常見的五類翻車這一章是血淚經(jīng)驗集中區(qū)。下面五條都是實際調(diào)試中反復(fù)出現(xiàn)的每條按現(xiàn)象、原因、解決寫照著排查能省大量時間。5.1 現(xiàn)象發(fā)命令完全無響應(yīng)原因物理層沒通或者串口參數(shù)與 PLC 不一致。最常見的是 USB 轉(zhuǎn)串口線的電平不對或者 TX/RX 沒交叉或者 PLC 側(cè)通訊口被設(shè)成了無協(xié)議模式而不是 Host Link。解決先用萬用表確認線序再用串口助手以 8N1、9600 發(fā)一條最簡單的 Host Link 讀命令。如果串口助手能通、代碼不通問題在代碼如果串口助手也不通問題在物理層或 PLC 參數(shù)。PLC 側(cè)要確認 DM 區(qū)里通訊模式設(shè)的是 Host Link 而不是無協(xié)議。5.2 現(xiàn)象能收到回應(yīng)但 FCS 校驗失敗原因回幀里混入了上一幀的殘留數(shù)據(jù)或者讀取長度不對導(dǎo)致截斷。串口是流式的沒有幀邊界讀的時候如果沒清緩沖很容易把兩幀粘在一起。解決每次發(fā)送前reset_input_buffer接收時按回幀的結(jié)束符*和 CR 判斷邊界不要用固定長度讀。如果波特率較高考慮用read_until(b*\r)代替read(n)。5.3 現(xiàn)象偶爾丟包重發(fā)就好原因發(fā)送間隔太短PLC 還沒處理完上一條命令就來了下一條?;蛘呱衔粰C sleep 時間固定但 PLC 負載波動時響應(yīng)變慢。解決把固定 sleep 改成基于回幀的判斷收到完整回幀再發(fā)下一條。連續(xù)寫操作時每條之間留足間隔必要時加重試機制重試前先清緩沖。5.4 現(xiàn)象讀到的數(shù)據(jù)錯位或值不對原因地址算錯或者區(qū)域碼用錯或者數(shù)據(jù)解析偏移寫死。Host Link 的地址是十六進制但有些手冊里給的是十進制換算錯一位整個數(shù)據(jù)就偏了。解決先用已知值驗證比如往 DM0000 寫一個固定值再讀回來確認地址和解析都對。FINS 的區(qū)域碼要對照手冊DM 區(qū)是0x82CIO 區(qū)是0xB0別混。5.5 現(xiàn)象多臺 PLC 掛同一總線時互相干擾原因單元號或節(jié)點地址重復(fù)或者 485 總線終端電阻沒接、線太長導(dǎo)致信號反射。解決每臺 PLC 的單元號必須唯一Host Link 靠單元號區(qū)分FINS 靠 DA1 區(qū)分。485 總線超過一定長度要在兩端接終端電阻線材用雙絞屏蔽線屏蔽層單端接地。6. 進階技巧把串口通訊做成可維護的采集模塊跑通單條命令只是起點真正上線要的是穩(wěn)定、可觀測、可恢復(fù)。這一章講幾個我實際項目里會用的技巧幫你把實例升級成模塊。第一個技巧是命令隊列加狀態(tài)機。不要在主循環(huán)里直接發(fā)命令而是把讀任務(wù)放進隊列一個 worker 串行處理每條命令帶超時和重試次數(shù)。這樣即使某條命令失敗也不會阻塞整個采集。狀態(tài)機記錄當前處于「發(fā)送中」「等待響應(yīng)」「解析中」哪個階段出問題時能直接定位。第二個技巧是心跳與斷線重連。串口線松動、USB 轉(zhuǎn)接器掉驅(qū)動都會導(dǎo)致鏈路斷開代碼里要定期發(fā)一條輕量讀命令當心跳連續(xù)失敗就關(guān)閉串口重新打開。重連邏輯要帶退避不要瘋狂重試把 CPU 打滿。第三個技巧是原始幀日志。把每次發(fā)送和接收的原始字節(jié)按十六進制記下來出問題時翻日志比現(xiàn)場復(fù)現(xiàn)快得多。日志要帶時間戳和方向方便對齊 PLC 側(cè)的事件。技巧解決的問題實現(xiàn)要點命令隊列狀態(tài)機單條失敗阻塞全局串行 worker超時重試心跳重連鏈路意外斷開定期輕量讀退避重連原始幀日志現(xiàn)場難復(fù)現(xiàn)十六進制時間戳方向最后一個習慣任何參數(shù)改動都先記下來再改。波特率、單元號、DM 設(shè)置地址這些改之前截圖或抄一遍改完不通能快速回退。我吃過一次虧把 PLC 通訊口參數(shù)改了沒記結(jié)果調(diào)了一下午才發(fā)現(xiàn)是參數(shù)問題有后悔藥的話就是那份記錄。串口通訊這東西玄學(xué)感往往來自沒記錄的變化把變量控制住它就是個確定性的工程問題。希望幫到你。本文還有配套的精品資源點擊獲取