网络高级(学习)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;
}
相关推荐
...6227 小时前
WinForm中Socket通信服务端客户端代码实现讲解
tcp/ip·c#·socket
m0_734571768 小时前
深入理解5g <十二> pucch
网络·5g
蓝天下的守望者8 小时前
DRAM结构学习
学习·dram
闲云野鹤在人间8 小时前
Docker入门|第3章 镜像详解
linux·网络·docker·容器·centos·云计算·php
一隅论数智8 小时前
数据消费主体变了:智能时代如何重构业务管理逻辑
大数据·人工智能·经验分享·笔记·学习·行业数字化
luj_17688 小时前
击穿分析五大关键步骤
开发语言·网络·c++·经验分享·算法
wuyk5558 小时前
从零吃透 MQTT 通信|第 11 章 MQTT 项目调试验证、性能优化、常见疑难问题、OTA 升级基础
c语言·开发语言·stm32·学习·性能优化
传奇开心果编程9 小时前
【xilem0.4基础语法学与练】第53课 to_do_mvc 官方示例代码深度解析
学习·rust·前端框架
开开心心就好9 小时前
低年级识字练笔顺工具,电脑和安卓端都能用
前端·javascript·网络·安全·scala·erlang·语音识别
个性小王9 小时前
配置kali在线源(阿里云)
网络·网络安全