版本协议

LGPL 3.0

标签
专业版
#第九届立创电赛#USB条码枪功能扩展

创建时间:4个月前

400 0

视频

描述

<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>

文档

BOM

暂无

附件

附件名 下载
CH554USBHost2ASCII.zip
Ch554USBKBHost.zip

评论(0)

  • 表情
    emoji
    小嘉工作篇
    小嘉日常篇
  • 图片
成功
工程所有者当前已关闭评论
goToTop
svg-battery svg-battery-wifi svg-books svg-more svg-paste svg-pencil svg-plant svg-ruler svg-share svg-user svg-logo-cn svg-double-arrow