**
QT使用libmodbus庫與施耐德PLC通信
**
1.環境配置
2.modbus簡介
3.libmodbus簡介
4.施耐德PLC位址映射
5.程式源碼
1.環境配置
我目前使用的環境為WIN10 + MSVC2013 X64 + QT 5.9.9 +QT Creator 4.11.0
2.modbus簡介
modbus作為一種通信協定,支援RS232,RS485以及以太網等裝置。請求建立連接配接的稱為主站master,
作出響應的稱為從站slave,主站可以單獨與一個從站進行通訊,也可以以廣播的方式進行通信。
modbus的資料傳輸支援兩種方式:一種是ASCLL方式,一種是RTU方式。
兩種方式的主要差別是;前者以ascll碼的方式進行傳輸,調試起來更加直覺,但是傳輸效率低,因為一個ascll碼的二進制是8位,相反,後者以16位資料傳輸,效率要高得多。以傳輸資料0xA8為例,ascll傳輸需要将‘A’和’8’分開傳輸,也即0x41和0x38,而rtu直接傳輸0xA8即可。
從站位址從0-247,0是廣播位址所有從機都可以識别,是以實際上從機的尋址範圍是1-247,主從通信時從站位址設為1即可
modbus的功能碼
modbus通信資料格式,隻介紹用到的幾個:
關于CRC(Modbus)校驗,這裡貼一個連結,做調試用
CRC線上校驗
3.libmodbus簡介
關于libmodbus的使用網上很多,我也看了許多,有兩個寫的還是很全的
libmodbus的編譯
libmodbus的使用
4.施耐德PLC位址映射
其他PLC的位址映射見這裡
5.程式源碼
在.pro檔案中連結WS2_32.LIB庫,以及頭檔案路徑
LIBS += -L$$PWD/lib/ -lWS2_32
INCLUDEPATH += $$PWD/libmodbus
這是整個工程檔案
libmodbus檔案夾中有
在.h檔案中聲明modbus對象
.c檔案構造函數中添加
mb = modbus_new_rtu("COM5", 38400, 'N', 8, 1);
modbus_set_slave(mb, 1); //設定modbus從機位址
modbus_set_response_timeout(mb, 0, 1000000);/* 逾時時間1s */
析構函數中添加
MainWindow::~MainWindow()
{
modbus_close(mb);
modbus_free(mb);
delete ui;
}
裝置連接配接
if(modbus_connect(mb) < 0)
{
ui->textEdit->append(QString::fromLocal8Bit("連接配接失敗"));
}
else {
ui->textEdit->append(QString::fromLocal8Bit("連接配接成功"));
}
0x01碼 (讀QX輸出線圈)
void MainWindow::on_pushButton_01_clicked()
{
qDebug() << "0x01";
uint8_t dest[8] = {0};
if(modbus_read_bits(mb, 0, 8, dest) != 8 )/* 讀輸出端 */
{
qDebug() << "Read Coils Error";
}else{
qDebug() << "Read Coils Success";
}
}
0x02碼 (讀取IX離散輸入)
void MainWindow::on_pushButton_02_clicked()
{
qDebug() << "0x02";
uint8_t dest_2[8] = {0};
modbus_read_input_bits(mb, 0, 8, dest_2); /* 讀輸入端 */
qDebug() << dest_2[0] << ":" << dest_2[1] << ":" << dest_2[2] << ":" << dest_2[3];
qDebug() << dest_2[4] << ":" << dest_2[5] << ":" << dest_2[6] << ":" << dest_2[7];
}
0x03碼(讀取MW寄存器)
void MainWindow::on_pushButton_03_clicked()
{
qDebug() << "0x03";
uint16_t reg[5];
int rc = modbus_read_registers(mb, 0, 5, reg);
for(int i = 0; i < rc; i++) {
QString str = QString::fromLocal8Bit("寄存器reg[%1]的值為:%2").arg(i+300).arg(decTobin(QString::number(reg[i]))); /* 讀多個保持寄存器 */
ui->textEdit->append(str);
}
}
0x10碼 (寫MW寄存器)
void MainWindow::on_pushButton_10_clicked()
{
/* MW0 40001 */
/* MW359 40360*/
qDebug() << "0x10";
uint16_t tab_regs[5] = {206,0,1,0,0};
if(modbus_write_registers(mb, 360, 5, tab_regs) == (-1))
{
qDebug() << "write error";
}
}
以下是我修改後的使用0X01,0X02,0X03,0X10的例子供大家參考
void MainWindow::on_btnConfirm_clicked()
{
byte keyValue = 1 << 5;
qDebug() << keyValue;
byte mode = ui->comboBoxMode->currentIndex(); /* 0 1 2 3 */
qDebug() << mode;
int plateNum = ui->lineEditNum->text().toUInt();
qDebug() << plateNum;
byte cmode = 0;
switch (mode) {
case 0: cmode = 1 << 0; break;
case 1: cmode = 1 << 1; break;
case 2: cmode = 1 << 2; break;
case 3: cmode = 1 << 3; break;
default:cmode = 0; break;
}
qDebug() << cmode;
uint16_t tab_reg[5] = {plateNum, 0, cmode, keyValue, 0};/* 數組實際為如 101,0,1,32,0 */
byte rc = modbus_write_registers(mb, 360, 5, tab_reg); /* 判斷寫入的資料長度是不是5個,點對點通信時從站是有傳回值的 */
if(rc == sizeof (tab_reg)/sizeof (tab_reg[0])){
qDebug() << "write success";
}
}
void MainWindow::on_btnViewPlate_clicked() /* 檢視擴充闆輸入輸出狀态 */
{
uint16_t reg[2];
int rc = modbus_read_registers(mb, 0, 2, reg);
for(byte i = 0; i < rc; i++){
ui->textEdit->append(QString::fromLocal8Bit("擴充闆0%1").arg(i+1));
ui->textEdit->append("----------------------------");
for(byte j = 0; j < 16; j++){
QString str = QString("%1 : %2").arg(j+1).arg( (reg[rc] >> j) & 0x0001);/* 擷取位狀态 */
ui->textEdit->append(str);
}
}
}
void MainWindow::on_btnViewPlc_clicked() /* 檢視PLC輸入輸出點狀态 */
{
uint8_t dest[8] = {0};
byte rc = modbus_read_input_bits(mb, 0, 8, dest); /* 讀輸入端 */
for(byte i = 0; i < rc; i++){
QString in = QString("IX0.%1 = %2").arg(i).arg(dest[i]);
ui->textEdit->append(in);
}
memset(dest, '\0', sizeof (dest));
ui->textEdit->append("---------------------------");
rc = modbus_read_bits(mb, 0, 8, dest);
for(byte i = 0; i < rc; i++){
QString in = QString("QX0.%1 = %2").arg(i).arg(dest[i]);
ui->textEdit->append(in);
}
memset(dest, '\0', sizeof (dest));
}
輸入車位号206,點選确認,打開modscan32檢視一下PLC内部值是否改變
可以看到已經寫入成功
最後,說一下我調試的過程,首先先寫好一個測試用的DEMO,我把常用的幾個功能碼都使用了用按鈕觸發,就是上面貼的代碼,有幾個我沒貼,然後使用軟體VSPD虛拟兩個序列槽出來,再用軟體Modbus Slave來模拟從站,就可以對代碼進行調試了。如果有硬體,可以使用兩個USB轉485并聯與PLC相連,打開序列槽調試軟體比如我用的XCOM2.0,和你自己寫的軟體分别連接配接一個COM口,這樣你發送給PLC的資料就可以顯示在序列槽調試軟體上了,這裡要注意的是,點對點測試的時候,序列槽調試軟體會收到兩條資料,一條是你發送給PLC的資料,一條是PLC傳回的資料,一開始我以為是我發送的資料格式有誤。好了,今天就寫到這裡,關于工程檔案已經上傳CSDN了,積分窘迫的同學可以評論或者私信我,分享快樂。