网络高级(学习)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;
}
相关推荐
Slow菜鸟11 小时前
AI学习篇(五) | awesome-design-md 使用说明
人工智能·学习
狐狐生风12 小时前
LangChain 向量存储:Chroma、FAISS
人工智能·python·学习·langchain·faiss·agentai
狐狐生风12 小时前
LangChain RAG 基础
人工智能·python·学习·langchain·rag·agentai
ACP广源盛1392462567314 小时前
IX8024与科学大模型的碰撞@ACP#筑牢科研 AI 算力高速枢纽分享
运维·服务器·网络·数据库·人工智能·嵌入式硬件·电脑
努力努力再努力FFF15 小时前
医生对AI辅助诊断感兴趣,作为临床人员该怎么了解和学习?
人工智能·学习
Empty-Filled15 小时前
AI生成测试用例功能怎么测:一个完整实战案例
网络·人工智能·测试用例
码云数智-大飞16 小时前
本地部署大模型:隐私安全与多元优势一站式解读
运维·网络·人工智能
sakiko_16 小时前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
jinanwuhuaguo16 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
汤愈韬16 小时前
三种常用 NAT 的经典案例
网络协议·网络安全·security