查看: 487|回复: 7

[项目] 改造一个 USB 亮度计

[复制链接]
物理上,人们使用坎德拉/平方米(cd/m2)作为单位,单位投影面积上的发光强度。比如,我们可以使用这个单位来比较不同发光二极管之间的性能差别。
在我们日常使用的笔记本电脑上,功耗排在第二位的屏幕,而其中屏幕的功率和亮度有着直接的关系。为此,实验室购买了一个MAVO-MONITORUSB亮度计。它是德国 GMC-Instruments 集团旗下的 GOSSEN Foto-und Lichtmesstechnik Gmbh 有限公司生产的,这是一家专注于官学测量产品研发生产的公司,这款亮度计市场零售价格在1500美元左右。每次测试的时候,需要先将测试头贴在屏幕上,然后在设备的屏幕上度数,然后需要将数值抄写在纸上。这样做很不方便,因此我尝试将其改造为半自动的记录设备。
image001.png

设备提供了一个 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”。接下来用串口工具试验串口设定如下:
image002.png

  
*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?
输出:1
  
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
测量范围和精度
=
  
  
测量范围
  
Candela/m2(cd/m2)
测量范围
  
Candela/m2(ft)
分辨率
  
cd/m2
分辨率
  
  
  
亮度
  
I
0.01...19.99
0.001… 1.999
0.01
0.001
II
0.1...  199.9
0.01…   19.99
0.1
0.01
III
1 …     1999
0.1…     199.9
1
0.1
IV
10…    19990
1…         1999
10
1

从上面的的知识,我们可以制作一个带有 USB Host 的设备,通过串口命令来控制这个设备,在需要的时候将设备数值发送给 PC 上。经过比较,最终选择了 Teensy 3.6开发板。 这款开发板是PJRC推出的高性能开发板,最高支持180Mhz 的主频。此外接口丰富,自带一个 USB Device 此外还有一个 USB Host,二者可以同时使用。

因此,我们使用板载 USB Host 来连接这个USB亮度计,然后将信息从 USB Device 发送给 PC。为了使用方便,我们使用一个按键进行触发,按下之后USBHost 发送询问命令给设备取得当前读数, USB Device 将自身模拟成一个键盘,直接将读数发给PC
电路设计如下:
image004.png

可以看到,外围没有元件,只有连接器,U2 USB 母头,H3 是一个排针用于连接按键。PCB 设计如下:

image005.png
预览如下:
image006.png

拿到手的PCB 如下:
image007.png

下面就是程序设计的过程了,完整代码如下:
  1. #include "USBHost_t36.h"
  2. // 通讯参数要求: 9600,Data Bits: 7, Parity:Even, Stop Bits:2
  3. #define USBHOST_SERIAL_7E2 0x1207
  4. // 按键间隔(在间隔以内再次发生的按键都会被忽略)
  5. #define KEYDELAY 2000
  6. // 设置使用 A1 Pin 触发发送
  7. #define PRESSKEY A1
  8. // 发送标志 true-发送 false-不发送
  9. boolean Pressed=false;
  10. // 按键触发时间
  11. unsigned long Elsp;
  12. char *cmdECHOOFF = "ECHO 0"; //关闭命令回显
  13. char *cmdSETUNIT = "UNIT:PHOTOMETRIC LX"; //设定照度返回单位
  14. char *cmdREAD    = "MEASURE:PHOTO?";  //读取照度
  15. char *cmdVERSION = "VERSION?";  //显示版本
  16. USBHost myusb;
  17. USBSerial userial(myusb);
  18. void setup() {
  19.   Serial.begin(115200);
  20. // while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor
  21.   // 设置使用 PRESSKEY 触发中断
  22.   pinMode(PRESSKEY,INPUT_PULLUP);
  23.   attachInterrupt(digitalPinToInterrupt(PRESSKEY), KeyAssert, RISING);
  24.   Serial.println("Starting....");
  25.   myusb.begin();
  26.   userial.begin((uint32_t) 9600,(uint32_t) USBHOST_SERIAL_7E2);
  27.   // 打开键盘
  28.   Keyboard.begin();
  29.   //开始后需要等待设备准备好
  30.   delay(5000);
  31. }
  32. byte Status=0;
  33. String revData="";
  34. String HumanReadable="";
  35. void KeyAssert() {
  36.   Pressed=true;
  37. }
  38. // 将设备的科学计数法转为常用的表示,结果在 ToStr 中
  39. void SciToStr(String s) {
  40. /*分析设备返回的结果,得到有效数字和小数点位置*/
  41.   // 有效数字
  42.   int long validNum;
  43.   // 小数点位置,就是 E 后面的数值
  44.   int zeroPostion;
  45.   String sSub;
  46.   // Step1. 找到第一个空格,空格之前的就是有效数字
  47.   sSub=s.substring(0,s.indexOf(' '));
  48.   validNum=sSub.toInt();   
  49.   // Step2. 将第一个空格之后的字符串切割到另外的字符串中
  50.   sSub=s.substring(s.indexOf(' ')+1,s.length());
  51.   //Step3. 处理指数位
  52.   if (sSub.indexOf('-')!=-1) {
  53.       // 有负号,切割从负号到下一个空格
  54.       sSub=sSub.substring(sSub.indexOf('-')+1,sSub.indexOf(' '));
  55.       zeroPostion=-sSub.toInt();
  56.     }
  57.   else {
  58.       // 没有负号
  59.       sSub=sSub.substring(sSub.indexOf('E')+1,sSub.indexOf(' '));
  60.       zeroPostion=sSub.toInt();
  61.   }
  62. /*分析结束*/
  63. /*根据前面结果,将字符串转为人类易读的字符串*/
  64.    String tmp;
  65.   // 如果小数位置为正或者为0,只需要在有效数字末尾添加 0 即可
  66.   if (zeroPostion>=0) {
  67.     HumanReadable=String (validNum);
  68.     for (byte i=0;i<zeroPostion;i++) {
  69.       HumanReadable=HumanReadable+'0';
  70.     }
  71.     return;
  72.   }
  73.   // 如果小数位置小于0 有两种情况:
  74.   // 1.在左侧添加组成 0.XYZ 或者 0.0XYZ
  75.   // 2.在数字中间加入小数点 X.YZ
  76.   if (zeroPostion<0) {
  77.     tmp=(String)validNum;
  78.     // 处理情况 1
  79.     if (abs(zeroPostion)>=tmp.length()) {
  80.       HumanReadable="0.";
  81.       for (byte i=0;i<abs(zeroPostion)-tmp.length();i++) {
  82.         HumanReadable=HumanReadable+"0";
  83.       }
  84.       HumanReadable=HumanReadable+String(validNum);
  85.       return;
  86.     }
  87.     else {
  88.       // 处理情况 2
  89.       HumanReadable=tmp;
  90.       tmp=HumanReadable.substring(0,HumanReadable.length()-abs(zeroPostion))+'.'+HumanReadable.substring(HumanReadable.length()-abs(zeroPostion));
  91.       HumanReadable=tmp;
  92.       return;
  93.     }
  94.   }     
  95. }
  96. void loop() {
  97.     myusb.Task();
  98.   char c;
  99.   if (Status==0) {
  100.         userial.println(cmdECHOOFF);
  101.         Serial.println("Send EchoOff");
  102.         delay(500);
  103.         Serial.println("Send SETUNIT");
  104.         userial.println(cmdSETUNIT);
  105.         delay(500);
  106.         Status=1;
  107.         //if (userial.available()) {Status=1;}
  108.   }
  109.   if (Status==1) {
  110.           userial.println(cmdREAD);
  111.           //Serial.println("Read");
  112.           delay(100);
  113.           while (userial.available()) {
  114.                 c=userial.read();
  115.                 //Serial.write(c);
  116.                 // 有可能收到 0
  117.                 if (c==0) {continue;}
  118.                 revData.concat((String)c);
  119.                 if (c==0x0a) {Status=2;}
  120.                 delay(2);
  121.           }
  122.     }
  123.   if (Status==2) {
  124.         //Serial.print(">"); Serial.print(revData); Serial.println("<");
  125.         SciToStr(revData);
  126.         Serial.println(HumanReadable);
  127.         if ((Pressed)&(millis()>Elsp)) {
  128.           Pressed=false;
  129.           Keyboard.print(HumanReadable);
  130.           Keyboard.press(KEY_DOWN);
  131.           Keyboard.release(KEY_DOWN);
  132.           Elsp=millis()+KEYDELAY;
  133.         }
  134.    
  135.         revData="";
  136.         Status=1;
  137.   }
  138. }
复制代码
简单的流程介绍:
1.  对设备发送 ECHO OFF 的命令,设置返回值单位
2. 如果有按键按下,那么设置起来标志信息
3. 轮询对设备发送读取当前亮度的命令
4. 如果按键标志位设置起来,那么就将上一次查询到的当前亮度通过USB接口发送给 PC


Teensy 3.6 开发板提供了非常强大的 USB 支持,自带的例子涵盖了 HID  CDC MSD 以及 Audio,可以非常方便的用在自己的项目中。美中不足的是价格较高(300元左右),如果你的项目对于价格不敏感,或者需要快速实现 USB 相关功能,不妨考虑一下这款开发板。
image003.png
image008.png

zoologist  中级技师
 楼主|

发表于 2021-1-26 12:27:52

电路图和 PCB 如下 Project_Teensy3.6 亮度计_2021-01-26_10-51-54.zip (23.96 KB, 下载次数: 11)
回复

使用道具 举报

 初级技师

发表于 2021-1-26 21:43:13

zoologist 发表于 2021-1-26 12:27
电路图和 PCB 如下

完整代码如下

好的收到
回复

使用道具 举报

 初级技师

发表于 2021-1-26 22:20:28

zoologist 发表于 2021-1-26 12:27
电路图和 PCB 如下

完整代码如下

请问,PCB是用立创EDA设计的吗?
回复

使用道具 举报

zoologist  中级技师
 楼主|

发表于 2021-1-27 08:26:24

诩 发表于 2021-1-26 22:20
请问,PCB是用立创EDA设计的吗?

是的,立创这个比较方面,我开始用 eagle 后面都是立创 Eda了
回复

使用道具 举报

DFHkLLPkvkd  见习技师

发表于 2021-1-27 16:14:28

66666666666666666666666666666666666
回复

使用道具 举报

 初级技师

发表于 2021-1-27 17:19:46

zoologist 发表于 2021-1-27 08:26
是的,立创这个比较方面,我开始用 eagle 后面都是立创 Eda了

好的
回复

使用道具 举报

DFHkLLPkvkd  见习技师

发表于 2021-1-31 15:25:00

#在这里快速回复#谢谢啦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2021 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail