读操作:
发送 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