单片机USB应用

这篇具有很好参考价值的文章主要介绍了单片机USB应用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、USB简介

  USB ,是英文 Universal Serial BUS(通用串行总线)的缩写,而其中文简称为“通串线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。
  标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+和 D-,这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上,D-和 D+都是接了 15K 的电阻到地的,所以在没有设备接入的时候,D+、D-均是低电平。而在 USB 设备中,如果是高速设备,则会在 D+上接一个 1.5K 的电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。
  STM32F407 系列芯片自带有 USB OTG FS(全速)和 USB OTG HS(高速),其中 HS 需要外扩高速 PHY 芯片实现。STM32F407 的 USB OTG FS 是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合 USB 2.0 规范的 On-The-Go 补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机” 模式,完全符合 USB 2.0 规范。在主机模式下,OTG FS 支持全速(FS,12 Mb/s)和低速(LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。OTG FS 同时支持 HNP 和 SRP。对于USB OTG FS功能模块,STM32F4通过AHB总线访问(AHB频率必须大14.2Mhz),其中 48Mhz 的 USB 时钟,是来自时钟树图里面的 PLL48CK(和 SDIO 共用)。STM32F407 USB OTG FS 框图如图所示:
单片机usb通信,单片机,单片机,stm32

  STM32F407 的 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 带宽
  1. 主机(Host)模式特性
  • 通过外部电荷泵生成 VBUS 电压。
  • 多达 8 个主机通道(管道):每个通道都可以动态实现重新配置,可支持任何类型的USB 传输。
  • 内置硬件调度器可:
    1,在周期性硬件队列中存储多达 8 个中断加同步传输请求
    2,在非周期性硬件队列中存储多达 8 个控制加批量传输请求
  • 管理一个共享 RX FIFO、一个周期性 TX FIFO 和一个非周期性 TX FIFO,以有效使用USB 数据 RAM。
  1. 从机(Slave/Device)模式特性
  • 个双向控制端点 0
  • 3 个 IN 端点 (EP),可配置为支持批量传输、中断传输或同步传输
  • 3 个 OUT 端点(EP),可配置为支持批量传输、中断传输或同步传输
  • 管理一个共享 Rx FIFO 和一个 Tx-OUT FIFO,以高效使用 USB 数据 RAM
  • 管理多达 4 个专用 Tx-IN FIFO(分别用于每个使能的 IN EP),降低应用程序负荷支持软断开功能。

  ST 提供的 USB OTG 库,该库包含了 STM32F4 USB 主机(Host)和从机(Device)驱动库,并提供了 10 个例程供我们参考,下载链接如下:https://download.csdn.net/download/weixin_44567668/87853740

二、USB Slave实验

2.1 OTG及读卡器实验简介

  USB技术的发展,使得PC和周边设备能够通过简单的方式、适度的制造成本,将各种数据传输速度的设备连接在一起。我们提到的应用,都是通过USB连接到PC,并在PC的控制下进行数据交换。但这种方便的交换方式,一旦离开了PC,各设备间无法利用USB口进行操作,因为没有一个设备能够像PC一样充当主机。
  OTG是On-The-Go的缩写,是近年发展起来的技术。2001年12月18日由USB标准化组织公布,主要应用于不同的设备或移动设备间的联接,进行数据交换。OTG技术允许在没有主机(Host)的情况下,实现设备间的数据传送。例如数码相机直接连接到打印机上,通过OTG技术,连接两台设备间的USB口,将拍出的相片立即打印出来;也可以将数码照相机中的数据,通过OTG发送到USB接口的移动硬盘上,野外操作就没有必要携带价格昂贵的存储卡,或者背一个便携电脑。
  本实验通过获取SD卡或者外部SPI FLASH当作存储容量,来实现类似U盘功能,从而连接电脑时,可以当作U盘从而实现从设备Slave功能

2.2 USB Slave移植

  代码移植自 ST 官方例程:STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Device_Examples\MSC

2.2.1 文件移植

  1. 将STM32_USB-Host-Device_Lib_V2.1.0\Libraries文件夹里STM32_USB_Device_Library、STM32_USB_HOST_Library、STM32_USB_OTG_Driver三个文件夹放入新建的USB文件夹里
    单片机usb通信,单片机,单片机,stm32
  2. 在USB文件夹里新建APP文件夹,拷贝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通信,单片机,单片机,stm32

2.2.2 工程添加

  在工程上添加相关文件
单片机usb通信,单片机,单片机,stm32

2.3 代码修改

  移植时我们重点要修改的就是 USB_APP 文件夹下面的代码。其他代码(USB_OTG 和USB_DEVICE 文件夹下的代码)一般不用修改。

2.3.1 usb_bsp.c修改

  usb_bsp.c 提供了几个 USB 库需要用到的底层初始化函数,包括:IO 设置、中断设置、VBUS配置以及延时函数等,需要我们自己实现。USB Device(Slave)和 USB Host 共用这个.c 文件。
单片机usb通信,单片机,单片机,stm32

#include "usb_bsp.h" 
#include "sys.h"  
#include "delay.h" 
#include "usart.h" 

//USB主机电源控制口
#define USB_HOST_PWRCTRL 	PAout(15)	//PA15
   
//USB OTG 底层IO初始化
//pdev:USB OTG内核结构体指针
void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
  RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE);//使能USB OTG时钟	钟
  //GPIOA11,A12设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;//PA11/12复用功能输出	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
	
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15推挽输出		
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出功能
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
	
  USB_HOST_PWRCTRL=1;			//开启USB HOST电源供电

	GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_OTG_FS);//PA11,AF10(USB)
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_OTG_FS);//PA12,AF10(USB)
}
//USB OTG 中断设置,开启USB FS中断
//pdev:USB OTG内核结构体指针
void USB_OTG_BSP_EnableInterrupt(USB_OTG_CORE_HANDLE *pdev)
{
	NVIC_InitTypeDef   NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn; 
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;//子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道
  NVIC_Init(&NVIC_InitStructure);//配置  
}
//USB OTG 中断设置,开启USB FS中断
//pdev:USB OTG内核结构体指针
void USB_OTG_BSP_DisableInterrupt(void)
{ 
}
//USB OTG 端口供电设置(本例程未用到)
//pdev:USB OTG内核结构体指针
//state:0,断电;1,上电
void USB_OTG_BSP_DriveVBUS(USB_OTG_CORE_HANDLE *pdev, uint8_t state)
{ 
}
//USB_OTG 端口供电IO配置(本例程未用到)
//pdev:USB OTG内核结构体指针
void  USB_OTG_BSP_ConfigVBUS(USB_OTG_CORE_HANDLE *pdev)
{ 
} 
//USB_OTG us级延时函数
//本例程采用SYSTEM文件夹的delay.c里面的delay_us函数实现
//官方例程采用的是定时器2来实现的.
//usec:要延时的us数.
void USB_OTG_BSP_uDelay (const uint32_t usec)
{ 
   	delay_us(usec);
}
//USB_OTG ms级延时函数
//本例程采用SYSTEM文件夹的delay.c里面的delay_ms函数实现
//官方例程采用的是定时器2来实现的.
//msec:要延时的ms数.
void USB_OTG_BSP_mDelay (const uint32_t msec)
{  
	delay_ms(msec);
}

2.3.2 usbd_desc.c

  usbd_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里:usbd_desc.c 里面的:usbd 即 device 类,同样:usbh 即 host 类,所以通过文件名我们可以很容易区分该文件是用在 device 还是 host,而只有 usb 字样的那就是 device 和 host 可以共用的。

2.3.3 usbd_usr.c修改

  usbd_usr.c 提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态,比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。

#include "usbd_usr.h" 
#include "usb_dcd_int.h"
#include <stdio.h> 
#include <usart.h>    

//表示USB连接状态
//0,没有连接;
//1,已经连接;
vu8 bDeviceState=0;		//默认没有连接  


extern USB_OTG_CORE_HANDLE  USB_OTG_dev;

//USB OTG 中断服务函数
//处理所有USB中断
void OTG_FS_IRQHandler(void)
{
  	USBD_OTG_ISR_Handler(&USB_OTG_dev);
}  
//指向DEVICE_PROP结构体
//USB Device 用户回调函数. 
USBD_Usr_cb_TypeDef USR_cb =
{
  USBD_USR_Init,
  USBD_USR_DeviceReset,
  USBD_USR_DeviceConfigured,
  USBD_USR_DeviceSuspended,
  USBD_USR_DeviceResumed,
  USBD_USR_DeviceConnected,
  USBD_USR_DeviceDisconnected,    
};
//USB Device 用户自定义初始化函数
void USBD_USR_Init(void)
{
	//printf("USBD_USR_Init\r\n");
} 
//USB Device 复位
//speed:USB速度,0,高速;1,全速;其他,错误.
void USBD_USR_DeviceReset (uint8_t speed)
{
	switch (speed)
	{
		case USB_OTG_SPEED_HIGH:
			printf("USB Device Library v1.1.0  [HS]\r\n");
			break; 
		case USB_OTG_SPEED_FULL: 
			printf("USB Device Library v1.1.0  [FS]\r\n");
			break;
		default:
			printf("USB Device Library v1.1.0  [??]\r\n"); 
			break;
	}
}
//USB Device 配置成功
void USBD_USR_DeviceConfigured (void)
{
	printf("MSC Interface started.\r\n"); 
} 
//USB Device挂起
void USBD_USR_DeviceSuspended(void)
{
	printf("Device In suspend mode.\r\n");
} 
//USB Device恢复
void USBD_USR_DeviceResumed(void)
{ 
	printf("Device Resumed\r\n");
}
//USB Device连接成功
void USBD_USR_DeviceConnected (void)
{
	bDeviceState=1;
	printf("USB Device Connected.\r\n");
}
//USB Device未连接
void USBD_USR_DeviceDisconnected (void)
{
	bDeviceState=0;
	printf("USB Device Disconnected.\r\n");
} 

2.3.4 usbd_storage_msd.c修改

  usbd_storage_msd.c 提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本章我们设置了 2 个磁盘:SD 卡和 SPI FLASH。

#include "usbd_msc_mem.h"
#include "usb_conf.h"
#include "w25qxx.h"			 
#include "sdio_sdcard.h"	   
 
//最大支持的设备数,2个
#define STORAGE_LUN_NBR 	2

自己定义的一个标记USB状态的寄存器///
//bit0:表示电脑正在向SD卡写入数据
//bit1:表示电脑正从SD卡读出数据
//bit2:SD卡写数据错误标志位
//bit3:SD卡读数据错误标志位
//bit4:1,表示电脑有轮询操作(表明连接还保持着)
vu8 USB_STATUS_REG=0;



//USB Mass storage 标准查询数据(每个lun占36字节)
const int8_t  STORAGE_Inquirydata[] = { 
  
	/* LUN 0 */ 
	0x00,		
	0x80,		
	0x02,		
	0x02,
	(USBD_STD_INQUIRY_LENGTH - 4),
	0x00,
	0x00,	
	0x00,
    /* Vendor Identification */
    'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K', ' ',//9字节
    /* Product Identification */
    'S', 'P', 'I', ' ', 'F', 'l', 'a', 's', 'h',//15字节
    ' ','D', 'i', 's', 'k', ' ',
    /* Product Revision Level */	
    '1', '.', '0', ' ',							//4字节		
	
	/* LUN 1 */
	0x00,
	0x80,		
	0x02,		
	0x02,
	(USBD_STD_INQUIRY_LENGTH - 4),
	0x00,
	0x00,	
	0x00,
	/* Vendor Identification */
	'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K',' ',	//9字节
	/* Product Identification */				
	'S', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',//15字节
	'D', 'i', 's', 'k', ' ', ' ',  
    /* Product Revision Level */
	'1', '.', '0' ,' ',                      	//4字节
}; 
int8_t STORAGE_Init (uint8_t lun);
int8_t STORAGE_GetCapacity (uint8_t lun,uint32_t *block_num,uint32_t *block_size);
int8_t  STORAGE_IsReady (uint8_t lun);
int8_t  STORAGE_IsWriteProtected (uint8_t lun);
int8_t STORAGE_Read (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len);
int8_t STORAGE_Write (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len);
int8_t STORAGE_GetMaxLun (void);

//USB Device 用户回调函数接口
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,
};
USBD_STORAGE_cb_TypeDef  *USBD_STORAGE_fops=&USBD_MICRO_SDIO_fops;//指向USBD_MICRO_SDIO_fops结构体.

//初始化存储设备
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//返回值:0,成功;
//    其他,错误代码
int8_t STORAGE_Init (uint8_t lun)
{
	SD_Init();
	W25QXX_Init();
	return 0; 
} 

//获取存储设备的容量和块大小
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//block_num:块数量(扇区数)
//block_size:块大小(扇区大小)
//返回值:0,成功;
//    其他,错误代码
int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_size)
{     
	if(lun==1)
	{
		*block_size=512;  
		*block_num=SDCardInfo.CardCapacity/512;
	}else
	{
		*block_size=512;  
		*block_num=1024*1024*12/512;	//SPI FLASH的前面12M字节,文件系统用
	}	
	return 0; 
} 

//查看存储设备是否就绪
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//返回值:0,就绪;
//    其他,未就绪
int8_t  STORAGE_IsReady (uint8_t lun)
{ 
	USB_STATUS_REG|=0X10;//标记轮询
	return 0;
} 

//查看存储设备是否写保护
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//返回值:0,没有写保护;
//    其他,写保护(只读)
int8_t  STORAGE_IsWriteProtected (uint8_t lun)
{
	return  0;
} 

//从存储设备读取数据
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//buf:数据存储区首地址指针
//blk_addr:要读取的地址(扇区地址)
//blk_len:要读取的块数(扇区数) 
//返回值:0,成功;
//    其他,错误代码 
int8_t STORAGE_Read (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len)
{
	int8_t res=0;
	USB_STATUS_REG|=0X02;//标记正在读数据
	if(lun==1)
	{
 		res=SD_ReadDisk(buf,blk_addr,blk_len);
		if(res)USB_STATUS_REG|=0X08;//SD卡读错误!
	}else
	{
		W25QXX_Read(buf,blk_addr*512,blk_len*512);
	} 
	return res;
}
//向存储设备写数据
//lun:逻辑单元编号,0,SD卡;1,SPI FLASH
//buf:数据存储区首地址指针
//blk_addr:要写入的地址(扇区地址)
//blk_len:要写入的块数(扇区数) 
//返回值:0,成功;
//    其他,错误代码 
int8_t STORAGE_Write (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len) 
{
	int8_t res=0;
	USB_STATUS_REG|=0X01;//标记正在写数据
	if(lun==1)
	{
		res=SD_WriteDisk (buf,blk_addr,blk_len);
		if(res)USB_STATUS_REG|=0X04;//SD卡写错误!	 
	}else
	{
		W25QXX_Write(buf,blk_addr*512,blk_len*512);
	} 
	return res; 
}
//获取支持的最大逻辑单元个数
//返回值:支持的逻辑单元个数-1
int8_t STORAGE_GetMaxLun (void)
{
	return (STORAGE_LUN_NBR - 1);
}

2.4 配置选项

  1. 要使用 USB OTG FS,必须在 MDK 编译器的全局宏定义里面,定义:USE_USB_OTG_FS宏
    单片机usb通信,单片机,单片机,stm32
  2. 如果用不到VUSB 电压检测,所以要在 usb_conf.h 里面,将宏定义:#define VBUS_SENSING_ENABLED,屏蔽掉。
  3. 通过修改 usbd_conf.h 里面的 MSC_MEDIA_PACKET 定义值大小,可以一定程度提高USB 读写速度(越大越快),本例程我们设置 12*1024,也就是 12K 大小。
  4. 官方例程不支持大于 4G 的 SD 卡,得修改 usbd_msc_scsi.c 里面的 SCSI_blk_addr 类型为 uint64_t,才可以支持大于 4G 的卡,官方默认是 uint32_t,最大只能支持 4G 卡。

2.5 实验测试函数

  只需要在主函数初始化里添加USB初始化。

USB_OTG_CORE_HANDLE USB_OTG_dev;

//主函数里添加
USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,&USR_desc,&USBD_MSC_cb,&USR_cb);

  其中,USB_OTG_CORE_HANDLE 是一个全局结构体类型,用于存储 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);
}

三、USB Host实验

3.1 U盘实验简介

  U 盘,全称 USB 闪存盘,英文名“USB flash disk”。它是一种使用 USB 接口的无需物理驱动器的微型高容量移动存储产品,通过 USB 接口与主机连接,实现即插即用,是最常用的移动存储设备之一。本实验就是将单片机当作Host,以通过 STM32F4 的 USB HOST 接口,读写 U盘或 SD 卡读卡器等设备。

3.2 USB Host移植

  代码移植自 ST 官方例程:STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Examples\MSC

3.2.1 文件移植

  在上个实验的基础上,只需要修改APP文件夹里的文件即可。将STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Examples\MSC\src文件夹里的usb_bsp.c、usbh_usr.c拷贝进来,再将STM32_USB-Host-Device_Lib_V2.1.0\Project\USB_Host_Examples\MSC\inc里的usb_conf.h、usbh_conf.h、usbh_usr.h总共3个文件拷贝进来,结果如下:
单片机usb通信,单片机,单片机,stm32

3.2.2 工程移植

  1. 项目添加
    单片机usb通信,单片机,单片机,stm32
  2. 在STM32_USB-Host-Device_Lib_V2.1.0\Libraries\STM32_USB_HOST_Library\Class\MSC\src里的usbh_msc_fatfs.c,是为了支持 fatfs 而写的一些底层接口函数,我们就直接放到diskio.c 里面,方便统一管理。

3.3 代码修改

  usb_bsp.c 的代码,和上一章的一样,可以用上一章的代码直接替换即可正常使用。usbh_usr.c 提供用户应用层接口函数,相比上章例程,USB HOST 通信的回调函数更多一些

#include "usbh_usr.h" 
#include "led.h"
#include "ff.h" 
#include "usart.h" 
   
static u8 AppState;
extern USB_OTG_CORE_HANDLE  USB_OTG_Core;

//USB OTG 中断服务函数
//处理所有USB中断
void OTG_FS_IRQHandler(void)
{
  	USBH_OTG_ISR_Handler(&USB_OTG_Core);
} 
//USB HOST 用户回调函数.
USBH_Usr_cb_TypeDef USR_Callbacks=
{
	USBH_USR_Init,
	USBH_USR_DeInit,
	USBH_USR_DeviceAttached,
	USBH_USR_ResetDevice,
	USBH_USR_DeviceDisconnected,
	USBH_USR_OverCurrentDetected,
	USBH_USR_DeviceSpeedDetected,
	USBH_USR_Device_DescAvailable,
	USBH_USR_DeviceAddressAssigned,
	USBH_USR_Configuration_DescAvailable,
	USBH_USR_Manufacturer_String,
	USBH_USR_Product_String,
	USBH_USR_SerialNum_String,
	USBH_USR_EnumerationDone,
	USBH_USR_UserInput,
	USBH_USR_MSC_Application,
	USBH_USR_DeviceNotSupported,
	USBH_USR_UnrecoveredError
};
/
//以下为各回调函数实现.

//USB HOST 初始化 
void USBH_USR_Init(void)
{
	printf("USB OTG HS MSC Host\r\n");
	printf("> USB Host library started.\r\n");
	printf("  USB Host Library v2.1.0\r\n\r\n");
	
}
//检测到U盘插入
void USBH_USR_DeviceAttached(void)//U盘插入
{
	LED1=1;
	printf("检测到USB设备插入!\r\n");
}
//检测到U盘拔出
void USBH_USR_DeviceDisconnected (void)//U盘移除
{
	LED1=0;
	printf("USB设备拔出!\r\n");
}  
//复位从机
void USBH_USR_ResetDevice(void)
{
	printf("复位设备...\r\n");
}
//检测到从机速度
//DeviceSpeed:从机速度(0,1,2 / 其他)
void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
{
	if(DeviceSpeed==HPRT0_PRTSPD_HIGH_SPEED)
	{
		printf("高速(HS)USB设备!\r\n");
 	}  
	else if(DeviceSpeed==HPRT0_PRTSPD_FULL_SPEED)
	{
		printf("全速(FS)USB设备!\r\n"); 
	}
	else if(DeviceSpeed==HPRT0_PRTSPD_LOW_SPEED)
	{
		printf("低速(LS)USB设备!\r\n");  
	}
	else
	{
		printf("设备错误!\r\n");  
	}
}
//检测到从机的描述符
//DeviceDesc:设备描述符指针
void USBH_USR_Device_DescAvailable(void *DeviceDesc)
{ 
	USBH_DevDesc_TypeDef *hs;
	hs=DeviceDesc;   
	printf("VID: %04Xh\r\n" , (uint32_t)(*hs).idVendor); 
	printf("PID: %04Xh\r\n" , (uint32_t)(*hs).idProduct); 
}
//从机地址分配成功
void USBH_USR_DeviceAddressAssigned(void)
{
	printf("从机地址分配成功!\r\n");   
}
//配置描述符获有效
void USBH_USR_Configuration_DescAvailable(USBH_CfgDesc_TypeDef * cfgDesc,
                                          USBH_InterfaceDesc_TypeDef *itfDesc,
                                          USBH_EpDesc_TypeDef *epDesc)
{
	USBH_InterfaceDesc_TypeDef *id; 
	id = itfDesc;   
	if((*id).bInterfaceClass==0x08)
	{
		printf("可移动存储器设备!\r\n"); 
	}else if((*id).bInterfaceClass==0x03)
	{
		printf("HID 设备!\r\n"); 
	}    
}
//获取到设备Manufacturer String
void USBH_USR_Manufacturer_String(void *ManufacturerString)
{
	printf("Manufacturer: %s\r\n",(char *)ManufacturerString);
}
//获取到设备Product String 
void USBH_USR_Product_String(void *ProductString)
{
	printf("Product: %s\r\n",(char *)ProductString);  
}
//获取到设备SerialNum String 
void USBH_USR_SerialNum_String(void *SerialNumString)
{
	printf("Serial Number: %s\r\n",(char *)SerialNumString);    
} 
//设备USB枚举完成
void USBH_USR_EnumerationDone(void)
{ 
	printf("设备枚举完成!\r\n\r\n");    
} 
//无法识别的USB设备
void USBH_USR_DeviceNotSupported(void)
{
	printf("无法识别的USB设备!\r\n\r\n");    
}  
//等待用户输入按键,执行下一步操作
USBH_USR_Status USBH_USR_UserInput(void)
{ 
	printf("跳过用户确认步骤!\r\n");
	return USBH_USR_RESP_OK;
} 
//USB接口电流过载
void USBH_USR_OverCurrentDetected (void)
{
	printf("端口电流过大!!!\r\n");
} 

extern u8 USH_User_App(void);		//用户测试主程序
//USB HOST MSC类用户应用程序
int USBH_USR_MSC_Application(void)
{
	u8 res=0;
  	switch(AppState)
  	{
    	case USH_USR_FS_INIT://初始化文件系统 
			printf("开始执行用户程序!!!\r\n");
			AppState=USH_USR_FS_TEST;
      		break;
    	case USH_USR_FS_TEST:	//执行USB OTG 测试主程序
			res=USH_User_App(); //用户主程序
     		res=0;
			if(res)AppState=USH_USR_FS_INIT;
      		break;
    	default:break;
  	} 
	return res;
}
//用户要求重新初始化设备
void USBH_USR_DeInit(void)
{
  	AppState=USH_USR_FS_INIT;
}
//无法恢复的错误!!  
void USBH_USR_UnrecoveredError (void)
{
	printf("无法恢复的错误!!!\r\n\r\n");	
}

//用户定义函数,实现fatfs diskio的接口函数 
extern USBH_HOST              USB_Host;

//获取U盘状态
//返回值:0,U盘未就绪
//      1,就绪
u8 USBH_UDISK_Status(void)
{
	return HCD_IsDeviceConnected(&USB_OTG_Core);//返回U盘状态
}

//读U盘
//buf:读数据缓存区
//sector:扇区地址
//cnt:扇区个数	
//返回值:错误状态;0,正常;其他,错误代码;		 
u8 USBH_UDISK_Read(u8* buf,u32 sector,u32 cnt)
{
	u8 res=1;
	if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)//连接还存在,且是APP测试状态
	{  		    
		do
		{
			res=USBH_MSC_Read10(&USB_OTG_Core,buf,sector,512*cnt);
			USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);		      
			if(!HCD_IsDeviceConnected(&USB_OTG_Core))
			{
				res=1;//读写错误
				break;
			};   
		}while(res==USBH_MSC_BUSY);
	}else res=1;		  
	if(res==USBH_MSC_OK)res=0;	
	return res;
}

//写U盘
//buf:写数据缓存区
//sector:扇区地址
//cnt:扇区个数	
//返回值:错误状态;0,正常;其他,错误代码;		 
u8 USBH_UDISK_Write(u8* buf,u32 sector,u32 cnt)
{
	u8 res=1;
	if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)//连接还存在,且是APP测试状态
	{  		    
		do
		{
			res=USBH_MSC_Write10(&USB_OTG_Core,buf,sector,512*cnt); 
			USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);		      
			if(!HCD_IsDeviceConnected(&USB_OTG_Core))
			{
				res=1;//读写错误
				break;
			};   
		}while(res==USBH_MSC_BUSY);
	}else res=1;		  
	if(res==USBH_MSC_OK)res=0;	
	return res;
}

  其中,USBH_USR_MSC_Application 函数通过状态机的方式,处理相关事务,执行到这个函数,说明 U 盘已经被成功识别了,此时用户可以执行一些自己想要做的事情,比如读取 U 盘文件什么的,这里我们直接进入到 USH_User_App 函数,执行各种处理,后续会介绍该函数。
  USBH_UDISK_Read 和 USBH_UDISK_Write 这两个函数,用于 U 盘读写,从指定扇区地址读写指定个数的扇区数据,这两个函数,再配合 fatfs,即可实现对 U 盘的文件读写访问。文章来源地址https://www.toymoban.com/news/detail-737278.html

3.4 Host 测试函数

#include "usbh_usr.h" 

USBH_HOST  USB_Host;
USB_OTG_CORE_HANDLE  USB_OTG_Core;

//用户测试主程序
//返回值:0,正常
//       1,有问题
u8 USH_User_App(void)
{ 
	u32 total,free;
	u8 res=0;
	printf("设备连接成功!.");	 
	res=exf_getfree("2:",&total,&free);		//2为U盘
	if(res==0)
	{	 	    
		printf("U Disk Total Size:%d MB\r\n",total); //显示U盘总容量 MB
		printf("U Disk  Free Size:%d MB\r\n",free);  //显示U盘剩余容量 MB
	} 
 
	while(HCD_IsDeviceConnected(&USB_OTG_Core))//设备连接成功
	{	
		LED1=!LED1;
		delay_ms(200);
	}
	printf("设备连接中...");
	return res;
} 


int main(void)
{        
	u8 t;
	
/*************
	一系列初始化
	*/
	
  f_mount(fs[2],"2:",1); 	//挂载U盘
     
	//初始化USB主机
  USBH_Init(&USB_OTG_Core,USB_OTG_FS_CORE_ID,&USB_Host,&USBH_MSC_cb,&USR_Callbacks);  
	while(1)
	{
		USBH_Process(&USB_OTG_Core, &USB_Host);
		delay_ms(1);
		t++;
		if(t==200)
		{
			LED0=!LED0;
			t=0;
		}
	}	
}
  1. 使用 USB 主机的时候,需要两个结构体:USB_OTG_CORE_HANDLE和 USB_HOST。
  2. USB 初始化,如果是:USB SLAVE 通信,在只需要调用 USBD_Init 函数即可,不过 USB HOST 则还需要调用另外一个函数 USBH_Process,该函数用于实现 USB 主机通信的核心状态机处理,该函数必须在主函数里面,被循环调用,而且调用频率得比较快才行(越快越好),以便及时处理各种事务
  3. USH_User_App 函数,该函数是在 USBH_USR_MSC_Application 函数里面被调用,用于实现 U 盘插入后,用户想要实现的功能

到了这里,关于单片机USB应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【单片机】基于STM32的UART串口通信

    简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作。实验内容基于 正点原子精英板 开发板,单片机芯片为 STM32F103ZET6 。 在后面我会以我使用的STM32F429开发板来举例讲解(其他STM32系列芯片大多数都可以按照这些步骤来操作

    2024年01月17日
    浏览(70)
  • STM32单片机初学4-IIC通信(软件模拟)

    IIC ( Inter-Integrated Circuit )又称I2C(习惯读“I方C”),是 IIC Bus简称,中文名为 集成电路总线 ,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。适用于IC间的短距离数据传输。 最初的IIC通信速

    2024年02月05日
    浏览(69)
  • openmv识别物体并与单片机通信(STM32)

    感光元件 寻找色块 find_blobs函数:此函数参数较多,一般只用第一个参数就可以找到需要的色块。thresholds是颜色的阈值,是一个列表,这个列表可以有多个颜色阈值。 一个颜色阈值的结构是这样的: 画图 x,y:是坐标 size:是两侧的尺寸 color:画十字的颜色 rect_tuple 的格式是

    2023年04月08日
    浏览(47)
  • STM32单片机初学5-IIC通信驱动OLED屏幕

    在我上篇文章(STM32-软件模拟IIC通信)讲解了软件模拟IIC通信。这篇文章详将细讲解利用软件模拟IIC来控制0.96寸的OLED屏幕(如下图),使其显示字符串。本文将不再对IIC通信原理做详细讲解,所以对IIC通信原理不熟悉的话可以参考我上篇文章(点击上面的链接直接跳转)。

    2023年04月10日
    浏览(51)
  • 关于两个STM32F103系列单片机的蓝牙通信

       毕设做的是掌控小车,因此采用蓝牙通信作为小车和手部通信,前段时间做出实物,对其遇到的问题以及解决的方法做一些总结。一个主控芯片采用STM32F103ZET6,另一个主控芯片采用STM32F103C8T6,原因是本来准备了两个主控C8T6,不小心烧了一个。 1.两个蓝牙的配对  需要准

    2024年02月13日
    浏览(58)
  • STM32+esp8266实现单片机与服务器的WiFi通信

             源码已上传至gitee: stm32: 一些stm32模块使用经验记录 - Gitee.com https://gitee.com/lrf1125962926/stm32/tree/esp8266wifi%E9%80%9A%E4%BF%A1/         本实验采用STM32F1系列+esp8266 01s模块,采用HAL库开发。主控芯片哪个系列和型号都可以,只要有两个串口(UART或者USART,以下统称串口)

    2024年02月07日
    浏览(53)
  • 通信工程毕设 Stm32酒驾检查系统 - 单片机 嵌入式 物联网

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月19日
    浏览(131)
  • 通信工程毕设 stm32智能运动计步系统 - 物联网 嵌入式 单片机

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月21日
    浏览(120)
  • 通信工程毕设 Stm32单片机的音乐播放器设计 - 物联网 嵌入式

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月19日
    浏览(53)
  • 通信工程毕设 基于Stm32的便携体测仪(心率 体温) - 单片机 嵌入式 物联网

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月20日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包