描述
<div class="document">
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">* 1、项目功能介绍</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p>条形码已经成为当今生活中密不可分的一部分,它因为实现简单价格低廉的原因,广泛用于用于商品销售,工矿企业的物料管理等等方面。</p>
<p>通常人们使用条码枪来进行条码的读取。</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/NMzPbp2WZZ2KArJKnObkwoRfI2ipahkIGPI8FLrG.jpeg" alt="" width="468" height="312"></p>
<p>这种设备插入电脑后通常会将自身模拟为键盘,这样可以在光标处直接键入扫描到的条码;同样的,它也可以切换为串口设备,这样能够通过应用程序读取到扫描到的条码。但是,目前为止我还没有见过同时模拟为键盘和串口设备的条码枪。而在生产中,我们希望在指定的位置输入条码同时后台也能从串口读取到条码。</p>
<p>这次制作的设备就是一个可以使得USB条码枪同时实现USB键盘和串口设备的设计。将USB条码枪插入这次的设备后,再将设备接入主机后,主机端会出现 USB键盘和 USB串口。当使用条码枪扫描到条码后,一方面会以键盘的形式键入条码,另外一方面,你可以使用应用程序进行串口通讯,从串口获得条码。</p>
<p> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">*2、项目属性</h3>
<hr class="horizontal-splitline normal-bold-2">
<p>原创,首次公开。 </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">* 3、开源协议</h3>
<hr class="horizontal-splitline normal-bold-2">
<p><strong>LGPL</strong> </p>
<p style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><strong><span style="color: #0093e6;">请在竞赛阶段填写 ↓</span></strong></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">*4、硬件部分</h3>
<hr class="horizontal-splitline normal-bold-2">
<p>整体设计分为2部分,第一部分是要实现从USB口读取条码枪的输入。这里使用 CH554 来实现。CH554一款兼容MCS51指令集的增强型E8051内核单片机,最高24MHz系统主频,内置16K程序存储器ROM和256字节内部iRAM以及1K 字节片内xRAM,xRAM支持DMA直接内存存取。<br>CH554内置了ADC模数转换、触摸按键电容检测、3组定时器和信号捕捉及PWM、双异步串口、SPI等功能模块,支持USB-Host主机模式和USB-Device设备模式。</p>
<p><img src="//image.lceda.cn/pullimage/IGBdM3DJeamPUMpf3bzLb934fywy62ZSPLozCjb6.jpeg" alt="" width="544" height="258"></p>
<p>我们这次的设计就是使用到了它提供的USB Host 功能。选择Ch554e 这是MSOP10封装,体积非常小。对应电路如下,就是一个最小系统,通过USB 母头连接USB条码枪,对外通过串口进行通讯:</p>
<p><img src="//image.lceda.cn/pullimage/ysHseNIXunNMD7WDYSg4x9C6gH6ZcDPeRWsAhuIf.png" alt="" width="624" height="421"></p>
<p>另外一部分是 ESP32-S3, S3 带有 USB 接口, 能够方便的实现USB设备的模拟。同样电路设计非常简单,外围只是让它能够工作的电容和电阻:</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/ixDN9UANgx1LbXY32mCXaLKFxFlEJlCPz2gq9IxP.png" alt="" width="624" height="397"></p>
<p>最终的 PCB设计如下:</p>
<p><img src="//image.lceda.cn/pullimage/cUoVpqIW3IaGQ4GqG5W44BYxNIMZBjCbdGiihj9S.png" alt="" width="624" height="465"></p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">*5、软件部分</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p>基本设计思路是:CH554 作为 USB Host 负责解析USB条码枪的数据,然后通过串口将数据发送给 ESP32-S3。后者负责模拟一个USB键盘一个 USB CDC 设备。每次收到条码数据先从USB键盘发送给主机,然后将条码数据同时存放在内存中等待上位机通过串口进行查询。</p>
<p> </p>
<p>Ch554的关键代码:</p>
<p> </p>
<p> /* 操作HID复合设备 */</p>
<p> loc = SearchTypeDevice( USB_DEV_CLASS_HID ); // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号 </p>
<p> if ( loc != 0xFFFF ){ // 找到了</p>
<p> printf( "Query USB_DEV_CLASS_HID @%04X\n", loc ); </p>
<p> loc = (UINT8)loc; //554只有一个USB,只需低八位即可</p>
<p> </p>
<p> for(k=0;k!=4;k++)</p>
<p> { </p>
<p> //端点是否有效?</p>
<p> endp = loc ? DevOnHubPort[loc-1].GpVar[k] : ThisUsbDev.GpVar[k]; // 中断端点的地址,位7用于同步标志位 </p>
<p> if ( (endp & USB_ENDP_ADDR_MASK) == 0 ) break;</p>
<p> </p>
<p> printf("endp: %02X\n",(UINT16)endp);</p>
<p> SelectHubPort( loc ); // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址</p>
<p> s = USBHostTransact( USB_PID_IN << 4 | endp & 0x7F, endp & 0x80 ? bUH_R_TOG | bUH_T_TOG : 0, 0 );// CH554传输事务,获取数据,NAK不重试</p>
<p> if ( s == ERR_SUCCESS ){</p>
<p> endp ^= 0x80; // 同步标志翻转</p>
<p> if ( loc ) DevOnHubPort[loc-1].GpVar[k] = endp; // 保存同步标志位</p>
<p> else ThisUsbDev.GpVar[k] = endp;</p>
<p> len = USB_RX_LEN; // 接收到的数据长度</p>
<p> if ( len ){</p>
<p> printf("keyboard data: ");</p>
<p> for ( i = 0; i < len; i ++ ){</p>
<p> printf("x%02X ",(UINT16)(RxBuffer[i]) );</p>
<p> }</p>
<p> printf("\n");</p>
<p> </p>
<p> </p>
<p> IsSame=TRUE;</p>
<p> for ( i = 0; i < len; i ++ ){</p>
<p> if (LastBuffer[i]!=RxBuffer[i]) {</p>
<p> IsSame=FALSE;</p>
<p> LastBuffer[i]=RxBuffer[i];</p>
<p> }</p>
<p> }</p>
<p> //只有与前一次不同才进行输出</p>
<p> if (IsSame==FALSE) {</p>
<p> checksum=0x00;</p>
<p> CH554UART1SendByte(0x57);CH554UART1SendByte(0xAB);CH554UART1SendByte(0x88);CH554UART1SendByte(len+3);CH554UART1SendByte(0x10);</p>
<p> for ( i = 0; i < len; i ++ ){</p>
<p> CH554UART1SendByte(RxBuffer[i]);</p>
<p> checksum=checksum+RxBuffer[i];</p>
<p> }</p>
<p> checksum=checksum+counter;</p>
<p> CH554UART1SendByte(counter); CH554UART1SendByte(checksum);</p>
<p> counter++;</p>
<p> }</p>
<p> }</p>
<p> }</p>
<p> else if ( s != ( USB_PID_NAK | ERR_USB_TRANSFER ) ){</p>
<p> printf("keyboard error %02x\n",(UINT16)s); // 可能是断开了</p>
<p> }</p>
<p> </p>
<p> } </p>
<p> SetUsbSpeed( 1 ); // 默认为全速</p>
<p> } </p>
<p> </p>
<p>每次收到的USB数据会和上一次的进行比较,只有不同的时候才会从串口发送出去。同时,发送的数据使用和 Ch9350相同的格式,波特率为 1500000:</p>
<p><img src="//image.lceda.cn/pullimage/MooW6HpMHSMDzkDMtFqELnbKlaLDmm8p1KRCRHM4.png" alt="" width="624" height="384"></p>
<p>ESP32-S3 代码主要部分如下:</p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt pap-left-indent-1.6em" style="line-height: 1.8;"> </p>
<p>1. 收取键盘输出,分析每一个 scancode,然后通过ScanCode2Ascii() 函数转换为 ascii码,放入OneInput 中。在试验中可以看到,条码枪的输出是以字符为单位的。比如,对于条码 “123”,是会有三笔有效的数据输出,分别输出 “1” “2” “3”,而不是在一笔中输出。下面处理后,会将这三笔数据合成为 ascii 字符串 “123”</p>
<p> </p>
<p> //如果是键盘</p>
<p> if (Data[i + 4] == 0x10) {</p>
<p> if (DEBUGMODE) {</p>
<p> Serial0.print("ASCII:[");</p>
<p> // 输出键盘的有效键值部分</p>
<p> for (int j = 3; j < Length - 2; j++) {</p>
<p> uint8_t Asc = ScanCode2Ascii(Data[i + 3 + 1], Data[i + 3 + j]);</p>
<p> if (Asc != 0) {</p>
<p> Serial0.write(Asc);</p>
<p> }</p>
<p> }</p>
<p> Serial0.println("]");</p>
<p> }</p>
<p> // 数据搬移,处理每一个键盘的数据包</p>
<p> for (int j = 3; j < Length - 2; j++) {</p>
<p> // 只处理可见的 ASCII</p>
<p> uint8_t Asc = ScanCode2Ascii(Data[i + 3 + 1], Data[i + 3 + j]);</p>
<p> if (Asc != 0) {</p>
<p> if (Asc == 0x13) {</p>
<p> // 特别处理回车键</p>
<p> OneInput[OneLength] = 0x0D;</p>
<p> OneLength++;</p>
<p> OneInput[OneLength] = 0x0A;</p>
<p> OneLength++;</p>
<p> } else {</p>
<p> // 取出输入放入Buffer</p>
<p> OneInput[OneLength] = Asc;</p>
<p> OneLength++;</p>
<p> }</p>
<p> // 开始计时</p>
<p> Elsp = millis();</p>
<p> </p>
<p>2. 完整的条码数据处理。如果200ms内没有再收到数据,我们认为这一次的条码扫描结束。完整的条码数据在OneInput[] 中,我们使用USB 键盘功能直接输出到主机,同时放置在缓冲区中等待上位机通过 USB串口取走:</p>
<p> </p>
<p> // 如果200ms内无输入,说明一组条码输入完成</p>
<p> if ((Elsp != 0) && (millis() - Elsp > INTERVALPERCODE)) {</p>
<p> if (DEBUGMODE) {</p>
<p> Serial0.print("Barcode:");</p>
<p> Serial0.println(OneInput);</p>
<p> Serial0.print("Lenth:");</p>
<p> Serial0.println(strlen(OneInput));</p>
<p> }</p>
<p> // 给字符串赋予一个结尾 0</p>
<p> OneInput[OneLength] = 0;</p>
<p> </p>
<p> for (int i=0;i<strlen(OneInput);i++) {</p>
<p> Keyboard.press(OneInput[i]);</p>
<p> delay(10);</p>
<p> Keyboard.release(OneInput[i]);</p>
<p> delay(10);</p>
<p> }</p>
<p> </p>
<p> // 将Barcode放入缓冲中,+1是给0的</p>
<p> memcpy(&InputBuffer[LastData], OneInput, strlen(OneInput) + 1);</p>
<p> IndexCounter++;</p>
<p> Index[IndexCounter] = LastData + strlen(OneInput) + 1;</p>
<p> LastData = Index[IndexCounter];</p>
<p> </p>
<p> OneLength = 0;</p>
<p> Elsp = 0;</p>
<p> } </p>
<p> </p>
<p>3. 串口设计。板子上有2个串口,Serial0用于烧写和调试,Serial1用于和Ch554的通讯;此外,还有一个模拟出来的USB CDC ,用于和应用程序通讯。定义了几个命令:首先是“1”,这是用于调试的命令,收到后 ESP32-S3会返回缓冲区数据用于调试;然后是“n”,用于返回当前缓冲区中记录的条码数量;“g”是返回当前记录中的第一个条码的命令,需要注意执行之后,对应的条码会从缓冲区中移除。此外,还有命令“l”,会返回 “z”,这是用于识别设备。</p>
<p> </p>
<p>// Serial0 用于 Debug 的几个命令</p>
<p> while (Serial.available()) {</p>
<p> char c = Serial.read();</p>
<p> if (c == '1') {</p>
<p> // 输出当前记录的条码数量</p>
<p> Serial.print("IndexCounter:");</p>
<p> Serial.println(IndexCounter);</p>
<p> for (int i = 0; i < IndexCounter; i++) {</p>
<p> Serial.print(i); Serial.print(':');</p>
<p> Serial.println(&InputBuffer[Index[i]]);</p>
<p> }</p>
<p> // 输出缓冲区保存的条码</p>
<p> for (int i = 0; i < 96; i++) {</p>
<p> if (InputBuffer[i] < 10) {</p>
<p> Serial.print("0");</p>
<p> }</p>
<p> Serial.print(InputBuffer[i], HEX);</p>
<p> Serial.print(' ');</p>
<p> if (((i + 1) % 16 == 0) && (i != 1)) {</p>
<p> Serial.println("");</p>
<p> }</p>
<p> </p>
<p> }</p>
<p> </p>
<p> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">*6、BOM清单</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">*7、大赛LOGO验证</h3>
<hr class="horizontal-splitline normal-bold-2">
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/o8H2dKZj1KQgi2zDbvYmeHVIZv7dvefrR1EJBhQF.png" alt="" width="831" height="600"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><img src="//image.lceda.cn/pullimage/VavdqpheZhoKkT0S8sLdkgyIsfoBRi6frLL6dVsg.png" alt="" width="800" height="600"></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<h3 class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;">* 8、演示您的项目并录制成视频上传</h3>
<hr class="horizontal-splitline normal-bold-2">
<p style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #95a5a6; font-size: 14px;">视频要求:请横屏拍摄,分辨率不低于1280×720,格式Mp4/Mov,单个视频大小限100M内;</span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="color: #95a5a6; font-size: 14px;">视频标题:立创电赛:{项目名称}-{视频模块名称};如立创电赛:《自动驾驶》-团队介绍。</span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"><span style="font-size: 14px;"><a href="/posts/de460543d4cf4dacb5f0326612455578" target="_blank">前往查看更多详情 ></a></span></p>
<p class="paragraph text-align-type-left pap-line-1.3 pap-line-rule-auto pap-spacing-before-3pt pap-spacing-after-3pt" style="line-height: 1.8;"> </p>
</div>
评论(0)