前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >第4章_USB 设备编程

第4章_USB 设备编程

作者头像
韦东山
发布于 2024-06-29 00:23:04
发布于 2024-06-29 00:23:04
33200
代码可运行
举报
文章被收录于专栏:韦东山嵌入式韦东山嵌入式
运行总次数:0
代码可运行

4.1 USB 学习指南

阅读源码时,经常碰到如下术语:

  • HCD(Host Controller Driver)
  • DCD(Device Controller Driver)
  • PCD(Low layer USB Peripheral Control Driver)
  • CDC(Communication Device Class)

4.1 USB 学习指南

USB 本身是一个很庞大、复杂的体系, 本课程的重点在于工业互联, USB 是其中的一个 小小知识点。本章课程的目的在于:能理解 USB 的一些概念,能使用 USB 传输数据。 4.2~4.5 节, 介绍 USB 概念;4.6~4.7 节,移植 USBX 实现 USB 串口功能。

参考资料:

  • 《圈圈教你玩 USB》
  • 官网:https://www.usb.org/documents
  • 我们从官网下载后放在网盘如下目录:
  • ST 官方资料:

https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Introduction_to_USBX过 USB 设备驱动,直接通过 USB 控制器驱动访问 USB 设备。

4.2 USB 系统硬件框架和软件框架

4.2.1 实验现象

现象: 把 USB 设备比如 Android 手机接到 PC

  • 右下角弹出"发现 android phone"
  • 跳出一个对话框, 提示你安装驱动程序
    • 问 1:USB 设备插到电脑上去, 接触到的对方设备是什么? 答 1:是 USB 控制器,是 USB 控制器内嵌的 root hub
    • 问 2. 既然还没有"驱动程序",为何能知道是"android phone"? 答 2. windows 里已经有了 USB 的总线驱动程序, 接入 USB 设备后, 是"总线驱动程序" 知道你是"android phone"、提示你安装的是"设备驱动程序"。USB 总线驱动程序负责:识 别 USB 设备, 给 USB 设备找到对应的驱动程序。
    • 问 3. 为什么一接入 USB 设备, PC 机就能发现它? 答 3. PC 的 USB 口内部,D-和 D+接有 15K 的下拉电阻, 未接 USB 设备时为低电平。USB 设备的 USB 口内部, D-或 D+接有 1.5K 的上拉电阻;它一接入 PC,就会把 PC USB 口的 D-或 D+拉高,从硬件的角度通知 PC 有新设备接入。
    • 问 4. USB 设备种类非常多,为什么一接入电脑, 就能识别出来它的种类? 答 4. PC 和 USB 设备都得遵守一些规范。比如: USB 设备接入电脑后, PC 机会发出"你 是什么"?USB 设备就必须回答"我是 xxx", 并且回答的格式是固定的。USB 总线驱动程序会 发出某些命令想获取设备信息(描述符),USB 设备必须返回"描述符"给 PC。
    • 问 5. PC 机上接有非常多的 USB 设备, 怎么分辨它们? 答 5. 每一个 USB 设备接入 PC 时, USB 总线驱动程序都会给它分配一个编号。 PC 机想 访问某个 USB 设备时,发出的命令都含有对应的编号(地址)。
    • 问 6. USB 设备刚接入 PC 时, 还没有编号; 那么 PC 怎么把"分配的编号"告诉它? 答 6. 新接入的 USB 设备的默认编号是 0,在未分配新编号前, PC 使用 0 编号和它通 信。
4.2.2 硬件框架

在 USB 系统中, 有 2 个硬件概念:

  • USB Host:它跟处理器相连,处理器通过 USB Host 跟各类 USB 设备通信。 USB Host 中 集成有一个 root hub
  • USB Device:这分为两类设备
    • Hub:用来扩展 USB 接口
    • Function:就是普通的 USB 设备,比如 U 盘、声卡等
4.2.3 软件框架

APP 可以通过 USB 设备驱动程序访问 USB 设备,也可以绕过 USB 设备驱动,直接通过 USB 控制器驱动访问 USB 设备。

4.3 软件工程师眼里的 USB 电气信号

参考资料:

  • 《圈圈教你玩 USB》
  • 简书 jianshu_kevin@126.com 的文章
    • https://www.jianshu.com/p/3afc1eb5bd32
    • https://www.jianshu.com/p/cf8e7df5ff09
    • USB 协议(三):https://www.jianshu.com/p/2a6e22194cd3
  • 官网:https://www.usb.org/documents
  • 《usb_20.pdf》的《chapter5 7 Electrical》
  • USB 的 NRZI 信号格式: https://zhuanlan.zhihu.com/p/460018993
  • USB2.0 包 Packet 的组成: https://www.usbzh.com/article/detail-459.html
4.3.1 USB 设备状态切换图

USB 2.0 协议支持 3 种速率: 低速(Low Speed,1.5Mbps)、全速(Full Speed, 12Mbps)、 高速(High Speed, 480Mbps)。

USB Hub、USB 设备, 也分为低速、全速、高速三种类型。 一个 USB 设备, 可能兼容低 速、全速, 可能兼容全速、高速, 但是不会同时兼容低速、高速。

4.3.2 硬件线路

下图是兼容高速模式的 USB 收发器电路图:

USB 连接涉及 Hub Port 和 USB 设备,硬件连接如下:

4.3.3 电子信号

USB 连接线有 4 条: 5V、D+、D-、GND。数据线 D+、D-,只能表示 4 种状态。 USB 协议 中,很巧妙地使用这两条线路实现了空闲(Idle)、开始(SOP)、传输数据(Data)、结束(EOP) 等功能。

4.3.4 低速/全速信号电平
4.3.5 高速信号电平
4.3.6 设备连接与断开
1. 连接

Hub 端口的 D+、D-都有 15K 的下拉电阻,平时为低电平。全速设备内部的 D+有 1.5K 的 上拉电阻, 低速设备内部的 D-有 1.5K 的上拉电阻,连接到 Hub 后会导致 Hub 的 D+或 D-电 平变化,Hub 根据变化的引脚分辨接进来的是全速设备还是低速设备。

高速设备一开始也是作为全速设备被识别的。

全速设备、高速设备连接时, D+引脚的电平由低变高:

低速设备连接时,D-引脚的电平由低变高:

img
img
2. 断开

对于低速、全速设备,接到 Hub 时导致 D-或 D+引脚变为高电平, 断开设备后, D-或 D+ 引脚变为低电平:

img
img

对于高速设备,它先作为全速设备被识别出来,然后再被识别为高速设备。工作于高 速模式时, D+的上拉电阻是断开的,所以对于工作于高速模式的 USB 设备, 无法通过 D+的 引脚电平变化监测到它已经断开。

工作于高速模式的设备, D+、D-两边有 45 欧姆的下拉电阻,用来消除反射信号:

img
img

当断开高速设备后, Hub 发出信号,得到的反射信号无法衰减, Hub 监测到这些信号后 就知道高速设备已经断开,内部电路图如下:

img
img
4.3.7 复位

从状态切换图上看,一个 USB 设备连接后,它将会被供电, 然后被复位。当软件出错 时,我们也可以发出复位信号重新驱动设备。

那么, USB Hub 端口或 USB 控制器端口如何发出复位信号? 发出 SE0 信号,并维持至少 10ms。

USB 设备看到 Reset 信号后,需要准备接收"SetAddress()“请求; 如果它不能回应这个 请求, 就是"不能识别的设备”。

4.3.8 设备速率识别
1. 低速/全速

Hub 端口的 D+、D-都有 15K 的下拉电阻,平时为低电平。全速设备内部的 D+有 1.5K 的 上拉电阻, 低速设备内部的 D-有 1.5K 的上拉电阻,连接到 Hub 后会导致 Hub 的 D+或 D-电

平变化,Hub 根据变化的引脚分辨接进来的是全速设备还是低速设备。

img
img
2. 高速

高速设备必定兼容全速模式, 所以高速设备内部 D+也有 1.5K 的上拉电阻, 只不过这个 电阻是可以断开的: 工作于高速模式时要断开它。

高速设备首先作为全速设备被识别出来,然后 Hub 如何确定它是否支持高速模式? Hub 端口如何监测一个新插入的 USB 设备能否工作于高速模式? 流程如下:

  • 对于低速设备,Hub 端口不会监测它能否工作于高速模式。低速设备不能兼容高速模式。
  • Hub 端口发出 SE0 信号,这就是复位信号
  • USB 设备监测到 SE0 信号后,会发出"a high-speed detection handshake"信号表示自 己能支持高速模式, 这可以细分为一下 3 种情景:
    • 如果 USB 设备原来处于"suspend"状态,它检测到 SE0 信号后, 就发出"a high- speed detection handshake"信号。
    • 如果 USB 设备原来处于"non-suspend"状态,并且处于全速模式, 它检测到 SE0 信 号后, 就发出"a high-speed detection handshake"信号。这个情景,就是一个设备刚插 到 Hub 端口时的情况,它一开始工作于全速模式。
    • 如果 USB 设备原来处于"non-suspend"状态, 并且处于高速模式,它会切换回到全 速模式(重新连接 D+的上拉电阻),然后发出"a high-speed detection handshake"信号。

“a high-speed detection handshake"信号,就是"高速设备监测握手信号”,既然是 握手信号, 自然是有来有回:

  • USB 设备维持 D+的上拉电阻,发出"Chirp K "信号, 表示自己能支持高速模式
  • 如果 Hub 没监测到"Chirp K "信号, 它就知道这个设备不支持高速模式
  • 如果 Hub 监测到"Chirp K “信号后, 如果 Hub 能支持高速模式, 就发出一系列的"Chirp K”、"Chirp J"信号,这是用来通知 USB 设备: Hub 也能支持高速模式。发出一系列的 “Chirp K”、"Chirp J"信号后,Hub 继续维持 SE0 信号直到 10ms。
  • USB 设备发出"Chirp K “信号后,就等待 Hub 回应一系列的"Chirp K”、"Chirp J"信号
    • 收到一系列的"Chirp K"、"Chirp J"信号: USB 设备端口 D+的上拉电阻,使能高速模式
    • 没有收到一系列的"Chirp K"、"Chirp J"信号: USB 设备转入全速模式
img
img
4.3.9 数据信号
img
img
1. 低速/全速的 SOP 和 EOP

SOP:Start Of Packet,Hub 驱动 D+、D-这两条线路从 Idle 状态变为 K 状态。 SOP 中 的 K 状态就是 SYNC 信号的第 1 位数据, SYNC 格式为 3 对 KJ 外加 2 个 K。

EOP:End Of Packet,由数据的发送方发出EOP,数据发送方驱动D+、D-这两条线路, 先设为 SE0 状态并维持 2 位时间, 再设置为 J 状态并维持 1 位时间, 最后 D+、D-变为高阻 状态, 这时由线路的上下拉电阻使得总线进入 Idle 状态。

img
img
2. 高速的 SOP

高速的 EOP 比较复杂,作为软件开发人员无需掌握。

高速模式中,Ide 状态为:D+、D-接地。SOP 格式为: 从 Idle 状态切换为 K 状态。 SOP 中的 K 状态就是 SYNC 信号的第 1 位数据。

高速模式中的 SYNC 格式为:KJKJKJKJ KJKJKJKJ KJKJKJKJ KJKJKJKK,即 15 对 KJ,外 加 2 个 K。

3. NRZI 与位填充

参考文章:USB 的 NRZI 信号格式, https://zhuanlan.zhihu.com/p/460018993

NRZI:Non Return Zero Inverted Code,反向不归零编码。 NRZI 的编码方位为:对于 数据 0,波形翻转;对于数据 1,波形不变。

img
img

使用 NRZI,发送端可以很巧妙地把"时钟频率"告诉接收端: 只要传输连续的数据 0 即 可。在下图中, 低速/全速协议中"Sync Pattern"的原始数据是"00000001",接收端从前面 的 7 个 0 波形就可以算出"时钟频率"。

img
img

使用 NRZI 时, 如果传输的数据总是"1",会导致波形维持不变。如果电平长时间维持 不变, 比如传输 100 位 1 时, 如果接收方稍有偏差,就可能认为接收到了 99 位 1、101 位 1。而 USB 中采用了 Bit-Stuffing 位填充处理,即在连续发送 6 个 1 后面会插入 1 个 0,强 制翻转发送信号,从而让接收方调整频率,同步接收。而接收方在接收时只要接收到连续 的 6 个 1 后,直接将后面的 0 删除即可恢复数据的原貌。 NRZI 数据格式如上图所示。


sidebar_position: 5


4.4 USB 协议层数据格式

参考资料:

  • 《圈圈教你玩 USB》
  • 简书 jianshu_kevin@126.com 的文章
    • https://www.jianshu.com/p/3afc1eb5bd32
    • https://www.jianshu.com/p/cf8e7df5ff09
    • USB 协议(三):https://www.jianshu.com/p/2a6e22194cd3
  • 官网:https://www.usb.org/documents
img
img
  • 《usb_20.pdf》的《chapter5 8 Protocol Layer》
  • USB 的 NRZI 信号格式: https://zhuanlan.zhihu.com/p/460018993
  • USB2.0 包 Packet 的组成: https://www.usbzh.com/article/detail-459.html
4.4.1 硬件拓扑结构
img
img

compound device :多个设备组合起来,通过 HUB 跟 Host 相连

composite device :一个物理设备有多个逻辑设备(multiple interfaces)

在软件开发过程中, 我们可以忽略 Hub 的存在,硬件拓扑图简化如下:

img
img

一个物理设备里面可能有多个逻辑设备, Hos 可以外接多个逻辑设备, 硬件拓扑图如 下:

4.4.2 协议层

要理解协议层、理解数据如何传输,带着这几个问题去看文档、看视频:

  • 如何寻址设备?
  • 如何表示数据方向(读、还是写)
  • 如何确认结果?

提前罗列出答案:

  • USB 系统是一个 Host 对应多个设备, 要传输数据首先要通知设备:
    • 发出 IN 令牌包: 表示想读数据,里面含有设备地址
    • 发出 OUT 令牌包:表示想写数据, 里面含有设备地址
  • 数据阶段:
    • Host 想读数据: 前面发出 IN 令牌包后, 现在读取数据包
    • Host 想发出数据:前面发出 OUT 令牌包后, 现在发出数据包
  • 结果如何?有握手包
    • Host 想读数据, 设备可能未就绪, 就会回应 NAK 包
    • Host 想写数据, 它发出数据后,设备正确接收了, 就回复 ACK 包
4.4.3 字节/位传输顺序

先传输最低位(LSB)。在后续文档中,描述数据时按照传输顺序从左到右列出来

4.4.4 SYNC 域

Host 发出 SOP 信号后, 就会发出 SYNC 信号:它是一系列的、最大传输频率的脉冲,接 收方使用它来同步数据。对于低速/全速设备, SYNC信号是8位数据(从做到右是00000001); 对于高速设备, SYNC信号是32位数据(从左到右是00000000000000000000000000000001)。 使用 NRZI 编码时,前面每个"0"都对应一个跳变。

在很多文档里, 把 SOP 和 SYNC 统一称为"SYNC",它的意思是"SYNC"中含有"SOP"。

4.4.5包格式
img
img

USB 总线上传输的数据以包为单位。 USB 包里含有哪些内容(“域”)?

  • SOP:用来表示包的起始
  • SYNC:用来同步时钟
  • PID:表示包的类型
  • 地址:在 USB 硬件体系中, 一个 Host 对应多个 Logical Device,那么 Host 发出的包, 如何确定发给谁?
    • 发给所有设备:包里不含有设备地址
    • 发给某个设备:包里含有设备地址、端点号
  • 帧号、数据等跟 PID 相关的内容
  • CRC 校验码

发起一次完整的传输, 可能涉及多个包。那么,第 1 个包里含有设备地址、端点号, 后续的包就没必要包含设备地址、端点号。

1. PID 域

注意: 所有的 USB 文档提到的"输入"、“输出”,都是基于 Host 的角度, "输出"表示从 Host 输出到设备,"输入"表示 Host 从设备得到数据。

有哪些 USB 包? 根据包数据里的 PID 的 bit1, bit0 可以分为 4 类:

  • 令牌包(Token):01B
  • 数据包(Data):11B
  • 握手包(Handshake):10B
  • 特殊包(Special):00B

PID 有 4 位,使用 bit1,bit0 确定分类, 使用 bit3,bit2 进一步细分。如下表(来自 《圈圈教你玩 USB》)所示:

img
img

在 USB 包中,PID 域使用 8 位来表示,格式如下:

img
img

前 4 位表示 PID,后 4 位是对应位的取反。接收方发现后 4 位不是前 4 位的取反的话, 就认为发生了错误。

2. 令牌包(Token)

令牌类 的 PID ,起 "通知作用 " ,通知谁 ?SOF 令牌包被用来通 知所有设 备, OUT/IN/SETUP 令牌包被用来通知某个设备。

对于 OUT、IN、SETUP 令牌包, 它们都是要通知到具体的设备, 格式如下:

img
img

USB 设备的地址有 7 位,格式如下:

img
img

USB 设备的端点号有 4 位, 格式如下:

img
img

对于 SOF 包,英文名为"Start-of-Frame marker and frame number"。对于 USB 全速 设备, Host 每 1ms 产生一个帧; 对于高速设备, 每 125us 产生一个微帧, 1 帧里有 8 个微 帧。 Host 会对当前帧号进行累加计数, 在每帧或每微帧开始时, 通过 SOF 令牌包发送帧号。 对于高速设备, 每 1 毫秒里有 8 个微帧,这 8 个微帧的帧号是一样的, 每 125us 发送一个 SOF 令牌包。

SOF 令牌包格式如下:

img
img
3. 数据包

Host 使用 OUT、IN、SETUP 来通知设备:我要传输数据了。数据通过"数据包"进行传 输。

数据包也有 4 种类型:DATA0、DATA1、DATA2、MDATA。其中 DATA2、MDATA 在高速设备 中使用。对软件开发人员来说,我们暂时仅需了解 DATA0、DATA1。

为什么要引入 DATA0、DATA1 这些不同类型的数据包? 为了纠错。

Host 和设备都会维护自己的数据包切换机制,当数据包成功发送或者接收时,数据包 类型切换。当检测到对方使用的数据包类型不对时,USB 系统认为发生了错误。

比如:

  • Host 发送 DATA0 给设备,设备返回 ACK 表示成功接收, 设备期待下一个数据是 DATA1
  • 但是 Host 没有接收到 ACK,Host 认为数据没有发送成功,Host 继续使用 DATA0 发送上 一次的数据
  • 设备再次接收到 DATA0 数据包, 它就知道:哦,这是重传的数据包

数据包格式如下:

img
img

对于全速设备, 数据包中的数据做大是 1023 字节;对于全速设备, 数据包中的数据做 大是 1024 字节。

4. 握手包

握手包有 4 类: ACK、NAK、STALL、NYET

  • ACK:数据接收方用来回复发送方,表示正确接收到了数据并且有足够的空间保存数据。
  • NAK:Host 发送数据给设备时, 设备可以回应 NAK 表示"我还没准备好,没办法接收数据"; Host 想读取设备的数据时, 设备可以回复 NAK 表示"我没有数据给你"。
  • STALL:表示发生了错误,比如设备无法执行这个请求(不支持该断点等待)、断点已经挂起。设备返回 STALL 后,需要主机进行干预才能接触 STALL 状态。
  • NYET:仅适用于高速设备。 Host 可以发出 PING 包用来确认设备有数据,设备可以回应 NYET 表示"还没呢"。Hub 也可以回应 NYET 表示低速/全速传输还没完结。
4.4.6 传输细节
1. 传输(Transfer)和事务(Transaction)

USB 传输的基本单位是包(Packet),包的类型由PID 表示。 一个单纯的包,是无法传输 完整的数据。

为什么?比如想输出数据,可以发出 OUT 令牌包, OUT 令牌包可以指定目的地。但是数 据如何传输呢? 还需要发出 DATA0 或 DATA1 数据包。设备收到数据后, 还要回复一个 ACK 握手包。

所以,完整的数据传输, 需要涉及多个包:令牌包、数据包、握手包。这个完整的数 据传输过程,被称为事务(Transaction)。

有些事务需要握手包,有些事务不需要握手包,有些事务可以传输很大的数据,有些 事务只能传输小量数据。

  • 有四类事务:
    • 批量事务:用来传输大量的数据,数据的正确性有保证,时效没有保证。
    • 中断事务:用来传输周期性的、小量的数据, 数据的正确性和时效都有保证。 ③ 实时事务:用来传输实时数据, 数据的正确性没有保证,时效有保证。
    • 建立事务:跟批量事务类似,只不过令牌包是 SETUP 令牌包。
  • 有四类传输(Transfer):
    • 批量传输:就是使用批量事务实现数据传输, 比如 U 盘。
    • 中断传输:就是使用中断事务实现数据传输, 比如鼠标。
    • 实时传输:就是使用实时事务实现数据传输, 比如摄像头。
    • 控制传输:由建立事务、批量事务组成,所有的 USB 设备都必须支持控制传输, 用于" 识别/枚举"
  • 暂时记住这个关系:
    • BIT 组成域(Field)
    • 域组成包(Packet)
    • 包组成事务(Transaction)
    • 事务组成传输(Transfer)
2. 过程(stage)和阶段(phase)

事务由多个包组成, 比如 Host 要发送数据给设备,这就会涉及很多个包:

  • Host 发出 OUT 令牌包, 表示要发数据给哪个设备
  • Host 发出 DATA0 数据包
  • 设备收到数据后, 回应 ACK 包

这个完整的事务涉及 3 个包(Packet),分为 3 个阶段(Phase):

  • 令牌阶段(Token phase):由令牌包实现
  • 数据阶段(Data phase):由数据包实现
  • 握手阶段(Handshake phase):由握手包实现

事务由包组成, 这些包分别处于 3 个阶段(phase):令牌阶段,数据阶段, 握手阶段。

对于批量传输、中断传输、实时传输,它们分别由一个事务组成,不再细分为若干个 过程。

但是控制传输由多个事务组成,这些事务分别处于 3 个过程: 建立过程(stage)、数据 过程(stage)、状态过程(stage)。

总结起来就是:

  • 控制传输由多个过程(stage)组成, 每个过程由一个事务来实现
  • 每个事务由多个阶段(phase)组成, 每个阶段有一个包来实现
3. 批量传输

批量传输用批量事务来实现,用于传输大量的数据, 数据的正确性有保证, 时效没有 保证。

批量事务由 3 个阶段(phase)组成: 令牌阶段、数据阶段、握手阶段。每个阶段都是一 个完整的包,含有 SOP、SYNC、PID、EOP。

下图中各个矩形框就对应一个完整的包。

img
img

《圈圈教你玩 USB》中有详细的示例:

img
img
4.中断传输

中断传输用中断事务来实现,用于传输小量的、周期性的数据,数据的正确性和时效 都有保证。

中断事务由 3 个阶段(phase)组成: 令牌阶段、数据阶段、握手阶段。每个阶段都是一 个完整的包,含有 SOP、SYNC、PID、EOP。

下图中各个矩形框就对应一个完整的包。

img
img

中断事务跟批量事务非常类似,Host 使用它来周期性地读数据、写数据。

以鼠标为例,我们需要及时获得鼠标的数据, 不及时的话你会感觉鼠标很迟钝。但是 USB 协议中并没有中断功能,它使用"周期性的读、写"来实现及时性。具体过程如下:

  • Host 每隔 n 毫秒发出一个 IN 令牌包
  • 鼠标有数据的话,发出 DATA0 或 DATA1 数据包给 Host;鼠标没有数据的话,发出 NAK 给 Host。

中断事务的优先级比批量事务更高,它要求实时性,而批量事务不要求实时性。

5.实时传输

实时传输用实时事务来实现, 用于传输实时数据, 对数据的正确性没有要求。

实时事务由 2 个阶段(phase)组成: 令牌阶段、数据阶段。每个阶段都是一个完整的包, 含有 SOP、SYNC、PID、EOP。

实时事务不需要握手阶段,一个示例的场景是:为了传输摄像头的实时数据,偶尔的 数据错误是可以忍受的,大不了出现短暂的花屏。如果为了解决花屏而重传数据, 那就会

导致后续画面被推迟,实时性无法得到保证。

下图中各个矩形框就对应一个完整的包。

img
img

实时事务跟中断事务非常类似,Host 也会周期性的发起实时事务,主要区别在于:

  • 实时事务不要求准确性,没有握手阶段
  • 实时事务传输的数据量比较大, 中断事务传输的数据量比较小
6. 控制传输

在使用批量传输时, 使用 IN 令牌包或 OUT 令牌包表示数据传输方向。

控制传输的令牌包永远是 SETUP,怎么分辨是读数据, 还是写数据? 发出 SETUP 令牌包 后,还要发出 DATA0 数据包,根据数据的内容来确定后续是读数据,还是写数据。这个过 程称为"建立事务"(SETUP Transaction)

但是控制传输由多个事务组成,这些事务分别处于 3 个过程: 建立过程(stage)、数据 过程(stage)、状态过程(stage)。

  • 建立过程(stage),使用 SETUP 事务:Host 发出 SETUP 令牌包、DATA0 数据包、得到 ACK 握手包
  • 数据过程(stage),使用批量事务:
    • 对于输出:Host 发出 OUT 令牌包,发出 DATA0、DATA1 数据包、得到 ACK 握手包
    • 对于输入:Host 发出 IN 令牌包,读到 DATA0、DATA1 数据包、发出 ACK 握手包 ③ 状态过程(stage),使用批量事务:
    • 对于输出:Host 发出 IN 令牌包,读到 DATA1 数据包,发出 ACK 握手包 b. 对于输入:Host 发出 OUT 令牌包,发出 DATA1 数据包,等待 ACK 握手包
img
img

上图中的每一个方框,都是一个完整的事务, 含有: Token Packet、Data Packet、 Handshake Packet。

4.4.7 使用工具体验数据格式

LeCroy(力科)成立于 1964 年, 是一家专业生产示波器厂家。旗下生产有数字示波器、 SDA 系列数字示波器、混合信号示波器、模块化仪器、任意波形发生器。

官网是:https://teledynelecroy.com/,似乎无法注册新用户,无法下载软件。 可以在搜索引擎里搜"usbprotocolsuite"。

安装"usbprotocolsuite"后, 可以在文档目录里找打很多示程序(后缀名为 usb):

img
img

使用"usbprotocolsuite"打开这些文件,即可体验 USB 数据传输:

img
img

4.5 USB 描述符

4.5.1
1. USB 设备状态切换图
img
img
img
img
4.5.2 标准设备请求
1.SETUP事务的数据格式

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

img
img

在"setup 事务"中,

  • SETUP 令牌包:用来通知设备, “要开始传输了”
  • DATA0 数据包:它含有固定的格式, 用来告诉设备"是读还是写"、“读什么”、“写什么”

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

img
img
2. 标准设备请求

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

img
img

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

img
img
3. 设备/配置/接口/端点

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

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

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

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

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

4.5.3 描述符

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

一个 USB 设备:

  • 只有一个设备描述符:用来表示设备的 ID、它有多少个配置、它的端点 0一次最大能传 输多少字节数据
  • 可能有多个配置描述符:用来表示它有多少个接口、供电方式、最大电流
  • 一个配置描述符下面,可能有多个接口描述符:用来表示它是哪类接口、有几个设置 (Setting)、有几个端点
  • 一个接口描述符符下面,可能有多个端点描述符: 用来表示端点号、方向(IN/OUT)、类 型(批量/中断/同步)

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

img
img
1. 设备描述符
img
img
2. 配置描述符
img
img
3. 接口描述符
img
img
4. 端点描述符
img
img
5.示例

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
4.5.4 设备枚举过程示例

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

  • 使用控制传输,读取设备信息(设备描述符):第一次读取时, 它只需要得到 8 字节数据, 因为第 8 个数据表示端点 0 能传输的最大数据长度。
img
img
  • Host 分配地址给设备, 然后把新地址发给设备:
img
img
  • 使用新地址, 重新读取设备描述符, 设备描述符长度是 18:
img
img
  • 读取配置描述符: 它传入的长度是 255,想一次性把当前配置描述符、它下面的接口描 述符、端点描述符全部读出来
img
img
  • 读取字符描述符
img
img

4.6 USBX 组件

4.6.1 Azure RTOS 介绍

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

img
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 透明支持。

4.6.2 USBX 层次

参考资料:

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

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

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

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

img
img

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

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

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

  • 使用单独模式或 RTOS 模式:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* Defined, this macro will enable the standalone mode of usbx.  */
#define UX_STANDALONE

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);

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

  • 非阻塞模式:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 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 模式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 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。

4.7 移植 USBX 实现虚拟串口

本节程序源码为“3_程序源码01_视频配套的源码 4-7_移植 USBX 实现虚拟串口 uart_usb.7z”,在上一节代码 uart_rtos.7z 的基础上修改得来。

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

  • 怎么初始化硬件以确保 Controller layer 可以正常运行
  • 怎么编写 APP:提供设备信息、传输数据
4.7.1 配置 USB
img
img
4.7.2 添加 USBX 代码
1. 复制代码

找到固件库,如下:

img
img

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

img
img
2. 添加进工程

需要添加 USBX 的 3 层源码。

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

img
img

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

img
img

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

img
img
4.7.3 添加 USBX APP 代码

参考工程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
STM32CubeH5\Projects\NUCLEO-H563ZI\Applications\USBX\Ux_Device_HID_CDC_ACM

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

img
img

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

img
img

各个文件的作用为:

  • 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 串口

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

img
img
4.7.4 修改 usb.c

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

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 }

第 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 控制器。

4.7.5 创建 USBX 任务

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 的系统函数。

4.7.6 设置 MDK-ARM 工程

如下图配置:

  • 添加宏开关: UX_INCLUDE_USER_DEFINE_FILE(图中标号 2)
  • 添加头文件目录(图中标号 5)
img
img
4.7.7 添加使用串口的代码

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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”中,有如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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, 0x0000ff00, 0);
118     }
119         return 0;
120 }

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

4.7.8 上机实验

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

img
img

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

img
img

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

4.8 虚拟串口源码分析与改造

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

4.8.1 描述符的设置

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

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

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

4.8.2 数据收发函数

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 启动发送 */
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”唤醒:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.8.3 使用 FreeRTOS 改造代码

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);

然后提供这个函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout);
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
数电实现八路抢答器Proteus仿真,74LS148等,含论文
3.抢答器具有数据锁存和显示功能。抢答开始后,如有选手抢答成功,选手编号立即锁存,数码管显示该选手编号,同时倒计时停止,蜂鸣器发出提示音;
蒋宇智
2024/04/03
1.2K0
数电实现八路抢答器Proteus仿真,74LS148等,含论文
基于单片机的8路抢答器系统设计(数显),仿真与代码
3)抢答器具有锁存与显示功能。即选手按动按钮,锁存相应的编号,并在优先抢答选手的编号一直保持到主持人将系统清除为止;
蒋宇智
2024/04/12
9480
基于单片机的8路抢答器系统设计(数显),仿真与代码
数电课设 八路抢答器设计详解
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/01
7220
基于51单片机的八路抢答器设计开题报告_8路抢答器设计51单片机
随着科学技术的发展和普及,各种各样的竞赛越来越多,其中抢答器的作用也越来越重要。本文设计出以STC89C52RC单片机为核心的八路抢答器。
全栈程序员站长
2022/11/01
5330
基于51单片机的八路抢答器设计开题报告_8路抢答器设计51单片机
设计分享|单片机8路抢答器
51单片是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C51为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。具有以下标准功能:8k字节Flash,512字节RAM,32 位I/O 口线,看门狗定时器,内置4KB EEPROM,MAX810复位电路,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口。另外 STC89X51 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。最高运作频率35Mhz,6T/12T可选。
电子工程师成长日记
2022/07/27
6390
设计分享|单片机8路抢答器
八路抢答器系统51单片机设计【附Proteus仿真、C程序、原理图及PCB文件、元器件清单和论文等】「建议收藏」
设计要求 1)抢答器同时供8名选手或2个代表队比赛,分别用8个按钮S0-S7表示; 2)设置一个系统清除和抢答控制开关S,该开关由主持人控制; 3)抢答器具有锁存与显示功能。即选手按动按钮,锁存相应的编号,并在优先抢答选手的编号一直保持到主持人将系统清除为止; 4)抢答器具有定时抢答功能,且一次抢答的时间由主持人设定(如30s等)。当主持人启动“开始”按键后,定时器进行减计时,同时扬声器发出短暂的声响,声响持续时间为0.5s左右; 5)参赛选手在设定时间内进行抢答,抢答有效,定时器停止工作,显示器上显示选手编号和抢答时间,并保持到主持人将系统清除为止; 6)如果定时时间到,无人抢答,本次抢答无效,系统报警并禁止抢答,定时显示器上显示00; 7)设计出软件编程方法,并写出源代码; 8)主机与从机实现无线抢答; 9)用Proteus进行仿真,Altium Designer绘制原理图和PCB;
全栈程序员站长
2022/10/02
1.7K0
八路抢答器系统51单片机设计【附Proteus仿真、C程序、原理图及PCB文件、元器件清单和论文等】「建议收藏」
四路抢答器c语言程序_八路抢答器原理讲解
uc code table[]={0x3f,0x06,0x5b,0x4f,0x66,
全栈程序员站长
2022/11/01
4130
设计分享|单片机抢答器(汇编)
主持人按下抢答按键后,进入抢答模式,8位选手开始抢答,数码管显示抢答成功的选手号码,主持人按键再次按下后复位。
电子工程师成长日记
2022/07/27
5210
设计分享|单片机抢答器(汇编)
51单片机八路抢答器proteus仿真
由于51单片机小板,按键比较少,还有一些功能上的缺陷,所以说无法完成八路抢答器,所以我们用proteus仿真,代码与实验结果如下:
全栈程序员站长
2022/11/01
4920
数字电路-5路呼叫显示和8路抢答器
​本内容涉及两个电路,分别为5路呼叫显示电路和8路抢答器,包含Multisim仿真原文件,对掌握FPGA做个铺垫。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易!
爱上电路设计
2024/05/28
3000
数字电路-5路呼叫显示和8路抢答器
基于51单片机的八路抢答器设计_单片机八路抢答器课程设计
写一下寒假做的51小项目,本次是基于AT89C51的八路抢答器,课设水平难度。 具体说明:硬件分为两部分,主持人主控部分和选手使用部分。可以实现:按动开始可以开启程序或者开启答题倒计时,按动复位可以实现归零;八个选手各有一个按键,按下即可抢答,与此同时,蜂鸣器响一秒钟,选手的LED点亮。在答题时间还剩十秒钟时,发出提示音,时间耗尽时,所有LED点亮,蜂鸣器鸣响。当抢答倒计时结束仍没有选手抢答,所有LED点亮,同时蜂鸣器报警一秒钟,之后主持人可以复位重新开始。
全栈程序员站长
2022/11/01
6740
基于51单片机的八路抢答器设计_单片机八路抢答器课程设计
单片机八路抢答器计设计_基于单片机的三路抢答器设计
二、基本要求: 利用8051单片机中断系统,制作一个有8个按键的比赛抢答器。在有人按键时进行对应选手显示。 三、设计任务: 1.设计硬件电路,画出电路原理图; 2.画出程序流程图; 3.编制程序,写出源程序代码; 4.写出5000字的详细说明书,要求字迹工整,原理叙述正确,会计算主要元器件的一些参数,并选择元器件; 5.个人总结。 四、参考资料: 1.教材; 2.单片机实验指导书》 **
全栈程序员站长
2022/11/01
6330
单片机八路抢答器计设计_基于单片机的三路抢答器设计
基于单片机的八路抢答器设计论文_抢答器的程序流程图
文末下载完整资料 1.1八路扫描式抢答器的概述     本文介绍的八路数显抢答器具有电路简单、成本较低、操作方便、灵敏可靠等优点,经使用效果良好, 具有较高的推广价值。无线遥控抢答器,它由8个发射器和1个接收器组成,可用于8组或8组以下的智力竞赛中。比赛前,将参赛组从0至7编号,每组发给对应的一个发射器。将接收器放于各组中央或前方。主持人按一下启动键后,抢答开始。此后,哪一组最先按下发射器上的抢答键,接收器就立即显示该组的组号并锁定,同时发出3次清脆的“叮咚”声。以后,按下任何一路抢答键均不起反映。只有主持人再次按动启动键后,才能进行下一次抢答该电路由直流稳压电源、抢答器、超时报警与电子计分四部分组成。 1.2本设计任务及要求     任务:设计一个供8名选手参加八路扫描式抢答器。 1.3系统主要功能     每名选手有一个抢答按钮,按钮的编号与选手的编号相对应,抢答器具有第一个抢答信号的鉴别和数据锁存、显示的功能。抢答开始后,若有选手按抢答按钮,刚该选手指示灯亮,并在数码管上显示相应编号,扬声器发出音响提示。同时,电路应具备自锁功能,禁止其他选手再抢答,优先抢答选手的编号一直保持到主持人将系统清0 为止。抢答器具有计分、显示功能。预置分数可由主持人设定,并显示在每名选手的计分牌上,选手答对加10分,答错扣10分。抢答器具有定时抢答的功能。一次抢答的时间由主持人设定,在主持人发出抢答指令后,定时器立即进行减计时,并在显示器上显示,同时扬声器发出短暂声响,声响时间持续0.5s左右。选手在设定的时间内进行抢答,抢答有效,定时器停止工作,显示器显示选手编号和抢答时刻的时间,并保持到主持人将系统清0为止。                        第2节 系统硬件设计 2.1芯片的选择    本设计使用到的元器件包括:8051芯片、数码LED显示器、七段LED数码管的译码。 2.2工作原理    基于这个设计的上述要求,根据功能要求,须设计有抢答电路、译码显示电路、主持人控制电路、定时电路、报警电路,各个电路都有其自己的功能。通过复位按键FW,电路进入就绪状态,等待抢答。首先由主持人根据题目的难易程度,可以用“JIA SHI”和“JIAN SHA”两个按键,设定时间在(0S-99S)之间,然后再由主持人发布抢答命令(按下KS按键)同时发光二极管随即变亮,当看到二极管亮,进入倒计时状态和抢答状态。在电路中“S1-S8”为8路抢答器的8个按键,如果有人按下按键,程序就会判断是谁先按下的,然后从P2口输出抢答者号码的七段码值,经GAL16V8驱动,送到码管显示,并封锁键盘,保持刚才按键按下时刻的时间,禁止其他人按键的输入,从而实现了抢答的功能。如果在设定的时间中没有一个人按下按键,一到时间,则产生报警信号已经超时,不可以抢答。当要进行下一次的抢答时,由主持人先按一下复位按键FW,电路复位,进入下一次抢答的就绪状态。 2.3系统的硬件构成及功能 2.3.1 抢答器的电路框图   &emsp如图11、1所示为电路框图。其工作原理为:接通电源后,主持人将开关拨到“清除”状态,抢答器处于禁止状态,编号显示器灭灯,定时器显示设定时间;主持人将开关置,“开始”状态,宣布“开始”抢答器工作。定时器倒计时,扬声器给出声响提示。选手在定时时间内抢答时,抢答器完成:优先判断、编号锁存、编号显示、扬声器提示。当一轮抢答之后,定时器停止、禁止二次抢答、定时器显示剩余时间。如果再次抢答必须由主持人再次操作”清除”和”开始”状态开关。
全栈程序员站长
2022/11/01
8730
基于单片机的八路抢答器设计论文_抢答器的程序流程图
数电设计-八路抢答器
设计一个能支持八路抢答的智力竞赛抢答器;主持人按下开始抢答的按键后,有短暂的报警声提示抢答人员抢答开始且指示灯亮表示抢答进行中;在开始抢答后数码管显示30秒倒计时;有抢答人员按下抢答键后,在数码管上显示抢答成功人员的编号,倒计时暂停,同时后续抢答人员的抢答将无效;当主持人再次按下按键回到复位状态,倒计时的数码管保持显示30,显示人员编号的数码管灭,指示灯灭。
全栈程序员站长
2022/07/22
3.1K0
数电设计-八路抢答器
数字电子技术课程设计八路抢答器报告_八路抢答器课程设计参考
做的东西还是有一些bug,到最后答辩完事之后就开始复习期末考试了,没时间再整 有错误请指正
全栈程序员站长
2022/11/01
1.2K0
数字电子技术课程设计八路抢答器报告_八路抢答器课程设计参考
基于51单片机的流水灯设计
设计思路一(未用中断): 8个LED灯正极解电源,负极接单片机I/O口。 死循环:设置P2口为11111110,使用左移函数,循环七次。 同时每次位移中间加入延时函数。 三个按键:A按键启动、B按键控制不同流水速度(低中高)、C按键控制流水灯暂停蜂鸣器长响: 思路一:设置一个变量i,起初为0,按下A键后为1;当i为1进入死循环 设置变量j,按下B,j++,当j大于3,j=j-3;使用j*1000,来空置循环函数的延时时间。设置bit变量s=0,按下按键C,s++,当s=1,j进入循环蜂鸣器响,s=0退出循环。 在每次延时时检查按键
全栈程序员站长
2022/08/18
8420
基于51单片机的流水灯设计
八路抢答器单片机c语言程序_八路抢答器单片机c语言程序
uchar code table[]={0xc0,0xf9,0xa4,0xb0,0x99,
全栈程序员站长
2022/11/01
1K0
51单片机设计8位抢答器_51单片机八路抢答器原理图
毕业设计(论文)题 目:基于 51 单片机八路抢答器的设计 系 部: 专 业: 学 号: 学生姓名: 指导教师姓名: 指导教师职称: 2013 年 xx 月 xx 日XXXXXXXX 学院(论文)I摘 要随着科学技术的发展和普及,各种各样的竞赛越来越多,其中抢答器的作用也就显而易见。目前很多抢答器基本上采用小规模数字集成电路设计,使用起来不够理想。因此设计一更易于使用和区分度高的抢答器成了非常迫切的任务。现在单片机已进入各个领域,以其功耗小、智能化而著称,所以若利用单片机来设计抢答器,便使以上问题得以解决.针对以上情况,本文设计出以 STC89C52RC 单片机为核心的八路抢答器。我们采用了数字显示器直接指示,自动锁存显示结果,并自动复位的设计思想,它能根据不同的抢答输入信号,经过单片机的控制处理并产生不同的与输入信号相对应的输出信号,最后通过 LED 数码管显示相应的路数,即使两组的抢答时间相差几微秒,也可分辨出是哪组优先按下的按键,它充分利用了单片机系统的优点,具有结构简单、功能强大、可靠性好、实用性强的特点。本设计是以八路抢答为基本理念。考虑到依需设定限时回答的功能,利用 51单片机及外围接口实现的抢答系统,利用单片机的定时器/计数器定时和记数的原理,将软、硬件有机地结合起来,使得系统能够正确地进行计时,同时使数码管能够正确地显示时间。用开关做键盘输出,扬声器发生提示。同时系统能够实现:在抢答中,只有开始后抢答才有效,如果在开始抢答前抢答为犯规;满时后系统计时自动复位及主控强制复位;按键锁定,在有效状态下,按键无效非法。【关键词】STC89C52RC 共阴数码管 按键 蜂鸣器目录XXXXXXXX 学院(论文)II前 言 1第一章 工作原理 21.1 设计目标 .21.1.1 基本功能 .21.1.2 主要技术参数 .2第二章 硬件设计与原理 32.1 总设计框图 32.2 硬件设计分析 32.2.1 电源的设计 32.2.2 单片机最小系统 42.2.3 数码管显示电路 102.2.4 按键输入电路 112.2.5 报警与指示电路 14第三章 软件设计与分析 153.1 软件设计的组成 153.2 各部分软件分析 153.2.1 延时子函数 153.2.2 初始化子函数 153.2.3 开始键扫描子函数 163.2.4 选手抢答按键扫描子函数 173.2.5 显示子函数 203.2.6 调整时间键扫描子函数 243.2.7 定时器 0 中断子函数 263.2.8 定时器 1 中断子函数 273.2.9 主函数 283.3 总源程序 30第四章 软件仿真 464.1 PROTEUS 简介 .464.2 仿真图 .484.3 原理图 .494.4 元件清单 .50XXXXXXXX 学院(论文)III4.5 仿真结果图 51总结 54致 谢 56参考文献 57XXXXXXXX 学院(论文)1前 言最近几年来,随着科技的飞速发展,单片机领域正在不断的走向社会各个角落,还带动传统控制检测日新月异更新。在实时运作和自动控制的单片机应用到系统中,单片机如今是作为一个核心部件来使用,仅掌握单片机方面知识是不够的,还应根据其具体硬件结构,以及针对具体应用对象特点的软件结合,加以完善。 “单片机原理及应用课程设计”是电子类专业的学科基础科,它是继“汇编语言程序设计” , “接口技术”等课程之后开出的实践环节课程。XXXXXXXX 学院(论文)2第一章 工作原理1.1 设计目标1.1.1 基本功能1、同时供 8 名选手比赛,分别用 8 个按钮 K1 ~ K8 表示。2、设置一个系统抢答控制开关 K0,该开关由主持人控制。3、抢答器具有锁存与显示功能。即选手按动按钮,锁存相应的编号,扬声器发出声响提示,并在七段数码管上显示选手号码。选手抢答实行优先锁存,优先抢答选手的编号一直保持到主持人将系统清除为止。4、抢答器具有定时抢答功能,且一次抢答的时间由主持人设定(如 30 秒) 。当主持人启动“开始“键后,定时器进行减计时。5、参赛选手在设定的时间内进行抢答,抢答有效,定时器停止工作,显示器上显示选手的编号和抢答的时间,并保持到主持人将系统清除为止。在这段时间如果定时时间已到,无人抢答,本次抢答无效,系统报警并禁止抢答,定时显示器上显示 00。1.1.2 主要技术参数 1、在抢答中,只有开始后抢答才有效,如果在开始抢答前抢答为犯规。2、抢答限定时间和回答问题的时间是在 10~60s 设定。3、可以显示是哪位选手有效抢答和无效抢答,正确按键后有音提示。4、抢答时间和回答问题时间倒记时显示,时间完后系统自动复位。5、按键锁定,在有效状态下,按键无效非法。XXXXXXXX 学院(论文)3第二章 硬件设计与原理以 STC89C52RC 单片机为核心,起着控制作用。系统包括数码管显示电路、复位电路、时钟电路、按键输入电路和蜂鸣器报警电路。设计思路分为六
全栈程序员站长
2022/11/01
6350
51单片机设计8位抢答器_51单片机八路抢答器原理图
八路抢答器一个数码管C语言,八路抢答器设计 – 八路抢答器电路设计方案汇总(五款模拟电路设计原理及工作原理详细)…「建议收藏」
在许多比赛活动中,为了准确、公正、直观地判断出第一抢答者,通常设置一台抢答器,通过数显、灯光或音响等多种手段指示出第一抢答者。
全栈程序员站长
2022/11/01
1K0
八路抢答器一个数码管C语言,八路抢答器设计 – 八路抢答器电路设计方案汇总(五款模拟电路设计原理及工作原理详细)…「建议收藏」
基于51单片机的多功能八路抢答器[通俗易懂]
1.功能介绍 多功能八路抢答器是基于51单片机来设计的,除了可以实现最基本功能——8路抢答外,还具有自动处理犯规选手,抢答时间调整,还可以进行答题,计分,并且可以查询或修改分数。
全栈程序员站长
2022/11/01
9670
基于51单片机的多功能八路抢答器[通俗易懂]
推荐阅读
相关推荐
数电实现八路抢答器Proteus仿真,74LS148等,含论文
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 4.1 USB 学习指南
    • 4.1 USB 学习指南
  • 4.2 USB 系统硬件框架和软件框架
    • 4.2.1 实验现象
    • 4.2.2 硬件框架
    • 4.2.3 软件框架
  • 4.3 软件工程师眼里的 USB 电气信号
    • 4.3.1 USB 设备状态切换图
    • 4.3.2 硬件线路
    • 4.3.3 电子信号
    • 4.3.4 低速/全速信号电平
    • 4.3.5 高速信号电平
    • 4.3.6 设备连接与断开
    • 4.3.7 复位
    • 4.3.8 设备速率识别
    • 4.3.9 数据信号
  • 4.4 USB 协议层数据格式
    • 4.4.1 硬件拓扑结构
    • 4.4.2 协议层
    • 4.4.3 字节/位传输顺序
    • 4.4.4 SYNC 域
    • 4.4.5包格式
    • 4.4.6 传输细节
    • 4.4.7 使用工具体验数据格式
  • 4.5 USB 描述符
    • 4.5.1
    • 4.5.2 标准设备请求
    • 4.5.3 描述符
    • 4.5.4 设备枚举过程示例
  • 4.6 USBX 组件
    • 4.6.1 Azure RTOS 介绍
    • 4.6.2 USBX 层次
    • 4.6.3 USBX 的基本配置
  • 4.7 移植 USBX 实现虚拟串口
    • 4.7.1 配置 USB
    • 4.7.2 添加 USBX 代码
    • 4.7.3 添加 USBX APP 代码
    • 4.7.4 修改 usb.c
    • 4.7.5 创建 USBX 任务
    • 4.7.6 设置 MDK-ARM 工程
    • 4.7.7 添加使用串口的代码
    • 4.7.8 上机实验
  • 4.8 虚拟串口源码分析与改造
    • 4.8.1 描述符的设置
    • 4.8.2 数据收发函数
    • 4.8.3 使用 FreeRTOS 改造代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档