Skip to main content

ModbusRTU协议

在网上找了很久都没找的Stm32 有关Modbus协议的代码,所以根据modbus的协议文档自己写了一个,可以使用。逻辑并不复杂很简单

主文件:stm32.ino

#define BAUD 9600

char pin[27] = {PA0,PA1,PA2,PA3,PA4,PA5,PA6,PA7,PA8,PA11,PA12,PA15,PB0,PB1,PB2,PB3,PB4,PB5,PB6,PB7,PB8,PB9,PB10,PB11,PB12,PB13,PB15};

void setup() {
for(int i=0;i < 26;i++){
pinMode(pin[i], OUTPUT);
}

Serial.begin(BAUD); // BAUD has no effect on USB serial: placeholder for physical UAR
// wait for serial monitor to be connected.
while (!Serial)
{
digitalWrite(33,!digitalRead(33));// Turn the LED from off to on, or on to off
delay(100); // fast blink
}
Serial.println("Serial Read Until Example:");
Serial.print("Type a few characters & press ENTER\r\n(make certain serial monitor sends CR+LF)");
}

void loop() {
mloop();
updatePin();
}

// 读取保持寄存器并设置对应引脚
void updatePin(){
char *holdingRegister = getHoldingRegister({0x00});
char high1 = holdingRegister[0];
for(int i=0;i < 8;i++){
int dp = high1 >> i & 0x01;
digitalWrite(pin[i],dp != 0 ? LOW : HIGH);
}

char low1 = holdingRegister[1];
for(int i=8;i < 27;i++){
int dp = high1 >> i & 0x01;
digitalWrite(pin[i],dp != 0 ? LOW : HIGH);
}
}

modbus实现逻辑:modbus.ino

#define MODBUSADDRESS 0x02

enum FUN {
// 读取保持寄存器
READE_HOLD = 0x03,
// 写多个保持寄存器
WRITE_MULTIPLE_HOLD = 0x10
};

char serialdata[32];
int idx = -1;
int lastreadtime = 0;
// 保持寄存器
char holdingRegister[16][64];
char result[32];

// 读串口数据
void mloop(){
if (Serial.available() > 0){
serialdata[++idx] = Serial.read();
lastreadtime = millis();
}else if(lastreadtime != 0 && millis() - lastreadtime > (1.04 * 3.5)){
int requestLen = analysisdata();
if (requestLen != NULL){
Serial.write(result,requestLen);
}
idx = -1;
lastreadtime = 0;
}
}

// 获取保持寄存器
char *getHoldingRegister(char address){
return holdingRegister[address];
}

int analysisdata(){
int address = serialdata[0];
if(MODBUSADDRESS != address){
return NULL;
}

if(!checkCrc()){
return NULL;
}

char fun = serialdata[1];
char data[1024];
char dlen = 0;
switch(fun){
case READE_HOLD:
dlen = readHoldingRegister(data);
break;
case WRITE_MULTIPLE_HOLD:
dlen = writeHoldingRegister(data);
break;
default:
return NULL;
}

int len = 3 + dlen + 2;

result[0] = MODBUSADDRESS;
result[1] = fun;
result[2] = dlen;

for(int i= 0;i < dlen;i++){
result[i+3] = data[i];
}

uint16_t crc = generateCrc(result,len - 2);
result[len - 1] = crc >> 8;
result[len - 2] = crc & 0x00ff;

return len;
}

// 检查CRC
bool checkCrc(){
int dlen = idx - 1;
char data[dlen];
memcpy(data,serialdata,dlen);

uint16_t crc = generateCrc(data,dlen);
return serialdata[idx - 1] == (crc & 0x00ff) && serialdata[idx] == (crc >> 8);
}

// 冗余计算CRC
uint16_t generateCrc(char *data,int dlen){
uint16_t crcData = 0xffff;
for(int i = 0;i < dlen;i++){
crcData = oneByteCrc(data[i],crcData);
}
return crcData;
}

// 计算CRC
uint16_t oneByteCrc(char data,uint16_t crcData){
uint16_t crcDataTmp1 = ((crcData & 0x00ff) ^ data) + (0xff00 & crcData);
int len = 8;
while((len--) != 0){
if ((crcDataTmp1 & 0x0001) == 0){
crcDataTmp1 = crcDataTmp1 >> 1;
}else{
crcDataTmp1 = ((crcDataTmp1 >> 1) ^ 0xA001);
}
}
return crcDataTmp1;
}



void getAddressAndRegisterNum(char *data,char *val){
// 寄存器起始位置
char high = serialdata[2];
char low = serialdata[3];
int address = high+low;
val[0] = address;

// 读寄存器个数
char highNum = serialdata[4];
char lowNum = serialdata[5];
int len = high+lowNum;
val[1] = len;
}

// 返回读取保持寄存器的字节数
int readHoldingRegister(char *data){
char val[2];
getAddressAndRegisterNum(serialdata,val);
int address = val[0];
int len = val[1];

char *holdData = holdingRegister[address];
int hold = len * 2;
for(int i= 0, j = 0;i < hold;i++){
data[i] = holdData[i];
}
reverse(data,hold);
return hold;
}

// 返回读取保持寄存器的字节数
int writeHoldingRegister(char *data){
char val[2];
getAddressAndRegisterNum(serialdata,val);

int address = val[0];
int len = val[1];
int byteLen = serialdata[6];

char bytes[byteLen];
char *holdData = holdingRegister[address];
for (int i= 0,j= 7 + byteLen -1;i < byteLen;i++,j--){
holdData[i] = serialdata[j];
}

data[0] = {0x00};
data[1] = {0x01};

data[2] = serialdata[4];
data[3] = serialdata[5];
return 4;
}

// 反转数组
void reverse(char *data,int len){
char i,n = len,temp;
for(i= 0;i < len / 2;i++){
temp= data[i];
data[i]= data[n-1-i];
data[n-1-i]= temp;
}
}