完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
1、硬件介绍
1、SGM7227,USB高速切换开关,OE是芯片使能,低电平才能使总线导通; S脚是切换控制; USB协议 2、软件移植 移植文件分析 stm32自带USB接口,OTG-FS(全速)和OTG-HS(高速),因为stm32f4只带有高速PHY,想使用高速模式,就需要外扩高速PHY,在此为USB3300。 系统配置一个Device端口,一个Host端口; Device端口连接主机,在此处为POS机,外接USB3300作为高速PHY;移植时,我们重点要修改的就是 USB_APP 文件夹下面的代码。其他代码(USB_OTG 和USB_DEVICE 文件夹下的代码)一般不用修改。 需要修改的文件: u***_bsp.c 提供了几个 USB 库需要用到的底层初始化函数,包括: IO 设置、中断设置、VBUS配置以及延时函数等,需要我们自己实现。 USB Device(Slave)和 USB Host 共用这个.c 文件。 u***d_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里: u***d_desc.c 里面的: u***d 即 device 类,同样: u***h 即 host 类,所以通过文件名我们可以很容易区分该文件是用在 device 还是 host,而只有 u*** 字样的那就是 device 和 host 可以共用的。 u***d_usr.c 提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态,比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。 u***d_storage_msd.c 提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本章我们设置了 2 个磁盘: SD 卡和 SPI FLASH。这些文件都是以回调函数的方式被内核调用,需要用户编写具体的应用程序; 使用时,需要定义 USB_OTG_CORE_HANDLE ,是一个全局结构体类型,用于存储 USB 通信中 USB 内核需要使用的的各种变量、状态和缓存等,任何 USB 通信(不论主机,还是从机),我们都必须定义这么一个结构体以实现 USB 通信。 USB 初始化非常简单,只需要调用 USBD_Init 函数即可,本章的 USB 读卡器属于 USB 设备类,所以使用该函数。该函数初始化了USB 设备类处理的各种回调函数,以便 USB 驱动库调用。执行完该函数以后, USB 就启动了,所有 USB 事务,都是通过 USB 中断触发,并由 USB 驱动库自动处理。 USB设备需要存储介质,需要用户定义存储介质驱动。 USB驱动会自己调用处理函数进行处理,用户需要改动的是底层的驱动,即磁盘的读写;具体在u***d_storage_msd.c里面, 磁盘回调函数结构体: USBD_STORAGE_cb_TypeDef USBD_MICRO_SDIO_fops = { STORAGE_Init, STORAGE_GetCapacity, STORAGE_IsReady, STORAGE_IsWriteProtected, STORAGE_Read, STORAGE_Write, STORAGE_GetMaxLun, (int8_t *)STORAGE_Inquirydata, }; 读操作: int8_t STORAGE_Read (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { buf=&U_DISK[blk_addr*512]; return 0; } USB内核处理 USB_OTG_CORE_HANDLE 是全局变量,
typedef struct USB_OTG_handle { USB_OTG_CORE_CFGS cfg; USB_OTG_CORE_REGS regs; #ifdef USE_DEVICE_MODE DCD_DEV dev; #endif #ifdef USE_HOST_MODE HCD_DEV host; #endifdda #ifdef USE_OTG_MODE OTG_DEV otg; #endif } USB_OTG_CORE_HANDLE , *PUSB_OTG_CORE_HANDLE; USB_OTG_CORE_HANDLE分析: 这是个全局结构体,内部存储内核处理USB设备,主机的变量;当USB作为Device设备时,USB_OTG_CORE_HANDLE定义为DCD_DEV ; 其参数如下: typedef struct _DCD { uint8_t device_config; uint8_t device_state; uint8_t device_status; uint8_t device_old_status; uint8_t device_address; uint8_t connection_status; uint8_t test_mode; uint32_t DevRemoteWakeup; USB_OTG_EP in_ep [USB_OTG_MAX_TX_FIFOS]; USB_OTG_EP out_ep [USB_OTG_MAX_TX_FIFOS]; uint8_t setup_packet [8*3]; USBD_Class_cb_TypeDef *class_cb; USBD_Usr_cb_TypeDef *usr_cb; USBD_DEVICE *usr_device; uint8_t *pConfig_descriptor; } DCD_DEV , *DCD_PDEV; DCD_DEV 结构说明: DCD_DEV结构包含用于实时保存与设备,控制传输状态机以及端点信息和状态相关的所有信息的所有变量和结构。中断处理
On-The-Go,即OTG威廉希尔官方网站 就是实现在没有Host的情况下,实现设备间的数据传送。例如数码相机直接连接到打印机上,通过OTG威廉希尔官方网站 ,连接两台设备间的USB口,将拍出的相片立即打印出来;也可以将数码照相机中的数据,通过OTG发送到USB接口的移动硬盘上,野外操作就没有必要携带价格昂贵的存储卡,或者背一个便携电脑。 3、USB作为Device设备 u***d_req和u***d_ioreq 文件描述:该文件处理USB事务的结果。u***_dcd和u***_dcd_int 这两个文件为USB库提供设备接口层封装; 其中u***_dcd文件包含以下函数: DCD_EP_PrepareRxUSB OTG低电平驱动器的DCD层有一个必须通过USB中断调用的功能(高速或全速): DCD_Handle_ISR (USB_OTG_CORE_HANDLE *pdev) u***_dcd_int文件说明: 上图是USB设备库构架; 如上图所示,USB设备库由两个主要部分组成:库核心和类驱动程序 库核心包括三个主要部分: USB device core USB requests USB I/O requests USB设备库处理流程图 上图的分析: 分为4个模块,MSC设备类驱动 MSC设备类包含文件: u***d_msc_core ----- MSC类的核心处理文件MSC(Mass storage class) USB大容量存储类是围绕仅批量传输(BOT)构建的。 它使用SCSI透明命令集。 SCSI指令集: 4、伪装成打印机 u***d_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字 符串、制造商等重要信息。 在这里,需要将单片机伪装成打印机,因此需要封装其USB信息。 需要讲解一个概念:USB设备描述符; USB设备描述符 说到USB设备,不得不提到各种描述符(descriptors), 一般来说,描述符有如下几种: 1:设备描述符(Device Descriptors)2:配置描述符(Configuration Descriptors)2:接口描述符(Interface Descriptors)3:端点描述符(Endpoint Descriptors) 他们之间的关系如下图所示: 图片说明:设备描述符定义了配置数多少,配置描述符又定义了端口多少,一层层向下定义。 USB描述符信息存储在USB设备中,在枚举过程中,USB主机会向USB设备发送GetDescriptor请求,设备描述符包含信息: 描述符(Descriptor )是一个完整的数据结构, 存储在USB 设备中, 用于描述一个USB 设备的所有属性。USB主机通过一系列命令要求设备发送这些信息。 USB 设备的属性包括很多内容, 为了便于管理, USB2.0协议将这些信息做了分类, 定义了多种描述符,包括标准和HID类的描述符,下面是标准描述符描述。 标准描述符 [tr]标准描述符bDescriptorType字段[/tr]
描述符间的关系 一个设备有且只能有一个设备描述符,之后的描述符都允许有多个不同的描述符。
用于接口的每个端点都有自己的描述符。 此描述符包含信息 需要主机确定每个端点的带宽要求。 端点描述符为始终由GetDescriptor(获取配置描述符),作为配置信息的一部分返回。 端点描述符不能直接用GetDescriptor()或SetDescriptor()直接访问。对于端点零,不存在端点描述符。
伪装成打印机的设备描述符 __ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END = { 0x12, /*bLength */ 0x01, /*bDescriptorType*/ 0x00, /*bcdUSB */ // u*** 版本 2 0x02, // 0x00, /*bDeviceClass*/ 0x00, /*bDeviceSubClass*/ 0x00, /*bDeviceProtocol*/ USB_OTG_MAX_EP0_SIZE, /*bMaxPacketSize*/ // 传输包大小 7 LOBYTE(USBD_VID), /*idVendor*/ // 厂商编码 8 HIBYTE(USBD_VID), /*idVendor*/ LOBYTE(USBD_PID), /*idVendor*/ // 产品编码 10 HIBYTE(USBD_PID), /*idVendor*/ 0x00, /*bcdDevice rel. 2.00*/ // 设备出厂编号 12 0x02, USBD_IDX_MFC_STR, /*Index of manufacturer string*/ // 厂商字符串索引 14 USBD_IDX_PRODUCT_STR, /*Index of product string*/ // 产品字符串索引 15 0x00, /*Index of serial number string*/ // 设备序列号字符串索引 16 USBD_CFG_MAX_NUM /*bNumConfigurations*/ // 配置描述符个数 17 } ; /* USB_DeviceDescriptor */ |
|||||
|
|||||
__ALIGN_BEGIN uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ] __ALIGN_END = { //---------- 配置描述符 ---------- 0x09, /* bLength: Configuation Descriptor size */ 0x02, /* bDescriptorType: Configuration */ 0x20, // 返回数值大小 0x00, 0x01, /* bNumInterfaces: 1 interface */ 0x01, /* bConfigurationValue: */ 0x05, /* iConfiguration: */ // 描述配置的字符索引 USBD_MSC_CfgDesc[6] 0xC0, /* bmAttributes: */ 0x32, /* MaxPower 100 mA */ //-------------------------------- //---------- 接口描述符 ---------- 0x09, /* bLength: Interface Descriptor size */ 0x04, /* bDescriptorType: */ 0x00, /* bInterfaceNumber: Number of Interface */ 0x00, /* bAlternateSetting: Alternate setting */ 0x02, /* bNumEndpoints*/ // jz-80 打印机型号 0x07, /* bInterfaceClass: MSC Class */ // 接口 类型 USBD_MSC_CfgDesc[14] 0x01, /* bInterfaceSubClass : SCSI transparent*/ // 接口 子类型 USBD_MSC_CfgDesc[15] 0x02, /* nInterfaceProtocol */ // 接口 协议 USBD_MSC_CfgDesc[16] 0x04, /* iInterface: */ // 接口描述字符串索引 USBD_MSC_CfgDesc[17] //-------------------------------- //---------- 端点1描述符 ---------- 0x07, /*Endpoint descriptor length = 7*/ 0x05, /*Endpoint descriptor type */ MSC_IN_EP, /*Endpoint address (IN, address 1) */ // USBD_MSC_CfgDesc[20] 0x02, /*Bulk endpoint type */ LOBYTE(MSC_MAX_PACKET), // USBD_MSC_CfgDesc[22] HIBYTE(MSC_MAX_PACKET), // USBD_MSC_CfgDesc[23] 0x00, /*Polling interval in milliseconds */ //--------------------------------- //---------- 端点1描述符 ---------- 0x07, /*Endpoint descriptor length = 7 */ 0x05, /*Endpoint descriptor type */ MSC_OUT_EP, /*Endpoint address (OUT, address 1) */ // USBD_MSC_CfgDesc[27] 0x02, /*Bulk endpoint type */ LOBYTE(MSC_MAX_PACKET), // USBD_MSC_CfgDesc[29] HIBYTE(MSC_MAX_PACKET), // USBD_MSC_CfgDesc[30] 0x00 /*Polling interval in milliseconds*/ //--------------------------------- }; PID和VID 概念: PID/VID唯一标识一个设备,HardwareID是为了给系统识别的 ,他是根据PID/VID而生成的。这个与序列号没什么关系,序列号一般都是厂家固化到芯片中的信息而已。GUID只是为了标志你安装的设备是属于一个什么类当中,这个类可以显示再设备管理器中。比如:你可以定义一个类,当然这个类有与系统中任何类都不同的GUID,然后选择一个图标和类名,就可以同网卡等其他设备一起显示在设备管理器下的根目录中了根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备,VID和PID都是两个字节长。 其中,供应商ID(VID)由供应商向USB执行论坛申请,每个供应商的VID是唯一的,PID由供应商自行决定。 所以理论上一个USB存储设备的VID应该是设备生产商的VID,而不是主控生产商的VID,这两个VID应该是不同的(主控生产商自己生产的设备除外)。 作为Host主机 函数分析 USB_Host 存储主机状态结构; 使用 USB 主机的时候,需要两个结构体: USB_OTG_CORE_HANDLE和 USB_HOST。 然后, USB 初始化,使用的是 USBH_Init,用于 USB 主机初始化,包括对 USB 硬件和 USB驱动库的初始化。如果是: USB SLAVE 通信,在只需要调用 USBD_Init 函数即可,不过 USBHOST 则还需要调用另外一个函数 USBH_Process, 该函数用于实现 USB 主机通信的核心状态机处理,该函数必须在主函数里面,被循环调用,而且调用频率得比较快才行(越快越好),以便及时处理各种事务。 注意, USBH_Process 函数仅在 U 盘识别阶段,需要频繁反复调用,但是当 U 盘被识别后,剩下的操作(U 盘读写),都可以由 USB 中断处理。 POS发送信息处理 拦截POS信息 在u***_dcd_int.c文件中,修改了USBD_OTG_EP1OUT_ISR_Handler中断,可以看到里面调用函数StorePosData将数据从xfer_buff中压缩存储到PosData.frompos_buf中,也就是压缩进DataFromPosBuf中。 if((PosData.status == 0)||(PosData.status == 1)) { StorePosData(pdev->dev.out_ep[1].xfer_buff,pdev->dev.out_ep[1].xfer_count); if(PosData.status == 0) PosData.status = 1; } } /* Inform upper layer: data ready */ /* RX COMPLETE */ USBD_DCD_INT_fops->DataOutStage(pdev , 1); 发送给设备的信息 在u***h_msc_bot.c文件中,更改了USBH_MSC_HandleBOTXfer函数,其中检测到USBH_MSC_SEND_CBW状态,调用USBH_BulkSendData将数据发送给设备; //USBH_MSC_HandleBOTXfer函数处理: switch (USBH_MSC_BOTXferParam.BOTState) { case USBH_MSC_SEND_CBW: USBH_BulkSendData (pdev, PosData.frompos_buf, PosData.datalen , MSC_Machine.hc_num_out); USBH_MSC_BOTXferParam.BOTStateBkp = USBH_MSC_SEND_CBW; USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SENT_CBW; break; // USBH_BulkSendData 函数定义: pdev ------选中的设备分析此部分函数,可以了解到数据从pos到打印机的过程; 首先pos发送的数据被stm32拦截,压缩存储在DataFromPosBuf中,然后通过中断,将指令发送给打印机。 Host用户回调函数 u***h_usr.c里面定义了作为主机的用户回调函数: 这里讲解几个重要的函数: 1、USBH_USR_Device_DescAvailable if((PosData.status == 0)||(PosData.status == 1)) { StorePosData(pdev->dev.out_ep[1].xfer_buff,pdev->dev.out_ep[1].xfer_count); if(PosData.status == 0) PosData.status = 1; } } /* Inform upper layer: data ready */ /* RX COMPLETE */ USBD_DCD_INT_fops->DataOutStage(pdev , 1); 发送给设备的信息 在u***h_msc_bot.c文件中,更改了USBH_MSC_HandleBOTXfer函数,其中检测到USBH_MSC_SEND_CBW状态,调用USBH_BulkSendData将数据发送给设备; //USBH_MSC_HandleBOTXfer函数处理: switch (USBH_MSC_BOTXferParam.BOTState) { case USBH_MSC_SEND_CBW: USBH_BulkSendData (pdev, PosData.frompos_buf, PosData.datalen , MSC_Machine.hc_num_out); USBH_MSC_BOTXferParam.BOTStateBkp = USBH_MSC_SEND_CBW; USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SENT_CBW; break; // 这里当连接打印机后,检测打印机的设备描述符,再将VID和PID打印出来,因此我们在设置Device设备描述符时,按照这个参数设置; VID: 0416h2、USBH_USR_Manufacturer_String,USBH_USR_Product_String,USBH_USR_SerialNum_String 这三个函数用来从打印机获得其设备描述符信息; diskio接口函数 diskio里需要用户自定义接口,对存储磁盘进行读写等; 在此处,将打印机作为一个外接磁盘,提供接口函数: USBH_UDISK_Status1、u***d_storage_msd.c函数分析 u***d_storage_msd.c是设备作为Device的数据存储相关函数,在此文件中需要用户自定义存储空间,读写操作等回调函数。 在.c文件中定义了虚拟磁盘,封装了用户读写函数; 2、Pos数据状态 自定义了接收pos机数据的状态结构体; //POS信息; typedef struct DATA_FROM_pos { uint8_t status; // 状态 0: 空闲 1:正在获取数据 2:获取完成,等待打印 3:正在打印 4:打印完成、等待转移 //----------------------------------------------------------- uint8_t *frompos_buf; // 收到的数据缓冲区 uint32_t buflen; // 压缩后的长度 uint32_t datalen; // 压缩前的长度 uint8_t zerototal; // 正在压缩的 0x00 的数量 //----------------------------------------------------------- uint8_t needprint; // 需要打印 //----------------------------------------------------------- uint8_t *upzipPointer; // 解压指针,指向需要继续解压的位置,主缓冲区 uint8_t ****uf; // 发送数据缓冲区 uint8_t ***uflen; // 发送数据长度 uint8_t *unzipbuf; // 解压缓冲区 uint8_t unziplen; // 解压区现存数据长度 uint8_t cmdtype; // 命令类型 uint8_t delayziplen; // 等待一些数据再开始进行压缩 //----------------------------------------------------------- }DATA_FROM_POS; 结构体成员分析 1、status status是一个标志位,用来标识接收数据的状态; // 状态其中 status改变为1时,是在USBD_OTG_EP1OUT_ISR_Handler中改变的状态; USBD_OTG_EP1OUT_ISR_Handler如果定义#ifdef USB_OTG_HS_DEDICATED_EP1_ENABLED才能使用; 找到u***_conf.h文件; 在u***_conf.h文件中,注释掉了原文件的宏定义,自己定义了宏; 因为单片机模拟打印机时,使用的是高速PHY,因此USB OTG HS CONFIGURATION对应是Device的驱动定义。 /****************** USB OTG HS CONFIGURATION **********************************/ #ifdef USB_OTG_HS_CORE #define RX_FIFO_HS_SIZE 512 #define TX0_FIFO_HS_SIZE 128 #define TX1_FIFO_HS_SIZE 372 #define TX2_FIFO_HS_SIZE 0 #define TX3_FIFO_HS_SIZE 0 #define TX4_FIFO_HS_SIZE 0 #define TX5_FIFO_HS_SIZE 0 #define TXH_NP_HS_FIFOSIZ 256 #define TXH_P_HS_FIFOSIZ 256 // #define USB_OTG_HS_LOW_PWR_MGMT_SUPPORT // old 注释// // #define USB_OTG_HS_SOF_OUTPUT_ENABLED // old 注释 #ifdef USE_ULPI_PHY #define USB_OTG_ULPI_PHY_ENABLED #endif #ifdef USE_EMBEDDED_PHY #define USB_OTG_EMBEDDED_PHY_ENABLED #endif #define USB_OTG_HS_INTERNAL_DMA_ENABLED #define USB_OTG_HS_DEDICATED_EP1_ENABLED #define USB_OTG_EXTERNAL_VBUS_ENABLED #endif |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1786 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1622 浏览 1 评论
1089 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
730 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1680 浏览 2 评论
1942浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
739浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
576浏览 3评论
599浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
561浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 22:06 , Processed in 1.136220 second(s), Total 48, Slave 42 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号