网络高级(学习)2024.9.11

目录

Modbus库函数

1.初始化和释放函数

2.功能函数

3.功能案例

[Modbus RTU](#Modbus RTU)

1.特点

2.协议格式

3.编程思路


Modbus库函数

1.初始化和释放函数

modbus_t* modbus_new_tcp(const char *ip, int port)

功能:以TCP方式创建Modbus实例,并初始化

参数:

ip:ip地址

port:端口号

返回值:

成功:Modbus实例

失败:NULL
int modbus_set_slave(modbus_t *ctx, int slave)

功能:设置从机ID

参数:

ctx:Modbus实例

slave:从机ID

返回值:

成功:0

失败:-1
int modbus_connect(modbus_t *ctx)

功能:和从机(slave)建立连接

参数:

ctx:Modbus实例

返回值:

成功:0

失败:-1
void modbus_free(modbus_t *ctx)

功能:释放Modbus实例

参数:

ctx:Modbus实例
void modbus_close(modbus_t *ctx)

功能:关闭套接字

参数:

ctx:Modbus实例


2.功能函数

int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01

参数:

ctx :Modbus实例

addr :寄存器起始地址

nb:寄存器个数

dest:得到的状态值
int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)

功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02

参数:

ctx:Modbus实例

addr :寄存器起始地址

nb:寄存器个数

dest :得到的状态值

返回值:成功:返回nb的值
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03

参数:

ctx:Modbus实例

addr :寄存器起始地址

nb:寄存器个数

dest:得到的寄存器的值

返回值:

成功:读到寄存器的个数

失败:-1
int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)

功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04

参数:

ctx:Modbus实例

addr :寄存器起始地址

nb:寄存器个数

dest :得到的寄存器的值

返回值:

成功:读到寄存器的个数

失败:-1
int modbus_write_bit(modbus_t *ctx, int addr, int status);

功能:写入单个线圈的状态(对应功能码为0x05

参数:

ctx :Modbus实例

addr:线圈地址

status:线圈状态

返回值:

成功:0

失败:-1
int modbus_write_register(modbus_t *ctx, int addr, int value);

功能: 写入单个寄存器(对应功能码为0x06

参数:

ctx:Modbus实例

addr:寄存器地址

value:寄存器的值

返回值:

成功:0

失败:-1
int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);

功能:写入多个连续线圈的状态(对应功能码为15

参数:

ctx:Modbus实例

addr:线圈地址

nb:线圈个数

src:多个线圈状态

返回值:

成功:0

失败:-1
int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);

功能:写入多个连续寄存器(对应功能码为16

参数:

ctx:Modbus实例

addr:寄存器地址

nb:寄存器的个数

src:多个寄存器的值

返回值:

成功:0

失败:-1


3.功能案例

读取保持寄存器的值03功能:

cpp 复制代码
#include <stdio.h>
#include "modbus-tcp.h"
#include "modbus.h"

int main(int argc, char const *argv[])
{
    // 创建modbus实例
    modbus_t *ctx;
    ctx = modbus_new_tcp(argv[1], 502);
    if (ctx == NULL)
    {
        perror("modbus new tcp失败");
        return -1;
    }

    // 设置从机ID
    modbus_set_slave(ctx, 1);

    // 建立连接
    if (modbus_connect(ctx) < 0)
    {
        printf("modbus connect err\n");
        modbus_free(ctx);
        return -1;
    }
    printf("connect ok\n");
    uint16_t dest[32];
    int addr, nb;
    printf("请输入寄存器起始地址,寄存器个数:");
    scanf("%d %d", &addr, &nb);
    getchar();
    int mrr = modbus_read_registers(ctx, addr, nb, dest);
    if (mrr <= 0)
    {
        perror("读保持寄存器的值失败");
        return -1;
    }
    else
    {
        for (int i = 0; i < mrr; i++)
        {
            printf("寄存器的值为:%d\n", dest[i]);
        }
    }

    // 释放
    modbus_free(ctx);
    modbus_close(ctx);

    return 0;
}

写入单个线圈的状态05功能:

cpp 复制代码
#include <stdio.h>
#include "modbus-tcp.h"
#include "modbus.h"

int main(int argc, char const *argv[])
{
    // 创建modbus实例
    modbus_t *ctx;
    ctx = modbus_new_tcp(argv[1], 502);
    if (ctx == NULL)
    {
        perror("modbus new tcp失败");
        return -1;
    }

    // 设置从机ID
    modbus_set_slave(ctx, 1);

    // 建立连接
    if (modbus_connect(ctx) < 0)
    {
        printf("modbus connect err\n");
        modbus_free(ctx);
        return -1;
    }
    printf("connect ok\n");
    uint16_t dest[32];
    int addr, status;
    printf("请输入线圈地址,线圈状态:");
    scanf("%d %d", &addr, &status);
    getchar();
    int mrr = modbus_write_bit(ctx, addr, status);
    if (mrr <= 0)
    {
        perror("写入单个线圈的状态失败");
        return -1;
    }
    else
    {
        printf("写入单个线圈的状态成功\n");
    }

    // 释放
    modbus_free(ctx);
    modbus_close(ctx);

    return 0;
}

Modbus RTU

1.特点

(1)Modbus RTU和Modbus ASCII协议是基于串口进行通信的协议,在一般工业场景使用modbus RTU的场景还是更多一些

(2)与modbus TCP不同的是RTU没有报文头MBAP字段,但是在尾部增加了两个CRC检验字节(CRC16),因为网络协议中自带校验,所以在TCP协议中不需要使用CRC校验码


2.协议格式

ModbusRTU数据帧包含地址码、功能码、数据、校验码四部分

地址码:1个字节的从机地址码,=0:广播地址,=1-247:从机地址,=248-255:保留

功能码:与Modbus TCP相同

数据区:数据区包含这么几部分:起始地址、数量、数据,这三项是大端模式

CRC校验:两个字节,校验的数据范围为:地址码+功能码+数据区,校验码的产生可以通过函数自动生成。


3.编程思路

com1在虚拟机识别的是一个设备文件: /dev/ttyS1就是对这个文件进行操作--文件IO

(1)打开设备文件 //open

(2)对设备文件进行配置:对com1设置,波特率、数据位、停止位等

(3)写文件

write

(4)读文件

read

4.代码

Crc_Calc.h:

cpp 复制代码
#ifndef _CRC_CALC_H_
#define _CRC_CALC_H_

#include "stdint.h"

#ifdef _CRC_CALC_C_
#define CRC_CALC_EXT
#else
#define CRC_CALC_EXT extern
#endif


unsigned short GetCRC16(unsigned char *ptr,  unsigned char len);

#endif 

Crc_Calc.c:

cpp 复制代码
#define _CRC_CALC_C_
#include "stdint.h"
#include "stdio.h"

unsigned char const TabH[] = {  //CRC高位字节值表
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
                                0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
                                0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
                                0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
                                0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
                                0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
                                0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
                                0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
                                0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
                                0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
                                0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
                                0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
                                0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
                                0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
                                0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
                             } ;
unsigned char const TabL[] = {  //CRC低位字节值表
                                0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
                                0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
                                0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
                                0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
                                0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
                                0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
                                0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
                                0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
                                0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
                                0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
                                0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
                                0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
                                0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
                                0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
                                0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
                                0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
                                0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
                                0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
                                0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
                                0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
                                0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
                                0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
                                0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
                                0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
                                0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
                                0x43, 0x83, 0x41, 0x81, 0x80, 0x40
                             } ;

/*******************************************************************************
** 函数名称: crc
** 功能描述: CRC校验函数
** 参    数: *puchMsg-要校验数据的指针
             usDataLen-要校验数据的长度
** 返 回 值: crc-16位的CRC校验值      
*******************************************************************************/
unsigned short GetCRC16(unsigned char *ptr,  unsigned char len)
{ 
    unsigned int index;
    unsigned char crch = 0xFF;  //高CRC字节
    unsigned char crcl = 0xFF;  //低CRC字节


    while (len--)  //计算指定长度的CRC
    {
        index = crch ^ *ptr++;
        crch = crcl ^ TabH[index];
        crcl = TabL[index];
    }
    
    return ((crch<<8) | crcl);
} 

serial_init.c:

cpp 复制代码
#include <termios.h>

void uart_init(int fd)
{
    struct termios options;
    //设置串口属性
	//获取串口原有属性
    tcgetattr(fd, &options);
	//激活选项CLOCAL(本地连接)和CREAD(接受使能)
    options.c_cflag |= ( CLOCAL | CREAD );
	//设置字符大小
    options.c_cflag &= ~CSIZE;
    //设置流控
    options.c_cflag &= ~CRTSCTS;
	//设置8位数据位
    options.c_cflag |= CS8;
	//设置停止位
    options.c_cflag &= ~CSTOPB;
    //忽略奇偶错字符
    options.c_iflag |= IGNPAR;
    //将输入的CR转换为NL和停止输出控制流起作用
    options.c_iflag &= ~(ICRNL | IXON);
    options.c_oflag = 0;
    options.c_lflag = 0;
	//设置波特率(输入和输出的波特率)
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
	//激活配置
    tcsetattr(fd, TCSANOW, &options);
}

modbus_rtu.c:

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "stdint.h"
#include <termios.h>
#include "Crc_Calc.h"

void uart_init(int fd);

int main(int argc, char const *argv[])
{
    int fd;
    fd = open("/dev/ttyS1", O_RDWR);
    if (fd < 0)
    {
        perror("open失败");
        return -1;
    }
    // 对串口进行初始化
    uart_init(fd);

    // 写一个线圈的状态
    uint8_t buf[16] = {0x01, 0x05, 0x00, 0x00, 0xFF, 0x00};
    unsigned short crc;
    crc = GetCRC16(buf, 6);
    buf[6] = crc >> 8;
    buf[7] = crc & 0xff;
    write(fd, buf, sizeof(buf));
    printf("发送成功\n");

    uint8_t buf1[16] = {0};

    int ret = read(fd, buf1, sizeof(buf1));

    for (int i = 0; i < ret; i++)
    {
        printf("%#x ", buf1[i]);
    }
    printf("\n");

    close(fd);
    return 0;
}
相关推荐
梁辰兴28 分钟前
计算机网络基础:使用集线器的星型拓扑
服务器·网络·计算机网络·集线器·计算机网络基础·梁辰兴·星型拓扑
专注于大数据技术栈42 分钟前
java学习--StringBuilder
java·学习
疆鸿智能研发小助手1 小时前
造纸厂车间联网改造记:疆鸿智能MODBUS TCP与PROFIBUS的桥梁搭建
modbus·modbus tcp·工业自动化·工业通讯·协议转换网关·profibus
qcwl662 小时前
操作系统 真象还原 学习笔记#13
笔记·学习
元亓亓亓2 小时前
考研408--计算机网络--day10--传输层&UDP&TCP
tcp/ip·计算机网络·考研·udp·408
车载测试工程师2 小时前
CAPL学习-CAN相关函数-概述
网络协议·学习·capl·canoe
roman_日积跬步-终至千里2 小时前
【人工智能导论】08-学习-如何让计算机理解序列数据——用RNN/LSTM建模时序依赖,用文本嵌入表示序列元素
人工智能·rnn·学习
TG:@yunlaoda360 云老大2 小时前
如何在华为云国际站代理商控制台进行SFS Turbo的性能与容量核查?
服务器·网络·数据库·华为云
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商的CBR主要有什么作用呢?
java·网络·华为云
m0_689618282 小时前
30 分钟打印!多材料3D打印软机器人内置驱动 + 自主避障
笔记·学习·机器人