在很多应用程序中都需要用到pc机与外部设备如:嵌入式系统、传感器、开关设备等进行数据通讯。其中,最常用的接口就是RS-232串口和并口。SUN的CommAPI分别提供了对常用的RS232串行端口和IEEE1284并行端口通讯的支持。
至于java串口通讯的配置以及通讯模式在sun的demo以及网上都有很多具体的实例。
下面是我在开发一个叫号功能模块时通过串口通信来控制LED显示的实例,由于第一次进行相关的开发,看似一个非常简单的功能在实际开发中却遇到了一些问题,希望本人的解决方式能够对大家有所帮助,也希望大家能够提出更好的解决方式。
先看一下LED显示屏厂商提供的通讯协议:
至于java串口通讯的配置以及通讯模式在sun的demo以及网上都有很多具体的实例。
下面是我在开发一个叫号功能模块时通过串口通信来控制LED显示的实例,由于第一次进行相关的开发,看似一个非常简单的功能在实际开发中却遇到了一些问题,希望本人的解决方式能够对大家有所帮助,也希望大家能够提出更好的解决方式。
先看一下LED显示屏厂商提供的通讯协议:
---遥控单双色、单双行、混合屏 一、 每一次对任一特定地址发送信息由内码帧(7f/7e),数码帧 (6f/6e),定时帧(5f),时间帧(4f)中的一种或多种构成,结束 时发送一结束帧。 二、帧结构:每帧由84字节构成。 1、 内码帧:一幕由一起始帧和零或多个中间帧组成,一次 发送可有多幕。 1) 起始帧:地址(1字节)+帧控制7F(1字节) +幕号(1字节)+COMMAND(8字节) +内码/ASCII(73字节) 2) 中间帧:地址(1字节)+帧控制7E(1字节)+ 幕号(1字节)+COMMAND(8字节)+内码/ASCII (73字节) 3)COMMAND: 前4字节未定义,后4字节依次为动画(0~4), 移入及移出(各16种),速度(0~255),追加 (D3连续、D2停止、D0闪烁、D4时间、D6暂停、 D7动画) 4)内码/ASCII结构: a、 内码4字节,依次为控制字节(D7宽体/正常体、 D4绿色、D5红色、D3粗体、D2细体反白、D1 粗体反白、D0细体),内码高位,内码低位,未用 b、ASCII 2字节,依次为控制字节(D7宽体/正常体、 D5绿色、D4红色、D3粗体、D2细体反白、D1粗体 反白、D0细体),ASCII码 2、数码帧:由一起始帧和零或多中间帧组成。 1) 起始帧:地址(1字节)+帧控制6F(1字节)+ 数据(82字节) 2) 间帧:地址(1字节)+帧控制6E(1字节)+ 数据(82字节) 3、定时帧:由一帧组成。 起始帧:地址(1字节)+帧控制5F(1字节)+ 数据(48字节)+无效数据 包括8个定时器,每个6字节,结构如下: 开/关(0为OFF、1为ON),日期(0~6为 sunday~satday、7为每一天),小时(0~23), 分钟(0~59),起始幕,结束幕。 4、时间帧:由一帧组成。 地址(1字节)+帧控制4F(1字节)+年高二位 (1字节)+年低二位(1字节)+月(1字节)+日 (1字节) +时(1字节)+分(1字节)+星期(1字节) +无效数据 日期都用十进制表示,星期部分0为星期日。 4、结束帧:由一帧组成。 地址(1字节)+帧控制7D(1字节)+无效数据(82 字节) 3)移入,移出模式:各16种模式,可任意组合。 三、 移入模式: 移出模式: 模式0:移入← 移出← 模式1:移入→ 移出→ 模式2:移入↑ 移出↑ 模式3:移入↓ 移出↓ 模式4:跳入← 跳出← 模式5:展开→ 展开→ 模式6:展开← 展开← 模式7:展开↑ 展开↑ 模式8:展开↓ 展开↓ 模式9:展开←→ 展开←→ 模式10:展开→← 展开→← 模式11:展开↑↓ 展开↑↓ 模式12:展开↓↑ 展开↓↑ 模式13:即入 即出 模式14:预备 预备 模式15:随机(已设为循环)随机(已设为循环) 四、通讯卡接口: 1)初始化通讯卡: a、将0xFF写入地址211H b、从211H读入一字节,判断D3是否为‘1’,如为 ‘0’则重复此步骤 c、将0x00写入地址211H d、从211H读入一字节,判断D3是为‘0’,如为 ‘1’则重复此步骤 e、初始化完成 2)写入数据地址:210H 3)读状态地址:211H 状态标志:D0写允许,高电平有效 注:未使用字节必须置为0X00. 232口: 8位数据位, 1位停止位, 无效验位, 波特率为9600. * 每帧84字节, 每幕发一个7F帧, 超过18个字的条屏, 每幕需加发一个7E帧, 所有幕发完后,发7D帧(结束帧) |
串口通讯涉及到的部分主要代码:
- import java.io.*;
- import javax.comm.*;
- public class SerialBean {
- static SerialBean bean;
- String PortName;
- CommPortIdentifier portId;
- SerialPort serialPort;
- OutputStream out;
- InputStream in;
- public String getPortName() {
- return PortName;
- }
- public void setPortName(String portName) {
- PortName = portName;
- }
- private SerialBean(int PortID) {
- PortName = "COM" + PortID;
- }
- public static SerialBean getInstance() {
- if (bean == null) {
- if (!portInit(1))
- return null;
- }
- return bean;
- }
- public static boolean portInit(int port) {
- if (bean != null) {
- bean.ClosePort();
- }
- bean = new SerialBean(port);
- return bean.initialize();
- }
- public boolean initialize() {
- try {
- portId = CommPortIdentifier.getPortIdentifier(PortName);
- try {
- serialPort = (SerialPort)
- portId.open("SerialBean", 3000);
- } catch (PortInUseException e) {
- e.printStackTrace();
- return false;
- }
- try {
- in = serialPort.getInputStream();
- out = serialPort.getOutputStream();
- } catch (IOException e) {
- return false;
- }
- try {
- serialPort.setSerialPortParams(9600,
- SerialPort.DATABITS_8,
- SerialPort.STOPBITS_1,
- SerialPort.PARITY_NONE);
- } catch (UnsupportedCommOperationException e) {
- e.printStackTrace();
- return false;
- }
- } catch (NoSuchPortException e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
- public void writePort(byte[] bytes) {
- for(byte b:bytes){
- writePort(b);
- }
- }
- public void writePort(byte b) {
- try {
- out.write(b);
- out.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public void ClosePort() {
- if (out != null) {
- try {
- out.close();
- in.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- serialPort.close();
- }
- }
import java.io.*;
import javax.comm.*;
public class SerialBean {
static SerialBean bean;
String PortName;
CommPortIdentifier portId;
SerialPort serialPort;
OutputStream out;
InputStream in;
public String getPortName() {
return PortName;
}
public void setPortName(String portName) {
PortName = portName;
}
private SerialBean(int PortID) {
PortName = "COM" + PortID;
}
public static SerialBean getInstance() {
if (bean == null) {
if (!portInit(1))
return null;
}
return bean;
}
public static boolean portInit(int port) {
if (bean != null) {
bean.ClosePort();
}
bean = new SerialBean(port);
return bean.initialize();
}
public boolean initialize() {
try {
portId = CommPortIdentifier.getPortIdentifier(PortName);
try {
serialPort = (SerialPort)
portId.open("SerialBean", 3000);
} catch (PortInUseException e) {
e.printStackTrace();
return false;
}
try {
in = serialPort.getInputStream();
out = serialPort.getOutputStream();
} catch (IOException e) {
return false;
}
try {
serialPort.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
return false;
}
} catch (NoSuchPortException e) {
e.printStackTrace();
return false;
}
return true;
}
public void writePort(byte[] bytes) {
for(byte b:bytes){
writePort(b);
}
}
public void writePort(byte b) {
try {
out.write(b);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
public void ClosePort() {
if (out != null) {
try {
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
serialPort.close();
}
}
向LED发送数据代码,以下测试代码将大部分涉及到通讯协议中的模式以及指令都写死在程序中,有必要可以进行重构
- static int number = 0;
- static byte target = 0x00;
- static byte font = (byte) 0; //默认字体
- static int LED_LENGTH = 16; //默认LED显示字体数目(汉字)
- static byte MODULE=0x00; //默认模式
- static byte[] beg = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- byte[] end = new byte[]{0x7D,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00
- };
- public void display(String meg) {
- SerialBean sb = SerialBean.getInstance();
- int bytes = LED_LENGTH * 4;
- writeNext(sb);
- //汉字在LED中显示所占的宽度是字母或者数字的2倍,以下进行区分
- for (int i = 0; i < meg.length(); i++) {
- String s = meg.substring(i, i + 1);
- byte[] b = s.getBytes();
- if (b.length == 2) {
- if (bytes < 4) {
- for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
- sb.writePort(0x00);
- }
- bytes = LED_LENGTH * 4;
- writeNext(sb);
- }
- sb.writePort(font);
- sb.writePort(s.getBytes());
- sb.writePort(0x00);
- bytes -= 4;
- } else if (b.length == 1) {
- if (bytes < 2) {
- for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
- sb.writePort(0x00);
- }
- bytes = LED_LENGTH * 4;
- writeNext(sb);
- }
- sb.writePort(font);
- sb.writePort(meg.charAt(i));
- bytes -= 2;
- }
- }
- for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
- sb.writePort(0x00);
- }
- sb.writePort(target);
- sb.writePort(end);
- sb.ClosePort();
- }
- private void writeNext(SerialBean sb) {
- sb.writePort(target);
- sb.writePort(0x7F);
- sb.writePort((byte) number++);
- sb.writePort(beg);
- sb.writePort(MODULE);
- }
static int number = 0;
static byte target = 0x00;
static byte font = (byte) 0; //默认字体
static int LED_LENGTH = 16; //默认LED显示字体数目(汉字)
static byte MODULE=0x00; //默认模式
static byte[] beg = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
byte[] end = new byte[]{0x7D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
public void display(String meg) {
SerialBean sb = SerialBean.getInstance();
int bytes = LED_LENGTH * 4;
writeNext(sb);
//汉字在LED中显示所占的宽度是字母或者数字的2倍,以下进行区分
for (int i = 0; i < meg.length(); i++) {
String s = meg.substring(i, i + 1);
byte[] b = s.getBytes();
if (b.length == 2) {
if (bytes < 4) {
for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
sb.writePort(0x00);
}
bytes = LED_LENGTH * 4;
writeNext(sb);
}
sb.writePort(font);
sb.writePort(s.getBytes());
sb.writePort(0x00);
bytes -= 4;
} else if (b.length == 1) {
if (bytes < 2) {
for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
sb.writePort(0x00);
}
bytes = LED_LENGTH * 4;
writeNext(sb);
}
sb.writePort(font);
sb.writePort(meg.charAt(i));
bytes -= 2;
}
}
for (int n = 0; n < bytes + 73 - LED_LENGTH * 4; n++) {
sb.writePort(0x00);
}
sb.writePort(target);
sb.writePort(end);
sb.ClosePort();
}
private void writeNext(SerialBean sb) {
sb.writePort(target);
sb.writePort(0x7F);
sb.writePort((byte) number++);
sb.writePort(beg);
sb.writePort(MODULE);
}
以上代码刚开始写完后进行测试认为基本上没有问题,但是一测试却发现LED上没有任何反应,刚开始以为没有将数据发送过去,采用一些串口监测工具却发现数据已经发送成功;在网上也没有查到任何资料,后来折腾了半天突然想到是不是电脑数据发送太快导致LED中处理数据时导致丢失数据帧,于是立马在发送每个字节后加了一个时间延迟,结果立马有反应,后来适当调节延迟时间一切ok。
public void writePort(byte b) {
try {
out.write(b);
out.flush();
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
(后来想了一下,LED处理数据时可能采用缓存操作,当pc向其发送信息时首先发送至缓存区域,LED此时读取缓存开始处理数据,但是如果pc发送信息太快以致led还没有处理完数据并且数据的大小超出了LED的缓存区域,那么此时可能就会出现将早先的数据进行覆盖的问题,以上只是猜测,后来程序调试好了也没有再理会)