From ff5c5db787686747ef0968f23f26b3607240b487 Mon Sep 17 00:00:00 2001 From: JingweiCui Date: Thu, 9 Jan 2025 18:05:09 +0800 Subject: [PATCH] =?UTF-8?q?[Add]=20Modbus=E9=80=9A=E4=BF=A1=E5=BA=93+?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过直接修改结构体参数的地址去修改具体的变量,结构体把所有定义的参数作为一个整体管理;ModbusRTU目前支持03,06和0A功能码。 --- br_com_service.c | 58 +++++++ br_com_service.h | 18 +++ br_dataset_def.c | 5 + br_dataset_def.h | 51 +++++++ br_modbus.c | 382 +++++++++++++++++++++++++++++++++++++++++++++++ br_modbus.h | 61 ++++++++ br_modbus_reg.c | 114 ++++++++++++++ br_modbus_reg.h | 36 +++++ main.c | 13 +- output/main.exe | Bin 0 -> 40943 bytes 10 files changed, 737 insertions(+), 1 deletion(-) create mode 100644 br_com_service.c create mode 100644 br_com_service.h create mode 100644 br_dataset_def.c create mode 100644 br_dataset_def.h create mode 100644 br_modbus.c create mode 100644 br_modbus.h create mode 100644 br_modbus_reg.c create mode 100644 br_modbus_reg.h create mode 100644 output/main.exe diff --git a/br_com_service.c b/br_com_service.c new file mode 100644 index 0000000..2dd6a00 --- /dev/null +++ b/br_com_service.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include "br_com_service.h" +#include "br_modbus.h" + + +uint8_t payload[PAYLOAD_MAX_LEN] = {0}; + + +BrComStatus_t br_com_process(uint8_t * pucRecvBuffer, uint16_t usRecvBufferLen, uint8_t * pucSendBuffer, uint16_t * pusSendBufferLen) +{ + BrComStatus_t status = BRCOMSTATUS_ERR; + + uint8_t header[2] = {0}; + uint16_t dataLen = 0; + uint8_t recvDataLen, resDataLen = 0; + uint8_t modbusResBuffer[256] = {0}; + + /* 提取HEADER */ + header[0] = pucRecvBuffer[0]; + header[1] = pucRecvBuffer[1]; + if( header[0] != 0xAA && header[1] != 0xAA) + { + status = BRCOMSTATUS_HEADR_ERR; + return status; + } + + dataLen = ((uint16_t)pucRecvBuffer[2] << 8) + pucRecvBuffer[3]; + recvDataLen = dataLen & 0x00FF; + /* 提取长度 */ + if( dataLen > 255 ) + { + status = BRCOMSTATUS_LEN_ERR; + return status; + } + + else + { + /* 提取内容 */ + memcpy(payload, pucRecvBuffer + 4, PAYLOAD_MAX_LEN); + if(modbus_process(pucRecvBuffer, recvDataLen, modbusResBuffer, &resDataLen) == MODBUS_STATUS_OK) + { + + pucSendBuffer[0] = 0xFF; + pucSendBuffer[1] = 0xFF; + pucSendBuffer[3] = 0x00; + pucSendBuffer[4] = resDataLen; + memcpy(pucSendBuffer + 4, modbusResBuffer, resDataLen); + /* modbus协议处理 */ + status = BRCOMSTATUS_OK; + return status; + } + + + } + return status; +} diff --git a/br_com_service.h b/br_com_service.h new file mode 100644 index 0000000..217b8b3 --- /dev/null +++ b/br_com_service.h @@ -0,0 +1,18 @@ +#ifndef _BR_COM_SERVICE_H +#define _BR_COM_SERVICE_H + +#define PAYLOAD_MAX_LEN (256u) + + +typedef enum { + BRCOMSTATUS_OK, + BRCOMSTATUS_HEADR_ERR, + BRCOMSTATUS_LEN_ERR, + BRCOMSTATUS_ERR +} BrComStatus_t; + + +BrComStatus_t br_com_process(uint8_t * pucRecvBuffer, uint16_t usRecvBufferLen, uint8_t * pucSendBuffer, uint16_t * pusSendBufferLen); + + +#endif diff --git a/br_dataset_def.c b/br_dataset_def.c new file mode 100644 index 0000000..725c88f --- /dev/null +++ b/br_dataset_def.c @@ -0,0 +1,5 @@ +#include "br_dataset_def.h" + +/* 参数定义 */ +BR_Dataset0_t br_dataset0 = {0}; + diff --git a/br_dataset_def.h b/br_dataset_def.h new file mode 100644 index 0000000..77d754d --- /dev/null +++ b/br_dataset_def.h @@ -0,0 +1,51 @@ +#ifndef _BR_PARAMS_DEF_H +#define _BR_PARAMS_DEF_H + +#include + +#define aenc_con_SEG_NUM (10u) +#define TF_LEN (200u) +#define SPEAKER_ERR_NUM (4u) +#define MIC_NUM (4u) +#define BLOCK_SIZE (32u) + + +typedef struct { + float TachoOfAmp[aenc_con_SEG_NUM]; + float VarAmp[aenc_con_SEG_NUM]; + float TachoOfStep[aenc_con_SEG_NUM]; + float VarStepDelta[aenc_con_SEG_NUM]; + float VarDelta[aenc_con_SEG_NUM]; + float TachoOfLeaky[aenc_con_SEG_NUM]; + float VarLeaky[aenc_con_SEG_NUM]; + float TachoOfErrWei[aenc_con_SEG_NUM]; + float VarErrWei[aenc_con_SEG_NUM]; +}ENCLMSDebugParams_t; + +/* param dataset struct's size must be less than 65536 */ +typedef struct { + ENCLMSDebugParams_t ENCLMSDebugParams[3]; + /* control order flag. */ + uint32_t orderOne; + uint32_t orderTwo; + uint32_t orderThree; + + /* input and output amplifier */ + uint32_t inputAmp; + uint32_t outputAmp; + + /* control and calibration flag */ + uint32_t encConFlag; + uint32_t encCalFlag; + uint32_t disableAllSignal; + + /* tf data */ + float transfers[SPEAKER_ERR_NUM][MIC_NUM][TF_LEN]; + float outputCheck[SPEAKER_ERR_NUM][BLOCK_SIZE]; + float InputCheck[MIC_NUM][BLOCK_SIZE]; +} BR_Dataset0_t; + + +extern BR_Dataset0_t br_dataset0; + +#endif diff --git a/br_modbus.c b/br_modbus.c new file mode 100644 index 0000000..cfd194f --- /dev/null +++ b/br_modbus.c @@ -0,0 +1,382 @@ +#include +#include +#include "br_modbus.h" +#include +#include "br_modbus_reg.h" + + +uint8_t * prv_rx_buffer; + +static uint8_t BrMdBufferRes[256]; +uint8_t BrMdBufferCmd[256] = {0}; + +uint8_t modbus_SlaveID = SLAVE_ID_DEFAULT; + +uint8_t * modbus_FrameBuff; +uint8_t modbus_FrameCnt; + +// Private function prototype. +void modbus_03_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen); +void modbus_06_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen); +void modbus_0A_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen); + + + + +uint8_t modbus_extract_slaveid(void); +uint8_t modbus_extract_funcode(void); +uint16_t modbus_extract_startAddr(void); +uint8_t modbus_extract_size(void); +uint16_t modbus_extract_crc(uint8_t mFunCode); + +uint16_t modbus_extract_06_val(void); +uint16_t modbus_extract_0a_val(uint8_t idx); + + +void modbus_FrameClearBuff(void); +uint8_t modbus_FrameLoadByte(uint8_t aByte); +uint8_t modbus_FrameLoadWord(uint16_t aWord); + +uint8_t modbus_extract_slaveid(void); +uint8_t modbus_extract_funcode(void); + +/* --------------------------------------------------------------- */ +uint16_t modbus_extract_0a_bytenum(void); +uint16_t modbus_Retrieve16Value(uint8_t index); + + +static uint16_t modbus_crc16_v3( const unsigned char *buf, unsigned int len ) +{ + static const uint16_t table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; + + uint8_t xor = 0; + uint16_t crc = 0xFFFF; + + while(len--) + { + xor = (*buf++) ^ crc; + crc >>= 8; + crc ^= table[xor]; + } + + return crc; +} + +// process modbus frame +ModbusStatus_t modbus_process(const uint8_t * buffer_cmd, uint8_t recv_len, uint8_t * buffer_resp, uint8_t * resp_len) +{ + uint8_t funCode; + uint8_t slaveId; + ModbusStatus_t status = MODBUS_STATUS_OK; + + memcpy(BrMdBufferCmd, buffer_cmd, recv_len); + /* 提取slaveid */ + slaveId = modbus_extract_slaveid(); + + /* 判断ID是否接受 */ + + /* 提取funCode */ + funCode = modbus_extract_funcode(); + + switch(funCode) + { + case 3: + modbus_03_solver(slaveId, buffer_resp, resp_len); + break; + case 6: + modbus_06_solver(slaveId, buffer_resp, resp_len); + break; + case 0x10: + modbus_0A_solver(slaveId, buffer_resp, resp_len); + } + return MODBUS_STATUS_OK; +} + + +uint8_t modbus_extract_slaveid(void) +{ + return BrMdBufferCmd[0]; +} + +uint8_t modbus_extract_funcode(void) +{ + return BrMdBufferCmd[MODBUS_FUN_CODE]; +} + +uint16_t modbus_extract_startAddr(void) +{ + return ((uint16_t)BrMdBufferCmd[MODBUS_START_ADDR_H]<<8) + + BrMdBufferCmd[MODBUS_START_ADDR_L]; +} + +uint8_t modbus_extract_size(void) +{ + return ((uint16_t)BrMdBufferCmd[MODBUS_SIZE_H]<<8) + + BrMdBufferCmd[MODBUS_SIZE_L]; +} + + +uint16_t modbus_extract_crc(uint8_t mFunCode) +{ + uint16_t crcRecv = 0; + uint16_t size = 0; + if( mFunCode == 3U || mFunCode == 6U ) { + crcRecv = (((uint16_t) BrMdBufferCmd[7] ) << 8) + + BrMdBufferCmd[6]; + } + if( mFunCode == 0x10) + { + + size = modbus_extract_size(); + crcRecv = (((uint16_t) BrMdBufferCmd[8 + size * 2] ) << 8) + + BrMdBufferCmd[7 + size * 2] ; + } + return crcRecv; +} + + +void modbus_03_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen) +{ + uint16_t startAddr, size; + volatile uint16_t crcReceived,crcCalculate; + uint8_t i; + ModbusRegRwData_t rw_data; + + // get start reg address + startAddr = modbus_extract_startAddr(); + size = modbus_extract_size(); + // get crc + crcReceived = modbus_extract_crc(3); + // calculate crc + crcCalculate = modbus_crc16_v3(BrMdBufferCmd, 6); + + if(crcCalculate == crcReceived) + { + rw_data.slaveId = slaveId; + rw_data.num = size; + rw_data.operation = RW_OPERATION_READ; + rw_data.startAddr = startAddr; + + if(modbus_reg_proxy(&rw_data) != 0) + { + /* 返回错误码 */ + printf("modbus_reg_proxy err!\n"); + } + // clear frame buffer + modbus_FrameClearBuff(); + // load adrress + modbus_FrameLoadByte(modbus_SlaveID); + // load fun code + modbus_FrameLoadByte(3u); + modbus_FrameLoadByte(size * 2); + + for(i = 0; i < size; i++) + { + modbus_FrameLoadWord(rw_data.registers[i]); + } + + // crcCalculate = HAL_CRC_Calculate(&hcrc, (uint32_t *)&modbus_FrameBuff, modbus_FrameCnt); + crcCalculate = modbus_crc16_v3(BrMdBufferRes, modbus_FrameCnt); + modbus_FrameLoadByte(crcCalculate & 0x00FF); + modbus_FrameLoadByte((crcCalculate & 0xFF00) >> 8); + *resLen = modbus_FrameCnt; + memcpy(resBuffer, BrMdBufferRes, *resLen); + } +} + + + + +void modbus_06_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen) +{ + uint16_t startAddr, val, crcCal, crcRecv; + ModbusRegRwData_t rw_data; + + + startAddr = modbus_extract_startAddr(); + + crcCal = modbus_crc16_v3(BrMdBufferCmd, 6); + crcRecv = modbus_extract_crc(6); + + if( crcCal == crcRecv ) { + val = modbus_extract_06_val(); + rw_data.slaveId = slaveId; + rw_data.num = 1; + rw_data.operation = RW_OPERATION_WRITE; + rw_data.startAddr = startAddr; + + rw_data.registers[0] = val; + if(modbus_reg_proxy(&rw_data) != 0) + { + /* 返回错误码 */ +// printf("modbus_reg_proxy err!\n"); + ; + } + + modbus_FrameClearBuff(); + modbus_FrameLoadByte(modbus_SlaveID); + modbus_FrameLoadByte(6U); + modbus_FrameLoadWord(startAddr); + modbus_FrameLoadWord(val); + modbus_FrameLoadWord(crcCal>>8 | crcCal << 8); + *resLen = modbus_FrameCnt; + memcpy(resBuffer, BrMdBufferRes, *resLen); + } + else + { + /* 校验码错误处理 */ + } + +} + +void modbus_0A_solver(uint8_t slaveId, uint8_t * resBuffer, uint8_t * resLen) +{ + uint16_t address, byte_num, val, crcCal, crcRecv, size; + uint8_t idx = 0; + ModbusRegRwData_t rw_data = {0}; + + address = modbus_extract_startAddr(); + byte_num = modbus_extract_0a_bytenum(); + size = modbus_extract_size(); + + crcCal = modbus_crc16_v3(BrMdBufferCmd, 7 + byte_num); + crcRecv = modbus_extract_crc(0x10); + + + if( crcCal == crcRecv ) { + rw_data.slaveId = slaveId; + rw_data.num = size; + rw_data.operation = RW_OPERATION_WRITE; + rw_data.startAddr = address; + + rw_data.registers[0] = val; + + for(idx=0; idx>8 | crcCal << 8); + *resLen = modbus_FrameCnt; + memcpy(resBuffer, BrMdBufferRes, *resLen); + } + else + { + /* 校验码错误处理 */ + } +} + + +uint16_t modbus_extract_0a_bytenum(void) +{ + return BrMdBufferCmd[6]; +} + + +uint16_t modbus_extract_0a_val(uint8_t idx) +{ + return ((uint16_t)BrMdBufferCmd[7 + idx*2]<<8) + + BrMdBufferCmd[8 + idx*2]; +} + +uint16_t modbus_extract_06_val(void) +{ + return ((uint16_t)BrMdBufferCmd[MODBUS_06_VAL_H]<<8) + + BrMdBufferCmd[MODBUS_06_VAL_L]; +} + +uint8_t modbus_RetrieveSlaveID(void) +{ + return prv_rx_buffer[MODBUS_SLAVE_ID]; +} + + + +void modbus_FrameClearBuff(void){ + modbus_FrameCnt = 0; +} + + +uint8_t modbus_FrameLoadByte(uint8_t aByte) +{ + uint8_t status; + + if(modbus_FrameCnt == 255) { + status = 1; + } + else { + BrMdBufferRes[modbus_FrameCnt] = aByte; + modbus_FrameCnt++; + status = 0; + } + return status; +} + + +uint8_t modbus_FrameLoadWord(uint16_t aWord) +{ + uint8_t status; + + if(modbus_FrameCnt == 254) + { + status = 1; + } + else + { + BrMdBufferRes[modbus_FrameCnt] = (uint8_t)((aWord&0xFF00)>>8); + modbus_FrameCnt++; + BrMdBufferRes[modbus_FrameCnt] = (uint8_t)(aWord&0x00FF); + modbus_FrameCnt++; + status = 0; + } + return status; +} diff --git a/br_modbus.h b/br_modbus.h new file mode 100644 index 0000000..1e973bd --- /dev/null +++ b/br_modbus.h @@ -0,0 +1,61 @@ +#ifndef _MODBUS_H +#define _MODBUS_H + +#include + +#define SLAVE_ID_DEFAULT (1u) +#define MODBUS_SLAVE_ID 0 +#define MODBUS_FUN_CODE 1 +#define MODBUS_START_ADDR_H 2 +#define MODBUS_START_ADDR_L 3 +#define MODBUS_SIZE_H 4 +#define MODBUS_SIZE_L 5 + +#define MODBUS_06_VAL_H 4 +#define MODBUS_06_VAL_L 5 + + +typedef enum { + MODBUS_STATUS_OK, + MODBUS_STATUS_ERR +} ModbusStatus_t; + + +ModbusStatus_t modbus_process(const uint8_t * buffer_cmd, uint8_t recv_len, uint8_t * buffer_resp, uint8_t * resp_len); + + + +typedef struct { + uint8_t slaveAddr; + uint8_t funCode; + uint16_t startAddr; + uint16_t num; + uint16_t crc; +} ModbusFrameCmd03_t; + +typedef struct { + uint8_t slaveAddr; + uint8_t funCode; + uint8_t byteNum; + uint16_t data[125]; + uint16_t crc; +} ModbusFrameRes03_t; + +/* 06功能码命令和响应格式一样 */ +typedef struct { + uint8_t slaveAddr; + uint8_t funCode; + uint16_t regAddr; + uint16_t regValue; + uint16_t crc; +} ModbusFrameCmdRes06_t; + +typedef struct { + +} ModbusFrameCmd0A_t; + +typedef struct { + +} ModbusFrameRes0A_t; + +#endif diff --git a/br_modbus_reg.c b/br_modbus_reg.c new file mode 100644 index 0000000..6dcad32 --- /dev/null +++ b/br_modbus_reg.c @@ -0,0 +1,114 @@ +#include +#include "br_modbus_reg.h" + +#include "br_dataset_def.h" + + +ModbusRegDataBlock_t DataBlocks[MODBUS_REG_BLOCK_NUM] = { + { + .block_id = 1, + .address = (uint32_t)(&br_dataset0), + .size = sizeof(BR_Dataset0_t) + } +}; + + + +/* private funtion prototype. */ +uint8_t modbus_reg_write_process(uint8_t slave, uint16_t startAddr, uint8_t num, uint16_t * dataBuffer); +uint8_t modbus_reg_read_process(uint8_t slave, uint16_t startAddr, uint8_t num, uint16_t * dataBuffer); + +uint8_t modbus_reg_blockdata_set(ModbusRegDataBlock_t *datablock, uint16_t startAddr, uint8_t num, uint16_t * dataBuffer); +uint8_t modbus_reg_blockdata_get(ModbusRegDataBlock_t *datablock, uint16_t startAddr, uint8_t num, uint16_t * dataBuffer); + +/* public functoin definition. */ + +uint8_t modbus_reg_proxy(ModbusRegRwData_t * rw_data) +{ + switch (rw_data->operation) + { + case RW_OPERATION_READ: + /* code */ + modbus_reg_read_process(rw_data->slaveId, rw_data->startAddr, rw_data->num, rw_data->registers); + break; + case RW_OPERATION_WRITE: + modbus_reg_write_process(rw_data->slaveId, rw_data->startAddr, rw_data->num, rw_data->registers); + break; + default: + break; + } +} + +uint8_t modbus_reg_write_process(uint8_t slaveId, uint16_t startAddr, uint8_t num, uint16_t * dataBuffer) +{ + uint8_t status = -1; + /* 在此实现数据写入逻辑 */ + uint8_t blockIndex; + for(blockIndex=0; blockIndex datablock->size) /* (startAddr+num)*2 每个寄存器数据长度为2字节对应两个内存地址 */ + { + + } + else + { + for(i=0; i datablock->size) /* (startAddr+num)*2 每个寄存器数据长度为2字节对应两个内存地址 */ + { + + } + else + { + for(i=0; iaddress + (startAddr + i)*2 ) ) = *(dataBuffer + i); + } + status = 0; /* return 0表示没有异常发生 */ + } + return status; +} diff --git a/br_modbus_reg.h b/br_modbus_reg.h new file mode 100644 index 0000000..a261c46 --- /dev/null +++ b/br_modbus_reg.h @@ -0,0 +1,36 @@ +#ifndef _MODBUS_REG_H +#define _MODBUS_REG_H + + +#include + +#define MODBUS_REG_DEFAULT_VAL 0xFFFFFFFF +#define RW_REGISTERS_MAXNUM 125 +#define MODBUS_REG_BLOCK_NUM 1 + + + + +typedef struct { + uint8_t block_id; + uint32_t address; + uint16_t size; +} ModbusRegDataBlock_t; + +typedef enum { + RW_OPERATION_READ, + RW_OPERATION_WRITE +} ModbusRegOp_t; + +typedef struct { + uint8_t slaveId; /* 从机地址 */ + ModbusRegOp_t operation; /* 操作类型 */ + uint16_t startAddr; /* 起始地址 */ + uint8_t num; /* 寄存器个数 */ + uint16_t registers[RW_REGISTERS_MAXNUM]; /* 寄存器数据 */ +}ModbusRegRwData_t; + +/* public funtion prototype. */ +uint8_t modbus_reg_proxy(ModbusRegRwData_t * rw_data); + +#endif diff --git a/main.c b/main.c index c71ae25..fa4ec78 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,17 @@ #include +#include "br_dataset_def.h" + +#define BUFFER_LEN (1000u) + + +uint8_t buffer_recv[BUFFER_LEN] = {0}; +uint8_t buffer_send[BUFFER_LEN] = {0}; + +uint8_t test_data = {0x01, 0x03, 0x00, 0x00, 0x00, 0x0A, 0xC5, 0xCD}; + int main() { - printf("Hello World!\n"); + BR_Dataset0_t param_test = {0}; + printf("Hello World!\n %d", sizeof(BR_Dataset0_t)); } \ No newline at end of file diff --git a/output/main.exe b/output/main.exe new file mode 100644 index 0000000000000000000000000000000000000000..05a0807f12b5e100ec30ffccbca2520862da0784 GIT binary patch literal 40943 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 literal 0 HcmV?d00001