[#0x0006] private、package权限字段不可继承

本文解释了基类的私有成员为何不会被派生类继承,并介绍了如何通过基类提供的非私有getters来访问这些私有成员。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  base class的private member是不可继承的(详细请参见[0x0004]),所以在ext class中也不会有这些member。

  可以这样理解:ext class隐式包含一个base class,base class根据member的访问权限决定是否将member暴露给ext class。

  不过ext class可以通过base class的非private getter来access这些private member。

 

  package权限字段不可继承的理由同。

package demo1; import javax.swing.*; public class Main { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { TemperatureMonitorGUI gui = new TemperatureMonitorGUI(); gui.setVisible(true); }); } } package demo1; public class ModbusRequestBuilder { public static byte[] buildReadRequest(byte slaveId, int registerAddr) { return buildRequest(slaveId, 0x03, registerAddr, 0x0001); } public static byte[] buildWriteRequest(byte slaveId, int registerAddr, int value) { return new byte[]{ slaveId, 0x06, (byte) (registerAddr >> 8), (byte) registerAddr, (byte) (value >> 8), (byte) value }; } private static byte[] buildRequest(byte slaveId, int functionCode, int registerAddr, int wordCount) { return new byte[]{ slaveId, (byte) functionCode, (byte) (registerAddr >> 8), (byte) registerAddr, (byte) (wordCount >> 8), (byte) wordCount }; } public static byte[] calculateCRC(byte[] data) { int crc = 0xFFFF; for (byte b : data) { crc ^= (b & 0xFF); for (int i = 0; i < 8; i++) { if ((crc & 0x0001) != 0) { crc >>= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return new byte[]{(byte) crc, (byte) (crc >> 8)}; } public static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString().trim(); } } package demo1; public class RegisterAddress { // 测量值类 public static final int TEMP_MEASURED = 0x0000; public static final int HUMIDITY_MEASURED = 0x0001; public static final int RPM_MEASURED = 0x000D; public static final int CO2_MEASURED = 0x0012; public static final int PRESSURE_MEASURED = 0x0013; public static final int O2_MEASURED = 0x00E8; // 控制设定类 public static final int RUN_CONTROL = 0x0002; public static final int TEMP_SETPOINT = 0x0003; public static final int TIMER_SETPOINT = 0x0006; public static final int HUMIDITY_SETPOINT = 0x0019; public static final int RPM_SETPOINT = 0x0018; public static final int LIGHT_GROUP1 = 0x001A; public static final int LIGHT_GROUP2 = 0x001E; public static final int LIGHT_GROUP3 = 0x001F; public static final int CO2_SETPOINT = 0x001B; public static final int O2_SETPOINT = 0x001C; public static final int PRESSURE_SETPOINT = 0x001D; // 状态与报警类 public static final int ALARM_STATUS = 0x000A; public static final int DEVICE_STATUS = 0x000B; // 连续读取 public static final int CONTINUOUS_READ = 0x00F8; } package demo1; import com.fazecast.jSerialComm.SerialPort; import com.fazecast.jSerialComm.SerialPortDataListener; import com.fazecast.jSerialComm.SerialPortEvent; public class SerialPortDataListenerImpl implements SerialPortDataListener { private final TemperatureMonitorGUI gui; private final TemperatureController controller; private final SerialPort serialPort; private final byte[] buffer = new byte[256]; private int bufferIndex = 0; public SerialPortDataListenerImpl(SerialPort serialPort, TemperatureMonitorGUI gui, TemperatureController controller) { this.serialPort = serialPort; this.gui = gui; this.controller = controller; } @Override public int getListeningEvents() { return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; } @Override public void serialEvent(SerialPortEvent event) { if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) { byte[] newData = new byte[serialPort.bytesAvailable()]; int numRead = serialPort.readBytes(newData, newData.length); System.arraycopy(newData, 0, buffer, bufferIndex, numRead); bufferIndex += numRead; processBuffer(); } } private void processBuffer() { while (bufferIndex >= 5) { // 至少一个完整的MODBUS头 int functionCode = buffer[1] & 0xFF; if ((functionCode & 0x80) != 0) { handleError(); removeProcessedBytes(5); continue; } switch (functionCode) { case 0x03: handleReadResponse(); break; case 0x06: handleWriteResponse(); break; case 0x08: handleLoopbackResponse(); break; default: removeProcessedBytes(1); // 忽略未知功能码 } } } private void handleReadResponse() { if (bufferIndex < 3) return; int byteCount = buffer[2] & 0xFF; int frameLength = 3 + byteCount + 2; // 包含CRC if (bufferIndex < frameLength) return; if (!verifyCRC(buffer, frameLength)) { gui.appendLog("❌ CRC校验失败"); removeProcessedBytes(frameLength); return; } int registerAddr = ((buffer[3] & 0xFF) << 8) | (buffer[4] & 0xFF); switch (registerAddr) { case RegisterAddress.TEMP_MEASURED: int tempValue = ((buffer[5] & 0xFF) << 8) | (buffer[6] & 0xFF); double temperature = tempValue / 10.0; controller.updateTemperature(temperature); gui.appendLog(String.format("✅ 读取温度: %.1f°C", temperature)); break; case RegisterAddress.HUMIDITY_MEASURED: int humidity = ((buffer[5] & 0xFF) << 8) | (buffer[6] & 0xFF); controller.updateHumidity(humidity); gui.appendLog(String.format("💧 读取湿度: %d%%", humidity)); break; case RegisterAddress.CONTINUOUS_READ: handleContinuousReadResponse(); break; default: gui.appendLog("📦 未知寄存器地址: " + Integer.toHexString(registerAddr)); } removeProcessedBytes(frameLength); } private void handleContinuousReadResponse() { int frameLength = 3 + 29 + 2; // 固定长度+CRC if (bufferIndex < frameLength) return; if (!verifyCRC(buffer, frameLength)) { gui.appendLog("❌ 连续读取CRC校验失败"); removeProcessedBytes(frameLength); return; } int index = 3; // 跳过MODBUS头部 int tempMeasured = ((buffer[index++] & 0xFF) << 8) | (buffer[index++] & 0xFF); int tempSetpoint = ((buffer[index++] & 0xFF) << 8) | (buffer[index++] & 0xFF); int humidityMeasured = ((buffer[index++] & 0xFF) << 8) | (buffer[index++] & 0xFF); int humiditySetpoint = ((buffer[index++] & 0xFF) << 8) | (buffer[index++] & 0xFF); controller.updateTemperature(tempMeasured / 10.0); controller.updateHumidity(humidityMeasured); gui.appendLog(String.format("🔁 连续读取:温度=%.1f°C, 设定=%.1f°C, 湿度=%d%%", tempMeasured / 10.0, tempSetpoint / 10.0, humidityMeasured)); removeProcessedBytes(frameLength); } private void handleWriteResponse() { int frameLength = 8; if (bufferIndex < frameLength) return; if (!verifyCRC(buffer, frameLength)) { gui.appendLog("❌ 写响应CRC校验失败"); removeProcessedBytes(frameLength); return; } int registerAddr = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); int value = ((buffer[4] & 0xFF) << 8) | (buffer[5] & 0xFF); gui.appendLog(String.format("✅ 寄存器写入成功: 地址=0x%04X, 值=0x%04X", registerAddr, value)); gui.setStatus("寄存器写入成功"); removeProcessedBytes(frameLength); } private void handleLoopbackResponse() { int frameLength = 8; if (bufferIndex < frameLength) return; if (!verifyCRC(buffer, frameLength)) { gui.appendLog("❌ 回路侦测CRC校验失败"); removeProcessedBytes(frameLength); return; } int subFunction = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); int data = ((buffer[4] & 0xFF) << 8) | (buffer[5] & 0xFF); gui.appendLog(String.format("🔁 回路测试成功: 子功能=0x%04X, 数据=0x%04X", subFunction, data)); gui.setStatus("回路侦测成功"); removeProcessedBytes(frameLength); } private void handleError() { if (bufferIndex < 3) return; int errorCode = buffer[2] & 0xFF; String errorMessage = "❌ 错误响应: "; switch (errorCode) { case 0x01: errorMessage += "功能码错误"; break; case 0x02: errorMessage += "寄存器地址错误"; break; case 0x03: errorMessage += "资料内容值错误"; break; case 0x04: errorMessage += "控制器无法处理"; break; case 0x09: errorMessage += "CRC校验错误"; break; case 0x0A: errorMessage += "奇偶校验错误"; break; case 0x0C: errorMessage += "接收数据低于规定长度"; break; case 0x0D: errorMessage += "接收数据超过规定长度"; break; default: errorMessage += "未知错误码: " + errorCode; } gui.appendLog(errorMessage); gui.setStatus(errorMessage); removeProcessedBytes(5); } private boolean verifyCRC(byte[] data, int length) { byte[] frameData = new byte[length - 2]; System.arraycopy(data, 0, frameData, 0, length - 2); byte[] receivedCRC = new byte[]{data[length - 2], data[length - 1]}; byte[] calculatedCRC = ModbusRequestBuilder.calculateCRC(frameData); return receivedCRC[0] == calculatedCRC[0] && receivedCRC[1] == calculatedCRC[1]; } private void removeProcessedBytes(int count) { if (count > bufferIndex) count = bufferIndex; System.arraycopy(buffer, count, buffer, 0, bufferIndex - count); bufferIndex -= count; } } package demo1; class SerialRequest { public byte[] data; public boolean isManual; public SerialRequest(byte[] data, boolean isManual) { this.data = data; this.isManual = isManual; } } package demo1; import com.fazecast.jSerialComm.SerialPort; import static demo1.ModbusRequestBuilder.buildReadRequest; import static demo1.ModbusRequestBuilder.buildWriteRequest; import java.util.concurrent.*; public class TemperatureController { private final TemperatureMonitorGUI gui; private final String portName; private SerialPort serialPort; private volatile boolean running = false; // 请求队列 private final BlockingQueue<SerialRequest> requestQueue = new LinkedBlockingQueue<>(); private ScheduledExecutorService queueExecutor; public TemperatureController(TemperatureMonitorGUI gui, String portName) { this.gui = gui; this.portName = portName; } public void start() { new Thread(this::connectSerialPort).start(); } private void connectSerialPort() { serialPort = SerialPort.getCommPort(portName); serialPort.setComPortParameters(9600, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY); serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0); if (serialPort.openPort()) { System.out.println("✅ 成功打开串口"); gui.setStatus("已连接到 " + portName); serialPort.addDataListener(new SerialPortDataListenerImpl(serialPort, gui, this)); queueExecutor = Executors.newScheduledThreadPool(1); queueExecutor.submit(this::consumeRequests); queueExecutor.scheduleAtFixedRate(() -> enqueueRequest(buildReadRequest((byte) 0x01, RegisterAddress.TEMP_MEASURED), false), 0, 1, TimeUnit.SECONDS); } else { System.err.println("❌ 无法打开串口"); gui.setStatus("无法打开串口:" + portName); } } private void consumeRequests() { while (!Thread.interrupted()) { try { SerialRequest request = requestQueue.take(); sendRequest(request.data); if (request.isManual) { Thread.sleep(500); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } public void readRegister(byte slaveId, int registerAddr) { enqueueRequest(buildReadRequest(slaveId, registerAddr), true); } public void writeRegister(byte slaveId, int registerAddr, int value) { enqueueRequest(buildWriteRequest(slaveId, registerAddr, value), true); } public void loopbackTest(byte slaveId) { enqueueRequest(new byte[]{slaveId, 0x08, 0x00, 0x00, 0x12, 0x34}, true); } public void continuousRead(byte slaveId) { enqueueRequest(new byte[]{slaveId, 0x03, 0x00, (byte) 0xF8, 0x00, 0x14}, true); } private void enqueueRequest(byte[] data, boolean isManual) { requestQueue.offer(new SerialRequest(data, isManual)); } public void sendRequest(byte[] data) { if (serialPort == null || !serialPort.isOpen()) { System.err.println("串口未打开,无法发送数据"); return; } byte[] crc = ModbusRequestBuilder.calculateCRC(data); byte[] fullRequest = new byte[data.length + 2]; System.arraycopy(data, 0, fullRequest, 0, data.length); fullRequest[data.length] = crc[0]; // CRC低字节 fullRequest[data.length + 1] = crc[1]; // CRC高字节 serialPort.writeBytes(fullRequest, fullRequest.length); System.out.println("发送请求: " + ModbusRequestBuilder.bytesToHex(fullRequest)); } public void updateTemperature(double temperature) { gui.updateTemperature(temperature); } public void updateHumidity(int humidity) { gui.updateHumidity(humidity); } } package demo1; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Objects; public class TemperatureMonitorGUI extends JFrame { private JLabel temperatureLabel; private JLabel humidityLabel; private JLabel statusLabel; private JButton connectButton; private JButton readButton; private JButton writeButton; private JButton loopbackButton; private JButton continuousReadButton; private JComboBox<String> portComboBox; private TemperatureController controller; // 新增字段 private JTextField slaveIdField = new JTextField("01"); private JComboBox<String> functionCodeCombo = new JComboBox<>(new String[]{"03H: 读寄存器", "06H: 写寄存器", "08H: 回路侦测"}); private JTextField registerAddrField = new JTextField("0000"); private JTextField valueField = new JTextField("0000"); private JTextField floatTemperatureField = new JTextField("20.5"); private JComboBox<String> inputTypeComboBox = new JComboBox<>(new String[]{"DEC", "HEX", "FLOAT"}); private JTextArea logArea = new JTextArea(5, 40); // 用于包裹动态组件的容器 private JPanel dynamicInputPanel; public TemperatureMonitorGUI() { setTitle("MODBUS RTU 温控系统"); setSize(800, 700); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout(10, 10)); // 主面板 JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); JScrollPane scrollPane = new JScrollPane(mainPanel); // 表单面板 JPanel formPanel = new JPanel(new GridLayout(0, 2, 5, 5)); // 动态输入面板(包裹 valueField 和 floatTemperatureField) dynamicInputPanel = new JPanel(new GridLayout(0, 2, 5, 5)); // 串口选择 addFormRow(formPanel, "串口:", createPortSelection()); // 地址码输入 addFormRow(formPanel, "地址码 (HEX):", slaveIdField); // 功能码选择 addFormRow(formPanel, "功能码:", functionCodeCombo); // 寄存器地址 addFormRow(formPanel, "寄存器地址 (HEX):", registerAddrField); registerAddrField.addActionListener(e -> updateInputFieldsVisibility()); // 输入类型选择 addFormRow(formPanel, "输入类型:", inputTypeComboBox); inputTypeComboBox.addActionListener(e -> updateInputFieldsVisibility()); // 值输入框 addFormRow(dynamicInputPanel, "值 (HEX):", valueField); // 浮点温度输入框 addFormRow(dynamicInputPanel, "浮点温度 (FLOAT):", floatTemperatureField); // 将动态面板加入主表单 mainPanel.add(formPanel); mainPanel.add(dynamicInputPanel); // 按钮面板 JPanel buttonPanel = new JPanel(new GridLayout(1, 5, 5, 5)); connectButton = new JButton("连接串口"); readButton = new JButton("读取"); writeButton = new JButton("写入"); loopbackButton = new JButton("回路测试"); continuousReadButton = new JButton("连续读取"); buttonPanel.add(connectButton); buttonPanel.add(readButton); buttonPanel.add(writeButton); buttonPanel.add(loopbackButton); buttonPanel.add(continuousReadButton); // 状态栏 statusLabel = new JLabel("状态: 未连接", SwingConstants.CENTER); // 当前温度显示 temperatureLabel = new JLabel("当前温度: --°C", SwingConstants.CENTER); temperatureLabel.setFont(new Font("Arial", Font.BOLD, 24)); // 湿度显示 humidityLabel = new JLabel("当前湿度: --%", SwingConstants.CENTER); humidityLabel.setFont(new Font("Arial", Font.BOLD, 24)); // 日志区域 logArea.setEditable(false); JScrollPane logScrollPane = new JScrollPane(logArea); // 组装主界面 mainPanel.add(buttonPanel); mainPanel.add(statusLabel); mainPanel.add(temperatureLabel); mainPanel.add(humidityLabel); mainPanel.add(logScrollPane); // 初始化控制器 controller = null; // 事件绑定 connectButton.addActionListener(e -> { String selectedPort = (String) portComboBox.getSelectedItem(); if (selectedPort != null && controller == null) { controller = new TemperatureController(this, selectedPort); controller.start(); } }); readButton.addActionListener(e -> { if (controller != null) { try { int slaveId = Integer.parseInt(slaveIdField.getText(), 16); int registerAddr = Integer.parseInt(registerAddrField.getText(), 16); controller.readRegister((byte) slaveId, registerAddr); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请输入有效的十六进制数字!"); } } }); writeButton.addActionListener(e -> { if (controller != null) { try { int slaveId = Integer.parseInt(slaveIdField.getText(), 16); int registerAddr = Integer.parseInt(registerAddrField.getText(), 16); String inputType = (String) inputTypeComboBox.getSelectedItem(); if (registerAddr == RegisterAddress.TEMP_SETPOINT && "FLOAT".equals(inputType)) { // 浮点写入 float floatValue = Float.parseFloat(floatTemperatureField.getText()); int intBits = Float.floatToIntBits(floatValue); int highWord = (intBits >> 16) & 0xFFFF; int lowWord = intBits & 0xFFFF; controller.writeRegister((byte) slaveId, registerAddr, highWord); controller.writeRegister((byte) slaveId, registerAddr + 1, lowWord); appendLog(String.format("写入浮点温度 %.2f -> %04X %04X", floatValue, highWord, lowWord)); } else { int value = Integer.parseInt(valueField.getText(), 16); controller.writeRegister((byte) slaveId, registerAddr, value); appendLog("写入寄存器 0x" + Integer.toHexString(registerAddr).toUpperCase() + " 成功,值:" + valueField.getText()); } // 写入后自动读取一次 controller.readRegister((byte) slaveId, registerAddr); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请输入有效的数值!"); } } }); loopbackButton.addActionListener(e -> { if (controller != null) { try { int slaveId = Integer.parseInt(slaveIdField.getText(), 16); controller.loopbackTest((byte) slaveId); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请输入有效的十六进制数字!"); } } }); continuousReadButton.addActionListener(e -> { if (controller != null) { try { int slaveId = Integer.parseInt(slaveIdField.getText(), 16); controller.continuousRead((byte) slaveId); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请输入有效的十六进制数字!"); } } }); // 初始更新一次 updateInputFieldsVisibility(); } private void addFormRow(JPanel panel, String label, Component component) { panel.add(new JLabel(label)); panel.add(component); } private JComboBox<String> createPortSelection() { portComboBox = new JComboBox<>(); refreshPortList(); return portComboBox; } // 控制输入框显示/隐藏 private void updateInputFieldsVisibility() { try { int registerAddr = Integer.parseInt(registerAddrField.getText(), 16); boolean isFloatTemp = registerAddr == RegisterAddress.TEMP_SETPOINT && "FLOAT".equals(inputTypeComboBox.getSelectedItem()); floatTemperatureField.setVisible(isFloatTemp); valueField.setVisible(!isFloatTemp); // 修复:对动态面板调用 revalidate 和 repaint dynamicInputPanel.revalidate(); dynamicInputPanel.repaint(); } catch (NumberFormatException ignored) {} } public void refreshPortList() { portComboBox.removeAllItems(); for (com.fazecast.jSerialComm.SerialPort port : com.fazecast.jSerialComm.SerialPort.getCommPorts()) { portComboBox.addItem(port.getSystemPortName()); } } public void updateTemperature(double temperature) { temperatureLabel.setText(String.format("当前温度: %.1f°C", temperature)); } public void updateHumidity(int humidity) { humidityLabel.setText(String.format("当前湿度: %d%%", humidity)); } public void setStatus(String status) { statusLabel.setText("状态: " + status); } public void appendLog(String message) { SwingUtilities.invokeLater(() -> logArea.append(message + "\n")); } } MODBUS_RTU通讯协议(客户) 1.字元结构 1.1 10―bit字元框(FOR ASCII) 资料格式 8. N .1 START BIT 0 1 2 3 4 5 6 7 STOP BIT 8-data bits 10-bits character fram 1.2 11―bit字元框(FOR RTU) 资料格式 8. N .2 START BIT 0 1 2 3 4 5 6 7 STOP BIT STOP BIT 8-data bits 11-bits character fram 资料格式 8. E .1 START BIT 0 1 2 3 4 5 6 7 Even Parity STOP BIT 8-data bits 11-bits character fram 资料格式 8. O. 1 START BIT 0 1 2 3 4 5 6 7 Odd Parity STOP BIT 8-data bits 11-bits character fram 波特率:1200,2400,4800,9600,19200 2.通信资料格式 RTU模式 START 保持无输入讯号≧ 20ms Adress 通信位址:8-bit 二进制位址。00H为广播地址,使用广播地址时只能接一台控制器。 Function 功能码:8-bit 二进制位址 DATA(n-1) 资料内容: n8-bit 资料内容 …… DATA 0 CRC CHK Low CRC 检查码: 由2个8-bit二进制码组成 CRC CHK High END Hi 保持无输入讯号≧20ms 2.3功能码: 03H:读出暂存器内容 06H:写入一个WORD至寄存器 08H:回路侦测 2.3.1功能码08H:回路侦测。 RTU 模式:询问格式: 回应格式: Address 01H Address 01H Function 08H Function 08H Sub-Func-Hi 00H (任意) Sub-Func-Hi 00H Sub-Func-Lo 00H (任意) Sub-Func-Lo 00H Data Content 12H (任意) Data Content 12H 34H (任意) 34H CRC Lo EDH CRC Lo EDH CRC Hi 7CH CRC Hi 7CH 2.3.2功能码03H:读出暂存器内容。 例如:从起始暂存器(位址0000)读出2个连续资料内容,假设寄存器(0000)=0100H,(0001)=00F0H。 RTU模式:询问格式: 回应格式 Address 01H Address 01H Function 03H Function 03H Data Addr 00H Number of data (count by byte) 04H 00H Number of data (count by word) 00H Content of data (Address 000001H 02H 00H CRC Low C4H Content of data (Address 0001) 00H CRC Hight 0BH F0H CRC CHK Low FBH CRC CHK Hight 8BH 2.3.3功能码06H:写入一个WORD至暂存器。 例如:对驱动器位址01H,写入03E8H到参数0010H。 询问格式: 回应格式: Address 01H Address 01H Function 06H Function 06H Data Addr 00H Data Addr 00H 10H 10H Data Content 03H Data Content 03H E8H E8H CRC Low 88H CRC CHK Low 88H CRC Hight B1H CRC CHK Hight B1H 2.4.错误通讯时的额外回应: 当控制器做通信连接时,如果产生错误,此时控制器会回应错误码且将Function code AND 80H回应给主控系统,让主控系统知道有错误产生。参考错误通信时错误码的意义 RTU模式: Address 01H Function 86H Except code 02H CRC CHR Low C3H CRC CHR Hight A1H 错误码的意义: 错误码 说明 01 功能码错误; 控制器可以辨识的功能码为03H,06H,08H 02 寄存器地址错误; 资料的位址控制器无法辨识 03 资料内容值错误 资料内容值太大或者太小,不是控制器所能辨识的内容值 04 控制器无法处理; 控制器对此命令,无法执行 09 CRC或者LRC校验错误 10 奇偶校验错误 12 接收数据低于规定长度 13 接收数据超过规定长度 其中将原功能号AND 80H后返回。并在Except code中返回错误码(见右上表格) 2.5 RTU模式的检查码(CRC Check) 检查码由Address到Data content结束。其运算规则如下: 步骤1:令16-bit暂存器(CRC暂存器)=FFFFH。 步骤2:Exclusive OR第一个8-bite byte的讯息指令与低位元16-bite CRC暂存器,做Exclusive OR,将结果存入CRC暂存器内。 步骤3:右移位CRC暂存器,将0填入高位元处。 步骤4:检查右移的值,如果是0,将步骤3的新值存入CRC暂存器内,否则Exclusive OR A001H与CRC暂存器,将结果存入CRC暂存器内。 步骤5:重复步骤3~步骤4,将8-bit全部运算完成。 步骤6:重复步骤2~步骤5,取下一个8-bit的讯息指令,直到所有讯息指令运算完成。最后,得到的CRC暂存器的值,即是CRC的检查码。值得注意的是CRC的检查码必须交换放置於讯息指令的检查码中。 以下为用c语言所写的crc检查码运算范例: unsigned char *data; unsigned char length; unsigned int crc_chk(unsigned char *data,unsigned char length) { int j;unsigned int reg_crc=0xffff; while(length--){ reg_crc^=*data++; for(j=0;j<8;j++){ if(reg_crc&0x01){ reg_crc=(reg_crc>>1)^0xa001;} else{ reg_crc=reg_crc>>1; } } } return reg_crc; } 3:通讯参数修改:同时按住SET键加上移位键4秒以上,出现LK代码,按数字键使LK数值为118,再:按SET键进入通讯参数设置(SET键即功能键)。 意义 参数名 说 明 通讯地址 Ad 0-32 0:广播地址 通讯波特率 bA 2:4800 3:9600 4:19200 通讯格式 Fo 3:MODBUS-RTU(8,N,2) 4:MODBUS-RTU(8,E,1) 5:MODBUS-RTU(8,O,1)6: MODBUS-RTU(8,N,1) 4.参数位址定义: 0x0000: 温度测量值 只能读 0x0001: 湿度测量值 只能读 0x0002: 读:剩余运行时间。 写:写入0,关闭手动输出;写入1,打开手动输出。比如照明,循环。 0x0003/0x0004:温度设定值 可读可写。 0x0006: 定时设定值 可读可写。 0x000a: 读:.0 超过允许最高温度报警 .1温度上偏差报警 .2温度传感器故障 .6 温度下偏差报警 .15 总报警 写:写入1 在鸣叫和静音之间切换。 0x000b: 读:0x000b.11=1运行 =0停止 写:写入1,启动或停止运行 读:0x000b.8=1 手动输出打开 =0 手动输出关闭。 0x000d: 转速测量值 只读 0x0012: CO2浓度测量值 只读 0x0013: 压力测量值 只读 0x0018: 转速设定值 可读可写 0x0019: 湿度设定值 可读可写 0x001a: 第一组光照设定值 可读可写 0x001b: CO2设定值 可读可写 0x001c: O2设定值 可读可写 0x001d: 压力设定值 可读可写 0x001e: 第二组光照设定值 可读可写 0x001f: 第三组光照设定值 可读可写 0x00e8 O2氧浓度测量值 只读 0x00f8: 连读 上位机发送:通讯地址+03H+00H+F8H+00H+14H+CRC(L)+CRC(H)。此处0x00F8必须优先。 控制器返回:通讯地址+03H+28H+运行时间高位+运行时间低位+设定时间高位+设定时间低位+测量温度高位+测量温度低位+设定温度高位+设定温度低位+测量湿度高位+测量湿度低位+设定湿度高位+设定湿度低位+第二组光照级数+第一组光照级数+第三组光照级数+第一组光照级数+测量转速高位+测量转速低位+设定转速高位+设定转速低位+测量CO2浓度高位+测量CO2浓度低位+设定CO2浓度高位+设定CO2浓度低位+测量O2浓度高位+测量O2浓度低位+设定O2浓度高位+设定O2浓度低位+测量压力高位+测量压力低位+设定压力高位+设定压力低位+备份1高位+备份1低位+备份2高位+备份2低位+备份3高位+备份3低位+备份4高位+备份4低位+ CRC(L)+CRC(H). 根据协议实现功能,将多余代码删除
最新发布
07-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值