RS485协议是什么?它和Modbus协议的区分以及C语言实现

6 篇文章 1 订阅
订阅专栏
4 篇文章 0 订阅
订阅专栏

1.RS485的定义

相信做自动化或者智能设备的朋友都听说过RS485协议,RS485通信协议是一种多点通信协议,它允许多个设备在同一总线上进行通信,且每个设备都可以发送和接收数据。RS485通讯协议采用差分信号传输,具有高速、远距离、可靠性强等特点,可实现长距离的数据传输。

RS485通信协议支持半双工通信模式,在同一总线上可以连接多个驱动器和接收器,方便建立设备网络。此外,RS485通信协议的接口电平低,不易损坏芯片,电平与TTL电平兼容,方便与TTL电路连接。RS485通信协议是一种适用于工业控制系统和智能家居等领域的通信协议,具有高速、远距离、可靠性强等优点,能够满足大量数据传输的需求,并提高数据传输的效率和实时性。

2.RS485与Modbus区别

协议性质:RS485是一种物理层通信标准,主要定义了电气特性、信号传输方式和连接方式等,而Modbus是一种通信协议,定义了一种常用的通信格式和规则,用于在主设备和从设备之间进行数据交换。

应用范围:RS485通常用作物理层协议,支持Modbus协议,而Modbus更常用于工业控制领域,是一种通用的通信数据格式,支持RS485、RS232等串口。

所以,RS485和Modbus在协议性质、应用范围和功能特性方面存在明显的区别。在工业控制和智能设备通信领域,二者常常被结合使用,以实现高效、稳定的数据传输。

在物联网中,RS485协议被广泛应用于各种领域,如工业控制、智能家居、城市管理、智能交通等。在这些领域中,RS485协议可以实现设备之间的快速、稳定、可靠的数据传输,提高设备的智能化程度和用户体验。例如,在智能家居中,RS485协议可以用于家庭内部的各种设备之间的通信,包括智能电视、智能音响、智能照明、智能安防等,实现设备的互联互通和集中控制。

3.MODBUS通讯协议及编程

MODBUS通讯协议大致分为以下几种

Modbus-RTU+Modbus-ASCII
Modbus-TCP
Modbus-Plus

Modbus是主从方式通信,也就是说,不能同步进行通信,总线上每次只有一个数据进行传输,即主机发送,从机应答,主机不发送,总线上就没有数据通信.,本文主要讲解RTU协议

3.1. Modbus-RTU协议

这种协议是基于异步串行通信上,一般的介质有:RS-232,RS485,RS-422上,这也是工业上使用的最多的。

3.1.1. 帧结构

帧结构 = 地址 + 功能吗 + 数据 + 校验

地址: 占用一个字节,范围0-255,其中有效范围是1-247,其他有特殊用途,比如255是广播地址(广播地址就是应答所有地址,正常的需要两个设备的地址一样才能进行查询和回复)

功能码:" 占用一个字节,功能码的意义就是,知道这个指令是干啥的,比如你可以查询从机的数据,也可以修改数据,所以不同功能码对应不同功能.

数据: 根据功能码不同,有不同结构,在后续的实例中有说明;

校验: 为了保证数据不错误,增加这个,然后再把前面的数据进行计算看数据是否一致,如果一致,就说明这帧数据是正确的,我再回复;如果不一样,说明你这个数据在传输的时候出了问题,数据不对的,所以就抛弃了;

字符结构

通讯数据结构

3.1.2.常用功能码

03-主机需要发送起始地址+寄存器数量,从机回复总字节数+数据;
06-主机发送起始地址+数据内容(因为你只需要修改一个,所以起始地址就是所要修改的地址),从机返回起始地址+数据内容(发现居然一样!)
10-主机发送起始地址+寄存器个数+总字节数+数据,从机返回起始地址+寄存器数量

.3常用功能码使用举例分析
1.3.1 功能码-0x03读保持寄存器

测试功能描述:
现在我是主机,我要查询从机地址为1的数据

主机发送: 01 03 00 00 00 01 84 0A
从机回复: 01 03 02 12 34 B5 33

3.2.C语言实现

mbrtu_master.h

#ifndef MBRTU_MASTER_H_
#define MBRTU_MASTER_H_

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "mbrtu_master.h"
#include "usart.h"
#include "tim.h"
#include "main.h"
#include "usart.h"
#include "stdio.h"  
///
/// MODBUS RTU 主机控制结构
///
///
typedef struct
{
	//
	// 收发数据缓存
	//
	uint8_t ucBuf[8];//读取可能是4个字节
	
	//
	// 收发数据状态
	//
	uint16_t usStatus;
	
	//
	// 如果使用了RTOS需要进行互斥,那么需要实现以下两个函数的绑定
	//
	void (*lock)(void);
	void (*unlock)(void);
	
	//
	// 微秒延时函数,用于等待超时
	//
	void (*delayms)(uint32_t nms);
	
	//
	// 定时器启动和停止函数
	//
	void (*timerStop)(void);
	void (*timerStart)(void);
	
	//
	// 发送数据函数,可以是串口、TCP等
	//
	uint32_t (*sendData)(const void* buf, uint32_t len);
	//把接受的数   
	uint32_t (*sendDataLog)(const void *buf, uint32_t len);
	//
	// 以下四个回调函数分别是:读线圈、读离散量输入、读保持寄存器、读输入寄存器
	//
	void (*readCoilsCallback)(uint16_t usStartAddr, uint16_t usNum, const uint8_t* pucBitsOfCoilsState, uint16_t usLen);
	void (*readDiscreteInputsCallback)(uint16_t usStartAddr, uint16_t usNum, const uint8_t* pucBitsOfDiscreteInputsState, uint16_t usLen);
	void (*readHoldingRegistersCallback)(uint16_t usStartAddr, uint16_t usNum, const uint8_t* pusHoldingRegistersVal, uint8_t usLen);
	void (*readInputRegistersCallback)(uint16_t usStartAddr, uint16_t usNum, const uint16_t* pusInputRegistersVal, uint16_t usLen);

}MBRTUMaterTypeDef;


static void timerStop(void);
static void timerStart(void);
static void delayms(uint32_t nms);
static uint32_t sendData(const void *buf, uint32_t len);
static uint32_t sendDataLog(const void *buf, uint32_t len);
static void readCoilsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfCoilsState, uint16_t usLen);
static void readDiscreteInputsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfDiscreteInputsState, uint16_t usLen);
static void readHoldingRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pusHoldingRegistersVal, uint8_t usLen);
static void readInputRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusInputRegistersVal, uint16_t usLen);


///
/// MODBUS RTU 主机 API
///
///
int MBRTUMasterReadCoils(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadDiscreteInputs(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadHoldingRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterReadInputRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout);
int MBRTUMasterWriteSingleCoil(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint8_t ucState, uint16_t usTimeout);
int MBRTUMasterWriteSingleRegister(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usRegVal, uint16_t usTimeout);
int MBRTUMasterWriteMultipleCoils(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint8_t* pucStateBitsBuf, uint16_t usTimeout);
int MBRTUMasterWriteMultipleRegisters(MBRTUMaterTypeDef* psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint16_t* pusRegVal, uint16_t usTimeout);


///
/// MODBUS RTU 主机接收数据回调函数和超时回调函数
/// 
/// MBRTUMasterRecvByteISRCallback:放置于串口接收中断中
/// MBRTUMasterTimerISRCallback:放置于定时器超时中断中
///
void MBRTUMasterRecvByteISRCallback(MBRTUMaterTypeDef* psModbus, uint8_t ucByte);
void MBRTUMasterTimerISRCallback(MBRTUMaterTypeDef* psModbus);
//发送日志显示报文
void MBRTUMasterSendDataLog(MBRTUMaterTypeDef *pMaster);
int16_t GetModbusCRC16_Cal(uint8_t *data, uint32_t len);
#endif /* MBRTU_MASTER_H_ */

mbrtu_master.c

/*
 * mbrtu_master.c
 */

#include "mbrtu_master.h"
#include "inttypes.h"

char str[50] = {0};
char str1[50] = {0};

static uint16_t usMBCRC16(uint8_t *pucFrame, uint16_t usLen)
{
    static const uint8_t aucCRCHi[] =
        {
            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};

    static const uint8_t aucCRCLo[] =
        {
            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};

    uint8_t ucCRCHi = 0xFF;
    uint8_t ucCRCLo = 0xFF;
    int iIndex;

    while (usLen--)
    {
        iIndex = ucCRCLo ^ *(pucFrame++);
        ucCRCLo = (uint8_t)(ucCRCHi ^ aucCRCHi[iIndex]);
        ucCRCHi = aucCRCLo[iIndex];
    }
    return (uint16_t)(ucCRCHi << 8 | ucCRCLo);
}
int16_t GetModbusCRC16_Cal(uint8_t *data, uint32_t len)//Modbus-CRC校验-----计算法
{
	uint8_t temp;
	uint16_t wcrc = 0XFFFF;//16位crc寄存器预置
	uint32_t i = 0, j = 0;//计数
	for (i = 0; i < len; i++)//循环计算每个数据
	{
		temp = data[i] & 0X00FF;//将八位数据与crc寄存器亦或
		wcrc ^= temp;						//将数据存入crc寄存器
		for (j = 0; j < 8; j++)	//循环计算数据的
		{
			if (wcrc & 0X0001)//判断右移出的是不是1,如果是1则与多项式进行异或。
			{
				wcrc >>= 1;//先将数据右移一位
				wcrc ^= 0XA001;//与上面的多项式进行异或
			}
			else//如果不是1,则直接移出
			{
				wcrc >>= 1;//直接移出
			}
		}
	}

  return ((wcrc << 8)|(wcrc>>8));//高低位置换
}
//读取数据
static uint32_t MBRTUMasterRead(MBRTUMaterTypeDef *pMaster, uint8_t ucSlaveAddr, uint8_t ucCmd, uint16_t usStartAddr, uint16_t usNum)
{
    uint16_t crc;

    pMaster->ucBuf[0] = ucSlaveAddr;
    pMaster->ucBuf[1] = ucCmd;
    pMaster->ucBuf[2] = ((usStartAddr & 0XFF00) >> 8);
    pMaster->ucBuf[3] = (usStartAddr & 0XFF);
    pMaster->ucBuf[4] = ((usNum & 0XFF00) >> 8);
    pMaster->ucBuf[5] = (usNum & 0XFF);

    crc = usMBCRC16((uint8_t *)pMaster->ucBuf, 6);
    pMaster->ucBuf[6] = (uint8_t)(crc & 0xFF);
    pMaster->ucBuf[7] = (uint8_t)(crc >> 8);
	  //pMaster->sendDataLog(pMaster->ucBuf, 8);//发送日志
		//char str[40] = {0};
		//sprintf(str,"%s0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\r\n","发送:",pMaster->ucBuf[0],pMaster->ucBuf[1],pMaster->ucBuf[2],pMaster->ucBuf[3],pMaster->ucBuf[4],pMaster->ucBuf[5],pMaster->ucBuf[6],pMaster->ucBuf[7]);
		//sendLOGStr(str);
    return pMaster->sendData(pMaster->ucBuf, 8);
}
void MBRTUMasterSendDataLog(MBRTUMaterTypeDef *pMaster)
{
	//pMaster->sendDataLog(pMaster->ucBuf, 8);
	
	//if(pMaster->ucBuf[0] != '\0')
	//{//接受485数据
	//	sendLOGStr(pMaster->ucBuf);
	//	pMaster->ucBuf[0] = 0;
//	}
}
/**
 * 主机读取线圈状态
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的线圈起始地址
 * @param  usCmd          0x01
 * @param  usNum          要读取的线圈数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadCoils(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X01, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X01)
            {
                psModbus->readCoilsCallback(usAddress, usNum, &psModbus->ucBuf[3], psModbus->ucBuf[2]);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机读取离散量输入
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的离散量起始地址
 * @param  usCmd          0x02
 * @param  usNum          要读取的离散量数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadDiscreteInputs(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X02, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X02)
            {
                psModbus->readDiscreteInputsCallback(usAddress, usNum, &psModbus->ucBuf[3], psModbus->ucBuf[2]);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机读取保持寄存器!!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的保持寄存器起始地址
 * @param  usCmd          0x03
 * @param  usNum          要读取的保持寄存器数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadHoldingRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X03, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X03)
            {
                psModbus->readHoldingRegistersCallback(usAddress, usNum, (const uint8_t *)&psModbus->ucBuf[3], psModbus->ucBuf[2]);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }
		
		char str1[40] = {0};
		sprintf(str1,"%02X%s%d\r\n",psModbus->ucBuf[0],"号伺服电机查询状态:",ret);
		sendLOGStr(str1);
    return ret;
}

/**
 * 主机读取输入寄存器
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要读取的输入寄存器起始地址
 * @param  usCmd          0x04
 * @param  usNum          要读取的输入寄存器数量
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterReadInputRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, uint16_t usTimeout)
{
    int ret = -1;
    int delay;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->usStatus = 0;

    MBRTUMasterRead(psModbus, ucSlaveAddress, 0X04, usAddress, usNum);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X04)
            {
                psModbus->readInputRegistersCallback(usAddress, usNum, (const uint16_t *)&psModbus->ucBuf[3], psModbus->ucBuf[2] >> 1);
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写单个线圈
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      线圈地址
 * @param  usCmd          0x05
 * @param  ucState        要设置的线圈状态,1或者0
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */

int MBRTUMasterWriteSingleCoil(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint8_t ucState, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[0] = ucSlaveAddress;
    psModbus->ucBuf[1] = 0X05;
    psModbus->ucBuf[2] = usAddress & 0XFF;
    psModbus->ucBuf[3] = usAddress >> 8;
    psModbus->ucBuf[4] = ucState ? 0XFF : 0X00;
    psModbus->ucBuf[5] = 0X00;
    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, 6);
    psModbus->ucBuf[6] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[7] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, 8);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X05)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写单个寄存器!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      寄存器地址(通信地址)
 * @param  usCmd          0x06
 * @param  usRegVal       寄存器值
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
//	MBRTUMasterWriteSingleRegister(&MBRTUHandle, 1, RUN_MODE_ADDR, 0x0003, 100);
int MBRTUMasterWriteSingleRegister(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usRegVal, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }
    psModbus->ucBuf[0] = ucSlaveAddress;
    psModbus->ucBuf[1] = 0X06;
    psModbus->ucBuf[2] = usAddress >> 8;
    psModbus->ucBuf[3] = usAddress & 0XFF;
    psModbus->ucBuf[4] = usRegVal >> 8;
    psModbus->ucBuf[5] = usRegVal & 0XFF;
    crc = GetModbusCRC16_Cal((uint8_t *)psModbus->ucBuf, 6);
    psModbus->ucBuf[6] = (uint8_t)(crc >> 8);
    psModbus->ucBuf[7] = (uint8_t)(crc & 0xFF);
	
    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, 8);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X06)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
						
        }
        delay = usTimeout > 1 ? 1 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }
    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }
		//sprintf(str,"%s0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\r\n","响应指令",psModbus->ucBuf[0],psModbus->ucBuf[1],psModbus->ucBuf[2],psModbus->ucBuf[3],psModbus->ucBuf[4],psModbus->ucBuf[5],psModbus->ucBuf[6],psModbus->ucBuf[7]);
		sprintf(str1,"%02X%s%s\r\n",psModbus->ucBuf[0],"号伺服电机返回:",(ret==0?"成功":"失败"));
		sendLOGStr(str1);
    return ret;
}

/**
 * 主机写多个线圈状态
 * @param  ucSlaveAddress  从机地址
 * @param  usAddress       线圈起始地址
 * @param  usNum           要写的线圈数量
 * @param  pucStateBitsBuf 存放线圈状态,1比特代表一个线圈状态
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterWriteMultipleCoils(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint8_t *pucStateBitsBuf, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;
    uint16_t usIndex = 0, usBytes = 0;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[usIndex++] = ucSlaveAddress;
    psModbus->ucBuf[usIndex++] = 0X0F;
    psModbus->ucBuf[usIndex++] = usAddress & 0XFF;
    psModbus->ucBuf[usIndex++] = usAddress >> 8;
    psModbus->ucBuf[usIndex++] = usNum >> 8;
    psModbus->ucBuf[usIndex++] = usNum & 0XFF;

    usBytes = (usNum - 1) / 8 + 1;
    psModbus->ucBuf[usIndex++] = usBytes;

    while (usBytes--)
    {
        psModbus->ucBuf[usIndex++] = *pucStateBitsBuf++;
    }

    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, usIndex);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, usIndex);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X0F)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

/**
 * 主机写多个寄存器!!!!!!!!!!!
 * @param  ucSlaveAddress 从机地址
 * @param  usAddress      要写的寄存器起始地址
 * @param  usCmd          0x10
 * @param  usNum          要写的寄存器数量
 * @param  pusRegVal      存放要写的寄存器值
 * @param  usTimeout      超时时间,单位毫秒
 * @return                0:成功  <0:执行失败
 */
int MBRTUMasterWriteMultipleRegisters(MBRTUMaterTypeDef *psModbus, uint8_t ucSlaveAddress, uint16_t usAddress, uint16_t usNum, const uint16_t *pusRegVal, uint16_t usTimeout)
{
    int ret = -1;
    int delay;
    uint16_t crc;
    uint16_t usIndex = 0;

    if (psModbus->lock != NULL)
    {
        psModbus->lock();
    }

    psModbus->ucBuf[usIndex++] = ucSlaveAddress;
    psModbus->ucBuf[usIndex++] = 0X10;
    psModbus->ucBuf[usIndex++] = usAddress & 0XFF;
    psModbus->ucBuf[usIndex++] = usAddress >> 8;
    psModbus->ucBuf[usIndex++] = usNum >> 8;
    psModbus->ucBuf[usIndex++] = usNum & 0XFF;
    psModbus->ucBuf[usIndex++] = usNum << 1;

    while (usNum--)
    {
        psModbus->ucBuf[usIndex++] = *pusRegVal >> 8;
        psModbus->ucBuf[usIndex++] = *pusRegVal & 0XFF;
        pusRegVal++;
    }

    crc = usMBCRC16((uint8_t *)psModbus->ucBuf, usIndex);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc & 0xFF);
    psModbus->ucBuf[usIndex++] = (uint8_t)(crc >> 8);

    psModbus->usStatus = 0;
    psModbus->sendData(psModbus->ucBuf, usIndex);

    while (usTimeout != 0)
    {
        if (psModbus->usStatus & 0X8000)
        {
            if (psModbus->ucBuf[0] == ucSlaveAddress && psModbus->ucBuf[1] == 0X10)
            {
                ret = 0;
            }
            else
            {
                ret = -2;
            }
            psModbus->usStatus = 0;
            break;
        }
        delay = usTimeout > 5 ? 5 : usTimeout;
        usTimeout -= delay;
        psModbus->delayms(delay);
    }

    if (psModbus->unlock != NULL)
    {
        psModbus->unlock();
    }

    return ret;
}

void MBRTUMasterRecvByteISRCallback(MBRTUMaterTypeDef *psModbus, uint8_t ucByte)
{
		
		psModbus->timerStop();
    if (psModbus->usStatus < (sizeof(psModbus->ucBuf)-1))
    {
        psModbus->ucBuf[psModbus->usStatus++] = ucByte;
        psModbus->timerStart();
    }
    else
    {
        psModbus->usStatus |= 0X8000;
    }
}

void MBRTUMasterTimerISRCallback(MBRTUMaterTypeDef *psModbus)
{
    psModbus->timerStop();
    psModbus->usStatus |= 0X8000;
}

#ifdef USE_RTOS

static void mutex_lock(void)
{
}

static void mutex_unlock(void)
{
}

#endif

static void timerStop(void)
{
   // HAL_TIM_Base_Stop_IT(&htim2);
}

static void timerStart(void)
{
  //  __HAL_TIM_SET_COUNTER(&htim2, 0);
  //  HAL_TIM_Base_Start_IT(&htim2);
}

static void delayms(uint32_t nms)
{
    HAL_Delay(nms);
}

static uint32_t sendData(const void *buf, uint32_t len)
{
    if (HAL_UART_Transmit(&huart2, (uint8_t *)buf, len, 100) != HAL_OK)
    {
        len = 0;
    }
    return len;
}
static uint32_t sendDataLog(const void *buf, uint32_t len)
{
    if (HAL_UART_Transmit(&huart1, (uint8_t *)buf, len, 100) != HAL_OK)
    {
        len = 0;
    }
    return len;
}

static void readCoilsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfCoilsState, uint16_t usLen)
{
    uint8_t ucLoops = (usNum - 1) / 8 + 1;
    uint8_t ucState, ucBits;

    printf(" Read %d coils starting at start address %d: ", usNum, usStartAddr);

    while (ucLoops != 0)
    {
        ucState = *pucBitsOfCoilsState++;
        ucBits = 0;
        while (usNum != 0 && ucBits < 8)
        {
            printf("%d ", ucState & 0X01 ? 1 : 0);
            ucState >>= 1;
            usNum--;
            ucBits++;
        }
        ucLoops--;
    }

    printf("\r\n");
}

static void readDiscreteInputsCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pucBitsOfDiscreteInputsState, uint16_t usLen)
{
    uint8_t ucLoops = (usNum - 1) / 8 + 1;
    uint8_t ucState, ucBits;

    printf(" Read %d discrete inputs starting at start address %d: ", usNum, usStartAddr);

    while (ucLoops != 0)
    {
        ucState = *pucBitsOfDiscreteInputsState++;
        ucBits = 0;
        while (usNum != 0 && ucBits < 8)
        {
            printf("%d ", ucState & 0X01 ? 1 : 0);
            ucState >>= 1;
            usNum--;
            ucBits++;
        }
        ucLoops--;
    }

    printf("\r\n");
}

static void readHoldingRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint8_t *pusHoldingRegistersVal, uint8_t usLen)
{
    uint8_t val[4] = {0};
   // printf(" Read %d hold registers starting at start address %d: ", usNum, usStartAddr);
    while (usLen--)
    {
        val[usLen] = *pusHoldingRegistersVal++;
        printf("%02X ", val[usLen]);
    }
    printf("\r\n"); 
}

static void readInputRegistersCallback(uint16_t usStartAddr, uint16_t usNum, const uint16_t *pusInputRegistersVal, uint16_t usLen)
{
    uint16_t val;
    printf(" Read %d input registers starting at start address %d: ", usNum, usStartAddr);
    while (usLen--)
    {
        val = *pusInputRegistersVal++;
        val = ((val & 0X00FF) << 8) | ((val & 0XFF00) >> 8); // 转换大小端
        printf("%04X ", val);
    }
    printf("\r\n");
}

MBRTUMaterTypeDef MBRTUHandle =
        {
                .delayms = delayms,
                .timerStart = timerStart,
                .timerStop = timerStop,
                .sendData = sendData,
								.sendDataLog = sendDataLog,
                .readCoilsCallback = readCoilsCallback,
                .readDiscreteInputsCallback = readDiscreteInputsCallback,
                .readHoldingRegistersCallback = readHoldingRegistersCallback,
                .readInputRegistersCallback = readInputRegistersCallback,

#ifdef USE_RTOS // 使用了RTOS那么需要实现互斥
                .lock = mutex_lock,
                .unlock = mutex_unlock,
#endif
        };

接受数据的时候只需要在串口回调中调用如下代码即可


uint8_t RS485P[1];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
		while(HAL_UART_Receive_IT(&huart2, (uint8_t *)&RS485P, 1)!=HAL_OK);
		MBRTUMasterRecvByteISRCallback(&MBRTUHandle,RS485P[0]);
}

RS485接口使用Modbus协议通讯 的实例工程
04-13
RS485接口使用Modbus协议通讯 的实例工程 公司项目 学习使用 项目用到modbus协议,用的串口是rs485 stm32F系列
RS485通信协议
01-06
C320 系列变频器提供标准 RS485 通信接口,用户可通过 PC/PLC 实现集中监控(发送运行命令设定, 变频器的工作参数和读取变频器的工作状态),以适应特定的使用要求。本附录的协议内容即是为实现上述功 能而设计的
RS-485通讯
王同学
01-20 1939
RS-485通讯协议简介与CAN类似,RS-485是一种工业控制环境中常用的通讯协议,它具有抗干扰能力强、传输距离远的特点。RS-485通讯协议由RS-232协议改进而来,协议层不变,只是改进了物理层,因而保留了串口通讯协议应用简单的特点。
modbus协议与RS-485协议的区别
最新发布
ddidi111的博客
08-22 743
RS-485作为一种差分信号传输的标准,具有抗干扰能力强、传输距离远的优势,适用于工控场景中长距离、高速、多节点的通信需求。RS-485是一种物理层通信标准,定义了串行通信中不同设备之间的连接方式和电气规范。Modbus协议和RS-485协议相辅相成,常常结合在一起使用,以实现工业控制系统中的稳定、可靠的数据通信。Modbus协议提供了数据传输规范和交换方式,而RS-485则提供了可靠的物理层连接,确保数据能够在设备之间高效传输。Modbus协议是一种通信协议,而RS-485则是一种物理层通信标准。
有线通信-485通信协议
L的博客
06-19 1346
本文包含485总线的基础知识。具体包括485总线介绍、485相关HAL库驱动、485配置步骤以及相关代码实现
RS-485通讯协议
FPGAerClub的博客
05-29 3802
更多精彩内容,请微信搜索“FPGAer俱乐部”关注我们          1. 硬件层协议 通讯协议主要是实现两个设备之间的数据交换功能,通讯协议分硬件层协议和软件层协议。硬件层协议决定数据如何传输问题,比如要在设备1向设备2发送0x63,0x63的二进制数为0110 0011,这8个二进制数从设备1传输到设备2,涉及到1怎么传,0怎么传的问题,这就是硬件层要解决的问题。硬件层协议目前比较多见的有...
RS——485通讯协议
weixin_45589030的博客
09-13 4865
1、RS485通讯实验简介 RS485是一种工业控制环境中 常用的通讯协议,它具有抗干扰能力强、传输距离远的特点。485协议又232协议改进而来,协议层不变,只改进了物理层,因而保留了串口通讯协议应用简单的特点。 看图就知道了,和CAN有点像,也是差分线传递的 2、RS485物理层 差分信号具有很强的抗干扰能力,特别适合应用于电磁环境。RS485通讯网络的打传输距离是1200米,总线上可挂载128个通讯结点,由于RS485网络只有一对差分信号线,它使用差分信号来表发逻辑,当电压差为-6~-2V时表示0,
RS485信号协议
小鱼仙倌的博客
07-05 913
RS485协议就是一种在电路板设计中非常常见的协议,是美国电子工业协会(EIA)在1983年批准的一个差分传输协议
RS-485接口协议详解
指尖动听知识库
07-05 5777
电子工业协会(EIA)于1983年制订并发布RS-485标准,并经通讯工业协会(TIA) 修订后命名为TIA/EIA-485-A,所以TIA/EIA-485-A才是真正的名字,因为人们已经叫习惯RS-485了,所以后续也一直沿用RS-485这个叫法。​RS-485标准是为弥补RS-232通信距离短、速率低等缺点而产生的。RS-485标准只规定了平衡发送器和接收器的电特性,而没有规定接插件、传输电缆和应用层通信协议。​RS-485标准与RS-232不一样,数据信号采用差分传输方式。
RS485通讯modbus协议
01-09
通讯计数器上的一个 标准MODBUS通讯协议MODBUS RTU 标准协议 举例: 控制器地址为:01 寄存器地址为:01 存放数值需要1个字节 则:电脑发给控制器的命令代码如下: 01 03 00 01 00 01 d5 ca 假如此时数值为...
Modbus(基于RS485协议介绍.rar_RS485 modbus_modbus协议_sharpffl
09-21
Modbus协议是一种广泛应用于工业控制系统的通信协议,它允许设备之间进行数据交换,尤其是在PLC(可编程逻辑控制器)和远程终端单元(RTU)之间。这个协议是开放的、简单且经济有效的,使得不同厂商的设备能够互相...
RS485通讯协议.pdf
02-15
RS485通讯协议.pdf
RS485 串口通讯协议
03-11
RS485 串口通讯协议 RS485 通讯协议 一.首先要知道什么是 RS232和 RS485. 典型的串行通讯标准是 RS232和 RS485.它们定义了电压,阻抗等.但不对软件协议给予定义区别于 RS232
rs485典型通讯协议,并有例子
03-17
8寸屏天吊系统天吊接口板串口rs485函数源文件,C语言程序,非常详尽
rs485通信协议
06-14
这个是一类人所485通讯协议
STM32+RS485+MODBUS协议(主机+从机代码)+串口+定时器
01-29
STM32+RS485+modbus主机 1、没有任何按键按下的情况下是主机模式,此时主机去寻址从机地址为01的设备获取数据 2、通过按键寻址不同的从机 //按键1查看从机01的数据 //按键2查看从机02的数据 //按键3查看从机03的...
CAN和Modbus RS485总线协议对比总结
03-30
CAN(Controller Area Network)和Modbus RS485是两种常见的工业通信协议,它们在工业自动化领域中扮演着重要角色,各自具有独特的工作原理、结构特点和应用场景。 CAN总线协议特点: 1. 多主结构:CAN允许网络中...
一文搞懂RS-485通信协议
热门推荐
qq_52049228的博客
11-21 1万+
是一种通用的通信标准,广泛用于数据采集和控制应用中。它的主要优点之一是它允许将多个 RS485 设备放在同一条总线上,这使得多个节点可以相互连接。RS-485是美国电子工业协会(EIA)在1983年批准了一个新的平衡传输标准(balanced transmission standard),EIA一开始将RS(Recommended Standard)做为标准的前缀,不过后来为了便于识别标准的来源,已将RS改为EIA/TIA。
RS-485 通讯协议简介
m0_70888041的博客
11-10 1万+
RS-485通讯网络的最大传输距离可达 1200米,总线上可挂载 128个通讯节点,而由于 RS-485 网络只有一对差分信号线,它使用差分信号来表达逻辑,当 AB 两线间的电压差为-6V~-2V时表示逻辑 1,当电压差为+2V~+6V 表示逻辑 0,在同一时刻只能表达一个信号,所以它的通讯是半双工形式的,它与 RS-232 通讯协议的特性对比见图。差分传输是一种信号传输的技术,区别于传统的一根信号线一根地线的做法,差分传输在这两根线上都传输信号,这两个信号的振幅相等,相位相差180度,极性相反。
MODbus协议和RS485协议有什么区别
03-20
MODbus协议是一种通信协议,用于在工业自动化系统中传输数据。RS485协议是一种物理层协议,用于在串行通信中传输数据。MODbus协议可以在RS485协议上运行,但是RS485协议不一定支持MODbus协议。因此,两者是不同的协议,但可以一起使用。
写文章

热门文章

  • RS485协议是什么?它和Modbus协议的区分以及C语言实现 4222
  • Command ‘nvidia-smi‘ not found, but can be installed with: 4010
  • 位置式PID算法讲解和C语言实现 2993
  • 增量式和位置式编码器的原理讲解以及步进电机测速方案实现 2838
  • 梯形加减速算法原理优缺点及对步进电机的控制 2008

分类专栏

  • 学习笔记 4篇
  • 嵌入式/单片机 4篇
  • 技术杂谈 1篇
  • C/C++ 6篇
  • openCV机器视觉 2篇
  • 刚体运动学/动力学 4篇
  • matlab 2篇
  • QT开发日常分享总结 7篇
  • 日常问题分享 2篇
  • ROS2常见问题 5篇
  • Moveit2相关 1篇
  • 科普 2篇

最新评论

  • Could not initialize NNPACK! Reason: Unsupported

    JadeZ?: 我安了也还是不行,请问你解决了吗

  • QT发布Release正式版本

    CSDN-Ada助手: 恭喜您发布了第20篇博客“QT发布Release正式版本”,持续创作是非常了不起的事情!在您的博客中分享了关于QT发布正式版本的经验,让读者受益匪浅。希望您能继续保持创作的热情和坚持,为大家带来更多有价值的内容。或许在下一篇博客中可以分享一些关于QT的实战经验和技巧,让读者更深入地了解这个主题。期待您的更多精彩内容,加油!

  • QT在线配置安装ROS2插件以及创建项目

    驽马匠人: 去官网下载离线版本安装,https://ros-qtc-plugin.readthedocs.io/en/latest/_source/Improve-ROS-Qt-Creator-Plugin-Developers-ONLY.html

  • QT在线配置安装ROS2插件以及创建项目

    qq_57183072: export PLUGIN_URL=`curl -s https://api.github.com/repos/ros-industrial/ros_qtc_plugin/releases/latest | grep -E 'browser_download_url.*ROSProjectManager-.*-Linux-.*.zip' | cut -d'"' -f 4` curl -SL $PLUGIN_URL | bsdtar -xzf - -C $QTC_ROOT 这个有没有离线安装的方法?

  • QT在线配置安装ROS2插件以及创建项目

    qq_57183072: export PLUGIN_URL=`curl -s https://api.github.com/repos/ros-industrial/ros_qtc_plugin/releases/latest | grep -E 'browser_download_url.*ROSProjectManager-.*-Linux-.*.zip' | cut -d'"' -f 4` curl -SL $PLUGIN_URL | bsdtar -xzf - -C $QTC_ROOT 网不好,有没有其它办法解决一下这个问题?

大家在看

  • Python 从入门到实战38(创建线程)
  • SQL注入实战——cookie注入
  • C++调试经验(4):Linux下调试CAN通信的方法
  • Qt(UI设计)
  • 校园驿站管理系统 98

最新文章

  • 【C语言基础】存储类型(自动变量、静态变量和寄存器变量)
  • 不管技术多牛都需要时刻理解的C语言基础知识-堆栈
  • 梯形加减速算法原理优缺点及对步进电机的控制
2024年15篇
2023年19篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驽马匠人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家玻璃钢仿铜雕塑制作江西商业街玻璃钢雕塑设计公司天津公园玻璃钢雕塑定制景观玻璃钢雕塑ip定制商场美陈网玻璃钢仿铜人物雕塑费用公园玻璃钢雕塑安装日照小区玻璃钢雕塑安装德阳玻璃钢雕塑制作厂家珠海透明玻璃钢雕塑现货玻璃钢异形雕塑订制和田玻璃钢雕塑制作厂家浙江杭州玻璃钢雕塑定做珠海玻璃钢人物雕塑成都人物玻璃钢雕塑厂家北京常用商场美陈研发苏州玻璃钢雕塑定做合肥设计玻璃钢雕塑公司南昌玻璃钢熊猫雕塑和田气球商场美陈装饰哪家有名正宗玻璃钢雕塑厂家高品质的选择玻璃钢雕塑是实惠的沈阳环保玻璃钢雕塑优势卡通雕塑玻璃钢有哪些平台福州动物玻璃钢雕塑安装湖南多彩玻璃钢雕塑定做价格玻璃钢发光雕塑制作视频铜仁玻璃钢花盆厂家商场美陈大树琼海玻璃钢牌匾雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化