Arduino中Modbus设置电压值及获取电压值
作者:野牛程序员:2024-01-29 15:14:53Arduino阅读 2981
#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

