STM32USB学习

STM32USB学习

正点原子"手把手教你学STM32"教学视频,198-199讲学习记录。

视频地址:点这里

一、USB简介

1.1 USB是什么?

对外供电超过500ma时,需要外供电。

D+D-电压值应该为3V。

主控制器和根集线器的USB版本,决定总的带宽。加集线器之后,公用总的带宽。

一根线ID,决定本设备是主机还是从机。

电脑是主机。

手机是从机,但是也有主机的功能。

STM32可以是主机(读写U盘),也可以是从机(当作 电脑的鼠标)。

1.2 USB2.0拓扑结构

HUB也属于一个USB设备。

设备为1-127个。没有0。

一层之间的距离为5m,最大6层,所以最大支持30m。

1.3 USB2.0电气特性



1.4USB学习资料

协议最麻烦。

正点原子开发板资料中有"USB学习资料"文件夹。

TI的DSPC2000中也提供了USB驱动库。

ST的USB库用于STM32。

探索者资料最全。

二、STM32的USB介绍

介绍STM32USB外设。

2.1 STM32的USB外设特性

具体特性需要看参考手册。

2.2 STM32USB外设框图


使用内部的PHY芯片

使用内部的PHY芯片,外部ULPI PHY没接。

2.3 开发板硬件连接

TYPEC的从机接口。

2.4 STM32 ST USB驱动库简介

分device库和host库,里面有Class和core库。设备分类和核心代码。

看一下USB设备库的架构。

2.5 SUB相关实验(探索者V3)

三、USB读卡器(slave)实验

STM32USB外设作为从机。

以探索者V3 HAL库为例。

STM32USB外设作为从机连接电脑,当作读卡器,SD卡作为卡。

步骤一 :查看ST官方的MSC例程

注意:使用的是全速的FS,而不是HS。

步骤二:拷贝工程所需要的文件

将SD卡工程复制,粘贴,命名为读卡器实验。

一个磁盘是SD卡,另一个磁盘是SPI FLASH,所以还需要再拷贝SPI文件夹和NorFlash文件夹。

步骤三:SDK工程设置

工程结构

步骤四:修改usbd_storage.c文件

都是对usbd_storage.c文件进行修改的。
第8步,最大逻辑单元个数为2个,一个SD卡,一个NORFLASH。看一下ST官方例程 和 读卡器例程,这个文件的区别。对比一下。关键修改1:

添加一个宏。

关键修改2:



关键修改3:

函数修改后。

关键修改4:

block_size扇区大小

block_num扇区数

关键修改5:

关键修改6:



关键修改7:

错误处理

关键修改8:

按需修改即可。

STORAGE_LUN_NBR为宏 标志最大支持设备数。

步骤五:修改usbd_conf.c/h











下面是修改各种回调函数,在usbd_conf.c文件中。

步骤6:修改usbd_msc.h文件

完成步骤5,读卡器功能就可以实现了。

完成步骤6,使功能更全面。


步骤7:其它文件的scsi_blk_nbr变量改为数组。

完成步骤6之后,调用相关变量的地方,也需要做相应修改。

其余函数位置做类似修改。

步骤8:修改main.c文件。











四、USB声卡(slave)实验 1:37:11

STM32USB外设作为从机。

以探索者V3 HAL库为例。

五、USB虚拟串口(slave)实验 提高篇下

STM32USB外设作为从机。

以探索者V3 HAL库为例。

六、USBU盘(host)实验

STM32USB外设作为主机。

以探索者V3 HAL库为例。

步骤1:查看ST官方的MSC例程

步骤二:拷贝工程所需文件

步骤三:MDK工程设置

步骤四:修改usbh_conf.c/.h文件



就是一个IO控制三级管/MOS管,从而控制给U盘供电。


之前USB作为从机,没有供电选择这一步。

正点没有修改许多子回调函数,只是在ST官方例程中添加了printf的提示信息。

下面这里不需要控制VBUS所以未实现。

步骤五:修改usbh_diskio.c文件






这些读写U盘的函数,可以去查看前面提到的"主机库""设备库"的两个文件。

步骤六:修改diskio.c文件 以支持对U盘的访问












步骤7:修改main.c文件

看一下main.c的文件。

没讲FATFS文件系统。

测试:

借助USMART 进行测试。

USMART 是由 ALIENTEK 开发的一个灵巧的串口调试互交组件,通过它你可以通过串口助手调用程序里面的任何函数,并执行。
具体说明





打开文件测试:










七、USB鼠标键盘(host)实验 1:07:33

STM32USB外设作为主机。

以探索者V3 HAL库为例。

八、总结

补充:文件系统FATFS

视频地址:

层次化结构、目录和文件、文件属性、数据组织。

嵌入式常用的文件系统有:FAT16、FAT32。

基本概念与原理

FATFS基本入门详解

FAT16、FAT32在簇方面有差异

SD卡,U盘等存储介质最小存储介质为块/扇区,英文为block。


加载文件系统时,会从保留区获取文件系统的一些信息,包括整个存储介质扇区的数量和大小。



什么是FATFS

FatFs来源于"文件分配表(File Allocation Table)",以ANSI C(C89)规范编写,并且完全独立于磁盘IO层,具备跨平台移植性。

可以集成在资源有限的小型嵌入式微控制器中,如8051、ARM等。

还提供了适用于微小型微控制器的Petit FatFs,裁剪后的FatFs。

使用宏,裁剪文件系统的空间占用大小。

挂载文件系统



如何使用这个接口???

这个教程在电脑磁盘中虚拟创建一个磁盘,然后使用Vs,写挂载虚拟磁盘的程序。并不是嵌入式应用的。简单学习一下移植方法。

再对比正点的源码看看。


## 取消挂载文件系统

一个数组存了虚拟磁盘的路径,0对应数组的第一个元素,也就是第一个磁盘,1对应数组的第二个元素。

感兴趣可以看一下f_mount的内部实现。

先挂载文件系统,才能对SPI FLASH等存储介质进行文件操作。

文件操作接口的使用介绍

打开与关闭文件

标准C语言中使用f_Open函数打开文件。

Fatfs也是类似,使用f_Open函数,两者函数名字相同。

关闭文件使用f_close函数,直接传入参数即可。

看了一部分视频,发现大多是对一些函数的解释和操作,所以减少记录。

只记录重点内容。

文件的读取和写入

读取


读文件测试:

在标准C语言中,f_open打开文件时,会有记录文件位置的指针offset,刚打开时offset为0,指向文件的最开始位置。

f_write写10个字节,offset变为10.

在fatfs文件系统中,也有该指针记录文件的位置。




写入文件

有时,数据会先存在RAM中,需要的时候再写入到驱动器,此时如果出现断电(或程序崩溃)的情况,就会产生数据丢失的现象。



模拟程序运行中崩溃,导致数据丢失的现象。

查看生成的文件。

文件大小为0,没有任何数据,但是i已经是5000,f_write已经执行了5000次,所以期望结果是有0-4999行的数据。这些数据在程序崩溃的时候,都丢了。

实际,f_wriite在写入的时候,会将一些信息缓存到FIL,FATFS类型的结构体变量中,实际在RAM中,易丢失。

因为程序提前崩溃,所以没有正确执行f_close文件,导致关于文件占用空间大小的一些信息,没有写入到驱动器。

所以显示文件大小为零。

实际可能5000次的内容已经写入到了驱动器,每次执行f_write就已经把内容写到了磁盘,但是没有执行f_close,导致没有将文件属性(大小 日期等)相关的信息写入到驱动器,即文件不完整,所以文件的大小为0.。

FIL,FATFS类型的结构体变量有一部分缓存,这部分在突然停机、掉电等情况下,数据会丢掉。

使用f_sync。

O代表打开文件

W代表写入文件

C代表关闭文件

S代表SYNC

下面的图片中有介绍

崩溃前使用f_syns函数执行同步。




标准C语言中,fflush函数与f_close函数作用类似。

以字符串的方式读文件 f_get函数

前面都是以二进制(字节)的方式进行文件操作。


Tchar类型,根据不同情况,为char 或者 wchar。具体需要查一下。

f_gets一次读一行 效率低一些(不应该高一些吗?) 但使用方便

f_read一次读一个字节



不同系统下对回车换行操作的具体区别

f_gets的配置项

以字符串的方式写文件 f_puts函数




f_puts函数添加字符串,没有在最后添加换行符

C标准f_puts函数,在控制台打印字符串会添加换行符。

所以

同样的,f_puts函数写入时,也有宏开关,控制\n是否转换为\r\n。

与f_gets函数的转换相反。

查看c文件内部的字节 情况


文件读写定位 f_lseek函数

移动指针到任意文件位置

移动指针到任意文件开头

跳整写位置进行扩容


使用f_lseek函数对文件指针进行偏移,使文件大小得到扩充。

相当于划分了一部分存储器/磁盘的空间,给了这个文件。

内容不确定,原来磁盘位置上是什么内容,扩充后的内容就是什么。

获取文件信息

分两类:获取文件的大小和位置;获取文件的名称和属性等。


判断是否以到达文件尾部



截断文件




想要将文件内容清空时,

方式1:删除文件 重新创建新的文件。

方式2:可以将文件指针定位到0,然后截断。

为文件分配连续空间(FatFs的特有功能)

对文件读写性能有要求,可以使用。

一个文件存在于数据区的不同簇中,如果簇在存储空间中不连续,则读写效率较低。

如果一个文件的簇,在存储空间中连续,则读写效率高。

下图不连续时,写的步骤为:先写,查找下一个簇,再写,再查找下一个簇。

FatFs可以在创建文件时,可以设定文件的大小,同时要求文件在一段连续的簇中存储。




代码示例

注:字节数 / 扇区大小 = 扇区的数量 作为形参传进去。

磁盘所在的驱动器号(一个驱动器 可以控制多个磁盘)。
前面用f_write执行写数据,内部的底层实现也是调用了disk_write函数(内部调用多次disk_write函数)。

f_write内部可以执行查扇区号、写数据、查扇区号、写数据。。。

如果文件连续存储,可以直接调用底层的disk_write函数,连续写,只调用一次。更高效。

disk_write函数在diskio.c文件中。

使用disk_write函数的缺点:

需要使用一些变量计算驱动器号参数,文件所在扇区号。(计算过程较难理解,涉及FatFs的存储原理)

这些变量在不同版本的FatFs中,可能变化,从而需要修改计算的代码。

即FatFs版本升级,导致代码用不了了。
连续读用disk_read函数。

使用f_expand函数的注意事项:

1 使用f_expand扩充文件时,原文件必须无内容。(一般对刚打开的文件进行扩充)

如果有内容,无法使原来内容的存储位置,与扩充的存储位置连续起来。

2 使用f_expand扩充文件为100kb的连续空间,再用f_write函数在文件的末尾写10kb的内容。

这时文件大小为100kb + 10kb。这10kb的内容与100kb的内容,可能连续,也可能不连续。

需要注意这个问题。

不一定成功,20MB空闲空间,但不一定是连续的,f_expand函数必须找连续的空间扩充。

读取转发数据




f_read函数要求传入RAM缓存,而使用FIL结构体类型的内部缓存区,可以不需要使用f_read函数先将文件内容读到RAM数组缓冲区,再f_write将缓冲区内容写到其它存储介质。

使用f_forward函数,直接将一个存储介质中的文件内容,通过串口/SPI/IIC等接口发送数据(十六进制的数据)。

函数实现框架

目录与文件管理

查询文件以及文件信息 f_stat函数

f_stat函数不要求打开文件。








遍历目录

有一个目录,将目录下的子目录/子文件逐条 打印出来。


上图 最后一行 时间应右移11 少了>

在上面的图片上补充,添加遍历目录完成的代码。

在window中,在目录中,可以按大小、名称、修改时间等设置不同的文件展示顺序。

但是在Fatfs中,f_readdir函数读到的目录/文件属性,排列顺序是固定的。

即上图中运行结果的排列顺序,与文件名、大小、日期、时间没有任何关系。

应该是按文件在存储介质中实际的存储地址顺序有关。


在目录中查找




切换工作目录


前面是绝对路径,后面是相对路径。

删除文件或目录

删除文件


删除目录 有注意事项

f_unlink函数仅支持删除空的目录。

f_unlink函数无法删除当前目录。

如果目录下有子目录,或者目录下有文件,则f_unlink无法删除。

如果想删除非空的目录,需要自己写函数实现,递归删除目录下的所有目录/文件,再删除该目录。




创建目录

只能单层创建目录。

执行自然会创建成功。

更新文件属性

更改文件名、属性(只读、隐藏等)等。

本节三个内容

修改属性

修改名称

修改最后修改时间

修改属性:


修改名称:

f_rename函数可以实现:

1 重命名

2 移动文件 并改名

3 移动目录 并改名



修改时间戳:

更改当前驱动器

类似盘符。前面用到的0: 1:。

只挂载一个磁盘时,使用函数时,则不需要0:。



卷管理与系统配置

获取剩余空间



注:上面printf应该是%f

f_getfree函数内部是循环扫描 FAT表,获取当前剩余簇的数量。效率较低。

可以优化:

FATFS有一段数据区,专门记录空闲的簇,直接读取,效率最高。

FSinfo 扇区结构体(FAT英文白皮书)

有时使用FSinfo 函数扫描耗时,但是准确率高。

设置和获取卷标

嵌入式开发中,用的少,基本用不上。



DiskGenius软件,查看磁盘的文件。

设置为小写,但是显示大写。原因?

看下FatFs文件结构体简介,

DISK AA存在磁盘的一部分存储区.

大小写不需要关心.

应该是在代码中存的时候,以大写方式存的.

相关推荐
CS Beginner6 小时前
【JavaWeb学习】myabtis.xml一次性加载mapper相关的文件
xml·学习
矢志航天的阿洪6 小时前
用TikZ绘制专业流程图:从入门到进阶(基于D3QN训练流程)
学习
1379号监听员_6 小时前
嵌入式软件架构--按键消息队列3(测试)
开发语言·stm32·单片机·嵌入式硬件·架构
迎風吹頭髮6 小时前
Linux服务器编程实践57-功能强大的网络信息函数getaddrinfo:支持IPv4与IPv6
单片机·嵌入式硬件
一只侯子7 小时前
Tuning——CC调试(适用高通)
开发语言·图像处理·笔记·学习·算法
迷途呀7 小时前
Latex中的错误汇总
论文阅读·笔记·学习·其他·编辑器
Larry_Yanan7 小时前
QML学习笔记(四十六)QML与C++交互:Q_PROPERTY宏映射
c++·笔记·qt·学习·ui·交互
光影少年8 小时前
网络安全生态及学习路线
学习·安全·web安全
GilgameshJSS8 小时前
STM32H743-ARM例程26-TCP_CLIENT
c语言·arm开发·stm32·单片机·tcp/ip