物理上,人们使用坎德拉 /平方米( cd/m2)作为单位,单位投影面积上的发光强度。比如,我们可以使用这个单位来比较不同发光二极管之间的性能差别。
在我们日常使用的笔记本电脑上,功耗排在第二位的屏幕,而其中屏幕的功率和亮度有着直接的关系。为此,实验室购买了一个 MAVO-MONITORUSB亮度计。它是德国 GMC-Instruments 集团旗下的 GOSSEN Foto-und Lichtmesstechnik Gmbh 有限公司生产的,这是一家专注于官学测量产品研发生产的公司,这款亮度计市场零售价格在 1500美元左右。每次测试的时候,需要先将测试头贴在屏幕上,然后在设备的屏幕上度数,然后需要将数值抄写在纸上。这样做很不方便,因此我尝试将其改造为半自动的记录设备。
设备提供了一个 USB 端口,从数据手册上了解到,这款设备是通过内置的 FDTI 芯片来实现 USB对串口的转接。之前我们介绍过 USB HID设备的读取,相较而言串口和 HID相比,从编程角度来说前者更加方便和通用,资料更加丰富,无论什么编程语言都会支持这一传统设备的编程。但是从客户使用的角度来说, USB串口很可能需要用户安装驱动,虽然 Win10内置了很多 USB串口芯片的驱动,但是仍然有一些不支持,在使用过程中如果客户使用 Ghost 精简版也会碰到很多问题。而 HID设备只需要 Application 即可,驱动完全内置。
串口通讯需要设置通讯参数,对于这款设备使用的参数为“ 9600BPS/ 1 Start bit/ 7 Data bits/ 2 Stopbits/ Even parity/ no Flow control”。接下来用串口工具试验串口设定如下:
*RST
恢复设备参数为默认值。运行后设备会重启,但不会影响已存储值。默认设定为:打开自动量程,启用按键输入,屏幕显示打开,存储数据不变,标准采样率(2秒一次),系统时间不受影响。
*IDN?
查询设备信息,返回值包括制造商,型号,序列号,硬件版本和Firmware 版本。示例如下:
输入:*IDN
输出:GOSSEN,M 504,31123,02,V 2.04
VERSION?
查询命令解释器版本。示例如下:
输入:[SYSTEM:]VERSION?
输出:VERSION
V 1.01 (2004)
BEEPER num
当后面跟有数字作为参数时,将数字设置为发声时长;没有参数时直接发声。示例如下:
设定每次发生时长3秒
输入:BEEPER 3
发声一次(如果运行了上面的设定,那么会产生3秒的声音) 输入: BEEPER ON
KEYBOARD b
启用或者关闭按键输入功能。示例如下:
关闭按键
输入: KEYBOARD OFF
打开按键
输入: KEYBOARD ON
TIME?
读取自从上一次校准之后经过的时间
示例如下:
输入: TIME?
输出:00055,02,21
DISPLAY b
打开或者关闭屏幕。可选参数为 0,1,ON,OFF
关闭屏幕
输入:DISPLAY OFF
打开屏幕
输入:DISPLAY 1
UNIT:PHOTOMETRIC txt
选择测量结果显示的单位。可选参数为 LX,FC,CD_M2,FL
输入:UNIT:PHOTOMETRIC LX
实践显示 CD_M2和FL 会报错,提示不支持
PHOTOMETRIC?
不加参数时显示当前测量结果。
LX:
输入:PHOTOMETRIC?
输出:289 E-02 CD/M2
FC:
403 E-03 FL
1059 E00 FL
1870 E-02 FL
RANGE num
设定测量范围。测量范围和精度相关,具体可在表格后【Note 4】看到
RANGE?
RANGE:AUTO b
打开或者自动量程。参数 0,1,ON,OFF
输入:RANGE:AUTO ON
无输出
RANGE:AUTO?
查询当前自动量程是否打开。
输入:RANGE:AUTO
输出:ON
ECHo b
命令回显功能是否启用。参数 0,1,ON,OFF
在回显打开的情况下,设备在收到主机名后会将命令再次返回主机,这样的方便便于主机确认命令是否正确,但是可能会对处理结果早晨麻烦。
例如在回显打开的情况下
输入:MEASURE:PHTOT?
输出:MEASURE:PHTOT? 123E00 LX
在回显关闭的情况下
输入:MEASURE:PHTOT?
输出:123E00 LX
DISPLAY:BACKLIGHT b
打开或者关闭显示屏幕背光。参数 0,1,ON,OFF
打开背光
输入:DISPLAY:BACKLIGHT ON
关闭背光
输入:DISPLAY:BACKLIGHT OFF
MEMory:CLEar
清除保存的数据。
输入:MEMORY:CLEAR
输出:100,100
MEMory:FREE?
查询当前的存储空间剩余量。
输入:MEMORY:CLEAR
输出:100,100
MEMory:DATA?
查询已经保存的数据。
输入:MEMORY:DATA
输出:EMPTY
注意:
1.上述命令大小写敏感;
2.输入命令需要以回车作为结尾;
3.上述部分命令有缩写方式,比如:查询当前测量结果的命令“PHOtometric?”可以简写为“PHOT?”,具体建议以手册为准,这里皆使用完整命令。
4.命令必须以回车结尾。以 “*IDN”命令为例 ,串口实际发送的十六进制数值如下,需要特别注意结尾处的 0D 0A.
2A 49 44 4E 3F 0D 0A
测量范围和精度
=
从上面的的知识,我们可以制作一个带有 USB Host 的设备,通过串口命令来控制这个设备,在需要的时候将设备数值发送给 PC 上。经过比较,最终选择了 Teensy 3.6开发板。 这款开发板是 PJRC推出的高性能开发板,最高支持 180Mhz 的主频。此外接口丰富,自带一个 USB Device 此外还有一个 USB Host,二者可以同时使用。
因此,我们使用板载 USB Host 来连接这个 USB亮度计,然后将信息从 USB Device 发送给 PC。为了使用方便,我们使用一个按键进行触发,按下之后 USBHost 发送询问命令给设备取得当前读数, USB Device 将自身模拟成一个键盘,直接将读数发给 PC。
电路设计如下:
可以看到,外围没有元件,只有连接器, U2是 USB 母头, H3 是一个排针用于连接按键。 PCB 设计如下:
预览如下:
拿到手的 PCB 如下:
下面就是程序设计的过程了,完整代码如下:
#include "USBHost_t36.h"
// 通讯参数要求: 9600,Data Bits: 7, Parity:Even, Stop Bits:2
#define USBHOST_SERIAL_7E2 0x1207
// 按键间隔(在间隔以内再次发生的按键都会被忽略)
#define KEYDELAY 2000
// 设置使用 A1 Pin 触发发送
#define PRESSKEY A1
// 发送标志 true-发送 false-不发送
boolean Pressed=false;
// 按键触发时间
unsigned long Elsp;
char *cmdECHOOFF = "ECHO 0"; //关闭命令回显
char *cmdSETUNIT = "UNIT:PHOTOMETRIC LX"; //设定照度返回单位
char *cmdREAD = "MEASURE:PHOTO?"; //读取照度
char *cmdVERSION = "VERSION?"; //显示版本
USBHost myusb;
USBSerial userial(myusb);
void setup() {
Serial.begin(115200);
// while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor
// 设置使用 PRESSKEY 触发中断
pinMode(PRESSKEY,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PRESSKEY), KeyAssert, RISING);
Serial.println("Starting....");
myusb.begin();
userial.begin((uint32_t) 9600,(uint32_t) USBHOST_SERIAL_7E2);
// 打开键盘
Keyboard.begin();
//开始后需要等待设备准备好
delay(5000);
}
byte Status=0;
String revData="";
String HumanReadable="";
void KeyAssert() {
Pressed=true;
}
// 将设备的科学计数法转为常用的表示,结果在 ToStr 中
void SciToStr(String s) {
/*分析设备返回的结果,得到有效数字和小数点位置*/
// 有效数字
int long validNum;
// 小数点位置,就是 E 后面的数值
int zeroPostion;
String sSub;
// Step1. 找到第一个空格,空格之前的就是有效数字
sSub=s.substring(0,s.indexOf(' '));
validNum=sSub.toInt();
// Step2. 将第一个空格之后的字符串切割到另外的字符串中
sSub=s.substring(s.indexOf(' ')+1,s.length());
//Step3. 处理指数位
if (sSub.indexOf('-')!=-1) {
// 有负号,切割从负号到下一个空格
sSub=sSub.substring(sSub.indexOf('-')+1,sSub.indexOf(' '));
zeroPostion=-sSub.toInt();
}
else {
// 没有负号
sSub=sSub.substring(sSub.indexOf('E')+1,sSub.indexOf(' '));
zeroPostion=sSub.toInt();
}
/*分析结束*/
/*根据前面结果,将字符串转为人类易读的字符串*/
String tmp;
// 如果小数位置为正或者为0,只需要在有效数字末尾添加 0 即可
if (zeroPostion>=0) {
HumanReadable=String (validNum);
for (byte i=0;i<zeroPostion;i++) {
HumanReadable=HumanReadable+'0';
}
return;
}
// 如果小数位置小于0 有两种情况:
// 1.在左侧添加组成 0.XYZ 或者 0.0XYZ
// 2.在数字中间加入小数点 X.YZ
if (zeroPostion<0) {
tmp=(String)validNum;
// 处理情况 1
if (abs(zeroPostion)>=tmp.length()) {
HumanReadable="0.";
for (byte i=0;i<abs(zeroPostion)-tmp.length();i++) {
HumanReadable=HumanReadable+"0";
}
HumanReadable=HumanReadable+String(validNum);
return;
}
else {
// 处理情况 2
HumanReadable=tmp;
tmp=HumanReadable.substring(0,HumanReadable.length()-abs(zeroPostion))+'.'+HumanReadable.substring(HumanReadable.length()-abs(zeroPostion));
HumanReadable=tmp;
return;
}
}
}
void loop() {
myusb.Task();
char c;
if (Status==0) {
userial.println(cmdECHOOFF);
Serial.println("Send EchoOff");
delay(500);
Serial.println("Send SETUNIT");
userial.println(cmdSETUNIT);
delay(500);
Status=1;
//if (userial.available()) {Status=1;}
}
if (Status==1) {
userial.println(cmdREAD);
//Serial.println("Read");
delay(100);
while (userial.available()) {
c=userial.read();
//Serial.write(c);
// 有可能收到 0
if (c==0) {continue;}
revData.concat((String)c);
if (c==0x0a) {Status=2;}
delay(2);
}
}
if (Status==2) {
//Serial.print(">"); Serial.print(revData); Serial.println("<");
SciToStr(revData);
Serial.println(HumanReadable);
if ((Pressed)&(millis()>Elsp)) {
Pressed=false;
Keyboard.print(HumanReadable);
Keyboard.press(KEY_DOWN);
Keyboard.release(KEY_DOWN);
Elsp=millis()+KEYDELAY;
}
revData="";
Status=1;
}
} 复制代码
简单的流程介绍:
1. 对设备发送 ECHO OFF 的命令,设置返回值单位
2. 如果有按键按下,那么设置起来标志信息
3. 轮询对设备发送读取当前亮度的命令
4. 如果按键标志位设置起来,那么就将上一次查询到的当前亮度通过 USB接口发送给 PC
Teensy 3.6 开发板提供了非常强大的 USB 支持,自带的例子涵盖了 HID CDC MSD 以及 Audio,可以非常方便的用在自己的项目中。美中不足的是价格较高( 300元左右),如果你的项目对于价格不敏感,或者需要快速实现 USB 相关功能,不妨考虑一下这款开发板。