本帖最后由 shanyuxiang 于 2023-10-7 21:34 编辑
#申请原创# @21小跑堂 基于APM32E103的USB Printer实现
目前官方SDK的USB设备例程“APM32E10x_EVAL_SDK_v1.0”只有这几个,分别是: CDC类 虚拟串口 “USB_CDC_VirtualCOMPort” HID类 鼠标 “USB_HID_Mouse” MCS类 大容量存储 “USB_MSC_Disk” 上面是没有Printer类的,如果需要打印机类,可以在CDC例程的基础上进行修改,只需要几步就可以实现一个USB的打印机设备。
1、USB打印机类的基础知识 1.1、USB的设备类 常见的USB设备类有以下几种,这里要实现就是07h号打印机类。 
1.2、调试USB的工具软件
USB数据抓包软件 “Bus Hound” 枚举信息查看软件 “USBlyzer”
1.3、推荐几个关于USB的基础知识的网站 https://www.usbzh.com/article/detail-344.html http://usb.baiheee.com/
1.4、 USB标准组织官网USB-IF上的资料 关于USB 2.0资料: https://www.usb.org/document-library/usb-20-specification
关于 Printer Device Class 的文档: https://www.usb.org/document-library/printer-device-class-document-11
2、修改描述符 首先要通过修改描述符,来枚举出“打印支持”设备。 在极海官网下载USB的SDK https://www.geehy.com/support/apm32?id=305 APM32E10x_EVAL_SDK_v1.0.zip 注释掉不用的代码,例如LCD、KEY、LED,这些跟USB无关。
2.1 描述符的基础知识 描述符分为: 设备描述符 Device Descriptor (g_usbDeviceDescriptor) 配置描述符 Configuration Descriptor (g_usbConfigDescriptor) 端点描述符 Endpoint Descriptor (g_usbConfigDescriptor)
详细信息可以看这里: http://usb.baiheee.com/usb_article/usb_spec/usb_cmd_desc.html
2.2 修改设备描述符 打开源文件 ..\..\Source\usbd_descriptor.c ,修改数组 g_usbDeviceDescriptor[] , 这里只要把设备类从CDC 0x02 改为Printer 0x07。 - /** bDeviceClass */
- 0x07, //0x02,
2.3 修改配置描述符和端点描述符 配置描述符和端点描述符都在数组 g_usbConfigDescriptor[] 中,我们只需要1个配置,该配置下有2个端点,删除多余的端点,修改成下面这样。 - const uint8_t g_usbConfigDescriptor[USB_CONFIG_DESCRIPTOR_SIZE] =
- {
- /** bLength */
- 0x09,
- /** bDescriptorType */
- USBD_DESC_CONFIGURATION,
- /** wTotalLength */
- USB_CONFIG_DESCRIPTOR_SIZE & 0xFF, USB_CONFIG_DESCRIPTOR_SIZE >> 8,
- /** bNumInterfaces */
- 0x01,//0x02,
- /** bConfigurationValue */
- 0x01,
- /** iConfiguration */
- 0x00,
- /** bmAttributes */
- 0xC0,
- /** MaxPower */
- 0x32,
- /** bLength */
- 0x09,
- /** bDescriptorType */
- USBD_DESC_INTERFACE,
- /** bInterfaceNumber */
- 0x00, //0x01,
- /** bAlternateSetting */
- 0x00,
- /** bNumEndpoints */
- 0x02, //0x01,
- /** bInterfaceClass */
- 0x07,//0x02,
- /** bInterfaceSubClass */
- 0x01, //0x02,
- /** bInterfaceProtocol */
- 0x01, //0x01,
- /** iInterface */
- 0x00,
- 0x07, /** bLength: Endpoint Descriptor size */
- USBD_DESC_ENDPOINT, /** bDescriptorType: Endpoint */
- 0x01, /** bEndpointAddress */
- 0x02, /** bmAttributes: Bulk */
- 0x40,0x00, /** wMaxPacketSize: */
- 0x00, /** bInterval: */
-
- 0x07, /** bLength: Endpoint Descriptor size */
- USBD_DESC_ENDPOINT, /** bDescriptorType: Endpoint */
- 0x81, /** bEndpointAddress */
- 0x02, /** bmAttributes: Bulk */
- 0x40,0x00, /** wMaxPacketSize: */
- 0x00 /** bInterval: */
- };
2.4 测试是否枚举成功
修改完描述符后,运行程序,拔插USB,设备管理器中会出现“打印支持”,使用“USBlyzer”可以看到详细的枚举信息。
3 实现类特殊请求
3.1 添加GET_DEVICE_ID请求
DEVICE ID是打印机类特有的类请求,关系到打印机驱动的安装, 官方文档“usbprint11a021811.pdf”中有对这个的详细描


通过修改“usbd_class_cdc.c” 文件中的 “void USBD_ClassHandler()”函数来实现。
- void USBD_ClassHandler(USBD_DevReqData_T* reqData)
- {
- uint16_t length = ((uint16_t)reqData->byte.wLength[1] << 8) | \
- reqData->byte.wLength[0] ;
-
- switch (reqData->byte.bRequest)
- {
- case 0x00: //GET_DEVICE_ID
- USBD_CtrlInData(PRINTER_DEVICE_ID, sizeof(PRINTER_DEVICE_ID));
- break;
-
- case 0x01: //GET_PORT_STATUS
- USBD_CtrlOutData(&g_port_status, 1);
- break;
-
- case 0x02: //SOFT_RESET
- USBD_CtrlInData(cmdBuf, length);
- break;
-
- }
- }
3.2 抓包观察请求是否成功
拔插USB,通过软件“Bus Hound”可以看到USB主机发送了 GET_DEVICE_ID 请求,然后USB设备也返回了对应的字符串。
4、实现数据的接收
4.1 在OUT端点中保存接收到的数据包
在USB OUT端点处理函数进行数据的接收操作,在文件 “usbd_class_cdc.c”中的 “void USBD_VCP_OutEpCallback()” 函数中添加代码。
- void USBD_VCP_OutEpCallback(uint8_t ep)
- {
- uint32_t dataCnt;
- if (ep == USBD_EP_1)
- {
- dataCnt = g_usbDev.outBuf[USBD_EP_1].xferCnt;
-
- PrinterReceiveCount = dataCnt;
- memcpy(PrinterReceiveData,dataBuf,dataCnt); //保存接收到的这包数据
-
- USBD_RxData(USBD_EP_1, dataBuf, g_usbDev.outBuf[USBD_EP_1].maxPackSize); //如果想接收下一包,须加这个
- //USBD_TxData(USBD_EP_1, dataBuf, dataCnt);
- }
- }
4.2 测试数据接收功能
为了方便查看接收到的数据,在while(1)中把接收到的数据通过串口发送出去。
- int main(void)
- {
- APM_EVAL_Init();
- printf("This is an example of USB Printer \r\n");
-
- while(1)
- {
- if(PrinterReceiveCount>0)
- {
- for(unsigned char i=0;i<PrinterReceiveCount;i++)
- {
- USART_TxData(DEBUG_USART,PrinterReceiveData[i]);
- while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
- }
- PrinterReceiveCount=0;
- }
-
- }
- }
通过USB发送数据需要一个上位机,这里使用“条码打印机调试助手”,插上USB后,通讯端口选择USB,然后打开端口,
在数据发送框输入想发的数据,然后点右下角“发送数据”,这时可以在串口调试助手上看到MCU刚接收的数据。
如下图所示,每次USB接收到的数据都通过串口发送出来了。
|