全场景——(三)USB开发基础(2)

news2024/11/15 7:01:58

文章目录

  • 一、USB 描述符
    • 1.1 USB 设备状态切换图
    • 1.2 标准设备请求
      • 1.2.1 SETUP事务的数据格式
      • 1.2.2 标准设备请求
      • 1.2.3 设备/配置/接口/端点
    • 1.3 描述符
      • 1.3.1 设备描述符
      • 1.3.2 配置描述符
      • 1.3.3 接口描述符
      • 1.3.4 端点描述符
      • 1.3.5 示例
    • 1.4 设备枚举过程示例
  • 二、USBX 组件
    • 2.1 Azure RTOS 介绍
    • 2.2 USBX 层次
    • 2.3 USBX 的基本配置
  • 三、移植 USBX 实现虚拟串口
    • 3.1 配置 USB
    • 3.2 添加 USBX 代码
      • 3.2.1 复制代码
      • 3.2.2 添加进工程
    • 3.3 添加 USBX APP 代码
    • 3.4 修改 usb.c
    • 3.5 创建 USBX 任务
    • 3.6 设置 MDK-ARM 工程
    • 3.7 添加使用串口的代码
    • 3.8 上机实验
  • 四、虚拟串口源码分析与改造
    • 4.1 描述符的设置
    • 4.2 数据收发函数
    • 4.3 使用 FreeRTOS 改造代码

一、USB 描述符

1.1 USB 设备状态切换图

img
Attached为USB设备插上电脑,Powered是Host为其供电,接着Host需要去识别该设备为全速、高速还是低速设备,于是会发起一个复位(Reset),在复位过程中会识别设备速率,设备会进入默认状态(Default),一个处于默认状态的设备,其USB地址为0,以后主机会使用地址0与其进行通信,会访问地址为0的设备,访问端点0,将分配的新地址发给设备(Address Assigned),让设备工作与新地址模式下,此时Address会等于某一个新的值,以后Host会使用这个新地址与该设备进行通信,该设备可能会有多种配置,即使只有一种配置我们也需要对其进行配置(Device Configured),工作于某一种配置下,设就是设备的枚举过程。
img

1.2 标准设备请求

1.2.1 SETUP事务的数据格式

Host 使用控制传输来识别设备、设置设备地址、启动设备的某些特性, 对于控制传输, 它首先发出"setup 事务",如下:

img

在"setup 事务"中,

  • SETUP 令牌包:用来通知设备, “要开始传输了”

  • DATA0 数据包:它含有固定的格式, 用来告诉设备"是读还是写"、“读什么”、“写什么”

Host 通过 DATA0 数据包发送 8 字节数据给设备,它的格式如下图所示:

img
在bRequest可以列出自己的请求,是想去读描述符还是设置描述符,想去设置地址等等,比如去设置地址,地址的位置应该是wValue,wValue的数依赖于bRequest,windex也是依赖于bReaquest,wLength为数据长度,上面图片中set up事务的数据格式,其中Data Stage的长度(wLength)为多少,由建立事务中最后两个字节决定,在建立事务中,总共传输了8字节的数据,其中最后2字节决定了在数据阶段要传输的数据长度。(第一个bmRequestType表示请求类型)

1.2.2 标准设备请求

控制传输的建立事务中, 可以使用下列格式的数据:

在这里插入图片描述

上表中各个"宏"取值如下:

img

1.2.3 设备/配置/接口/端点

在 SETUP 事务的数据里, 表示了要访问的是什么: Device?Interface?Endpoint?

对于一个USB 设备, 它可以多种配置(Configuration)。比如4G 上网卡就有 2 种配置: U 盘、上网卡。第 1 次把 4G 上网卡插入电脑时,它是一个 U 盘,可以按照里面的程序。装好程序后, 把它再次插入电脑,它就是一个上网卡。驱动程序可以选择让它工作于哪种配 置,同一时间只能有一种配置。大多数的 USB 设备只有一种配置。

一个配置下,可以有多个接口(Interface),接口等同于功能(Function)。比如 USB 耳机有两个接口(功能):声音收发、按键控制。

一个接口, 可能有多个设置(Setting),比如默认设置下它使用较低的带宽, 可以选择其他设置以使用更高带宽。

一个接口, 由一个或多个端点(Endpoint)组成。端点 0 属于整个设备的, 端点 0 是双向的。接口还可以有其他端点, 这些端点是单向的, 要么是批量(Bulk)端点、要么是中断 (Interrupt)端点、要么是同步(Isochronous)端点。

1.3 描述符

怎么描述设备、配置、接口、端点?使用描述符(Descriptors),有设备描述符、配置 描述符、接口描述符、端点描述符。所谓描述符,就是一些格式化的数据, 用来描述信息。

一个 USB 设备:

  • 只有一个设备描述符:用来表示设备的 ID、它有多少个配置、它的端点 0一次最大能传输多少字节数据(为什么把端点0放在设备描述符这:因为一个设备刚插进电脑时,电脑以及Host软件都是与端点0进行传输的)

  • 可能有多个配置描述符:用来表示它有多少个接口、供电方式、最大电流

  • 一个配置描述符下面,可能有多个接口描述符:用来表示它是哪类接口、有几个设置 (Setting)、有几个端点

  • 一个接口描述符符下面,可能有多个端点描述符: 用来表示端点号、方向(IN/OUT)、类型(批量/中断/同步)

还有一些字符串描述符(String descriptors),它用可读的文字来描述设备,是可选的。

img

1.3.1 设备描述符

img

1.3.2 配置描述符

img

1.3.3 接口描述符

img

1.3.4 端点描述符

img

1.3.5 示例

在 Ubuntu 中可以执行 lsusb -v查看 USB 设备的描述符信息:

book@100ask:~$ sudo lsusb -v
[sudo] password for book:

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device Descriptor:
bLength                18
bDescriptorType         1
bcdUSB               2.00

bDeviceClass	9 Hub
bDeviceSubClass	0 Unused
bDeviceProtocol	0 Full speed (or root) hub
bMaxPacketSize0	64
idVendor	0x1d6b Linux Foundation
idProduct	0x0002 2.0 root hub
bcdDevice	5.04
iManufacturer	3 Linux 5.4.0-124-generic ehci_hcd
iProduct	2 EHCI Host Controller
iSerial	1 0000:02:03.0
bNumConfigurations      1
Configuration Descriptor:

bLength	9
bDescriptorType	2
wTotalLength	25
bNumInterfaces	1
bConfigurationValue	1
iConfiguration	0
bmAttributes	0xe0
Self Powered
Remote Wakeup
MaxPower                0mA
Interface Descriptor:
bLength                 9

bDescriptorType	4
bInterfaceNumber	0
bAlternateSetting	0
bNumEndpoints	1
bInterfaceClass	9 Hub
bInterfaceSubClass	0 Unused
bInterfaceProtocol	0 Full speed (or root) hub
iInterface	0
Endpoint Descriptor:
bLength                 7
bDescriptorType         5
bEndpointAddress     0x81  EP 1 IN
bmAttributes            3

Transfer Type		Interrupt
Synch Type		None
Usage Type		Data
wMaxPacketSize	0x0004	1x 4 bytes
bInterval	12	
Hub Descriptor:
bLength               9
bDescriptorType      41
nNbrPorts             6
wHubCharacteristic 0x000a
No power switching (usb 1.0)
Per-port overcurrent protection


bPwrOn2PwrGood
bHubContrCurrent DeviceRemovable  PortPwrCtrlMask
Hub Port Status:
Port 1: 0000.0100 Port 2: 0000.0100 Port 3: 0000.0100 Port 4: 0000.0100 Port 5: 0000.0100
Port 6: 0000.0100


10 * 2 milli seconds
0 milli Ampere
0x00
0xff

power
power
power
power
power
power

Device Status:     0x0001
Self Powered

Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Device Descriptor:

bLength	18
bDescriptorType	1
bcdUSB	1.10
bDeviceClass	9 Hub
bDeviceSubClass	0 Unused
bDeviceProtocol	0 Full speed (or root) hub
bMaxPacketSize0	8
idVendor	0x0e0f VMware, Inc.
idProduct	0x0002 Virtual USB Hub
bcdDevice	1.00
iManufacturer	1 VMware, Inc.
iProduct	2 VMware Virtual USB Hub
iSerial                 0
bNumConfigurations      1
Configuration Descriptor:
bLength                 9
bDescriptorType         2
wTotalLength           25
bNumInterfaces          1
bConfigurationValue     1

iConfiguration	1 VMware, Inc.
bmAttributes	0xe0
Self Powered	
Remote Wakeup	
MaxPower	0mA

Interface Descriptor:
bLength                 9

bDescriptorType	4
bInterfaceNumber	0
bAlternateSetting	0
bNumEndpoints	1
bInterfaceClass	9 Hub
bInterfaceSubClass	0 Unused
bInterfaceProtocol	0 Full speed (or root) hub
iInterface	1 VMware, Inc.
Endpoint Descriptor:
bLength                 7
bDescriptorType         5
bEndpointAddress     0x81  EP 1 IN
bmAttributes            3

Transfer Type		Interrupt
Synch Type		None
Usage Type		Data
wMaxPacketSize	0x0001	1x 1 bytes
bInterval	255	
Hub Descriptor:
bLength               9
bDescriptorType      41
nNbrPorts             7
wHubCharacteristic 0x0009
Per-port power switching
Per-port overcurrent protection

bPwrOn2PwrGood
bHubContrCurrent DeviceRemovable  PortPwrCtrlMask
Hub Port Status:
Port 1: 0000.0100 Port 2: 0000.0100 Port 3: 0000.0100 Port 4: 0000.0100 Port 5: 0000.0100 Port 6: 0000.0100
Port 7: 0000.0100

50 * 2 milli seconds
100 milli Ampere
0x00
0xfe

power
power
power
power
power
power
power

Device Status:     0x2909
Self Powered

Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Device Descriptor:
bLength	18
bDescriptorType	1
bcdUSB	1.10
bDeviceClass	0 (Defined at Interface level)
bDeviceSubClass	0
bDeviceProtocol	0
bMaxPacketSize0	8
idVendor	0x0e0f VMware, Inc.
idProduct	0x0003 Virtual Mouse
bcdDevice	1.03
iManufacturer	1 VMware
iProduct	2 VMware Virtual USB Mouse
iSerial                 0
bNumConfigurations      1
Configuration Descriptor:
bLength                 9
bDescriptorType         2
wTotalLength           34
bNumInterfaces          1
bConfigurationValue     1

iConfiguration	1 VMware
bmAttributes	0xc0
Self Powered	
MaxPower	0mA
Interface Descriptor:
bLength                 9

bDescriptorType	4
bInterfaceNumber	0
bAlternateSetting	0
bNumEndpoints	1
bInterfaceClass	3 Human Interface Device
bInterfaceSubClass	1 Boot Interface Subclass
bInterfaceProtocol	2 Mouse
iInterface	1 VMware
HID Device Descriptor:
bLength                 9
bDescriptorType        33
bcdHID               1.10

bCountryCode	0 Not supported
bNumDescriptors	1
bDescriptorType	34 Report
wDescriptorLength	46
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength                 7
bDescriptorType         5
bEndpointAddress     0x81  EP 1 IN
bmAttributes            3

Transfer Type		Interrupt
Synch Type		None
Usage Type		Data
wMaxPacketSize	0x0008	1x 8 bytes
bInterval	1	
Device Status:     0x0001
Self Powered

Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Device Descriptor:

bLength	18
bDescriptorType	1
bcdUSB	1.10
bDeviceClass	9 Hub
bDeviceSubClass	0 Unused
bDeviceProtocol	0 Full speed (or root) hub
bMaxPacketSize0	64
idVendor	0x1d6b Linux Foundation
idProduct	0x0001 1.1 root hub
bcdDevice	5.04
iManufacturer	3 Linux 5.4.0-124-generic uhci_hcd
iProduct	2 UHCI Host Controller
iSerial	1 0000:02:00.0
bNumConfigurations      1
Configuration Descriptor:

bLength	9
bDescriptorType	2
wTotalLength	25
bNumInterfaces	1
bConfigurationValue	1
iConfiguration	0
bmAttributes	0xe0
Self Powered
Remote Wakeup
MaxPower                0mA
Interface Descriptor:
bLength                 9
bDescriptorType         4
bInterfaceNumber        0
bAlternateSetting	0
bNumEndpoints	1
bInterfaceClass	9 Hub
bInterfaceSubClass	0 Unused
bInterfaceProtocol	0 Full speed (or root) hub
iInterface	0
Endpoint Descriptor:
bLength                 7
bDescriptorType         5
bEndpointAddress     0x81  EP 1 IN
bmAttributes            3

Transfer Type		Interrupt
Synch Type		None
Usage Type		Data
wMaxPacketSize	0x0002	1x 2 bytes
bInterval	255	
Hub Descriptor:
bLength               9
bDescriptorType      41
nNbrPorts             2
wHubCharacteristic 0x000a
No power switching (usb 1.0)
Per-port overcurrent protection

bPwrOn2PwrGood	1 * 2 milli seconds
bHubContrCurrent	0 milli Ampere
DeviceRemovable	0x00
PortPwrCtrlMask	0xff
Hub Port Status:
Port 1: 0000.0103 power enable connect
Port 2: 0000.0107 power suspend enable connect
Device Status:     0x0001
Self Powered

1.4 设备枚举过程示例

使用"usbprotocolsuite"打开,可以看到设备的枚举过程:

  • 使用控制传输,读取设备信息(设备描述符):第一次读取时, 它只需要得到 8 字节数据, 因为第 8 个数据表示端点 0 能传输的最大数据长度。

在这里插入图片描述

①、设置令牌包表明要跟0号设备的0号端点发起数据传输
②、Host发出一个Data0数据包 0x80表示bit7为1 1为Device到Host 表明想读取Device中的数据 第二个06表示相关操作 在9-3图表中可知为获得描述符 接下来的00 01为描述符的类型 在9-5图表中查询可知该描述符为设备描述符 最后的40 00表示数据的长度 十六进制就是64 表示读取64分字节的长度 第一部分就是Host想从Device中读取64字节的设备描述符
③、Device接收到请求后 需要将设备描述符发送给Host 此时Host发送一个读数据包(IN)的操作 首先发送一个IN令牌包 表明要读取数据 但Device正忙 回复了一个NAK握手包
④、Host过后再次发送一个读取数据的令牌包 表明要读取数据 Device不忙于是将数据返回给Host 返回的是18字节的数据(看到9-8图表可知设备描述符的长度为18字节)
⑤、最后就是状态阶段 Host向Device发送一个OUT令牌包 同时发送0字节数据 等到Device向Host发送一个ACK握手包 说明Host读取数据完毕

  • Host 分配地址给设备, 然后把新地址发给设备:

img
在这里插入图片描述

  • 使用新地址, 重新读取设备描述符, 设备描述符长度是 18:

img

  • 读取配置描述符: 它传入的长度是 255,想一次性把当前配置描述符、它下面的接口描 述符、端点描述符全部读出来

img

  • 读取字符描述符

img

二、USBX 组件

2.1 Azure RTOS 介绍

Azure RTOS 平台是运行时解决方案的集合,包括 Azure RTOS ThreadX、Azure RTOS NetX 和 NetX Duo、Azure RTOS FileX、Azure RTOS GUIX 和 Azure RTOS USBX。

img

Azure RTOS ThreadX 是专用于深度嵌入式应用程序的高级实时操作系统 (RTOS)。 Azure RTOS ThreadX 具有多种优势,其中包括高级调度设施、消息传递、中断管理和消息服务。 Azure RTOS ThreadX 具有许多高级功能, 其中包括 picokernel 体系结构、抢占 式阈值调度、事件链和一系列丰富的系统服务。

USBX 是 Azure®RTOS USB 主机和 USB 设备嵌入式堆栈。它与 ThreadX 紧密耦合。在某些类中, 它需要 FileX 和 NetX Duo 堆栈。它允许使用具有多种配置的 USB 设备、复合设备和 USB OTG 进行操作。它支持 USB 电源管理。

USBX 为 USB 主机和 USB 设备堆栈提供了大量的 USB 类。 一旦低级驱动程序能够响应 USBX 请求, 模块化架构就可以更容易地移植到不同的 USB 硬件 IP 上。

所有 STM32 USB IP(主机、设备、 OTG、高速和全速) 均由 USBX 通过通用 STM32 HAL 驱动程序 API 透明支持。

2.2 USBX 层次

参考资料:

https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Introduction_to_USBX

USBX 分为三层, 如下图所示:

  • 控制器层:最底层,USB 设备控制器的驱动程序,通常是 HAL 库
  • stack layer:实现 USB 设备的基本操作,比如描述符的操作、使用 endpoint 进行数据传输
  • Class layer:实现各类 USB 设备的操作,比如 HID 设备、音频设备、虚拟串口,给 APP 提供接口

img

在 STM32 的固件中, 可以看到 USBX 目录,比如:

在这里插入图片描述

移植 Controller layer、stack layer、Class layer 并不复杂, 重点在于 2 点:

  • 怎么初始化硬件以确保 Controller layer 可以正常运行
  • 怎么编写 APP:提供设备信息、传输数据

2.3 USBX 的基本配置

USBX 依赖于 Azure®RTOS ThreadX,但是也可以单独使用 USBX,这需要配置。通常在 “ux_user.h”里进行配置,配置项如下:

  • 使用单独模式或 RTOS 模式:
/* Defined, this macro will enable the standalone mode of usbx.  */
#define UX_STANDALONE

当没有定义“UX_STANDALONE”时就是使用 RTOS 模式, 可以使用 ThreadX 提供的互斥 量函数实现阻塞式读写(“blocking”), 比如对于 USB 虚拟串口, 可以使用如下函数:

UINT _ux_device_class_cdc_acm_read(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer,
ULONG requested_length, ULONG *actual_length);

UINT _ux_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, ULONG requested_length, ULONG *actual_length);

这 2 个函数发起数据传输,在传输过程中线程阻塞,传输完成后线程被唤醒。

当定义“UX_STANDALONE”时就是使用单独模式, 不能再使用上面的阻塞函数,而要使 用非阻塞的函数(non-blocke):

UINT _ux_device_class_cdc_acm_read_run(UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
UCHAR *buffer, ULONG requested_length, ULONG *actual_length);

UINT _ux_device_class_cdc_acm_write_run(UX_SLAVE_CLASS_CDC_ACM *cdc_acm,
UCHAR *buffer, ULONG requested_length, ULONG *actual_length);

它们只是发起传输,然后就即刻返回。需要提供回调函数,在回调函数里分辨数据是 否传输完成。

  • 非阻塞模式:
/* Defined, this macro disables CDC ACM non-blocking transmission support. */ //#define UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE

定义 UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE 是,就禁止了“非阻塞模式”, 这时只能使用基于 RTOS 的阻塞函数。

换句话说, 要使用单独模式的非阻塞函数, 就不能定义这个配置项。

  • USB HOST/Device 模式
/* Defined, this value will only enable the host side of usbx.  */
/* #define UX_HOST_SIDE_ONLY */

/* Defined, this value will only enable the device side of usbx.  */
#define UX_DEVICE_SIDE_ONLY

在这里我们定义“UX_DEVICE_SIDE_ONLY”, 仅作为 USB Device。

三、移植 USBX 实现虚拟串口

移植 Controller layer、stack layer、Class layer 并不复杂, 重点在于 2 点:

  • 怎么初始化硬件以确保 Controller layer 可以正常运行
  • 怎么编写 APP:提供设备信息、传输数据

3.1 配置 USB

在这里插入图片描述

3.2 添加 USBX 代码

3.2.1 复制代码

找到固件库,如下:

在这里插入图片描述

把 usbx 整个目录复制到工程“MiddlewaresThird_Party”目录下, 如下:

在这里插入图片描述

3.2.2 添加进工程

需要添加 USBX 的 3 层源码。

先仿照下图添加“Class layer”源码,添加含有“ux_device_class_cdc_acm ”前缀的 C 文件:

在这里插入图片描述

再仿照下图添加“stack layer”源码,可以从文件名的前面看出它们的作用, 比如 “ ux_device_stack ”表示这是 stack 源码,“ ux_utility ”表示这是辅助函数 , “ux_system”表示是这是系统函数:

在这里插入图片描述

最后仿照下图添加“Controller layer”, 添加“ux_dcd_stm32”前缀的 C 文件:

在这里插入图片描述

3.3 添加 USBX APP 代码

参考工程:

  • GIT 仓库:https://github.com/STMicroelectronics/STM32CubeH5.git,

  • 工程路径:

STM32CubeH5\Projects\NUCLEO-H563ZI\Applications\USBX\Ux_Device_HID_CDC_ACM

在网盘资料中, 找到如下目录:

在这里插入图片描述

把 app 文件夹复制到工程的“MiddlewaresThird_Partyusbx”目录下, 如下图所示:

在这里插入图片描述

各个文件的作用为:

  • ux_user.h:配置 USBX

  • ux_stm32_config.h:里面含有配置项, 表示 STM32 支持多少个 endpoint

  • ux_device_descriptors.c/h:USB 虚拟串口的描述符信息

  • ux_device_cdc_acm.c:USB 串口的 Activate/DeActivate 函数

  • app_usbx_device.c:调用 stack layer 函数, 模拟 USB 串口

在工程里添加上述文件, 如下图所示:

在这里插入图片描述

3.4 修改 usb.c

使用 STM32CubeMX 配置 usb 后生成的 usb.c 里,只是初始化了 USB 控制器,并未启动它,也没有跟 USBX 建立联系, 需要修改代码。

代码如下:

23 /* USER CODE BEGIN 0 */
24 #include "ux_port.h"
25 #include "ux_device_descriptors.h"
26 #include "ux_dcd_stm32.h"
27 /* USER CODE END 0 */
/* 省略 */
33 void MX_USB_PCD_Init(void)
34 {
35
36   /* USER CODE BEGIN USB_Init 0 */
37   UINT MX_USBX_Device_Init(void);
38   MX_USBX_Device_Init();
39
40   /* USER CODE END USB_Init 0 */
41
42   /* USER CODE BEGIN USB_Init 1 */
43
44   /* USER CODE END USB_Init 1 */
45   hpcd_USB_DRD_FS.Instance = USB_DRD_FS;
46   hpcd_USB_DRD_FS.Init.dev_endpoints = 8;
47   hpcd_USB_DRD_FS.Init.speed = USBD_FS_SPEED;
48   hpcd_USB_DRD_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
49   hpcd_USB_DRD_FS.Init.Sof_enable = DISABLE;
50   hpcd_USB_DRD_FS.Init.low_power_enable = DISABLE;
51   hpcd_USB_DRD_FS.Init.lpm_enable = DISABLE;
52   hpcd_USB_DRD_FS.Init.battery_charging_enable = DISABLE;
53   hpcd_USB_DRD_FS.Init.vbus_sensing_enable = DISABLE;
54   hpcd_USB_DRD_FS.Init.bulk_doublebuffer_enable = DISABLE;
55   hpcd_USB_DRD_FS.Init.iso_singlebuffer_enable = DISABLE;
56   if (HAL_PCD_Init(&hpcd_USB_DRD_FS) != HAL_OK)
57   {
58     Error_Handler();
59   }
60   /* USER CODE BEGIN USB_Init 2 */
61
62   HAL_PWREx_EnableVddUSB();
63   HAL_PWREx_EnableUSBVoltageDetector();
64
65   HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x00, PCD_SNG_BUF, 0x14);
66   HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x80, PCD_SNG_BUF, 0x54);
67   HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPINCMD_ADDR, PCD_SNG_BUF, 0x94); 68   HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPOUT_ADDR, PCD_SNG_BUF, 0xD4);
69   HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_CDCACM_EPIN_ADDR, PCD_SNG_BUF, 0x114); 70   ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&hpcd_USB_DRD_FS);
71
72   HAL_PCD_Start(&hpcd_USB_DRD_FS);
73
74   /* USER CODE END USB_Init 2 */
75 }

第24~26:包含相关头文件。

第 38 行:调用 USBX 的函数, 添加 USB 串口的支持。

第 62~63 行:使能 USB 控制器的电源。

第 65~69 行:设置 endpoint 的“Packet Buffer Memory”,这个概念可以参考:

http://www.51hei.com/bbs/dpj-40953-1.html。

第 70 行 : 把 STM32 USB 控制器的句柄,传给 USBX 系统 ,

“usbx_stm32_device_controllers”的代码会使用这个句柄来操作硬件。 第 72 行:启动 USB 控制器。

3.5 创建 USBX 任务

使 用 单 独 模 式 (STANDALONE ) 时 , 需 要 创 建 一 个 任 务 , 不 断 运 行 “_ux_system_tasks_run ”函数。以下代码是在 FreeRTOS 的默认任务里运行和这个函数:

26 /* USER CODE BEGIN Includes */
27 #include "stdio.h"
28 #include "draw.h"
29 #include "ux_api.h"
30 /* USER CODE END Includes */
/* 省略 */
195 /* USER CODE END Header_StartDefaultTask */
196 void StartDefaultTask(void *argument)
197 {
198   /* USER CODE BEGIN defaultTask */

199	/* Infinite loop */
200	for(;;)
201	{
202     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);
203     vTaskDelay(500);
204
205     HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);

206	vTaskDelay(500);
207	ux_system_tasks_run();
208	}
209   /* USER CODE END defaultTask */
210 }

第 29 行,包含 USBX 的头文件。

第 207 行, 调用 USBX 的系统函数。

3.6 设置 MDK-ARM 工程

如下图配置:

  • 添加宏开关: UX_INCLUDE_USER_DEFINE_FILE(图中标号 2)
  • 添加头文件目录(图中标号 5)

在这里插入图片描述

3.7 添加使用串口的代码

在“CoreSrcapp_freertos.c”里添加 USB 串口的发送测试代码:

26 /* USER CODE BEGIN Includes */
27 #include "stdio.h"
28 #include "draw.h"
29 #include "ux_api.h"
30 /* USER CODE END Includes */
/* 省略 */
69 static void SPILCDTaskFunction( void *pvParameters )
70 {
71      char buf[100];
72      int cnt = 0;
73
74      while (1)
75      {
76         sprintf(buf, "USB Serial Send Test : %d\r\n", cnt++);
77         //Draw_String(0, 0, buf, 0x0000ff00, 0);
78
79         int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);
80         ux_device_cdc_acm_send((uint8_t *)buf, strlen(buf), 1000);
81         vTaskDelay(1000);
82      }
83 }

第 29 行:包含头文件。

第 79~80 行:使用 USB 串口发送数据。

在“MiddlewaresThird_Partyusbxappux_device_cdc_acm.c”中,有如下代码:

111 static UINT ux_device_class_cdc_acm_read_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length)
112 {
113     int Draw_String(uint32_t x, uint32_t y, char *str, uint32_t front_color, uint32_t
back_color);
114     if (status == UX_SUCCESS)
115     {
116         data_pointer[length] = '\0';
117         Draw_String(0, 0, (char *)data_pointer, 0x0000ffff, 0);
118     }
119         return 0;
120 }

当 USB 串口收到数据后, ux_device_class_cdc_acm_read_callback 函数被调用。 第 117 行把接收到的数据在 LCD 上显示处来。

3.8 上机实验

烧写运行程序后,接上 USB 线,在电脑上可以识别出 USB 串口,查看设备管理器,可 以看到如下设备:

在这里插入图片描述

使用串口工具打开这个串口, 可以连续不断接收到数据,如下所示:

在这里插入图片描述

在串口工具上发送数据时,在板子的 LCD 上会有显示。

四、虚拟串口源码分析与改造

本节程序源码为“3_程序源码\01_视频配套的源码\ 4-8_虚拟串口源码分析与改造 \uart_usb_freertos.7z”,在上一节代码 uart_usb.7z 的基础上修改得来。

4.1 描述符的设置

在“Middlewares\Third_Party\usbx\app\ux_device_descriptors.c”有设备描述符、 配置描述符、接口描述符、端点描述符的定义。

比如, 设备描述符在如下代码中设置:

在这里插入图片描述
在这里插入图片描述
成功对应上产家ID和产品ID。

配置描述符在如下代码中设置:

img

4.2 数据收发函数

涉及文件为:demo\Middlewares\Third_Party\usbx\app\ux_device_cdc_acm.c。 开发板通过 USB 串口发出数据时, 使用以下函数:

/* 启动发送 */
UINT ux_device_class_cdc_acm_write_with_callback(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, ULONG requested_length);

/* 发送完毕的回调函数 */
static UINT ux_device_class_cdc_acm_write_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, ULONG length);

在这里插入图片描述
在这里插入图片描述
下面是根据初始化函数定义到的位置,需要在初始化中创建二值信号量和队列
在这里插入图片描述

我们将会实现如下函数,它使用“ux_device_class_cdc_acm_write_with_callback ” 来启动发送,然后等待“ux_device_class_cdc_acm_write_callback”唤醒:

int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);

开发板接收到 USB 串口数据时,以下回调函数被调用:

static UINT ux_device_class_cdc_acm_read_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length);

在这里插入图片描述

我们可以改造这个函数, 把接收到的数据写入队列。

4.3 使用 FreeRTOS 改造代码

对于发送, 实现以下函数:启动发送之后阻塞,等待回调函数唤醒或超时。

static UINT ux_device_class_cdc_acm_read_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length);

对于接收, 实现以下函数:把接收到的数据写入队列。

static UINT ux_device_class_cdc_acm_read_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length);

然后提供这个函数:

int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout);

在这里插入图片描述

在这里插入图片描述
顺序:ux_device_cdc_acm_send 启动传输 -> ux_device_class_cdc_acm_write_with_callback 调用此回调函数 -> ux_device_class_cdc_acm_write_callback 发送完成回调函数会被调用并释放信号量 -> pdTRUE == xSemaphoreTake(g_xUSBUARTSend, timeout) 得到信号量被唤醒 -> ux_device_class_cdc_acm_read_callback 此回调函数会被调用 当读取到数据时将数据写入队列中 -> ux_device_cdc_acm_getchar 此函数获取队列中的内容于是主函数调用并打印。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2054561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rhinoceros 8 for Mac/Win:重塑三维建模边界的革新之作

Rhinoceros 8(简称Rhino 8),作为一款由Robert McNeel & Assoc公司开发的顶尖三维建模软件,无论是对于Mac还是Windows用户而言,都是一款不可多得的高效工具。Rhino 8以其强大的功能、广泛的应用领域以及卓越的性能&…

阿里云发送短信功能(Java)

(1)注册用户,并且开通短信套餐 (2) 点击快速学习,然后绑定测试的手机号码。 选用专用测试签名(自定义的话阿里可能会验证什么什么的比较麻烦) 然后在选取调用API (3&…

Excel ——3个实用的随机函数(RAND 、RANDBETWEEN、RANDARRAY)

1、RAND 函数 RAND 函数是一个没有参数的函数:RAND () 返回一个大于等于 0,小于 1 的随机实数。 选中任一单元格,在编辑栏输入公式: 如果要生成 a 与 b 之间的随机数,就用: RAND()*(b-a)a 比如要生成-5…

Docker Compose运行Elasticsearch

前提:确保你已经安装了Docker和Docker Compose 创建项目目录 创建一个目录来存放项目文件 mkdir es cd es 创建docker-compose.yml文件 touch docker-compose.yml version: 3.3 services: elasticsearch: image: docker.elastic.co/elasticsearch/elastics…

mysql windows、Ubuntu安装与远程连接配置

下载 在Windows下安装MySQL需要在官网下载安装包 官网地址www.mysql.com 找到社区下载 选择适用于Windows的MYSQL安装程序 选择自己电脑对应的版本和所要下载的mysql版本一般是5.7版本和8.0版本 按照图片上的选项进行安装 到此就安装完成了 需要自己手动配置环境变量…

光纤猫光功率正常值是多少

光纤猫光功率正常值是多少 1. 光猫发送的光功率正常范围为负15dBm。 2. 光猫接收的光功率理想值为负9dBm至负27dBm。 3. 光模块发射的光功率应为0dBm或正1dBm以上。 4. 光信号通过一级光交箱跳纤会损失1到2dBm,因此到达一级光分箱的主纤光功率为负2dBm。 5. 经过一级…

模拟实现Stack的适配器【栈】【C++】

P. S.:以下代码均在VS2022环境下测试,不代表所有编译器均可通过。 P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。 博主主页:LiUEEEEE                        …

sentinel 02 核心类

01 02. 03. 04. 05. 4.1 4.2 4.3 4.4 5调用链

JavaScript学习笔记(十二):JS Web API

1、Web API - 简介 Web API 是开发人员的梦想。 它可以扩展浏览器的功能它可以极大简化复杂的功能它可以为复杂的代码提供简单的语法 1.1 什么是 Web API? API 指的是应用程序编程接口(Application Programming Interface)。 Web API 是 …

基于SpringBoot的网上宠物店系统

系统背景 在当今快节奏的城市生活中,宠物逐渐成为许多家庭不可或缺的一员,它们不仅是忠诚的伴侣,更是心灵的慰藉。随着宠物市场的日益扩大,人们对于宠物相关服务的需求也日益增长,从宠物食品、玩具到健康护理、训练课程…

SAP-MM-框架协议和货源清单的配合使用

一、业务场景: 某公司一直使用源清单功能,新增框架协议功能, 根据业务需求,公司与供应商签订备货框架协议,供应商要求我司提供单号用于备货使用,但业务部门要求,此单号不能在SRM系统中体现,不能直接做送货单,需要转正式采购订单才能在SRM系统中创建送货单,用于送货…

腾讯云 AI 代码助手四大基础功能介绍

引言 随着技术的不断进步,软件开发者们面临着日益复杂的编程任务和挑战。他们不仅需要处理大量的代码,还要在保证代码质量的前提下,提高开发效率。在这样的背景下,一款能够辅助开发者进行高效编码的工具显得尤为重要。 腾讯云AI…

数字文化产业:融合科技与人文的先锋力量

在当今数字化的时代,数字文化产业正以惊人的速度崛起,成为经济发展和文化繁荣的重要驱动力。那么,究竟什么是数字文化产业呢? 数字文化产业是文化与科技深度融合的产物。它借助先进的数字技术,如大数据、人工智能、虚拟…

【论文分享】通过社交媒体图片和计算机视觉分析城市绿道的使用情况

城市街道为路面跑步提供了环境。本次给大家带来一篇SCI论文的全文翻译!该论文提出了一种非参数方法,使用机器学习模型来预测路面跑步强度。该论文提供了关于路面跑步的实证证据,并突出了规划者、景观设计师和城市管理者在设计适于跑步的城市街…

【数学建模】MATLAB快速入门

文章目录 1. MATLAB界面与基本操作1.1 MATLAB的基本操作 2. MATLAB字符串和文本2.1 string变量2.2 char变量 3. MATLAB的矩阵运算 1. MATLAB界面与基本操作 初始界面: 刚开始的界面只要一个命令行窗口,为了使编辑界面出现我们需要新建一个文件&#xff…

探索 HarmonyOS 的层叠布局:灵活的 Stack 容器

在应用开发中,灵活的布局设计是提高用户体验的关键之一。HarmonyOS 提供了丰富的布局组件,其中层叠布局(Stack Layout)是一个强大的工具,可以帮助开发者轻松实现元素的重叠显示。本文将深入探讨 Stack 容器的功能和应用…

Spring Boot 整合 Spring AI 实现项目接入ChatGPT(OpenAl的调用)

当前各种AI项目层出不穷,但绝大多数都是用python写的,现在Spring开源了Spring AI项目,让Java开发者也可以轻松给自己的springboot项目集成AI能力。目前spring AI正式版本为0.8.1,支持接入openAI、Ollama、Azure openAI、Huggingfa…

流媒体服务器二:2.RTMP协议学习

一 RTMP协议详解 1.总体介绍 RTMP协议是应⽤层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在 基于传输层协议的链接建⽴完成后,RTMP协议也要客户端和服务器通过“握⼿”来建⽴基于传输层链接之 上的…

ES6之Promise对象

【图书介绍】《Node.jsMongoDBVue.js全栈开发实战》-CSDN博客 《Node.jsMongoDBVue.js全栈开发实战(Web前端技术丛书)》(邹琼俊)【摘要 书评 试读】- 京东图书 (jd.com) Promise是ES 6中新增的一种异步编程的解决方案,它可以将异步操作队列…

【电路笔记】-无源衰减器总结

无源衰减器总结 文章目录 无源衰减器总结1、概述2、L-型无源衰减器设计3、T-型无源衰减器设计4、桥接 T 型衰减器设计5、π型无源衰减器设计无源衰减器是一个纯电阻网络,可用于控制输出信号的电平。 1、概述 无源衰减器是一种纯电阻网络,用于削弱或“衰减”传输线的信号电平…