Arduino中Modbus设置电压值及获取电压值
作者:野牛程序员:2024-01-29 15:14:53Arduino阅读 2378
#include <ArduinoModbus.h> #include <SoftwareSerial.h> #include <TimerOne.h> #define TX_PIN 2 #define RX_PIN 3 #define SETTING_MODULE_ADDRESS 1 // 设置电压值的模块地址 #define READING_MODULE_ADDRESS 2 // 获取电压值的模块地址 // 定义设置保持寄存器地址 #define SETTING_REGISTER_ADDRESS_START 0x0000 #define NUM_SETTING_REGISTERS 6 // 定义获取保持寄存器地址 #define READING_REGISTER_ADDRESS_START 0x0000 #define NUM_READING_REGISTERS 6 // 保存最新电压值的数组 uint16_t inputRegisters[NUM_READING_REGISTERS] = {0}; // 存储6个通道的电压值 // 要写入的数据数组 uint16_t dataToWrite[NUM_SETTING_REGISTERS] = {100, 200, 300, 400, 500, 600}; // 记录上次获取电压值的时间 unsigned long lastReadingTime = 0; // 记录发送设置命令的时间 unsigned long settingCommandTime = 0; // 定义定时器中断的时间间隔 #define TIMER_INTERVAL 10000 // 10秒 // 定义软串口用于Modbus通信 SoftwareSerial modbusSerial(TX_PIN, RX_PIN); void setup() { Serial.begin(9600); // 初始化串口用于调试信息 modbusSerial.begin(9600); // 初始化Modbus串口 // 设置Modbus从设备地址 modbus_configure(&modbusSerial, SETTING_MODULE_ADDRESS, 0, 0); // 初始化Modbus库 modbus_setup(); // 设置定时器中断 Timer1.initialize(TIMER_INTERVAL); // 设置定时器中断周期为10秒 Timer1.attachInterrupt(timerISR); } void loop() { // 处理Modbus通信 modbus_update(); // 在这里可以添加其他代码,但不要阻塞主循环 } // 定时器中断处理函数 void timerISR() { // 获取当前时间 unsigned long currentTime = millis(); // 如果在发送设置命令后的10到20秒内 if (currentTime - settingCommandTime >= 10000 && currentTime - settingCommandTime <= 20000) { // 写入保持寄存器的数据 MB_WriteNumHoldingReg_10H(SETTING_MODULE_ADDRESS, SETTING_REGISTER_ADDRESS_START, NUM_SETTING_REGISTERS, (uint8_t *)dataToWrite); } // 判断是否到了获取电压值的时间 if (currentTime - lastReadingTime >= TIMER_INTERVAL) { // 如果当前时间减去上次获取的时间超过了10秒 // 读取输入模块中的6个通道的电压值 MB_ReadHoldingRegisters(READING_MODULE_ADDRESS, READING_REGISTER_ADDRESS_START, NUM_READING_REGISTERS); // 更新上次获取电压值的时间 lastReadingTime = currentTime; } } // Modbus读保持寄存器函数 void MB_ReadHoldingRegisters(uint8_t address, uint16_t reg, uint8_t num) { uint8_t buffer[64]; // 假设最大消息长度为64字节 uint16_t crc; modbusSerial.write(address); // Slave Address modbusSerial.write(0x03); // Function Code: Read Holding Registers modbusSerial.write(reg >> 8); // Starting Address High Byte modbusSerial.write(reg & 0xFF); // Starting Address Low Byte modbusSerial.write(num >> 8); // Quantity of Registers High Byte modbusSerial.write(num & 0xFF); // Quantity of Registers Low Byte crc = modbus_calc_crc(address, 0x03, reg >> 8, reg & 0xFF, num >> 8, num & 0xFF); modbusSerial.write(crc & 0xFF); // CRC Low Byte modbusSerial.write((crc >> 8) & 0xFF); // CRC High Byte delay(100); // 等待足够的时间以确保设备有足够的时间响应 // 检查串口缓冲区中是否有数据可用 while (modbusSerial.available() > 0) { // 读取串口缓冲区中的数据并解析响应消息 uint8_t length = modbusSerial.readBytes(buffer, 64); // 解析响应消息 if (length >= 5) { // 最小消息长度为5个字节 // 检查地址和功能码 if (buffer[0] == address && buffer[1] == 0x03) { // 计算数据长度 uint8_t dataLength = buffer[2]; // 检查消息长度是否符合预期 if (length == 5 + dataLength + 2) { // 地址(1) + 功能码(1) + 数据长度(1) + 数据 + CRC(2) // 解析数据 for (int i = 0; i < dataLength / 2; i++) { uint16_t value = buffer[3 + i * 2] << 8 | buffer[4 + i * 2]; // 高字节在前,低字节在后 inputRegisters[i] = value; } // 数据解析完毕,可以在这里对接收到的数据进行处理 // 比如输出到串口或进行其他操作 for (int i = 0; i < NUM_READING_REGISTERS; i++) { Serial.print("Register "); Serial.print(i); Serial.print(": "); Serial.println(inputRegisters[i]); } } else { Serial.println("Invalid message length!"); } } else { Serial.println("Invalid address or function code!"); } } else { Serial.println("Invalid message format!"); } } } // 写入 N 个保持寄存器 void MB_WriteNumHoldingReg_10H(uint8_t _addr, uint16_t _reg, uint16_t _num, uint8_t *_databuf) { uint16_t i; uint16_t TxCount = 0; uint16_t crc = 0; uint8_t Tx_Buf[256]; // Assuming buffer size Tx_Buf[TxCount++] = _addr; // 从站地址 Tx_Buf[TxCount++] = 0x10; // 功能码 Tx_Buf[TxCount++] = _reg >> 8; // 寄存器地址 高字节 Tx_Buf[TxCount++] = _reg; // 寄存器地址 低字节 Tx_Buf[TxCount++] = _num >> 8; // 寄存器(16bits)个数 高字节 Tx_Buf[TxCount++] = _num; // 低字节 Tx_Buf[TxCount++] = _num << 1; // 数据个数 for (i = 0; i < 2 * _num; i++) { Tx_Buf[TxCount++] = _databuf[i]; // 后面的数据长度 } crc = MB_CRC16((uint8_t*)&Tx_Buf, TxCount); Tx_Buf[TxCount++] = crc; // crc 低字节 Tx_Buf[TxCount++] = crc >> 8; // crc 高字节 UART_Tx((uint8_t *)&Tx_Buf, TxCount); } // 计算 Modbus CRC16 uint16_t modbus_calc_crc(uint8_t address, uint8_t func, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) { uint16_t crc = 0xFFFF; crc = _crc16_update(crc, address); crc = _crc16_update(crc, func); crc = _crc16_update(crc, byte1); crc = _crc16_update(crc, byte2); crc = _crc16_update(crc, byte3); crc = _crc16_update(crc, byte4); return crc; } // CRC16 更新 uint16_t _crc16_update(uint16_t crc, uint8_t a) { int i; crc ^= a; for (i = 0; i < 8; ++i) { if (crc & 1) { crc = (crc >> 1) ^ 0xA001; } else { crc = (crc >> 1); } } return crc; }
============================================================================
代码2:
#include <SoftwareSerial.h> #include <ModbusMaster.h> #include <TimerOne.h> // 定义虚拟串口对象并指定引脚 SoftwareSerial mySerial(2, 3); // RX, TX // 定义ModbusMaster对象 ModbusMaster modbus1; // 设备1 ModbusMaster modbus2; // 设备2 // 定义常量 const unsigned long DAC_UPDATE_INTERVAL = 10000; // DAC更新间隔,单位:毫秒(10秒) const unsigned long ADC_READ_INTERVAL = 15000; // ADC读取间隔,单位:毫秒(15秒) unsigned long currentMillis; // 定义变量 unsigned long previousDACMillis = 0; unsigned long previousADCMillis = 0; uint8_t result; //创建接收缓冲区 // 定时器中断服务程序声明 void timerISR(); void setup() { // 初始化串口通信 Serial.begin(9600); // 初始化虚拟串口 mySerial.begin(9600); // 设置定时器1 Timer1.initialize(10000); // 初始化定时器1,周期为10毫秒 Timer1.attachInterrupt(timerISR); // 将中断服务程序与定时器1关联 // 初始化Modbus通信,使用虚拟串口 modbus1.begin(1, mySerial); // 使用设备地址 1 modbus2.begin(1, mySerial); // 使用设备地址 2 } void loop() { // 主循环为空,因为我们使用定时器中断进行定时 // Serial.println("..."); if (currentMillis - previousDACMillis >= DAC_UPDATE_INTERVAL) { Serial.println("Updating DAC..."); // 设置DAC寄存器值 // 示例代码,实际需要根据你的DAC模块寄存器映射进行修改 uint16_t dacValues[6] = {1000, 2000, 3000, 4000, 5000, 6000}; // 示例的DAC值 for (int i = 0; i < 6; i++) { // 设置第i个通道的DAC寄存器值 modbus1.writeSingleRegister(0x03EE + i, dacValues[i]); delay(10); // 等待一段时间,确保Modbus通信完成 } // 更新previousDACMillis previousDACMillis = currentMillis; } // 检查是否到达读取ADC的时间间隔 if (currentMillis - previousADCMillis >= ADC_READ_INTERVAL) { Serial.println("Reading ADC..."); // 读取ADC寄存器值 uint8_t result; uint16_t adcValue2; for (int i = 0; i < 4; i++) { // 读取第i个通道的ADC寄存器值 result = modbus2.readInputRegisters(0x51B + i, 1); delay(10); // 等待一段时间,确保Modbus通信完成 // 检查通信是否成功 if (result == modbus2.ku8MBSuccess) { // 获取读取到的寄存器值 adcValue2 = modbus2.getResponseBuffer(0); // 打印读取到的寄存器值 Serial.print("设备地址 2, ADC通道 "); Serial.print(i); Serial.print(" 的值为: "); Serial.println(adcValue2, DEC); // 将值以十进制形式输出 } else { // 打印错误信息 Serial.print("Error: "); Serial.println(result, HEX); } } // 更新previousADCMillis previousADCMillis = currentMillis; } } // 定时器1中断服务程序 void timerISR() { // 获取当前时间 currentMillis = millis(); // 检查是否到达更新DAC的时间间隔 }
野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892