0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

第6章_libmodbus使用

嵌入式Linux那些事 ? 來(lái)源:嵌入式Linux那些事 ? 作者:嵌入式Linux那些事 ? 2024-06-29 14:36 ? 次閱讀

第6章 libmodbus使用

6.1 libmodbus開(kāi)發(fā)庫(kù)

6.1.1 功能概要

libmodbus是一個(gè)免費(fèi)的跨平臺(tái)支持RTU和TCP的Modbus庫(kù),遵循LGPL V2.1+協(xié)議。libmodbus支持Linux、Mac Os X、FreeBSD、QNX和Windows等操作系統(tǒng)。libmodbus可以向符合Modbus協(xié)議的設(shè)備發(fā)送和接收數(shù)據(jù),并支持通過(guò)串口或者TCP網(wǎng)絡(luò)進(jìn)行連接。

作為一個(gè)開(kāi)源項(xiàng)目,libmodbus庫(kù)還處于開(kāi)發(fā)測(cè)試階段,代碼量還不十分龐大,文檔和注釋也不夠全面,本章通過(guò)對(duì)libmodbus源代碼的閱讀過(guò)程,一方面可以進(jìn)一步理解Modbus協(xié)議,同時(shí)也可以學(xué)習(xí)一個(gè)好的開(kāi)源項(xiàng)目的代碼組織及開(kāi)發(fā)過(guò)程。 libmodbus的官方網(wǎng)站為 http://libmodbus.org/, 可以從 http://libmodbus.org/download/ 下載源代碼。作為開(kāi)源軟件,還可以從GitHub網(wǎng)站獲取最新版本的代碼GitHub: https://github.com/stephane/libmodbus.git

6.1.2 源碼獲取

libmodbus的源碼不斷更新,本教程選擇版本v3.1.10。打開(kāi)https://github.com/stephane/libmodbus/tags ,如下圖下載:

img

本源碼也放在網(wǎng)盤(pán)中如下目錄里:

img

解壓后,簡(jiǎn)單查看源代碼根目錄的構(gòu)成:

  • doc目錄: libmodbus庫(kù)的各API接口說(shuō)明文檔。
  • m4目錄: 存放GNU m4文件,在這里對(duì)理解代碼沒(méi)有意義,可忽略。
  • src目錄: 全部libmodbus源文件。
  • tests目錄: 包含自帶的測(cè)試代碼 其他文件對(duì)理解源代碼關(guān)系不大,可以暫時(shí)忽略

圖6-2解壓libmodbus源代碼:

img

進(jìn)一步展開(kāi)src代碼目錄,如圖6-3所示:

圖6-3libmodbus源碼構(gòu)成:

img

各文件作用如下:

  • win32: 定義在Windows下使用Visual Studio編譯時(shí)的項(xiàng)目文件和工程文件以及相關(guān)配置選項(xiàng)等。其中,modbus-9.sln默認(rèn)使用Visual Studio 2008。
  • Makefile.am: Makefile.am是Linux下AutoTool編譯時(shí)讀取相關(guān)編譯參數(shù)的配置文件,用于生成Makefile文件,因?yàn)橛糜贚inux下開(kāi)發(fā),所以在這里暫時(shí)忽略
  • modbus.c: 核心文件,實(shí)現(xiàn)Modbus協(xié)議層,定義共通的Modbus消息發(fā)送和接收函數(shù)各功能碼對(duì)應(yīng)的函數(shù)。
  • modbus.h: libmodbus對(duì)外暴露的接口API頭文件。
  • modbus-data.c: 數(shù)據(jù)處理的共通函數(shù),包括大小端相關(guān)的字節(jié)、位交換等函數(shù)
  • modbus-private.h: libmodbus內(nèi)部使用的數(shù)據(jù)結(jié)構(gòu)和函數(shù)定義。
  • modbus-rtu.c: 通信層實(shí)現(xiàn),RTU模式相關(guān)的函數(shù)定義,主要是串口的設(shè)置、連接及消息的發(fā)送和接收等。
  • modbus-rtu.h: RTU模式對(duì)外提供的各API定義
  • modbus-rtu-private.h: RTU模式的私有定義。
  • modbus-tcp.c: 通信層實(shí)現(xiàn),TCP模式下相關(guān)的函數(shù)定義,主要包括TCP/IP網(wǎng)絡(luò)的設(shè)置連接、消息的發(fā)送和接收等。
  • modbus-tcp.h: 定義TCP模式對(duì)外提供的各API定義
  • modbus-tcp-private.h: TCP模式的私有定義。
  • modbus-version.h.in: 版本定義文件。

6.1.3 源碼閱讀

對(duì)比比較復(fù)雜的源碼,使用sourceinsight可以很方便地閱讀、分析、編輯源碼。

1. 新建工程

運(yùn)行source Insight,點(diǎn)擊菜單“Project->New Project”,如下圖所示:

img

設(shè)置工程名及工程數(shù)據(jù)目錄:在彈出的New Project對(duì)話框中設(shè)置“New project name”(項(xiàng)目的名稱(chēng)),然后設(shè)置Where do you want to store the project data file? (項(xiàng)目文件保存位置),點(diǎn)擊Browse按鈕選擇源碼的目錄即可,如下圖:

img

指定源碼目錄:在上圖界面中點(diǎn)擊OK后,彈出如下圖所示窗口,填入源碼路徑:

img

添加源碼:在新彈出的對(duì)話框中,點(diǎn)擊“Add”或“Add All”?!癆dd”是手動(dòng)選擇需要添加的文件,而“Add All”是添加所有文件。我們使用“Add All”,在彈出的提示框中選中“Recursively add lower sub-directories”(遞歸添加下級(jí)的子目錄)并點(diǎn)擊OK。同樣的Remove File,Remove All是移除單個(gè)文件或者移除所有文件,如下圖所示:

img

添加文件完成后會(huì)彈出下面窗口,點(diǎn)擊“確定”即可:

img

此時(shí)界面會(huì)返回到主界面,如下圖所示,點(diǎn)擊“Close”:

img

2. 同步文件

同步文件的意思是讓Source Insight去解析源碼,生成數(shù)據(jù)庫(kù),這樣有助于以后閱讀源碼。比如點(diǎn)擊某個(gè)函數(shù)時(shí)就可以飛快地跳到它定義的地方。

先點(diǎn)擊菜單“Project->Synchronize Files”,如圖 2.23所示:

img

在彈出的對(duì)話框中 選中“Force all files to be re-parsed”(強(qiáng)制解析所有文件),并點(diǎn)擊“Start”按鈕開(kāi)始同步,如下圖所示:

img

3.打開(kāi)工程

前面建議工程后,就會(huì)自動(dòng)打開(kāi)了工程。如果下次你想打開(kāi)工程,啟動(dòng)Souce Insight后,點(diǎn)擊菜單“Project -> Open Porject”就可以在一個(gè)列表中選擇以前建立的工程,如下圖所示:

img

4. 操作示例

在工程中打開(kāi)文件:點(diǎn)擊"P"圖標(biāo)打開(kāi)文件列表,雙擊文件打開(kāi)文件,也可以輸入文件名查找文件,如下圖所示:

img

在文件中查看函數(shù)或變量的定義:打開(kāi)文件后,按住ctrl鍵的同時(shí),用鼠標(biāo)點(diǎn)擊函數(shù)、變量,就會(huì)跳到定義它的位置,如下圖所示:

img

查找函數(shù)或變量的引用:右鍵點(diǎn)擊函數(shù)或變量,彈出對(duì)話框選擇“Lookup Reference”;或者雙擊函數(shù)后,使用快捷鍵"ctrl+/"來(lái)查找引用,如下圖:

img

5. 快捷鍵

快捷鍵說(shuō)明
Alt + ,后退
Alt + .前進(jìn)
F8高亮選中的字符
Ctrl+F查找
F3或Shift+F3往前查找
F4或Shift+F4往后查找

6.1.4 libmodbus與應(yīng)用程序的關(guān)系

libmodbus是一個(gè)免費(fèi)的跨平臺(tái)支持RTU和TCP的Modbus開(kāi)發(fā)庫(kù),借助于libmodbus發(fā)庫(kù)能夠非常方便地建立自己的應(yīng)用程序或者將Modbus通信協(xié)議嵌入單體設(shè)備libmodbus開(kāi)發(fā)庫(kù)與應(yīng)用程序的基本關(guān)系如圖6-4所示。

圖6-4應(yīng)用程序與libmodbus的關(guān)系:

img

在對(duì)libmodbus的接口及代碼框架簡(jiǎn)單了解之后,不妨再深入細(xì)節(jié)一探究竟,看看libmodbus都實(shí)現(xiàn)了哪些基礎(chǔ)功能,以及源代碼中對(duì)Modbus各功能碼和消息頓是如何包裝的。具體內(nèi)容請(qǐng)參看下一章。

6.2 libmodbus源代碼解析

libmodbus作為一個(gè)優(yōu)秀且免費(fèi)開(kāi)源的跨平臺(tái)支持RTU和TCP模式的Modbus開(kāi)發(fā)庫(kù),非常值得大家借鑒和學(xué)習(xí)。本章對(duì)libmodbus源代碼進(jìn)行閱讀和分析。

6.2.1 核心函數(shù)

以Modbus RTU協(xié)議為例,主設(shè)備、從設(shè)備初始化后:

  • 主設(shè)備就可以啟動(dòng)請(qǐng)求,即“發(fā)送消息”給從設(shè)備
  • 從設(shè)備接收到請(qǐng)求后構(gòu)造數(shù)據(jù),啟動(dòng)響應(yīng)即“發(fā)送回復(fù)”
  • 主機(jī)收到響應(yīng)后,會(huì)“檢查響應(yīng)”

如下圖所示:

img

分析“l(fā)ibmodbus-3.1.10testsunit-test-client.c”、“l(fā)ibmodbus-3.1.10testsunit-test-server.c”,可以得到下面核心函數(shù)的使用過(guò)程:

img

6.2.2 框架分析與數(shù)據(jù)結(jié)構(gòu)

站在APP開(kāi)發(fā)的角度來(lái)說(shuō),使用上一節(jié)里介紹的libmodbus函數(shù)即可。但是,數(shù)據(jù)的傳輸必定涉及到底層數(shù)據(jù)傳輸。所以,從數(shù)據(jù)的收發(fā)過(guò)程,可以把使用libmodbus的源碼分為3層:

  • APP:它知道要做什么,主設(shè)備要讀寫(xiě)哪些寄存,從設(shè)備提供、接收什么數(shù)據(jù)
  • Modbus核心層:向上提供接口函數(shù),向下調(diào)用底層代碼構(gòu)造數(shù)據(jù)包并發(fā)送、接收數(shù)據(jù)包并解析
  • 后端(數(shù)據(jù)傳輸):進(jìn)行硬件相關(guān)的數(shù)據(jù)封包與發(fā)送、接收與解包

img

對(duì)于核心層、后端,抽象出了如下結(jié)構(gòu)體:

img

核心層modbus_t結(jié)構(gòu)體的成員含義如下:

成員含義
int slave;從站設(shè)備地址
int s;RTU下是串口句柄,TCP下是Socket
int debug;是否啟動(dòng)Debug模式(打印調(diào)試信息
int error_recovery;錯(cuò)誤恢復(fù)模式:MODBUS_ERROR_RECOVERY_NONE:由APP處理錯(cuò)誤MODBUS_ERROR_RECOVERY_LINK:如果有連接錯(cuò)誤,則重連MODBUS_ERROR_RECOVERY_PROTOCOL:如果數(shù)據(jù)不符合協(xié)議要求,則清空所有數(shù)據(jù)
int quirks;一些奇怪的功能,比如:MODBUS_QUIRK_MAX_SLAVE:從站地址最大值可以到達(dá)255MODBUS_QUIRK_REPLY_TO_BROADCAST:回應(yīng)廣播包
struct timeval response_timeout;等待回應(yīng)的超時(shí)時(shí)間,默認(rèn)是0.5S
struct timeval byte_timeout;接收一個(gè)字節(jié)的超時(shí)時(shí)間,默認(rèn)是0.5S
struct timeval indication_timeout;等待請(qǐng)求的超時(shí)時(shí)間
const modbus_backend_t *backend;硬件傳輸層的結(jié)構(gòu)體
void *backend_data;硬件傳輸層的私有數(shù)據(jù)

后端modbus_backend_t結(jié)構(gòu)體的成員含義如下:

成員含義
unsigned int backend_type;后端類(lèi)型,是RTU還是TCP
unsigned int header_length;頭部長(zhǎng)度,比如RTU數(shù)據(jù)包前面需要有1字節(jié)的設(shè)備地址,頭部長(zhǎng)度就是1
unsigned int checksum_length;校驗(yàn)碼長(zhǎng)度,RTU的校驗(yàn)碼是2字節(jié)
unsigned int max_adu_length;ADU(數(shù)據(jù)包)最大長(zhǎng)度
set_slave設(shè)置從站地址
build_request_basis設(shè)置RTU請(qǐng)求包的基本數(shù)據(jù),這些數(shù)據(jù)的格式是一樣的,比如req[0]是從設(shè)備地址,req[1]是功能碼,req[2]和req[3]是寄存器地址,req[4]和req[5]是寄存器數(shù)量
build_response_basis設(shè)置RTU回應(yīng)包的基本數(shù)據(jù),這些數(shù)據(jù)的格式是一樣的,比如req[0]是從設(shè)備地址,req[1]是功能碼
prepare_response_tid生產(chǎn)傳輸標(biāo)識(shí)TID,在TCP中使用
send_msg_pre發(fā)送消息前的準(zhǔn)備工作,對(duì)于RTU是填充CRC檢驗(yàn)碼,對(duì)于TCP是填充頭部的Length
send發(fā)送數(shù)據(jù)包
receive接收數(shù)據(jù)包
recv接收原始數(shù)據(jù),receive會(huì)調(diào)用recv得到原始數(shù)據(jù)然后解析出數(shù)據(jù)包
check_integrity檢查數(shù)據(jù)包的完整性
pre_check_confirmation檢查響應(yīng)數(shù)據(jù)包是否有效時(shí),先執(zhí)行pre_check_confirmation做一些簡(jiǎn)單的檢查
connect硬件相關(guān)的連接,對(duì)于RTU就是打開(kāi)串口、設(shè)置串口波特率等;對(duì)于TCP則是連接對(duì)端
is_connected判斷是否已經(jīng)連接
close關(guān)閉連接
flush清空接收到的、未處理的數(shù)據(jù)
select阻塞一段時(shí)間以等待數(shù)據(jù)
free釋放分配的modbus_t等結(jié)構(gòu)體

6.2.3 情景分析

以“modbus_write_bits”函數(shù)為例,分析下圖的執(zhí)行流程:

img

1. 初始化

2. 主設(shè)備發(fā)送請(qǐng)求

3. 從設(shè)備接收請(qǐng)求

4.從設(shè)備回應(yīng)

6.2.4 常用接口函數(shù)

下面分析 libmodbus開(kāi)發(fā)庫(kù)提供的所有接口API函數(shù)。其主要對(duì)象文括 modbus.h 和 modbus.c ,接口函數(shù)大致可分為3類(lèi),以下分別進(jìn)行介紹。

1. 各類(lèi)輔助接口函數(shù)

MODBUS_API int modbus_set_slave(modbus t * ctx,int slave)

此函數(shù)的功能是設(shè)置從站地址,但是由于傳輸方式不同而意義稍有不同。

  • RTU模式 :

如果 libmodbus應(yīng)用于 主站設(shè)備端,則相當(dāng)于定義 遠(yuǎn)端設(shè)備ID ;如果libmodbus應(yīng)用于從站設(shè)備端 ,則相當(dāng)于定義 自身設(shè)備 ID ;在 RTU 模式下參數(shù) slave 取值范圍為 0~247 ,其中 0(MODBUS_BROADCAST_ADDRESS) 為廣播地址。

  • TCP模式:

通常,TCP 模式下此函數(shù)不需要使用。在某些特殊場(chǎng)合,例如串行 Modbus設(shè)備轉(zhuǎn)換為 TCP模式傳輸?shù)那闆r下,此函數(shù)才被使用。此種情況下,參數(shù) slave取值范圍為 0~247 ,0 為廣播地址;如果不進(jìn)行設(shè)置,則 TCP 模式下采用默認(rèn)值 MODBUS TCP SLAVE(OXFF) 。

下面的代碼以 RTU模式、主設(shè)備(MASTER)端為例:

modbus_t * ctx;

ctx=modbus_new_rtu("COM4",115200,'N'8,1);

if (ctx ==NULL)

{

  fprintf(stderr"Unable to create the libmodbus contextn");

  return -1;

}

rc =modbus_set_slave(ctx,YOUR DEVICE ID);

if (rc==-1)

{

  fprintf(stderr,"Invalid slave IDn");

  modbus free(ctx);

  return -1;

}

if (modbus connect(ctx)==-1)

{

  fprintf(stderr,"Connection failed:sn",modbus strerror(errno));

  modbus free(ctx);

  return -1;

}

MODBUS_APIintmodbus_set_error_recovery(modbus_t*ctx,modbus_error_recovery_mode error_recovery):

此函數(shù)用于在連接失敗或者傳輸異常的情況下,設(shè)置錯(cuò)誤恢復(fù)模式。有 3種錯(cuò)誤恢復(fù)模式可選。

typedef enum

{

  MODBUS_ERROR_RECOVERY_NONE        =0,             //不恢復(fù)

  MODBUS_ERROR_RECOVERY_LINK        =(1< 1),           //鏈路層恢復(fù)

  MODBUS_ERROR_RECOVERY_PROTOCOL      =(1< 2)           //協(xié)議層恢復(fù)

}modbus error recovery mode;

默認(rèn)情況下,設(shè)置為 MODBUS_ERROR_RECOVERY_NONE ,由應(yīng)用程序自身處理錯(cuò)誤;若設(shè)置為 MODBUS_ERROR_RECOVERY_LINK ,則經(jīng)過(guò)一段延時(shí) libmodbus 內(nèi)部自動(dòng)嘗試進(jìn)行斷開(kāi)/連接;若設(shè)置為 MODBUS_ERROR_RECOVERY_PROTOCOL ,則在傳輸數(shù)據(jù) CRC 錯(cuò)誤或功能碼錯(cuò)誤的情況下,傳輸會(huì)進(jìn)入延時(shí)狀態(tài),同時(shí)數(shù)據(jù)直接被清除。在 SLAVE/SERVER 端,不推薦使用此函數(shù)。

基本用法舉例:

modbus_set_error_recovery(ctx,MODBUS_ERROR_RECOVERY_LINK|MODBUS_ERROR_RECOVERY_PROTOCOL);

MODBUS_API int modbus_set_socket(modbus t * ctx,int s)

此函數(shù)設(shè)置當(dāng)前 SOCKET 或串口句柄要用于多客戶(hù)端連接到單一服務(wù)器的場(chǎng)合。簡(jiǎn)單用法舉例如下,后續(xù)介紹函數(shù) modbus_tcp_listen() 時(shí)將會(huì)進(jìn)一步介紹相關(guān)用法。

#define NB_CONNECTION 5

modbus_t * ctx;

ctx=modbus_new_tcp("127.0.0.1", 1502)

server_socket = modbus_tcp_listen(ctx,NB_CONNECTION);

FD_ZERO(&rdset);

FD_SET(server_socket,&rdset);

/* ... */

if (FD_ISSET(master_socket,&rdset))

{

  modbus_set_socket(ctx,master_socket);

  rc =modbus_receive(ctx,query);

  if(rc!=-1)

  {

  modbus_reply(ctx,query, rc,mb_mapping);

  }

}

MODBUS_API int modbus_get_response_timeout (modbus_t * ctx, uint32_t * to_sec, uint32_t * to_usec);

MODBUS_API int modbus_set_response_timeout (modbus_t * ctx, uint32_t * to_sec, uint32_t * to_usec);

用于獲取或設(shè)置響應(yīng)超時(shí),注意時(shí)間單位分別是秒和微秒。

MODBUS_API int modbus_get_byte_timeout (modbus_t * ctx, uint32_t * to_sec,uint32_t * to_usec);

MODBUS_API int modbus_set_byte_timeout (modbus_t * ctx, uint32_t * to_sec,uint32_t * to_usec);

用于獲取或設(shè)置連續(xù)字節(jié)之間的超時(shí)時(shí)間,注意時(shí)間單位分別是秒和微秒。

MODBUS_API intmodbus_get_header_length (modbus_t * ctx);

獲取報(bào)文頭長(zhǎng)度。

MODBUS_API int modbus_connect (modbus_t * ctx);

此函數(shù)用于主站設(shè)備與從站設(shè)備建立連接。

在 RTU 模式下,它實(shí)質(zhì)調(diào)用了文件 modbus_rtu.c 中的函數(shù) static int modbus_rtu_connect (modbus_t * ctx) ;在此函數(shù)中進(jìn)行了串口波特率校驗(yàn)位、數(shù)據(jù)位、停止位等的設(shè)置。

在 TCP 模式下,modbus_connect() 調(diào)用了文件 modbus_tcp.c 中的函數(shù) static int_modbus_tcp_connect (modbus_t * ctx ) ;在函數(shù) _modbus_tcp_connect() 中,對(duì) TCP/IP 各參數(shù)進(jìn)行了設(shè)置和連接。

MODBUS_API void modbus_close (modbus_t * ctx);

關(guān)閉 Modbus 連接。在應(yīng)用程序結(jié)束之前,一定記得調(diào)用此函數(shù)關(guān)閉連接在 RTU 模式下,實(shí)質(zhì)是調(diào)用函數(shù) _modbus_rtu_close(modbus_t * ctx) 關(guān)閉串口句柄;在 TCP 模式下,實(shí)質(zhì)是調(diào)用函數(shù) _modbus_tcp_close(modbust * ctx) 關(guān)閉 Socket 句柄。

MODBUS_API void modbus_free (modbus_t * ctx);

釋放結(jié)構(gòu)體 modbus_t 占用的內(nèi)存。在應(yīng)用程序結(jié)束之前,一定記得調(diào)用此函數(shù)

MODBUS_API int modbus_set_debug (modbust * ctx, int flag);

此函數(shù)用于是否設(shè)置為DEBUG模式。

若參數(shù) flag 設(shè)置為T(mén)RUE,則進(jìn)入 DEBUG模式。若設(shè)置為FALSE,則切換為非 DEBUG模式。在 DEBUG模式下所有通信數(shù)據(jù)將按十六進(jìn)制方式顯示在屏幕上,以方便調(diào)試。

MODBUS_API const char * modbus_strerror (int errnum);

此函數(shù)用于獲取當(dāng)前錯(cuò)誤字符串。

2.各類(lèi)Modbus功能接口函數(shù)

MODBUS_API int modbus_read_bits (modbus t * ctx, int addr, int nb, uint8_t * dest);

此函數(shù)對(duì)應(yīng)于功能碼 01(0x01) 讀取線圈/離散量輸出狀態(tài)(Read Coil Status/DOs),其中,所讀取的值存放于參數(shù) uint8_t * dest 指向的數(shù)組空間因此 dest 指向的空間必須足夠大,其大小至少為 nb * sizeof(uint8_t) 個(gè)字節(jié)。

用法舉例:

#define SERVER ID        1
#define ADDRESS START      0
#define ADDRESS END       99

modbus_t * ctx;

uint8_t * tab_rp_bits;
int rc;
int nb;

ctx=modbus_new_tcp("127.0.0.1",502);
modbus_set_debug(ctx,TRUE);
if (modbus_connect(ctx)==-1)

{
  fprintf(stderr,"Connection failed:%sn", modbus_strerror(errno));
  modbus free(ctx);
  return -1;
}

//申請(qǐng)存儲(chǔ)空間并初始化
int nb = ADDRESS_END - ADDRESS_START;
tab_rp_bits = (uint8_t * ) malloc (nb * sizeof(uint8_t));
memset(tab_rp_bits, 0, nb * sizeof(uint8_t));

//讀取一個(gè)線圈
int addr =1;
rc =modbus_read_bits(ctx,addr,1,tab_rp_bits);
if (rc !=1)
{
  printf("ERROR modbus_read_bits_single (%d)n", rc);
  printf("address =%dn", addr);
}

//讀取多個(gè)線圈

rc =modbus_read_bits(ctx,addr,nb,tab_rp_bits);
if (rc !=nb)
{
  printf("ERROR modbus_read_bitsn");
  printf("Address =%d,nb =%dn", addr, nb);
}

//釋放空間關(guān)閉連接

free(tab_rp_bits);
modbus_close(ctx);
modbus_free(ctx);

MODBUS_API int modbus_read_input_bits (modbus_t * ctx, int addr, int nb,uint8_t * dest);

此函數(shù)對(duì)應(yīng)于功能碼 02(0x02) 讀取離散量輸入值(Read Input Status/DIs),各參數(shù)的意義與用法,類(lèi)似于函數(shù) modbus_read_bits() 。

MODBUS_API int modbus_read_registers (modbus_t * ctx, int addr, int nb,uint16_t * dest);

此函數(shù)對(duì)應(yīng)于功能碼 03(0x03) 讀取保持寄存器(Read Holding Register),其中,所讀取的值存放于參數(shù) uint16_t * dest 指向的數(shù)組空間因此 dest 指向的空間必須足夠大,其大小至少為 nb * sizeof(uint16_t) 個(gè)字節(jié)。

當(dāng)讀取成功后,返回值為讀取的寄存器個(gè)數(shù);若讀取失敗,則返回-1。此函數(shù)調(diào)用依賴(lài)關(guān)系如下圖6-5所示。

用法舉例:

img

modbust * ctx;
uint16_t tab_reg[64];
int rc;
int i;

ctx=modbus_new_tcp("127.0.0.1",502);
if (modbusconnect(ctx)==-1)
{
  fprintf(stderr,"Connection failed:%sn", modbus_strerror(errno));
  modbus_free(ctx);
  return -1;
}

//從地址0開(kāi)始連續(xù)讀取10個(gè)

rc =modbus_read_registers(ctx,0,10,tab_reg);
if (rc ==-1)
{
  fprintf(stderr,"%sn",modbus_strerror(errno));
  return -1;
}

for (i=0;i< rc;i++)
{
  printf("reg[%d]=%d(0x%X)n",i,tab_reg[i],tab_reg[i]);
}

modbus_close(ctx);
modbus_free(ctx);

MODBUS_API int modbus_read_input_registers (modbus_t * ctx,int addr, int nb, uint16_t * dest );

此函數(shù)對(duì)應(yīng)于功能碼 04(0x04) 讀取輸人寄存器(Read Iput Register),各參數(shù)的意義與用法,類(lèi)似于函數(shù) modbus_read_registers() 。

此函數(shù)的調(diào)用依賴(lài)關(guān)系如下圖 6-6 所示。

圖6-6函數(shù) modbus_read input_registers()的調(diào)用依賴(lài)關(guān)系

img

MODBUS_API int modbus_write_bit (modbus_t * ctx, int coil_addr, int status):

該函數(shù)對(duì)應(yīng)于功能碼 05(0x05) 寫(xiě)單個(gè)線圈或單個(gè)離散輸出(Force SingleCoil)。其中參數(shù) coil_addr 代表線圈地址;參數(shù) status 代表寫(xiě)值取值只能是TRUE(1)或 FALSE(0) 。

MODBUS_API int modbus_write_register (modbus_t * ctx,int reg_addr, int value):

該函數(shù)對(duì)應(yīng)于功能碼 06(0x06) 寫(xiě)單個(gè)保持寄存器(Preset Single Register)。

MODBUS_API int modbus_write_bits (modbus_t * ctx, int addr, int nb, const uint8_t * data):

該函數(shù)對(duì)應(yīng)于功能碼 15(0x0F) 寫(xiě)多個(gè)線圈(Force Multiple Coils)

參數(shù) addr 代表寄存器起始地址,參數(shù) nb 表示線圈個(gè)數(shù),而參數(shù) const uint8_t * data 表示待寫(xiě)入的數(shù)據(jù)塊。一般情況下,可以使用數(shù)組存儲(chǔ)寫(xiě)入數(shù)據(jù),數(shù)組的各元素取值范圍只能是 TRUE(1)或 FALSE(0) 。

MODBUS_API int modbus_write_registers (modbus_t * ctx, int addr, int nb, const uint16_t * data):

該函數(shù)對(duì)應(yīng)于功能碼 16(0x10) 寫(xiě)多個(gè)保持存器(Preset MultipleRegisters)

參數(shù) addr 代表寄存器起始地址,參數(shù) nb 表示存器的個(gè)數(shù)而參數(shù) const uint16_t * data 表示待寫(xiě)人的數(shù)據(jù)塊。一般情況下,可以使用數(shù)組存儲(chǔ)寫(xiě)入數(shù)據(jù)數(shù)組的各元素取值范圍是 0~0xFFFF 即數(shù)據(jù)類(lèi)型 uint16_t 的取值范圍。

MODBUS_API int modbus_mask_registers (modbus_t * ctx, int addr, uint16_t and_mask, uint16_t or_mask ):

modbus_mask_write_register() 函數(shù)應(yīng)使用以下算法修改遠(yuǎn)程設(shè)備地址“addr”處的保持寄存器的值:

新值 = (current value AND ‘a(chǎn)nd’) OR (‘or’ AND (NOT ‘a(chǎn)nd’)) 。

該功能使用 Modbus 功能代碼 0x16(掩碼單個(gè)寄存器)。

MODBUS_API int modbus_write_and_read_registers (mobus_t * ctx ,

int writer_addr,

int writer_nb,

const uint16_t * src,

int read_addr,

int read_nb,

uint16_t * dest);

modbus_write_and_read_registers() 函數(shù)應(yīng)將 write_nb 保持寄存器的內(nèi)容從數(shù)組 “src” 寫(xiě)入遠(yuǎn)程設(shè)備的地址 write_addr ,然后將 read_nb 保持寄存器的內(nèi)容讀取到遠(yuǎn)程設(shè)備的地址 read_addr 。讀取結(jié)果作為字值(16 位)存儲(chǔ)在 dest 數(shù)組中。

必須注意分配足夠的內(nèi)存來(lái)存儲(chǔ)結(jié)果 dest (至少 nb * sizeof(uint16_t))。該功能使用 Modbus 功能代碼 0x17(寫(xiě)/讀寄存器)。

MODBUS_API int modbus_report_slave_id (modbus_t * ctx, int max_dest, uint8_t * dest):

該函數(shù)對(duì)應(yīng)于功能碼 17(0x11) 報(bào)告從站ID。參數(shù) max_dest 代表最大的存儲(chǔ)空間,參數(shù) dest 用于存儲(chǔ)返回?cái)?shù)據(jù)。返回?cái)?shù)據(jù)可以包括如下內(nèi)容:從站 ID狀態(tài)值(0x00= OFF狀態(tài), 0xFF=ON狀態(tài)) 以及其他附加信息,具體的各參數(shù)意義由開(kāi)發(fā)者指定。

用法舉例:

uint8_t tab_bytes[MODBUS_MAX_PDU_LENGTH];

...

rc =modbus_report_slave_id(ctx, MODBUS_MAX_PDU_LENGTH, tab_bytes);

if (rc >1)
{
  printf("Run Status Indicator: %sn",tab_bytes[1] ?"ON":"OFF");
}

3. 數(shù)據(jù)處理的相關(guān)函數(shù)或宏定義

在libmodbus開(kāi)發(fā)庫(kù)中,為了方便數(shù)據(jù)處理在 modbus.h 文件中定義了一系列數(shù)據(jù)處理宏。

例如獲取數(shù)據(jù)的高低字節(jié)序宏定義:

#define MODBUS_GET_HIGH_BYTE (data) (((data) > >8) & 0xFF)
#define MODBUS_GET_LOW_BYTE (data) ((data) & 0xFF)

對(duì)于浮點(diǎn)數(shù)等多字節(jié)數(shù)據(jù)而言,由于存在字節(jié)序與大小端處理等的問(wèn)題,所以輔助定義了一些特殊函數(shù):

MODBUS_API float modbus_get_float (const uint16_t * src);

MODBUS_API float modbus_get_float_abcd (const uint16_t * src);

MODBUS_API float modbus_get_float_dcba (const uint16_t * src);

MODBUS_API float modbus_get_float_badc (const uint16_t * src);

MODBUS_API float modbus_get_float_cdab (const uint16_t * src);

MODBUS_API void modbus_set_float (float f,uint16_t * dest);

MODBUS_API void modbus_set_float_abcd (float f,uint16_t * dest);

MODBUS_API void modbus_set_float_dcba (float f,uint16_t * dest);

MODBUS_API void modbus_set_float_badc (float f,uint16_t * dest);

MODBUS_API void modbus_set_float_cdab (float f,uint16_t * dest);

當(dāng)然,可以參照 float 類(lèi)型的處理方法,繼續(xù)定義其他多字節(jié)類(lèi)型的數(shù)據(jù)例如int32_t、uint32_t、 int64_t、uint64_t 以及 double 類(lèi)型的讀寫(xiě)函數(shù)。

6.2.5 RTU/TCP關(guān)聯(lián)接口函數(shù)

在文件 modbus.h 的最后位置,有如下語(yǔ)句

#include "modbus-tcp.h"

#include "modbus-rtu.h"

可以發(fā)現(xiàn),除了 modbus.h 包含的接口函數(shù)之外,modbus-rtu.h 和 modbus-tcp.h 也包含了必要的接口函數(shù)。

1. RTU模式關(guān)聯(lián)函數(shù)

MODBUS_API modbus_t * modbus_new_rtu (const char * device, int baud, char parity, int data_bit, int stop_bit):

此函數(shù)的功能是創(chuàng)建一個(gè) RTU 類(lèi)型的 modbus_t 結(jié)構(gòu)體。參數(shù) const char * device 代表串口字符串,在 Windows 操作系統(tǒng)下形態(tài)如 “COMx” ,有一點(diǎn)需要注意的是,對(duì)于串口1串口9來(lái)說(shuō),,傳遞 “COM1”“COM9” 可以 成功 ,但是如果操作對(duì)象為 COM10及以上端口 ,則會(huì)出現(xiàn) 錯(cuò)誤。

產(chǎn)生這種奇怪現(xiàn)象的原因是:微軟預(yù)定義的標(biāo)準(zhǔn)設(shè)備中含有 “COM1” “COM9” 。所以,“COM1” “COM9” 作為文件名傳遞給函數(shù)時(shí)操作系統(tǒng)會(huì)自動(dòng)地將之解析為相應(yīng)的設(shè)備。但對(duì)于 COM10 及以上的串口,“COM10” 之類(lèi)的文件名系統(tǒng)只視之為 一般意義上的文件,而非串行設(shè)備。為了增加對(duì) COM10 及以上串行端口的支持,微軟規(guī)定,如果要訪問(wèn)這樣的設(shè)備,應(yīng)使用這樣的文件名(以COM10 為例):. COM10。

所以,使用時(shí)在代碼中可以如此定義:.

const char * device = “.COM10”;

在Linux操作系統(tǒng)下可以使用”/dev/ttySo”或”/dev/ttyUSB0”等形式的字符串來(lái)表示。而參數(shù) int baud 表示串口波特率的設(shè)置值,例如:9600、19200、57600、115200等。

參數(shù)char parity 表示奇偶校驗(yàn)位,取值范圍:

  • ‘N’:無(wú)奇偶校驗(yàn);
  • ‘E’:偶校驗(yàn);
  • ‘O’:奇校驗(yàn)。

參數(shù) int data_bit 表示數(shù)據(jù)位的長(zhǎng)度,取值范圍為 5、6、7和8。

參數(shù)int stop_bit 表示停止位長(zhǎng)度,取值范圍為1或2。

用法舉例:

modbus t *ctx;

ctx=modbus_new_rtu("\.COM10",115200,'N',8,1);

if (ctx ==NULL)
{
  fprintf(stderr,"Unable to create the libmodbus contextn");
  return -1;
}

modbus_set_slave(ctx,SLAVE_DEVICE_ID);

if (modbus connect(ctx)==-1)
{
  fprintf(stderr,"Connection failed:%sn",modbus_strerror(errno));
  modbus_free(ctx);
  return -1;
}

MODBUS_API int modbus_rtu_set_serial_mode (modbus_t * ctx, int mode):

該函數(shù)用于設(shè)置串口為 MODBUS RTU RS232或MODBUSRTU_RS485模式,此函數(shù)只適用于 Linux 操作系統(tǒng)下。

MODBUS_API int modbus_rtu_set_rts (modbus_t * ctx, int mode)。

MODBUS_API int modbus_rtu_set_custom_rts (modbus_t * ctx, void ( * set_rts) (modbus_t * ctx, int on))。

MODBUS_API int modbus_rtu_set_rts_delay (modbus_t * ctx, int us)。

以上函數(shù)只適用于 Linux 操作系統(tǒng)下,RTS 即Request ToSend 的縮寫(xiě),具體的意義可通過(guò)網(wǎng)絡(luò)搜索,一般情況下,此類(lèi)函數(shù)可忽略。

2. TCP模式關(guān)聯(lián)函數(shù)

*MODBUS_API modbus_t * modbus_new_tcp (const char ip_address, int port) 。

此函數(shù)的功能是創(chuàng)建一個(gè)TCP/IPv4 類(lèi)型的modbus_t 結(jié)構(gòu)體。

參數(shù) const char * ip_address 為IP地址,port 表示遠(yuǎn)端設(shè)備的端口號(hào)。

MODBUS_API int modbus_tcp_listen (modbus_t * ctx, int nb_connection)。

此函數(shù)創(chuàng)建并監(jiān)聽(tīng)一個(gè) TCP/IPv4 上的套接字。

參數(shù)int nb_connection 代表最大的監(jiān)聽(tīng)數(shù)量,在調(diào)用此函數(shù)之前,必須首先調(diào)用modbus_new_tcp() 創(chuàng)建modbus_t結(jié)構(gòu)體。

MODBUS_API int modbus_tcp_accept (modbus_t * ctx,int * s)。

此函數(shù)接收一個(gè) TCP/IPv4 類(lèi)型的連接請(qǐng)求,如果成功將進(jìn)入數(shù)據(jù)接收狀態(tài)。

6.3 libmodbus移植與使用

6.3.1 移植方法

以串口為例,libmodbus支持了windows系統(tǒng)、Linux系統(tǒng)。如果要在Freertos或者裸機(jī)上使用libmodbus,需要移植libmodbus里操作硬件的代碼。

根據(jù)下圖的層次,要移植libmodbus的“后端”,就是構(gòu)造自己的modbus_backend_t結(jié)構(gòu)體:

img

后端modbus_backend_t結(jié)構(gòu)體的成員含義如下:

成員含義
unsigned int backend_type;后端類(lèi)型,是RTU還是TCP
unsigned int header_length;頭部長(zhǎng)度,比如RTU數(shù)據(jù)包前面需要有1字節(jié)的設(shè)備地址,頭部長(zhǎng)度就是1
unsigned int checksum_length;校驗(yàn)碼長(zhǎng)度,RTU的校驗(yàn)碼是2字節(jié)
unsigned int max_adu_length;ADU(數(shù)據(jù)包)最大長(zhǎng)度
set_slave設(shè)置從站地址
build_request_basis設(shè)置RTU請(qǐng)求包的基本數(shù)據(jù),這些數(shù)據(jù)的格式是一樣的,比如req[0]是從設(shè)備地址,req[1]是功能碼,req[2]和req[3]是寄存器地址,req[4]和req[5]是寄存器數(shù)量
build_response_basis設(shè)置RTU回應(yīng)包的基本數(shù)據(jù),這些數(shù)據(jù)的格式是一樣的,比如req[0]是從設(shè)備地址,req[1]是功能碼
prepare_response_tid生產(chǎn)傳輸標(biāo)識(shí)TID,在TCP中使用
send_msg_pre發(fā)送消息前的準(zhǔn)備工作,對(duì)于RTU是填充CRC檢驗(yàn)碼,對(duì)于TCP是填充頭部的Length
send發(fā)送數(shù)據(jù)包
receive接收數(shù)據(jù)包
recv接收原始數(shù)據(jù),receive會(huì)調(diào)用recv得到原始數(shù)據(jù)然后解析出數(shù)據(jù)包
check_integrity檢查數(shù)據(jù)包的完整性
pre_check_confirmation檢查響應(yīng)數(shù)據(jù)包是否有效時(shí),先執(zhí)行pre_check_confirmation做一些簡(jiǎn)單的檢查
connect硬件相關(guān)的連接,對(duì)于RTU就是打開(kāi)串口、設(shè)置串口波特率等;對(duì)于TCP則是連接對(duì)端
is_connected判斷是否已經(jīng)連接
close關(guān)閉連接
flush清空接收到的、未處理的數(shù)據(jù)
select阻塞一段時(shí)間以等待數(shù)據(jù)
free釋放分配的modbus_t等結(jié)構(gòu)體

本節(jié)先寫(xiě)出模板:

根據(jù)這個(gè)源碼:

img

改出:

img

6.3.2 使用USB串口作為后端

基于這2個(gè)程序:

img

img

第1步:合并上述2個(gè)源碼,并修改到能編譯成功(但是libmodbus里對(duì)USB串口的操作),結(jié)果放在如下目錄:

img

第2步,繼續(xù)修改上圖的代碼,實(shí)現(xiàn)USB串口作為后端,得到以下代碼:

img

USB串口的操作函數(shù):

/* 發(fā)送數(shù)據(jù) */

int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);

/* 接收數(shù)據(jù) */

int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout);

6.3.3 libmodbus從機(jī)實(shí)驗(yàn)(USB串口)

本節(jié)源碼為:

img

參考“l(fā)ibmodbus-3.1.10testsunit-test-server.c”,把開(kāi)發(fā)板當(dāng)做從機(jī),使用PC上Modbus Poll軟件讀寫(xiě)開(kāi)發(fā)板:控制LED。

要點(diǎn):

① printf、fprintf、vfprintf都不能使用,改成空的宏

6.3.4 libmodbus主機(jī)實(shí)驗(yàn)(USB串口)

本節(jié)源碼為:

img

參考“l(fā)ibmodbus-3.1.10testsunit-test-client.c”,把開(kāi)發(fā)板當(dāng)做主機(jī),去讀寫(xiě)PC上Modbus Slave軟件模擬的從機(jī)。

6.3.5 使用板載串口作為后端

學(xué)習(xí)本節(jié)課程前,先觀看《3.5.3 面向?qū)ο蠓庋bUART》,并且觀看對(duì)應(yīng)視頻《3-9-1_面向?qū)ο蠓庋bUART_完善收發(fā)功能》、《3-9-2_面向?qū)ο蠓庋bUART_實(shí)現(xiàn)結(jié)構(gòu)體》。

本節(jié)代碼如下:

img

按照下圖連線:調(diào)試、供電、兩個(gè)485互連,使用CH1(左邊的RS485接口)作為主設(shè)備,訪問(wèn)CH2(右邊的RS485接口):

img

1. 使用UART_Device

這2個(gè)視頻,是在開(kāi)始本節(jié)課程之前才補(bǔ)錄的:《3-9-1_面向?qū)ο蠓庋bUART_完善收發(fā)功能》、《3-9-2_面向?qū)ο蠓庋bUART_實(shí)現(xiàn)結(jié)構(gòu)體》。這兩個(gè)視頻里,把UART2、UART4的發(fā)送、接收功能都補(bǔ)全了,并且構(gòu)造了對(duì)應(yīng)的UART_Device結(jié)構(gòu)體,里面實(shí)現(xiàn)了初始化、發(fā)送、接收一個(gè)自己的的函數(shù),如下:

把UART2、UART4封裝為UART_Device的代碼為:“3_程序源碼?1_視頻配套的源碼3-9_面向?qū)ο蠓庋bUARTuart_rtos_all_ok.7z”。需要把它的代碼移植到本節(jié)的工程里:

  • 使用STM32CubeMX配置UART2、UART4:發(fā)送、接收都使用DMA
  • 復(fù)制代碼:CoreSrcusart.c、DriversModule_driveruart_device.c/h

使用STM32CubeMX配置的過(guò)程如下:

  • 使能DMA通道:

img

  • 各個(gè)DMA通道的配置如下:

img

2. 用作后端

把UART2、UART4用作libmodbus后端時(shí),只需要修改這幾個(gè)函數(shù)即可:

img

有兩個(gè)UART_Device,調(diào)用哪個(gè)UART_Device?在使用“modbus_new_st_rtu”創(chuàng)建modbus_t時(shí),根據(jù)傳入的設(shè)備名在modbus_t結(jié)構(gòu)體里記錄對(duì)應(yīng)的UART_Device。_modbus_rtu_connect、_modbus_rtu_send、_modbus_rtu_recv這三個(gè)函數(shù),就可以直接調(diào)用modbus_t結(jié)構(gòu)體里的UART_Device函數(shù)了。

6.3.6 libmodbus實(shí)驗(yàn)(板載串口)

本節(jié)源碼為:

img

按照下圖連線:調(diào)試、供電、兩個(gè)485互連:

img

創(chuàng)建一個(gè)ClientTask,使用CH2(右邊的RS485接口)對(duì)外通信。

創(chuàng)建一個(gè)ServerTask,使用CH1(左邊的RS485接口)讀寫(xiě)從設(shè)備數(shù)據(jù)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • MODBUS
    +關(guān)注

    關(guān)注

    28

    文章

    1742

    瀏覽量

    76663
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    6603

    瀏覽量

    123019
  • 開(kāi)源
    +關(guān)注

    關(guān)注

    3

    文章

    3181

    瀏覽量

    42235
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    信號(hào)與系統(tǒng)6大例題(2

    `信號(hào)與系統(tǒng)6大例題(2)[hide][/hide]`
    發(fā)表于 05-13 23:17

    6管理文件系統(tǒng)

    6 - 管理文件系統(tǒng)
    發(fā)表于 05-13 11:24

    正弦波振蕩電路基礎(chǔ) 6

    正弦波振蕩電路基礎(chǔ) 6  本章主要內(nèi)容:6.1  自激振蕩的基本原理 6.2  LC振蕩電路 6.
    發(fā)表于 04-19 18:12 ?58次下載

    基于FPGA的嵌入式系統(tǒng)設(shè)計(jì)6介紹

    基于FPGA的嵌入式系統(tǒng)設(shè)計(jì) -6-
    發(fā)表于 10-30 10:44 ?0次下載

    6單片機(jī)定時(shí)器串口中斷(20150709213857)

    6單片機(jī)定時(shí)器串口中斷(20150709213857)
    發(fā)表于 12-15 22:28 ?6次下載

    3 MAXPLUS軟件的使用(6節(jié))

    3 MAXPLUS軟件的使用(6節(jié))
    發(fā)表于 07-13 10:07 ?0次下載

    信號(hào)與系統(tǒng)6大例題(2

    信號(hào)與系統(tǒng)6大例題(2
    發(fā)表于 08-07 11:06 ?0次下載

    DSP嵌入式系統(tǒng)開(kāi)發(fā)典型案例,6 數(shù)字和IP電話系統(tǒng)設(shè)計(jì)

    DSP嵌入式系統(tǒng)開(kāi)發(fā)典型案例,6 數(shù)字和IP電話系統(tǒng)設(shè)計(jì)
    發(fā)表于 10-20 14:28 ?6次下載
    DSP嵌入式系統(tǒng)開(kāi)發(fā)典型案例,<b class='flag-5'>第</b><b class='flag-5'>6</b><b class='flag-5'>章</b> 數(shù)字和IP電話系統(tǒng)設(shè)計(jì)

    6 部件工作原理與編程示例

    6 部件工作原理與編程示例
    發(fā)表于 10-27 09:44 ?3次下載
    <b class='flag-5'>第</b><b class='flag-5'>6</b><b class='flag-5'>章</b> 部件工作原理與編程示例

    《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》6、字符設(shè)備驅(qū)動(dòng)

    《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》6、字符設(shè)備驅(qū)動(dòng)
    發(fā)表于 10-27 11:46 ?23次下載
    《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》<b class='flag-5'>第</b><b class='flag-5'>6</b><b class='flag-5'>章</b>、字符設(shè)備驅(qū)動(dòng)

    STM8S BDLC電機(jī) 6源代碼

    STM8S BDLC電機(jī) 6 6.5.1 源代碼
    發(fā)表于 03-05 14:51 ?5次下載

    6:數(shù)字輸入/輸出模塊(I/O)

    6:數(shù)字輸入/輸出模塊(I/O)PPT下載
    發(fā)表于 10-08 14:51 ?19次下載

    6 端接.zip

    6端接
    發(fā)表于 12-30 09:22 ?3次下載

    電工電子技術(shù)基礎(chǔ)6 電工測(cè)量

    電子發(fā)燒友網(wǎng)站提供《電工電子技術(shù)基礎(chǔ)6 電工測(cè)量.ppt》資料免費(fèi)下載
    發(fā)表于 11-21 14:53 ?3次下載
    電工電子技術(shù)基礎(chǔ)<b class='flag-5'>第</b><b class='flag-5'>6</b><b class='flag-5'>章</b> 電工測(cè)量

    2 ANSYS分析基本過(guò)程--7 通用后處理器

    2 ?ANSYS分析基本過(guò)程--7 通用后處理器
    發(fā)表于 08-31 09:31 ?0次下載