fangyan123 发表于 2021-11-30 16:52:20

扩展库使用说明——modbus主机

本帖最后由 fangyan123 于 2021-11-30 17:03 编辑

在这里Modbus主要指Modbus-RTU协议,下面的说明均以Modbus-RTU协议进行说明。如果对Modbus协议不了解,建议先下载最下方的“Modbus协议.pdf”文档熟悉该协议。
1-协议简介
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
2-硬件连接
Modbus协议实际应用一般基于RS485物理层,下图为RS485模块接线图:

在测试协议时,可以通过串口与电脑上位机软件通信进行测试,下面的测试程序都是直接通过电脑上位机进行测试。
3-图形块说明在这里会挑一些重要或者特殊的进行说明。1.读线圈
      
      输入参数:1.超时时间:主机发送数据后,堵塞等待从机响应的最长时间。
                      2.buf:读回来的线圈值,为无符号8位整数的数组。
       返回参数:从机响应超时返回0,不正确类型返回-1,成功返回1。

2.读输入寄存器
      
      输入参数:1.buf:读回来的输入寄存器值,为无符号8位整数类型的数组,也就是会将输入寄存器的16位数据拆分    为2个8位的数据,高位在前低位在后。

3.写多个保持寄存器
      
      输入参数:1.buf:读回来的输入寄存器值,为无符号8位整数类型的数组,也就是会将输入寄存器的16位数据拆分    为2个8位的数据,高位在前低位在后。

4.读保持寄存器
      
      输入参数:1.buf:读回来的输入寄存器值,为无符号8位整数类型的数组,也就是会将输入寄存器的16位数据拆分    为2个8位的数据,高位在前低位在后。

5.读写多个保持寄存器
      
      输入参数:1.buf:该语句会将buf中的数据写入从机,并将从机读取回来的数据又写回buf中,为无符号8位整数类      型的数组,也就是会将输入寄存器的16位数据拆分为2个8位的数据,高位在前低位在后。

6.定时回调函数
      
      需要使用定时器每隔100us调用一次。

4-范例代码
1.读取线圈
图形块以及字符代码


#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    if(mbMaster.ReqReadCoils(1,1,9,buf,500)){
      spi_tft.clear((0x67FF));
      spi_tft.set_cursor(34,0);
      spi_tft.draw_hanzi_12("读线圈测试");
      spi_tft.set_cursor(0,15);
      spi_tft.println((String("buf:") + String(buf[(int)(0)])));
      spi_tft.println((String("buf:") + String(buf[(int)(1)])));
      delay(1000);
    }
}
return 1;
}
效果展示:

2.读离散量输入
图形块以及字符代码

#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    if(mbMaster.ReqReadDiscreteInputs(1,1,9,buf,500)){
      spi_tft.clear((0x67FF));
      spi_tft.set_cursor(34,0);
      spi_tft.draw_hanzi_12("读线圈测试");
      spi_tft.set_cursor(0,15);
      spi_tft.println((String("buf:") + String(buf[(int)(0)])));
      spi_tft.println((String("buf:") + String(buf[(int)(1)])));
      delay(1000);
    }
}
return 1;
}
效果展示:


3.读输入寄存器
图形块及字符代码


#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    if(mbMaster.ReqReadInputRegister(1,1,1,buf,500)){
      spi_tft.clear((0x67FF));
      spi_tft.set_cursor(34,0);
      spi_tft.draw_hanzi_12("读输入测试");
      spi_tft.set_cursor(0,15);
      spi_tft.println((String("buf:") + String(buf[(int)(0)])));
      spi_tft.println((String("buf:") + String(buf[(int)(1)])));
      delay(1000);
    }
}
return 1;
}
效果展示:

4.读保持寄存器
图形块及字符代码

#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    if(mbMaster.ReqReadHoldingRegister(1,1,1,buf,500)){
      spi_tft.clear((0x67FF));
      spi_tft.set_cursor(16,0);
      spi_tft.draw_hanzi_12("读保持寄存器测试");
      spi_tft.set_cursor(0,15);
      spi_tft.println((String("buf:") + String(buf[(int)(0)])));
      spi_tft.println((String("buf:") + String(buf[(int)(1)])));
      delay(1000);
    }
}
return 1;
}
效果展示:

5.写单个线圈
图形块及字符代码

#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,10);
    if(mbMaster.ReqWriteCoil(1,1,0,500)){
      spi_tft.draw_hanzi_12("写线圈成功");
    }
    else{
      spi_tft.draw_hanzi_12("写线圈失败");
    }
    delay(1000);
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,22);
    if(mbMaster.ReqWriteCoil(1,1,1,500)){
      spi_tft.draw_hanzi_12("写线圈成功");
    }
    else{
      spi_tft.draw_hanzi_12("写线圈失败");
    }
    delay(1000);
}
return 1;
}
效果展示:
       无

6.写多个线圈
图形块及字符代码

#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,10);
    buf[(int)(0)] = 0xff;
    if(mbMaster.ReqWriteCoil_S(1,1,6,buf,500)){
      spi_tft.draw_hanzi_12("写线圈成功");
    }
    else{
      spi_tft.draw_hanzi_12("写线圈失败");
    }
    delay(1000);
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,22);
    buf[(int)(0)] = 0;
    if(mbMaster.ReqWriteCoil_S(1,1,6,buf,500)){
      spi_tft.draw_hanzi_12("写线圈成功");
    }
    else{
      spi_tft.draw_hanzi_12("写线圈失败");
    }
    delay(1000);
}
return 1;
}
效果展示:


7.写单个保持寄存器
图形块及字符代码
#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,10);
    if(mbMaster.ReqWriteHoldingRegister(1,1,0x1234,500)){
      spi_tft.draw_hanzi_12("写寄存器成功");
    }
    else{
      spi_tft.draw_hanzi_12("写寄存器失败");
    }
    delay(1000);
}
return 1;
}
效果展示:



8.写多个保持寄存器
图形块及字符代码#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    buf[(int)(0)] = 0x12;
    buf[(int)(1)] = 0x34;
    buf[(int)(2)] = 0x56;
    buf[(int)(3)] = 0x78;
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,10);
    if(mbMaster.ReqWriteHoldingRegister_S(1,1,2,buf,500)){
      spi_tft.draw_hanzi_12("写寄存器成功");
    }
    else{
      spi_tft.draw_hanzi_12("写寄存器失败");
    }
    delay(1000);
}
return 1;
}
效果展示:


7.读写多个保持寄存器
图形块及字符代码
#define MBMASTER_USART1 1

#include <CH32V103.h>
#include "myLib/CH32V_ST7735S.h"
#include "mylib/MB_Master.h"
#include "CH32V_TIM.h"

uint8_t buf;

SPITFT spi_tft(128,128,PC5,PA2,PA4);
MBMaster mbMaster(USART1,9600,USART_Parity_No);
void TIM_attachInterrupt_2() {
mbMaster.TimerCallback();
}

int main(void)
{
CH32_Init();
spi_tft.init();
spi_tft.set_direction(2);
spi_tft.clear((0x67FF));
mbMaster.Init();
TIM_attachInterrupt(TIM2, 100, TIM_attachInterrupt_2);
while(1){
    buf[(int)(0)] = 0x12;
    buf[(int)(1)] = 0x34;
    buf[(int)(2)] = 0x56;
    buf[(int)(3)] = 0x78;
    spi_tft.clear((0x67FF));
    spi_tft.set_cursor(0,10);
    if(mbMaster.ReqReadWriteHoldingRegister_S(1,3,2,1,2,buf,500)){
      spi_tft.draw_hanzi_12("写寄存器成功");
      spi_tft.set_cursor(0,27);
      spi_tft.println((String("buf:") + String(buf[(int)(0)])));
      spi_tft.println((String("buf:") + String(buf[(int)(1)])));
    }
    else{
      spi_tft.draw_hanzi_12("写寄存器失败");
    }
    delay(1000);
}
return 1;
}
效果展示:


附件:
      
         


honestop 发表于 2021-12-30 16:22:06

您好,使用STC8H1K08,测试modbus读保持寄存器功能时,无法正常读取数值。经过检查,发现发送数据modbus的采集命令正常,回复也正常。但是判断接收时为错误。
#define ModBus_Master_UART UART_2

#include <STC8HX.h>
uint32 sys_clk = 24000000;//设置PWM、定时器、串口、EEPROM频率参数
#include "lib/twen_board.h"
#include "mylib/MB_Master.h"
#include "lib/UART.h"

uint8 mylist={0,0,0,0};

void Timer0Init(void)        //100微秒@24.000MHz
{
AUXR &= 0x7f;                //定时器时钟12T模式
TMOD &= 0xf0;                //设置定时器模式
TL0 = 0x38;                        //设定定时初值
TH0 = 0xff;                        //设定定时初值
}

void T_IRQ0(void) interrupt 1 using 1{
MBMasterTimerCallback();
}

void setup()
{
twen_board_init();//天问51初始化
Timer0Init();
MBMasterInit(UART_2, UART2_RX_P10, UART2_TX_P11, 9600, TIM_2, MB_M_PAR_NONE);
uart_init(UART_1, UART1_RX_P30, UART1_TX_P31, 9600, TIM_1);//初始化串口
}

void loop()
{
mylist[(int)(0)] = 1;
mylist[(int)(1)] = 2;
mylist[(int)(2)] = 0;
mylist[(int)(3)] = 1;
if(MBMasterReqWriteHoldingRegister_S(1,1,1,mylist,500)){

}
if(MBMasterReqReadHoldingRegister(1,2,1,mylist,500)){

}
uart_putchar(UART_1, mylist[(int)(0)]);//串口单个字符输出
uart_putchar(UART_1, mylist[(int)(1)]);//串口单个字符输出
uart_putchar(UART_1, mylist[(int)(2)]);//串口单个字符输出
uart_putchar(UART_1, mylist[(int)(3)]);//串口单个字符输出
}

void main(void)
{
setup();
while(1){
    loop();
}
}

chy1985 发表于 2022-1-16 15:39:32

图形例程方便打包发上来吗??

liuheshi 发表于 2022-1-26 14:46:46

从机的使用方法有空也发一下吧:)

asiaschain 发表于 2022-1-30 18:46:30

#include "mylib/MB_Master.h"老大这个文件有么

zly188 发表于 2022-2-4 11:48:40

天问Block软件,文件--项目中心--最新项目,搜:modbus

ccdrc 发表于 2022-4-1 07:06:25

modbus从机程序有问题呀,有完整的吗?可以付费

ylp92155 发表于 2022-4-4 21:32:31

可以直接用到stc32G芯片上吗

大野狼 发表于 2022-11-12 19:39:35

大神有微信吗?我这里有个485问题求助

liuguangfeng 发表于 2023-10-10 21:00:49

大师,如果两个主串口应该怎么排列呢,谢谢
页: [1]
查看完整版本: 扩展库使用说明——modbus主机