专业的编程技术博客社区

网站首页 > 博客文章 正文

「正点原子NANO STM32开发板资料连载」第34章 USB 读卡器实验

baijin 2024-09-09 01:13:33 博客文章 17 ℃ 0 评论

1)实验平台:alientek NANO STM32F411 V1开发板

2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子

第三十四章 USB 读卡器(Slave)实验

STM32F411 系列芯片都自带了 USB OTG FS(FS,即全速,12Mbps),支持 USB Host和 USB Device,所有 USB 相关例程,均使用 USB OTG FS 实现。

本章,我们将向大家介绍如何利用 USB OTG FS 在 ALIENTEK NANO STM32F4 开发板实现一个 USB 读卡器。本章分为如下几个部分:分为如下几个部分:

34.1 USB 简介

34.2 硬件设计

34.3 软件设计

34.4 下载验证

34.1 USB 简介

USB ,是英文 Universal Serial BUS(通用串行总线)的缩写,而其中文简称为“通串线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。USB 发展到现在已经有 USB1.0/1.1/2.0/3.0 等多个版本。目前用的最多的就是 USB1.1 和USB2.0,USB3.0 目前已经开始普及。STM32F103 自带的 USB 符合 USB2.0 规范。标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+,D-;这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上,D-和 D+都是接了 15K 的电阻到低的,所以在没有设备接入的时候,D+、D-均是低电平。而在 USB 设备中,如果是高速设备,则会在 D+上接一个1.5K 的电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。接下来,我们简单介绍一下 STM32 的 USB 控制器。STM32F411 的 USB OTG FS 是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合 USB 2.0 规范的 On-The-Go 补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机” 模式,完全符合 USB 2.0 规范。在主机模式下,OTG FS 支持全速(FS,12Mb/s)和低速(LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。OTG FS 同时支持 HNP 和 SRP。STM32F411 的 USB OTG FS 主要特性可分为三类:通用特性、主机模式特性和从机模式特性。

1,通用特性

?

经 USB-IF 认证,符合通用串行总线规范第 2.0 版

?

集成全速 PHY,且完全支持定义在标准规范 OTG 补充第 1.3 版中的 OTG 协议

1,支持 A-B 器件识别(ID 线)

2,支持主机协商协议(HNP)和会话请求协议(SRP)

3,允许主机关闭 VBUS 以在 OTG 应用中节省电池电量

4,支持通过内部比较器对 VBUS 电平采取监控

5,支持主机到从机的角色动态切换

?

可通过软件配置为以下角色:

1, 具有 SRP 功能的 USB FS 从机(B 器件)

2, 具有 SRP 功能的 USB FS/LS 主机(A 器件)

3,USB On-The-Go 全速双角色设备

?

支持 FS SOF 和 LS Keep-alive 令牌

1,SOF 脉冲可通过 PAD 输出

2,SOF 脉冲从内部连接到定时器 2 (TIM2)

3,可配置的帧周期

3, 可配置的帧结束中断

?

具有省电功能,例如在 USB 挂起期间停止系统、关闭数字模块时钟、对 PHY 和 DFIFO

电源加以管理

?

具有采用高级 FIFO 控制的 1.25 KB 专用 RAM

1,可将 RAM 空间划分为不同 FIFO,以便灵活有效地使用 RAM

2,每个 FIFO 可存储多个数据包

3,动态分配存储区

4,FIFO 大小可配置为非 2 的幂次方值,以便连续使用存储单元

?

一帧之内可以无需要应用程序干预,以达到最大 USB 带宽

2,主机(Host)模式特性

?

通过外部电荷泵生成 VBUS 电压。

?

多达 8 个主机通道(管道):每个通道都可以动态实现重新配置,可支持任何类型的

USB 传输。

?

内置硬件调度器可:

1,在周期性硬件队列中存储多达 8 个中断加同步传输请求

2,在非周期性硬件队列中存储多达 8 个控制加批量传输请求

?

管理一个共享 RX FIFO、一个周期性 TX FIFO 和一个非周期性 TX FIFO,以有效使用

USB 数据 RAM。

3,从机(Slave/Device)模式特性

? 1 个双向控制端点 0

? 3 个 IN 端点 (EP),可配置为支持批量传输、中断传输或同步传输

? 3 个 OUT 端点(EP),可配置为支持批量传输、中断传输或同步传输

?

管理一个共享 Rx FIFO 和一个 Tx-OUT FIFO,以高效使用 USB 数据 RAM

?

管理多达 4 个专用 Tx-IN FIFO(分别用于每个使能的 IN EP),降低应用程序负荷支

持软断开功能。

STM32F411 USB OTG FS 框图如图 34.1.1 所示:

对于 USB OTG FS 功能模块,STM32F4 通过 AHB 总线访问(AHB 频率必须大于 14.2Mhz),

其中 48Mhz 的 USB 时钟,是来自时钟树图里面的 PLL48CK(和 SDIO 共用)。

STM32F4 USB OTG FS 的其他介绍,请大家参考《STM32F411xC/E 参考手册》第 22 章内

容,我们这里就不再详细介绍了。

要正常使用 STM32F4 的 USB,就得编写 USB 驱动,而整个 USB 通信的详细过程是很复

杂的,本书篇幅有限,不可能在这里详细介绍,有兴趣的朋友可以去看看电脑圈圈的《圈圈教

你玩 USB》这本书,该书对 USB 通信有详细讲解。如果要我们自己编写 USB 驱动,那是一件

相当困难的事情,尤其对于从没了解过 USB 的人来说,基本上不花个一两年时间学习,是没法

搞定的。不过,ST 提供了我们一套完整的 USB OTG 驱动库(包括主机和设备),通过这个库,

我们可以很方便的实现我们所要的功能,而不需要详细了解 USB 的整个驱动,大大缩短了我们

的开发时间和精力。

ST 提供的 USB OTG 库,可以在: http://www.stmcu.org/download/index.php?act

=ziliao&id=150 这里下载到(UM1021)。不过,我们已经帮大家下载到开发板光盘:7,STM32

参考资料?STM32 USB 学习资料,文件名:stm32_f105-07_f2_f4_usb-host-device_lib.zip。该库

包含了 STM32F4 USB 主机(Host)和从机(Device)驱动库,并提供了 10 个例程供我们参考,

如图 34.1.2 所示:

如图 34.1.2 所示,ST 提供了 3 类例程:①即设备类(Device,即 Slave)、②主从一体类

(Host_Device)和③主机类(Host),总共 10 个例程。整个 USB OTG 库还有一个说明文档:

CD00289278.pdf(在光盘有提供),即 UM1021,该文档详细介绍了 USB OTG 库的各个组成部分以

及所提供的例程使用方法,有兴趣学习 USB 的朋友,这个文档是必须仔细看的。

这 10 个例程,虽然都是基于官方 EVAL 板的,但是很容易移植到我们的探索者 STM32F407

开发板上,本章我们就是移植:STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Device_

Examples\MSC 这个例程,以实现 USB 读卡器功能。

34.2 硬件设计

本节实验功能简介:开机的时候先检测 SPI FLASH 是否存在,如果存在则获取其容量,

并打印在串口调试助手(如果不存在,则报错)。之后开始 USB 配置,在配置成功之后就可

以在电脑上发现一个可移动磁盘。我们用 DS1 来指示 USB 正在读写 SPI FLASH,并在串口助

手上显示出来,同样我们还是用 DS0 来指示程序正在运行。

所要用到的硬件资源如下:

1) 指示灯 DS0 、DS1

2) 串口

3) USB SLAVE 接口

1) SPI FLASH

4)

这几个部分,在之前的实例中都已经介绍过了,我们在此就不多说了

31.3 软件设计

本章,我们在第二十四章(实验 19)的基础上修改,代码移植自 ST 官方例程:

STM32_USB-Host-Device_Lib_V2.1.0\Project\USBDevice_Examples\MSC,我打开该例程即可

知道 USB 相关的代码有哪些,如图 34.3.1 所示:


有了这个官方例程做指引,我们就知道具体需要哪些文件,从而实现本章例程。

首先,在本章例程(即实验 19 SPI 实验)的工程文件夹下面,新建 USB 文件夹,并拷贝

官方 USB 驱动库相关代码到该文件夹下,即拷贝:光盘? 7,STM32 参考资料?STM32 USB 学

习资料?STM32_USB-Host-Device_Lib_V2.1.0?Libraries 文件夹下的 STM32_USB_Device_Libr

ary、STM32_USB_HOST_Library 和 STM32_USB_OTG_Driver 等三个文件夹的源码到该文件夹

下面。

然后,在 USB 文件夹下,新建 USB_APP 文件夹存放 MSC 实现相关代码,即:STM32_USB

-Host-Device_Lib_V2.1.0?Project?USB_Device_Examples?MSC?src 下的部分代码:usb_bsp.c

usbd_storage_msd.c、

usbd_desc.c和 usbd_usr.c 等 4个.c文件,同时拷贝 STM32_USB-Host-Device

_Lib_V2.1.0?Project?USB_Device_Examples?MSC?inc 下面的:usb_conf.h、usbd_conf.h 和

usbd_desc.h 等三个文件到 USB_APP 文件夹下,最后 USB_APP 文件夹下的文件如图 34.3.2 所

示:

之后,根据 ST 官方 MSC 例程,在我们本章例程的基础上新建分组添加相关代码,具体细

节,这里就不详细介绍了,添加好之后,如图 34.3.3 所示:


移植时,我们重点要修改的就是 USB_APP 文件夹下面的代码。其他代码(USB_OTG 和

USB_DEVICE 文件夹下的代码)一般不用修改。

usb_bsp.c 提供了几个 USB 库需要用到的底层初始化函数,包括:

IO 设置、中断设置、VBUS

配置以及延时函数等,需要我们自己实现。USB Device(Slave)和 USB Host 共用这个.c 文件。

usbd_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字

符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,

这里:usbd_desc.c 里面的:usbd 即 device 类,同样:usbh 即 host 类,所以通过文件名我们可

以很容易区分该文件是用在 device 还是 host,而只有 usb 字样的那就是 device 和 host 可以共用

的。

usbd_usr.c 提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理

完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态,

比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以

执行不同操作,完成特定功能。

usbd_storage_msd.c 提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化

和读写等函数。本章我们设置了 1 个磁盘:SPI FLASH。

以上 4 个.c 文件里面的函数,基本上都是以回调函数的形式,被 USB 驱动库调用的。这些

代码的具体修改过程,我们这里不详细介绍,请大家参考光盘本例程源码,这里只提几个重点

地方讲解下:

1,要使用 USB OTG FS,必须在 MDK 编译器的全局宏定义里面,定义:USE_USB_OTG_FS

宏,如图 34.3.4 所示:

2,因为 NANO STM32F4 开发板没有用到 VUSB 电压检测,所以要在 usb_conf.h 里面,

将宏定义:#define VBUS_SENSING_ENABLED,屏蔽掉。

3,通过修改 usbd_conf.h 里面的 MSC_MEDIA_PACKET 定义值大小,可以一定程度提高

USB 读写速度(越大越快),本例程我们设置 12*1024,也就是 12K 大小。

以上 3 点,就是我们移植的时候需要特别注意的,其他我们就不详细介绍了(USB 相关源码解

释,请参考:CD00289278.pdf 这个文档),最后修改 test.c 里面代码如下:

USB_OTG_CORE_HANDLE USB_OTG_dev;

extern vu8 USB_STATUS_REG;

//USB 状态

extern vu8 bDeviceState;

//USB 连接 情况

int main(void)

{

u8 offline_cnt=0;

u8 tct=0;

u8 USB_STA;

u8 Divece_STA;

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(96,4,2,4);

//设置时钟,96Mhz

delay_init(96);

//初始化延时函数

uart_init(115200);

//初始化 USART

LED_Init();

//初始化 LED 时钟

W25QXX_Init();

//SPI FLASH 初始化

printf("NANO STM32\r\n");

printf("USB Card Reader TEST\r\n");

while(W25QXX_ReadID()!=W25Q16)//检测 W25Q16

{

printf("W25Q16 Check Failed!\r\n");

delay_ms(500);

printf("Please Check!\r\n");

delay_ms(500);

LED0=!LED0;//DS0 闪烁

}

printf("SPI FLASH Size:2048KB\r\n");

printf("USB Connecting...\r\n");//提示 USB 连接中

USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,

&USR_desc,&USBD_MSC_cb,&USR_cb);

delay_ms(1800);

while(1)

{

delay_ms(1);

if(USB_STA!=USB_STATUS_REG)//状态改变了

{

if(USB_STATUS_REG&0x01)//正在写

{

printf("USB Writing...\r\n");//提示 USB 正在写入数据

}

if(USB_STATUS_REG&0x02)//正在读

{

printf("USB Reading...\r\n");//提示 USB 正在读出数据

}

if(USB_STATUS_REG&0x04)printf("USB Write Err\r\n");//提示写入错误

if(USB_STATUS_REG&0x08)printf("USB Read Err\r\n");//提示读出错误

USB_STA=USB_STATUS_REG;//记录最后的状态

}

if(Divece_STA!=bDeviceState)

{

if(bDeviceState==1) printf("USB Connected\r\n");//提示 USB 连接已经建立

else printf("USB DisConnected\r\n");//提示 USB 被拔出了

Divece_STA=bDeviceState;

}

tct++;

if(tct==200)

{

tct=0;

LED0=!LED0;//提示系统在运行

if(USB_STATUS_REG&0x10)

{

offline_cnt=0;//USB 连接了,则清除 offline 计数器

bDeviceState=1;

}else//没有得到轮询

{

offline_cnt++;

if(offline_cnt>10)bDeviceState=0;

//2s 内没收到在线标记,代表 USB 被拔出了

}

USB_STATUS_REG=0;

}

}

}

其中,USB_OTG_CORE_HANDLE 是一个全局结构体类型,用于存储 USB 通信中 USB

内核需要使用的的各种变量、状态和缓存等,任何 USB 通信(不论主机,还是从机),我们都

必须定义这么一个结构体以实现 USB 通信,这里定义成:USB_OTG_dev。

然后,USB 初始化非常简单,只需要调用 USBD_Init 函数即可,顾名思义,该函数是 USB

设备类初始化函数,本章的 USB 读卡器属于 USB 设备类,所以使用该函数。该函数初始化了

USB 设备类处理的各种回调函数,以便 USB 驱动库调用。执行完该函数以后,USB 就启动了,

所有 USB 事务,都是通过 USB 中断触发,并由 USB 驱动库自动处理。USB 中断服务函数在

usbd_usr.c 里面:

//USB OTG 中断服务函数

//处理所有 USB 中断

void OTG_FS_IRQHandler(void)

{

USBD_OTG_ISR_Handler(&USB_OTG_dev);

}

该函数调用 USBD_OTG_ISR_Handler 函数来处理各种 USB 中断请求。因此在 main 函数里

面,我们的处理过程就非常简单,main 函数里面通过两个全局状态变量(USB_STATUS_REG

和 bDeviceState),来判断 USB 状态,并在串口上面打印显示相关提示信息。

USB_STATUS_REG 在 usbd_storage_msd.c 里面定义的一个全局变量,不同的位表示不同状

态,用来指示当前 USB 的读写等操作状态。

bDeviceState 是在 usbd_usr.c 里面定义的一个全局变量,0 表示 USB 还没有连接;1 表示

USB 已经连接。

软件设计部分,就给大家介绍到这里。

34.4 下载验证

在代码编译成功之后,我们通过下载代码 NANO STM32F4 开发板上,在 USB 配置成功后

注意:USB 数据线,要插在开发板的 USB_SLAVE 口!),串口助手打印如图 34.4.1 所示:

此时,电脑提示发现新硬件,并自动安装驱动,如图 34.4.2 所示:

等 USB 配置成功后,DS1 不亮,DS0 闪烁,并且在电脑上可以看到我们的磁盘,如图 34.4.3

所示:

我们打开设备管理器,在通用串行总线控制器里面可以发现多出了一个 USB Mass Storage

Device,同时看到磁盘驱动器里面多了 1 个磁盘,如图 35.4.4 所示:

此时,我们就可以通过电脑读写 SPI FLASH 里面的内容了。在执行读写操作的时候,就可

以看到 DS1 亮,并且会在串口调试助手上打印当前的读写状态。

注意,在对 SPI FLASH 操作的时候,最好不要频繁的往里面写数据,否则很容易将 SPI

FLASH 写爆!!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表