三菱PLC编程口通信上位机端报文和java示范代码

读操作:

  发送 02 30 XX XX XX XX YY YY 03 CS CS

  XX是寄存器地址转16进制字符串,定长4个ASCII字符,YY是读取的字节数转16进制字符串,定长2个ASCII字符,CS是对从30(含)到03(含)的每一个字节求和(字节本身的数字0~255,而非它所代表的文字含意'0'~'F')然后除以256的余数转十六进制的字符串,定长2个ASCII字符。字符串是以高位在低字节方式排序的,也就是阅读顺序。高位为0时不能省略,要写‘0’字符,对应的字节数字为30。

  返回 02 DD DD 03 CS CS

  DD是读取的字节转十六进制字符串格式,数量一定是2的倍数(2个字符代表一个字节)。如果读的是M,X,Y,则返回开关量的数量会自动补充成8的倍数,因为一字节是8位,如果你要3个,它会给你8个,低3位是你要的,高5位是赠送的。相邻两个字符代表一个8位字节,按阅读顺序(高4位在前),然后字节的含义是低位为低地址。如果读的是D,则返回寄存器数量不会自动补成整数,你可以只读半个寄存器(一个寄存器16位,但你可以对最后一个寄存器只读8位)。相邻两个字符代表一个8位字节,按阅读顺序(高4位在前),然后相邻两字节则是低8位在前。4位(ASCII字符串的一个字符代表4位)组成8位时是大端在前,8位组16位则变为小端在前。

写操作:

  发送 02 31 XX XX XX XX YY YY DD DD 03 CS CS

  把功能码换成0x31,也就是字符'1'。然后在读报文上加了DD,它是你需要发送的字节转16进制字符串,所以DD的个数一定是2的倍数。因为DD只需要是2的倍数,所以你可以只写半个寄存器,但最好别这样做,这可能会让你的代码出Bug,然后你找很久都找不出来。如果你只想改一个M怎么办?没办法,你只能先读出另外7个M,然后再原样写回去。当然,如果你确定另外7个M没有用,也可以不去管它。

  返回06

  表示成功操作

  读和写失败的时候都返回15,可能是报文问题,也可能是你操作了一个不存在的M或D,或者不可写的M或D。三菱PLC不会告诉你具体的原因,你需要自己去查手册然后去猜。

  M和D、X、Y被安排在连续的地址空间,每一个前缀需要不同的增量。增量见下面那个MITSUBISHI_CONST的接口文件,里面有定义各种增量。它是字节为单位的,也就是说一个地址可能代表8个M或半个D。

  读写M的时候需要地址对齐,这是硬件决定的,逻辑上需要注意。读写D的时候我建议你最好自觉对齐,不然不知道会发生什么。

  FX3U已经没有强制ON和强制OFF了。这两项功能在以前的梯形图调试的时候比较有用,GX work2已经没有这一功能了。FX3U也不支持该指令。

  我们可以看到三菱通信协议是基于字符串的,字符串(也叫“字符流”)是一种特殊的字节流(也就是常说的二进制数据格式),它在字节流的基础上对字节的值域进行分配,将0~255个数字分成可见字符、不可见字符、控制字符三类,对字节的数值范围分类使报文更容易设计。比如报文头02,它是一个不可见字符(在早期的计算机系统它是控制字符,但现在不是了),它不可能与业务逻辑相冲突,便于报文对齐。如果没有报文头,则只能使用超时机制,超时对齐不如报文头对齐稳定。

  由于可用字符的范围缩小,有些干扰会直接使报文中出现没有定义的字节,这样不需要校验和就可以确定报文错误。

  字符串便于阅读,但美中不足的是三菱的寄存器是低8位在前的,一个按书写顺序为H1234的十六进制数,在三菱报文中显示为H3412。东方人喜欢在小处偷懒,在字节的尺度上就直接用地址顺序,代码比较少;而单片机的地址尺度只细到8位的字节,字节中的位没有地址顺序,就只能按阅读顺序编码。所以造成了三菱通讯协议单个字节中的位是按阅读顺序,多字节中的每个字节按地址顺序。

  使用字符串也有缺点。字符串如果不压缩,要表达相同的信息会需要2倍于字节流的字节长度(一个字节当半个字节来用),如果压缩,则会给单片机的编程造成很大的麻烦。低端的单片机甚至没有足够的程序存储器来存放压缩算法。因为需要2倍的字节数量,要达到相同的信息传输速率,字符串需要的比特率为字节流的2倍。由此看来,字节流由于大幅降低了比特率,反而使线路本身坑干扰的能力提升。

  三菱PLC有一种DB9串口接头的编程线,虽然USB接头其实也是转串口下载,但DB9的接头更容易让人萌生手拉手总线通信的念头。但这在三菱PLC编程口通信是不行的。比较一下Modbus协议和三菱协议,在交互规则上有一点不同,Modbus如果校验不通过不会有回复,而三菱PLC编程口通信不管收到什么都会有回复。如果用485转换器把三菱PLC连接到手拉手总线通信上,不管上位机发信息还是其它下位机回复,三菱PLC都会有串口数据输出,这将导致大量的报文被干扰,线路无法使用。

 

下面是一些示范代码

常量定义:

package pers.laserpen.util.protocol.mitsubishi;

interface MITSUBISHI_CONST {
	public static final char CMD_READ = '0';
	public static final char CMD_WRITE = '1';
	@Deprecated
	public static final char CMD_FORCE_ON = '7';
	@Deprecated
	public static final char CMD_FORCE_OFF = '8';

	public static final byte STRUCT_ENQ = 5;
	public static final byte STRUCT_ACK = 6;
	public static final byte STRUCT_NAK = (byte) 0x15;
	public static final byte STRUCT_STX = 2;
	public static final byte STRUCT_ETX = 3;

	public static final int BASE_D = 0x1000;
	public static final int BASE_SPECIAL_D = 0xe00;
	public static final int BASE_Y = 0xa0;
	public static final int BASE_PY = 0x2a0;
	public static final int BASE_T = 0xc0;
	public static final int BASE_OT = 0x2c0;
	public static final int BASE_RT = 0x4c0;
	public static final int BASE_M = 0x100;
	public static final int BASE_PM = 0x300;
	public static final int BASE_S = 0x0;
	public static final int BASE_X = 0x80;
	public static final int BASE_C = 0x1c0;
	public static final int BASE_OC = 0x3c0;
	public static final int BASE_RC = 0x5c0;
	public static final int BASE_TV = 0x800;
	public static final int BASE_CV16 = 0xa00;
	public static final int BASE_CV32 = 0xc00;
}

工具方法:

package pers.laserpen.util.protocol.mitsubishi;

import static pers.laserpen.util.protocol.mitsubishi.MITSUBISHI_CONST.*;

import java.util.HashMap;
import java.util.Map;
import java.util.function.IntUnaryOperator;

abstract class StaticMitsubishiUtils {

	private static final Map<String, Integer> BASE_INDEX = new HashMap<>();

	static {
		BASE_INDEX.put("D", BASE_D);
		BASE_INDEX.put("SD", BASE_SPECIAL_D);
		BASE_INDEX.put("Y", BASE_Y);
		BASE_INDEX.put("PY", BASE_PY);
		BASE_INDEX.put("T", BASE_T);
		BASE_INDEX.put("OT", BASE_OT);
		BASE_INDEX.put("RT", BASE_RT);
		BASE_INDEX.put("M", BASE_M);
		BASE_INDEX.put("PM", BASE_PM);
		BASE_INDEX.put("S", BASE_S);
		BASE_INDEX.put("X", BASE_X);
		BASE_INDEX.put("C", BASE_C);
		BASE_INDEX.put("OC", BASE_OC);
		BASE_INDEX.put("RC", BASE_RC);
		BASE_INDEX.put("TV", BASE_TV);
		BASE_INDEX.put("CV16", BASE_CV16);
		BASE_INDEX.put("CV32", BASE_CV32);
	}

	private static final Map<String, IntUnaryOperator> SIZE_CONVERTER = new HashMap<>();

	static {
//		IntUnaryOperator same = i -> i;
		IntUnaryOperator bit = i -> {
			if (i % 8 != 0) {
				System.err.println("地址未对齐,将自动转换成" + (i & (-1 << 3)));
			}
			return i / 8;
		};
		IntUnaryOperator word = i -> i << 1;
		IntUnaryOperator dword = i -> i << 2;

		SIZE_CONVERTER.put("D", word);
		SIZE_CONVERTER.put("SD", word);
		SIZE_CONVERTER.put("Y", bit);
		SIZE_CONVERTER.put("PY", bit);
		SIZE_CONVERTER.put("T", word);
		SIZE_CONVERTER.put("OT", word);
		SIZE_CONVERTER.put("RT", word);
		SIZE_CONVERTER.put("M", bit);
		SIZE_CONVERTER.put("PM", bit);
		SIZE_CONVERTER.put("S", bit);
		SIZE_CONVERTER.put("X", bit);
		SIZE_CONVERTER.put("C", word);
		SIZE_CONVERTER.put("OC", word);
		SIZE_CONVERTER.put("RC", word);
		SIZE_CONVERTER.put("TV", word);
		SIZE_CONVERTER.put("CV16", word);
		SIZE_CONVERTER.put("CV32", dword);
	}

	private static final byte toAscii(int num) {
		num &= 0xf;
		if (num < 10) {
			return (byte) ('0' + num);
		}
		return (byte) ('A' - 10 + num);
	}

	/**
	 * 三菱PLC通信协议的校验
	 * 
	 * @param bytes         数据缓冲
	 * @param checkSumIndex 校验的第一个字节放在缓冲区的位置
	 */
	static final void checkSum(byte[] bytes, int checkSumIndex) {
		int sum = 0;
		for (int i = 1; i < checkSumIndex; ++i) {
			sum += 0xff & bytes[i];
		}
		bytes[checkSumIndex + 1] = toAscii(sum);
		sum /= 16;
		bytes[checkSumIndex] = toAscii(sum);
	}

	/**
	 * 三菱PLC寄存器地址
	 * 
	 * @param type
	 * @param index
	 * @return
	 */
	static final int getIndexOfRegex(String type, int index) {
		type = type.toUpperCase();
		int base = BASE_INDEX.get(type);
		index = SIZE_CONVERTER.get(type).applyAsInt(index);
		return base + index;
	}

}

 通信协议报文类(仅报文,不含通信):

package pers.laserpen.util.protocol.mitsubishi;

import static pers.laserpen.util.protocol.mitsubishi.MITSUBISHI_CONST.*;
import static pers.laserpen.util.protocol.mitsubishi.StaticMitsubishiUtils.*;
import static pers.laserpen.util.data.StaticDataUtils.*;

import pers.laserpen.util.data.AbstractBinaryDataSupplier;
import pers.laserpen.util.data.StaticDataUtils;
import pers.laserpen.util.protocol.CommunicationProtocol;

public class MitsubishiPLC extends AbstractBinaryDataSupplier implements CommunicationProtocol {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private char m_cmd = CMD_READ;

	private int m_index = 0;

	private int m_byteLength = 0;

	private boolean m_check;

	private byte[] m_data;

	/**
	 * 
	 * @param type       "D", "M", "X", "Y"等
	 * @param index      必须是字节对齐的
	 * @param byteLength 字节数(一字节为8个开关量)
	 */
	public void setRead(String type, int index, int byteLength) {
		setCmd(CMD_READ);
		setIndex(type, index);
		setByteLength(type, byteLength);
	}

	/**
	 * 
	 * @param type  "D", "M", "X", "Y"等
	 * @param index 必须是字节对齐的
	 * @param bytes
	 */
	public void setWrite(String type, int index, byte[] bytes) {
		setCmd(CMD_WRITE);
		setIndex(type, index);
		setByteLength(bytes.length);
		setData(bytes);
	}

	/**
	 * 
	 * @param type
	 * @param index 必须是字节对齐的
	 * @param nums  short型的数字
	 */
	public void setWriteD(String type, int index, short[] nums) {
		byte[] bytes = new byte[nums.length * 2];
		for (int i = 0, j = 0; i < nums.length; ++i) {
			bytes[j++] = (byte) (nums[i] & 0xff);
			bytes[j++] = (byte) ((nums[i] >>> 8) & 0xff);
		}
		setWrite(type, index, bytes);
	}

	public byte[] getRead() {
		return m_data;
	}

	public short[] getReadD() {
		if (m_data == null) {
			return null;
		}
		short[] words = new short[m_data.length / 2];
		for (int i = 0, j = 0; i < words.length; ++i) {
			int tempL = m_data[j++] & 0xff;
			int tempH = m_data[j++] & 0xff;
			int temp = (tempH << 8) | tempL;
			words[i] = (short) temp;
		}
		return words;
	}

	private void setCmd(char cmd) {
		m_cmd = cmd;
	}

	private void setIndex(String type, int index) {
		setIndex(getIndexOfRegex(type, index));
	}

	private void setIndex(int index) {
		m_index = index & 0xffff;
	}

	private void setByteLength(String type, int regexLength) {
		if ("XYM".contains(type)) {
			regexLength = (regexLength + 7) / 8;
		} else if ("D".contains(type)) {
			regexLength = regexLength * 2;
		}
		setByteLength(regexLength);
	}

	private void setByteLength(int byteLength) {
		m_byteLength = byteLength & 0xff;
	}

	private void setData(byte[] data) {
		m_data = data;
	}

	@Override
	public byte[] get() {
		byte[] bytes = null;
		switch (m_cmd) {
		case CMD_READ: {
			bytes = new byte[11];
			bytes[0] = STRUCT_STX;
			bytes[1] = (byte) m_cmd;
			{
				String hex = convertBytesToHexString(convertIntegerToBytes(1, 0, null, m_index), true, "", null);
				hex = hex.toUpperCase();
				for (int i = 0; i < 4; ++i) {
					bytes[i + 2] = (byte) hex.charAt(i);
				}
			}
			{
				String hex = convertBytesToHexString(convertIntegerToBytes(0, 0, null, m_byteLength), true, "", null);
				hex = hex.toUpperCase();
				for (int i = 0; i < 2; ++i) {
					bytes[i + 6] = (byte) hex.charAt(i);
				}
			}
			bytes[8] = STRUCT_ETX;
			checkSum(bytes, 9);
			break;
		}
		case CMD_WRITE: {
			bytes = new byte[11 + m_data.length * 2];
			bytes[0] = STRUCT_STX;
			bytes[1] = (byte) m_cmd;
			{
				String hex = convertBytesToHexString(convertIntegerToBytes(1, 0, null, m_index), true, "", null);
				hex = hex.toUpperCase();
				for (int i = 0; i < 4; ++i) {
					bytes[i + 2] = (byte) hex.charAt(i);
				}
			}
			{
				String hex = convertBytesToHexString(convertIntegerToBytes(0, 0, null, m_byteLength), true, "", null);
				hex = hex.toUpperCase();
				for (int i = 0; i < 2; ++i) {
					bytes[i + 6] = (byte) hex.charAt(i);
				}
			}
			{
				String hex = convertBytesToHexString(m_data, true, "", null);
				hex = hex.toUpperCase();
				for (int i = 0; i < m_data.length * 2; ++i) {
					bytes[i + 8] = (byte) hex.charAt(i);
				}
			}
			bytes[bytes.length - 3] = STRUCT_ETX;
			checkSum(bytes, bytes.length - 2);
			break;
		}
//		case CMD_FORCE_ON: {
//			break;
//		}
//		case CMD_FORCE_OFF: {
//			break;
//		}
		}
		return bytes;
	}

	@Override
	public void accept(byte[] bytes) {
		if (bytes == null || bytes.length == 0) {
			m_check = false;
			return;
		}
		if (bytes[0] == STRUCT_NAK || bytes[0] == STRUCT_ACK || bytes[0] == STRUCT_ENQ) {
			m_check = true;
			return;
		}

		switch (m_cmd) {
		case CMD_READ: {
			if (bytes[0] != STRUCT_STX || bytes[bytes.length - 3] != STRUCT_ETX) {
				m_check = false;
				return;
			}

			StringBuilder hex = new StringBuilder();
			for (int i = 1; i < bytes.length - 3; ++i) {
				hex.append((char) bytes[i]);
			}
			m_data = StaticDataUtils.convertHexStringToBytes(hex.toString());
			m_byteLength = m_data.length;

			byte[] temp = new byte[bytes.length];
			System.arraycopy(bytes, 0, temp, 0, bytes.length);
			checkSum(temp, temp.length - 2);
			if (temp[temp.length - 1] != bytes[bytes.length - 1] || temp[temp.length - 2] != bytes[bytes.length - 2]) {
				m_check = false;
				return;
			}
			m_check = true;
			break;
		}
		case CMD_WRITE: {
			m_data = null;
			m_byteLength = 0;
			break;
		}
		}

		return;
	}

	@Override
	public boolean isCorrect() {
		return m_check;
	}

}

串口通信类(通信体系太大,全放上来没人看得懂,这一部分只作展示,不能使用):

package pers.laserpen.util.communication.serialPort;

import static pers.laserpen.util.communication.serialPort.NativeSerialPortUtils.*;

import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

import pers.laserpen.util.communication.AbstractActiveIO;
import pers.laserpen.util.communication.BufferedReceivable;
import pers.laserpen.util.communication.CommunicationType;
import pers.laserpen.util.java.BufferClearable;
import pers.laserpen.util.thread.StaticThreadUtils;

public class SerialPortRS232Win32IO extends AbstractActiveIO<SerialPortMessage>
		implements BufferClearable, BufferedReceivable<SerialPortMessage> {

	private long m_handle = 0;

	private int m_comPort;
	private int m_recvNativeBuffer, m_sendNativeBuffer, m_recvTempBuffer;
	private long m_baudRate;
	ByteSize m_byteSize;
	Parity m_parity;
	StopBits m_stopBits;
	private long m_recvTimeout;

	private long m_running = Long.MIN_VALUE;

	private final BlockingQueue<SerialPortMessage> m_msgQue = new LinkedTransferQueue<>();

	/**
	 * 
	 * @param comPort     端口号1~254
	 * @param buffer      统一设置所有缓冲
	 * @param baudRate    比特率
	 * @param byteSize    字节长度
	 * @param stopBits    停止位
	 * @param parity      校验
	 * @param recvTimeout 内核运行周期(影响到缓冲队列的更新周期和关闭串口消耗的时间,所以不要用太大的数字。
	 */
	public SerialPortRS232Win32IO(int comPort, int buffer, int baudRate, ByteSize byteSize, StopBits stopBits,
			Parity parity, long recvTimeout) {
		this(comPort, buffer, buffer, buffer, baudRate, byteSize, stopBits, parity, recvTimeout);
	}

	/**
	 * 
	 * @param comPort          端口号1~254
	 * @param recvNativeBuffer 接收缓冲
	 * @param sendNativeBuffer 改善缓冲
	 * @param recvTempBuffer   应用层接收缓冲
	 * @param baudRate         比特率
	 * @param byteSize         字节长度
	 * @param stopBits         停止位
	 * @param parity           校验
	 * @param recvTimeout      内核运行周期(影响到缓冲队列的更新周期和关闭串口消耗的时间,所以不要用太大的数字。
	 */
	public SerialPortRS232Win32IO(int comPort, int recvNativeBuffer, int sendNativeBuffer, int recvTempBuffer,
			int baudRate, ByteSize byteSize, StopBits stopBits, Parity parity, long recvTimeout) {
		m_comPort = comPort;
		m_recvNativeBuffer = recvNativeBuffer;
		m_sendNativeBuffer = sendNativeBuffer;
		m_recvTempBuffer = recvTempBuffer;
		m_baudRate = baudRate;
		m_byteSize = byteSize;
		m_parity = parity;
		m_stopBits = stopBits;
		m_recvTimeout = recvTimeout;
	}

	private void receiveListener(long runId) {
		try {
			for (; m_running == runId; StaticThreadUtils.sleep(m_recvTimeout)) {
				SerialPortMessage msg = doRead();
				if (msg == null) {
					continue;
				}
				m_msgQue.offer(msg);
			}
		} catch (IOException e) {
			// e.printStackTrace();
		} finally {
			System.out.println("exit background listener of COM" + m_comPort);
		}
	}

	private SerialPortMessage doRead() throws IOException {
		byte[] buffer = new byte[m_recvTempBuffer];
		int recvLen = NativeSerialPortUtils.read(m_handle, buffer);
		if (recvLen == 0) {
			return null;
		} else if (recvLen < 0) {
			if (getLastErrorCode() != 0) {
				System.err.println("last error when doRead: " + getLastErrorCode());
				close();
				throw new IOException();
			}
			return null;
		}

		byte[] msgData = new byte[recvLen];
		System.arraycopy(buffer, 0, msgData, 0, recvLen);
		SerialPortMessage msg = new SerialPortMessage();
		msg.accept(msgData);
		msg.setPort(m_comPort);
		msg.setReplier(msgReplied -> {
			return send(msgReplied);
		});
		return msg;
	}

	@Override
	protected void disconnect() throws Throwable {
		System.out.println("order receive listener of COM" + m_comPort + " to quit");
		m_running = Long.MIN_VALUE;
		if (m_handle >= 0) {
			if (!closeCom(m_handle)) {
				System.err.println("last error when disconnect: " + getLastErrorCode());
			}
			m_handle = 0;
		}
		System.out.println("COM" + m_comPort + " closed");
	}

	@Override
	protected void connect() throws Throwable {
		if (m_handle >= 0) {
			if (closeCom(m_handle)) {
				System.err.println("last error when connect closeCom: " + getLastErrorCode());
			}
			m_handle = 0;
		}
		m_handle = openCom(m_comPort);
		if (m_handle < 0) {
			System.err.println("last error when connect openCom: " + getLastErrorCode());
			throw new IOException("open com" + m_comPort + "failed");
		}
		if (!setBuffer(m_handle, m_recvNativeBuffer, m_sendNativeBuffer)) {
			System.err.println("last error when connect setBuffer: " + getLastErrorCode());
			throw new IOException("open com" + m_comPort + "failed");
		}
		if (!setTimeout(m_handle, -1, 0, 0, 0, 0)) {
			System.err.println("last error when connect setTimeout: " + getLastErrorCode());
			throw new IOException("open com" + m_comPort + "failed");
		}
		if (!setState(m_handle, m_baudRate, m_byteSize, m_parity, m_stopBits)) {
			System.err.println("last error when connect setState: " + getLastErrorCode());
			throw new IOException("open com" + m_comPort + "failed");
		}
		m_running = System.currentTimeMillis();
		StaticThreadUtils.execute(() -> receiveListener(m_running));
		System.out.println("COM" + m_comPort + " is opened");
	}

	@Override
	protected SerialPortMessage read() throws Throwable {
		return m_msgQue.poll(m_recvTimeout, TimeUnit.MILLISECONDS);
	}

	@Override
	protected boolean write(Supplier<byte[]> msg) throws Throwable {
		int sendLen = NativeSerialPortUtils.write(m_handle, msg.get(), msg.get().length);
		if (sendLen <= 0) {
			System.err.println("last error when write: " + getLastErrorCode());
			throw new IOException("write com" + m_comPort + " failed");
			// return false;
		}
		return true;
	}

	@Override
	public SerialPortMessage receive(long blockTimeout) {
		if (!open()) {
			return null;
		}
		return BufferedReceivable.super.receive(blockTimeout);
	}

	@Override
	public SerialPortMessage receive(long timeout, int lineLimit, Function<byte[], Boolean> breakCondition) {
		if (!open()) {
			return null;
		}
		return BufferedReceivable.super.receive(timeout, lineLimit, breakCondition);
	}

	@Override
	public CommunicationType getCommunicationType() {
		return CommunicationType.SERIAL_PORT;
	}

	@Override
	public BlockingQueue<SerialPortMessage> getBlockingQueue() {
		return m_msgQue;
	}

	@Override
	public void clearBuffer() {
		m_msgQue.clear();
	}

}

串口Native接口:

package pers.laserpen.util.communication.serialPort;

import pers.laserpen.util.string.ENCODING;

public abstract class NativeSerialPortUtils {

	public static final int NOPARITY = 0;
	public static final int ODDPARITY = 1;
	public static final int EVENPARITY = 2;
	public static final int MARKPARITY = 3;
	public static final int SPACEPARITY = 4;

	public static final int ONESTOPBIT = 0;
	public static final int ONE5STOPBITS = 1;
	public static final int TWOSTOPBITS = 2;

	public static final int CBR_110 = 110;
	public static final int CBR_300 = 300;
	public static final int CBR_600 = 600;
	public static final int CBR_1200 = 1200;
	public static final int CBR_2400 = 2400;
	public static final int CBR_4800 = 4800;
	public static final int CBR_9600 = 9600;
	public static final int CBR_14400 = 14400;
	public static final int CBR_19200 = 19200;
	public static final int CBR_38400 = 38400;
	public static final int CBR_56000 = 56000;
	public static final int CBR_57600 = 57600;
	public static final int CBR_115200 = 115200;
	public static final int CBR_128000 = 128000;
	public static final int CBR_256000 = 256000;

	public static final long getLastErrorCode() {
		return getLastError();
	}

	public static final long openCom(int com) {
		String portStr = "\\\\.\\COM" + com + "\0";// C/C++无法识别byte[]的长度,需要添加一个0字符。
		return createFile(portStr.getBytes(ENCODING.US_ASCII()));
	}

	public static final boolean closeCom(long handle) {
		return closeHandle(handle);
	}

	public static boolean setBuffer(long handle, int inBuffer, int outBuffer) {
		return setupComm(handle, inBuffer, outBuffer);
	}

	public static boolean setSimpleTimeout(long handle) {
		return setCommTimeouts(handle, -1, 0, 0, 0, 0);
	}

	public static boolean setTimeout(long handle, long readIntervalTimeout, long readTotalTimeoutConstant,
			long readTotalTimeoutMultiplier, long writeTotalTimeoutConstant, long writeTotalTimeoutMultiplier) {
		return setCommTimeouts(handle, readIntervalTimeout, readTotalTimeoutConstant, readTotalTimeoutMultiplier,
				writeTotalTimeoutConstant, writeTotalTimeoutMultiplier);
	}

	public static final boolean setState(long handle, long baudRate, ByteSize byteSize, Parity parity,
			StopBits stopBits) {
		return setCommState(handle, baudRate, byteSize.getFlag(), parity.getFlag(), stopBits.getFlag());
	}

	public static final boolean ready(long handle) {
		return purgeComm(handle);
	}

	public static final int read(long handle, byte[] buffer) {
		return readFile(handle, buffer);
	}

	public static final int write(long handle, byte[] buffer, int length) {
		return writeFile(handle, buffer, length);
	}

	private native static long getLastError();

	private native static long createFile(byte[] portName);

	private native static boolean closeHandle(long handle);

	private native static boolean setupComm(long handle, long inBuffer, long outBuffer);

	private native static boolean setCommTimeouts(long handle, long readIntervalTimeout, long readTotalTimeoutConstant,
			long readTotalTimeoutMultiplier, long writeTotalTimeoutConstant, long writeTotalTimeoutMultiplier);

	private native static boolean setCommState(long handle, long baudRate, long byteSize, long parity, long stopBits);

	private native static boolean purgeComm(long handle);

	private native static int readFile(long handle, byte[] buffer);

	private native static int writeFile(long handle, byte[] buffer, int length);
}

串口Native实现——头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "JNIInclude/jni.h"
/* Header for class pers_laserpen_util_communication_serialPort_NativeSerialPortUtils */

#ifndef _Included_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
#define _Included_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    getLastError
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_getLastError
  (JNIEnv *, jclass);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    createFile
 * Signature: ([B)J
 */
JNIEXPORT jlong JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_createFile
(JNIEnv *, jclass, jbyteArray);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    closeHandle
 * Signature: (J)V
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_closeHandle
(JNIEnv *, jclass, jlong);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setupComm
 * Signature: (JJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setupComm
(JNIEnv *, jclass, jlong, jlong, jlong);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setCommTimeouts
 * Signature: (JJJJJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setCommTimeouts
(JNIEnv *, jclass, jlong, jlong, jlong, jlong, jlong, jlong);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setCommState
 * Signature: (JJJJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setCommState
(JNIEnv *, jclass, jlong, jlong, jlong, jlong, jlong);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    purgeComm
 * Signature: (J)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_purgeComm
(JNIEnv *, jclass, jlong);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    readFile
 * Signature: (J[B)I
 */
JNIEXPORT jint JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_readFile
  (JNIEnv *, jclass, jlong, jbyteArray);

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    writeFile
 * Signature: (J[BI)I
 */
JNIEXPORT jint JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_writeFile
(JNIEnv *, jclass, jlong, jbyteArray, jint);

#ifdef __cplusplus
}
#endif
#endif

串口Native实现——源文件:

#include <malloc.h>
#include <windows.h>
#include "pers_laserpen_util_communication_serialPort_NativeSerialPortUtils.h"

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    getLastError
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_getLastError
(JNIEnv *, jclass) {
    return (unsigned long long)GetLastError();
}


/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    createFile
 * Signature: ([B)J
 */
JNIEXPORT jlong JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_createFile
(JNIEnv *jEnv, jclass jCls, jbyteArray jPortName) {
    int len = jEnv->GetArrayLength(jPortName);
    char *buf = (char*)malloc(len);
    jEnv->GetByteArrayRegion(jPortName, 0, len, (jbyte*)buf);
    //异步通信不支持频繁开关操作,不能满足Java版LaserpenUtil的快速自动重启机制的要求,因此只能使用同步通信。
    HANDLE handle = CreateFile(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0&(FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED), NULL);
    free(buf);
    return (jlong)handle;
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    closeHandle
 * Signature: (J)V
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_closeHandle
(JNIEnv *jEnv, jclass jCls, jlong handle) {
    return CloseHandle((HANDLE)handle);
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setupComm
 * Signature: (JJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setupComm
(JNIEnv *jEnv, jclass jCls, jlong handle, jlong inBuffer, jlong outBuffer) {
    return SetupComm((HANDLE)handle, inBuffer, outBuffer);
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setCommTimeouts
 * Signature: (JJJJJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setCommTimeouts
(JNIEnv *jEnv, jclass jCls, jlong handle, jlong ReadIntervalTimeout, jlong ReadTotalTimeoutConstant, jlong ReadTotalTimeoutMultiplier, jlong WriteTotalTimeoutConstant, jlong WriteTotalTimeoutMultiplier) {
    COMMTIMEOUTS timeouts;
    timeouts.ReadIntervalTimeout=ReadIntervalTimeout;
    timeouts.ReadTotalTimeoutConstant=ReadTotalTimeoutConstant;
    timeouts.ReadTotalTimeoutMultiplier=ReadTotalTimeoutMultiplier;
    timeouts.WriteTotalTimeoutConstant=WriteTotalTimeoutConstant;
    timeouts.WriteTotalTimeoutMultiplier=WriteTotalTimeoutMultiplier;
    return SetCommTimeouts((HANDLE)handle, &timeouts);
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    setCommState
 * Signature: (JJJJJ)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_setCommState
(JNIEnv *jEnv, jclass jCls, jlong handle, jlong BaudRate, jlong ByteSize, jlong Parity, jlong StopBits) {
    DCB dcb;
    GetCommState((HANDLE)handle, &dcb);
    dcb.BaudRate=BaudRate;
    dcb.ByteSize=ByteSize;
    dcb.Parity=Parity;
    dcb.fBinary=TRUE;
    dcb.fParity=TRUE;
    dcb.StopBits=StopBits;
    return SetCommState((HANDLE)handle, &dcb);
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    purgeComm
 * Signature: (J)Z
 */
JNIEXPORT jboolean JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_purgeComm
(JNIEnv *jEnv, jclass jCls, jlong handle) {
    return PurgeComm((HANDLE)handle, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    readFile
 * Signature: (J[B)I
 */
JNIEXPORT jint JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_readFile
(JNIEnv *jEnv, jclass jCls, jlong handle, jbyteArray jBuffer) {
    int bufLen = jEnv->GetArrayLength(jBuffer);
    char *buf = (char*)malloc(bufLen);
    //jEnv->GetByteArrayRegion(jBuffer, 0, bufLen, (jbyte*)buf);
    DWORD recvLen;
    DWORD dwErrorFlags;
    COMSTAT ComStat;
    OVERLAPPED ov;
    ClearCommError((HANDLE)handle,&dwErrorFlags,&ComStat);
    memset(&ov, 0, sizeof(ov));
    if(!ReadFile((HANDLE)handle, buf, bufLen, &recvLen, &ov)) {
        if(GetLastError()==ERROR_IO_PENDING) {
            GetOverlappedResult((HANDLE)handle, &ov, &recvLen, TRUE);
            jEnv->SetByteArrayRegion(jBuffer, 0, recvLen, (jbyte*)buf);
        } else {
            recvLen = -1;
        }
    } else {
        jEnv->SetByteArrayRegion(jBuffer, 0, recvLen, (jbyte*)buf);
    }
    free(buf);
    return recvLen;
}

/*
 * Class:     pers_laserpen_util_communication_serialPort_NativeSerialPortUtils
 * Method:    writeFile
 * Signature: (J[BI)I
 */
JNIEXPORT jint JNICALL Java_pers_laserpen_util_communication_serialPort_NativeSerialPortUtils_writeFile
(JNIEnv *jEnv, jclass jCls, jlong handle, jbyteArray jBuffer, jint length) {
    int bufLen = jEnv->GetArrayLength(jBuffer);
    char *buf = (char*)malloc(bufLen);
    jEnv->GetByteArrayRegion(jBuffer, 0, bufLen, (jbyte*)buf);
    DWORD sendLen;
    DWORD dwErrorFlags;
    COMSTAT ComStat;
    OVERLAPPED ov;
    ClearCommError((HANDLE)handle,&dwErrorFlags,&ComStat);
    memset(&ov, 0, sizeof(ov));
    if(!WriteFile((HANDLE)handle, buf, length, &sendLen, &ov)) {
        if(GetLastError()==ERROR_IO_PENDING) {
            GetOverlappedResult((HANDLE)handle, &ov, &sendLen, TRUE);
        } else {
            sendLen = -1;
        }
    }
    //jEnv->SetByteArrayRegion(jBuffer, 0, recvLen, (jbyte*)buf);
    free(buf);
    return sendLen;
}


#ifdef __cplusplus
}
#endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值