USB复合设备(键盘鼠标U盘三合一)基于标准库

这篇具有很好参考价值的文章主要介绍了USB复合设备(键盘鼠标U盘三合一)基于标准库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

键盘鼠标属于HID,U盘功能属于MSC。至于这些定义,这里不再过多介绍。

网上有很多的例程,但是大多是基于HAL库的,标准库的我也找了不少例子看,但是没有HID+MSC的例程。最后还是看了个官方的复合设备例程才顿悟的,官方的例程,网上也很好找。搜USB Composite examples应该就能找到。

手上的设备是基于stm32f1系列的,目前已经复合了键盘和鼠标,想要新增加一个U盘的功能。由于已经是成熟的产品了,硬件方面不方便修改,所以这里采用单片机内部的flash来模拟U盘功能。要去掉程序存储的空间,我的单片机大小是512k,所以这里给U盘配置400k。

USB复合设备(键盘鼠标U盘三合一)基于标准库

首先修改的就是usb_desc.c文件。这个文件主要存放的是一些描述符。一般来说,设备描述符是不需要修改的,主要修改的是配置描述符。

`//USB配置描述符
const uint8_t HID_ConfigDescriptor[CONFIG_DESC_SIZE] =
{
 0x09, /* bLength: Configuration Descriptor size */
 CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
 CONFIG_DESC_SIZE,//***********配置描述符长度,定义在.h文件中,移植需修改。
 0x00,
 0x03,	//*********************接口数量配置,移植需修改。之前鼠标键盘为2,这里增加U盘改成3
 0x01,	//bConfiguration字段
 0x00,	//iConfigurationz字段
 0xC0,	//bmAttributes字段
 0x32,	//bMaxPower字段
 /*******************第一个接口描述符(键盘)*********************/
 0x09,	//bLength字段
 0x04,	//bDescriptorType字段
 0x00,	//bInterfaceNumber字段 接口的编号,第一个接口为00
 0x00,	//bAlternateSetting字段
 0x02,	//bNumEndpoints字段,端点的数量,这里键盘采用输入加输出,所以2个端点
 0x03,	//bInterfaceClass字段
 0x01,	//bInterfaceSubClass字段
 0x01,	//bInterfaceProtocol字段
 0x00,	//iConfiguration字段
 
 /******************HID描述符************************/
 0x09,	//bLength字段
 0x21,	//bDescriptorType字段
 0x10,	//bcdHID字段
 0x01,
 0x21,	//bCountyCode字段
 0x01,	//bNumDescriptors字段
 0x22,	//bDescriptorType字段
 sizeof(HID_ReportDescriptor_KEY)&0xFF,  //HID_ReportDescriptor_KEY为键盘的报告描述符
 (sizeof(HID_ReportDescriptor_KEY)>>8)&0xFF,
 
 /**********************输入端点描述符***********************/
 0x07,	//bLength字段
 0x05,	//bDescriptorType字段
 0x81,	//bEndpointAddress字段,01端点的输入地址
 0x03,	//bmAttributes字段
 0x10,	//wMaxPacketSize字段
 0x00,
 0x01,	//bInterval字段
 /**********************输出端点描述符***********************/
 0x07,	//bLength字段
 0x05,	//bDescriptorType字段
 0x01,	//bEndpointAddress字段,01d端点的输出地址
 0x03,	//bmAttributes字段
 0x10,	//wMaxPacketSize字段
 0x00,
 0x01,	//bInterval字段
 /*******************第二个接口描述符(鼠标)*********************/
 0x09,	//bLength字段
 0x04,	//bDescriptorType字段
 0x01,	//bInterfaceNumber字段,第二个接口,为01
 0x00,	//bAlternateSetting字段
 0x01,	//bNumEndpoints字段,我的鼠标采用了一个端点,所以为01
 0x03,	//bInterfaceClass字段
 0x01,	//bInterfaceSubClass字段
 0x02,	//bInterfaceProtocol字段
 0x00,	//iConfiguration字段
 /******************HID描述符************************/
 0x09,	//bLength字段
 0x21,	//bDescriptorType字段
 0x10,	//bcdHID字段
 0x01,
 0x21,	//bCountyCode字段
 0x01,	//bNumDescriptors字段
 0x22,	//bDescriptorType字段
 sizeof(HID_ReportDescriptor_MOUSE)&0xFF,
 (sizeof(HID_ReportDescriptor_MOUSE)>>8)&0xFF,
 /**********************输入端点描述符***********************/
 0x07,	//bLength字段
 0x05,	//bDescriptorType字段
 0x83,	//bEndpointAddress字段,03端点的地址。
 0x03,	//bmAttributes字段。D1~D0为端点传输类型选择
 0x40,	//wMaxPacketSize字段
 0x00,
 0x01, //bInterval字段
 
 
//新增的U盘接口相关代码
/
 /******************** 第三个接口描述符(U盘) ********************/
 0x09,   /* bLength: Interface Descriptor size */
 0x04,   /* bDescriptorType: */
 0x02,   /* bInterfaceNumber: Number of Interface 第三个接口的编号*/
 0x00,   /* bAlternateSetting: Alternate setting */
 0x02,   /* bNumEndpoints,U盘是有输入输出的,所以必须是两个端点。*/
 0x08,   /* bInterfaceClass: MASS STORAGE Class ,注意要识别成大容量存储设备,这里必须选08*/
 0x06,   /* bInterfaceSubClass : SCSI transparent*/
 0x50,   /* nInterfaceProtocol */
 1,          /* iInterface: */
 /******************** 输入端点描述符 ******************/
 0x07,   /*Endpoint descriptor length = 7*/
 0x05,   /*Endpoint descriptor type */
 0x82,   /*Endpoint address端点2的地址  */
 0x02,   /*Bulk endpoint type */
 0x40,   /*Maximum packet size (64 bytes) */
 0x00,
 0x00,   /*Polling interval in milliseconds */
 /******************** 输出端点描述符 ******************/
 0x07,   /*Endpoint descriptor length = 7 */
 0x05,   /*Endpoint descriptor type */
 0x02,   /*Endpoint address端点2的地址 */
 0x02,   /*Bulk endpoint type */
 0x40,   /*Maximum packet size (64 bytes) */
 0x00,
 0x00     /*Polling interval in milliseconds*/


};`

上面配置描述符和例程里面的都大同小异,主要是一些需要根据自己设备来修改的地方,但我进行了备注,根据备注理解改起来也很容易。
配置描述符改完,usb_desc.c文件的剩下的内容可以不做修改,要是没有鼠标键盘的报告描述符,可以搜一个,然后根据其长度,修改配置描述符的大小就可以。

然后要修改的是usb_endp文件。这个主要根据上面的端点描述符来修改。

`uint8_t Receive_Buffer[2];
__IO uint8_t PrevXferComplete;
//键盘的
void EP1_OUT_Callback(void)
{
  USB_SIL_Read(EP1_OUT, Receive_Buffer);
  SetEPRxStatus(ENDP1, EP_RX_VALID);
}

void EP1_IN_Callback(void)
{
  PrevXferComplete = 1;
}

//鼠标
void EP3_IN_Callback(void)
{
  PrevXferComplete = 1;
}

//u盘
void EP2_IN_Callback(void)
{
  Mass_Storage_In();
}

void EP2_OUT_Callback(void)
{
  Mass_Storage_Out();
}
`

当然还需要配置每个端点的地址。这里的地址好像可以随便配置,但是最好是需要相差64k的,也就是0x40.

/* EP0  */
/* rx/tx buffer base address */
#define ENDP0_RXADDR        (0x18)
#define ENDP0_TXADDR        (0x58)

/* EP1  */
/* tx buffer base address */
/* tx buffer base address */
#define ENDP1_TXADDR        (0x118)
#define ENDP1_RXADDR		(0x11C)
/* EP3  */
/* tx buffer base address */
#define ENDP3_TXADDR        (0x198)

/* EP2  */
/* tx buffer base address */
/* tx buffer base address */
#define ENDP2_TXADDR        (0x98)
#define ENDP2_RXADDR		(0xD8)

接下来就是配置usb_prop.c文件来识别这些设备。
主要修改的是下面几个函数。

/*******************************************************************************
* Function Name  : CustomHID_Reset.
* Description    : Custom HID reset routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_Reset(void)
{
	/* Set CustomHID_DEVICE as not configured */
	pInformation->Current_Configuration = 0;
	pInformation->Current_Interface = 0;/*the default Interface*/
	pInformation->Current_Feature = HID_ConfigDescriptor[7];

	SetBTABLE(BTABLE_ADDRESS);

	/* Initialize Endpoint 0 */
	SetEPType(ENDP0, EP_CONTROL);
	SetEPTxStatus(ENDP0, EP_TX_STALL);
	SetEPRxAddr(ENDP0, ENDP0_RXADDR);
	SetEPTxAddr(ENDP0, ENDP0_TXADDR);
	Clear_Status_Out(ENDP0);
	SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
	SetEPRxValid(ENDP0);

	/* Initialize Endpoint 1 */
	SetEPType(ENDP1, EP_INTERRUPT);
	SetEPTxAddr(ENDP1, ENDP1_TXADDR);
	SetEPRxAddr(ENDP1, ENDP1_RXADDR);
	SetEPTxCount(ENDP1, 8);
	SetEPRxCount(ENDP1, 2);
	SetEPRxStatus(ENDP1, EP_RX_VALID);
	SetEPTxStatus(ENDP1, EP_TX_NAK);


	/* Initialize Endpoint In 3 */
	SetEPType(ENDP3, EP_INTERRUPT); //初始化为中断端点类型
	SetEPTxAddr(ENDP3, ENDP3_TXADDR); //设置发送数据的地址
	SetEPTxCount(ENDP3, 5); //设置发送的长度
	SetEPTxStatus(ENDP3, EP_TX_NAK); //设置端点处于忙状态
	/* Set this device to response on default address */
	
	/* 初始化端点2的输入 */
	SetEPType(ENDP2, EP_BULK);
	SetEPTxCount(ENDP2, 64);
	SetEPTxAddr(ENDP2, ENDP2_TXADDR);
	SetEPTxStatus(ENDP2, EP_TX_NAK);
	/* 初始化端点2的输出 */
	SetEPType(ENDP2, EP_BULK);
	SetEPRxAddr(ENDP2, ENDP2_RXADDR);
	SetEPRxCount(ENDP2, 64);
	SetEPRxStatus(ENDP2, EP_RX_VALID);
	
	bDeviceState = ATTACHED;
	SetDeviceAddress(0);
}

根据前面自己的配置来修改,端点0是启动usb所需要的。端点2是U盘的,所以类型不一样。

/*******************************************************************************
* Function Name  : CustomHID_Data_Setup
* Description    : Handle the data class specific requests.
* Input          : Request Nb.
* Output         : None.
* Return         : USB_UNSUPPORT or USB_SUCCESS.
*******************************************************************************/
RESULT CustomHID_Data_Setup(uint8_t RequestNo)
{
	u8 *(*CopyRoutine)(u16);

	CopyRoutine = NULL;
	if ((RequestNo == GET_DESCRIPTOR)
	  && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
	  && (pInformation->USBwIndex0 < 2))
	{

	if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
	{
		if (pInformation->USBwIndex0 == 0)//进行轮询查询,0为键盘。其他为鼠标,若是复合三个或多个hid可以0123一次增加。
			CopyRoutine = KP_GetReportDescriptor;
		else
			CopyRoutine = Mouse_GetReportDescriptor;
	}
	else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
	{
		if (pInformation->USBwIndex0 == 0)
			CopyRoutine = KP_GetHIDDescriptor;
		else
			CopyRoutine = Mouse_GetHIDDescriptor;
	}

  } /* End of GET_DESCRIPTOR */

  /*** GET_PROTOCOL ***/
  else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
           && RequestNo == GET_PROTOCOL)
  {
    CopyRoutine = CustomHID_GetProtocolValue;
  }


  if (CopyRoutine == NULL)
  {
    return USB_UNSUPPORT;
  }

  pInformation->Ctrl_Info.CopyData = CopyRoutine;
  pInformation->Ctrl_Info.Usb_wOffset = 0;
  (*CopyRoutine)(0);
  return USB_SUCCESS;
}

如果你是从只有键盘的工程代码中修改的话,还需要添加鼠标相关的获取函数。这里直接贴出prop文件代码。

#include "hw_config.h" 
#include "usb_lib.h"
#include "usb_conf.h"
#include "usb_prop.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "usb_bot.h"
#include "memory.h"
#include "mass_mal.h"

uint32_t ProtocolValue;
__IO uint8_t EXTI_Enable;
__IO uint8_t Request = 0;
uint8_t Report_Buf[2];

DEVICE Device_Table =
{
	EP_NUM,
	1
};

/*CustomHID_SetReport_Feature function prototypes*/
uint8_t *CustomHID_SetReport_Feature(uint16_t Length);

extern unsigned char Bot_State;
extern Bulk_Only_CBW CBW;
uint32_t Max_Lun = 0; 

DEVICE_PROP Device_Property =
{
	CustomHID_init,
	CustomHID_Reset,
	CustomHID_Status_In,
	CustomHID_Status_Out,
	CustomHID_Data_Setup,
	CustomHID_NoData_Setup,
	CustomHID_Get_Interface_Setting,
	CustomHID_GetDeviceDescriptor,
	CustomHID_GetConfigDescriptor,
	CustomHID_GetStringDescriptor,
	0,
	0x40 /*MAX PACKET SIZE*/
};
USER_STANDARD_REQUESTS User_Standard_Requests =
{
	CustomHID_GetConfiguration,
	CustomHID_SetConfiguration,
	CustomHID_GetInterface,
	CustomHID_SetInterface,
	CustomHID_GetStatus,
	CustomHID_ClearFeature,
	CustomHID_SetEndPointFeature,
	CustomHID_SetDeviceFeature,
	CustomHID_SetDeviceAddress
};

ONE_DESCRIPTOR Device_Descriptor =
{
	(uint8_t*)HID_DeviceDescriptor,
	DEVICE_DESC_SIZE
};

ONE_DESCRIPTOR Config_Descriptor =
{
	(uint8_t*)HID_ConfigDescriptor,
	CONFIG_DESC_SIZE
};

ONE_DESCRIPTOR KP_Report_Descriptor =							   	
  {																	
    (u8 *)HID_ReportDescriptor_KEY,								
    63										
  };																
																	
ONE_DESCRIPTOR KP_Hid_Descriptor =									
  {																	
    (u8*)HID_ConfigDescriptor + 18,				
    9											
  };	
	
	ONE_DESCRIPTOR Mouse_Report_Descriptor =							
  {																	
    (u8 *)HID_ReportDescriptor_MOUSE,								
    54										
  };																
																	
ONE_DESCRIPTOR Mouse_Hid_Descriptor =						
  {														
    (u8*)HID_ConfigDescriptor + 50,			
    9											
  };

ONE_DESCRIPTOR String_Descriptor[4] =
{
	{ (uint8_t*)HID_LangIDString, LANGID_STRING },
	{ (uint8_t*)HID_VendorString, VENDOR_STRING_SIZE },
	{ (uint8_t*)HID_ProductString, PRODUCT_STRING_SIZE },
	{ (uint8_t*)HID_SerialString, SERIAL_STRING_SIZE }
};

/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/

/*CustomHID_SetReport_Feature function prototypes*/
uint8_t *CustomHID_SetReport_Feature(uint16_t Length);

/* Extern function prototypes ------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : CustomHID_init.
* Description    : Custom HID init routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_init(void)
{
	Get_SerialNum();

	pInformation->Current_Configuration = 0;
	/* Connect the device */
	PowerOn();

	/* Perform basic device initialization operations */
	USB_SIL_Init();

	bDeviceState = UNCONNECTED;
}

/*******************************************************************************
* Function Name  : CustomHID_Reset.
* Description    : Custom HID reset routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_Reset(void)
{
	/* Set CustomHID_DEVICE as not configured */
	pInformation->Current_Configuration = 0;
	pInformation->Current_Interface = 0;/*the default Interface*/
	pInformation->Current_Feature = HID_ConfigDescriptor[7];

	SetBTABLE(BTABLE_ADDRESS);

	/* Initialize Endpoint 0 */
	SetEPType(ENDP0, EP_CONTROL);
	SetEPTxStatus(ENDP0, EP_TX_STALL);
	SetEPRxAddr(ENDP0, ENDP0_RXADDR);
	SetEPTxAddr(ENDP0, ENDP0_TXADDR);
	Clear_Status_Out(ENDP0);
	SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
	SetEPRxValid(ENDP0);

	/* Initialize Endpoint 1 */
	SetEPType(ENDP1, EP_INTERRUPT);
	SetEPTxAddr(ENDP1, ENDP1_TXADDR);
	SetEPRxAddr(ENDP1, ENDP1_RXADDR);
	SetEPTxCount(ENDP1, 8);
	SetEPRxCount(ENDP1, 2);
	SetEPRxStatus(ENDP1, EP_RX_VALID);
	SetEPTxStatus(ENDP1, EP_TX_NAK);


	/* Initialize Endpoint In 3 */
	SetEPType(ENDP3, EP_INTERRUPT); //初始化为中断端点类型
	SetEPTxAddr(ENDP3, ENDP3_TXADDR); //设置发送数据的地址
	SetEPTxCount(ENDP3, 5); //设置发送的长度
	SetEPTxStatus(ENDP3, EP_TX_NAK); //设置端点处于忙状态
	/* Set this device to response on default address */
	
	/* 初始化端点2的输入 */
	SetEPType(ENDP2, EP_BULK);
	SetEPTxCount(ENDP2, 64);
	SetEPTxAddr(ENDP2, ENDP2_TXADDR);
	SetEPTxStatus(ENDP2, EP_TX_NAK);
	/* 初始化端点2的输出 */
	SetEPType(ENDP2, EP_BULK);
	SetEPRxAddr(ENDP2, ENDP2_RXADDR);
	SetEPRxCount(ENDP2, 64);
	SetEPRxStatus(ENDP2, EP_RX_VALID);
	
	bDeviceState = ATTACHED;
	SetDeviceAddress(0);
}
/*******************************************************************************
* Function Name  : CustomHID_SetConfiguration.
* Description    : Update the device state to configured and command the ADC
*                  conversion.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_SetConfiguration(void)
{
	if (pInformation->Current_Configuration != 0)
		bDeviceState = CONFIGURED;		// Device configured
}
/*******************************************************************************
* Function Name  : CustomHID_SetConfiguration.
* Description    : Update the device state to addressed.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_SetDeviceAddress(void)
{
//	bDeviceState = ADDRESSED;
}
/*******************************************************************************
* Function Name  : CustomHID_Status_In.
* Description    : Joystick status IN routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_Status_In(void)
{
	if (Report_Buf[1] == 0)
	{
		//Led_State = Bit_RESET;
	}
	else
	{
		//Led_State = Bit_SET;
	}

	switch (Report_Buf[0])
	{
		/*Change LED's status according to the host report*/

		case 1: /* Led 1 */
			break;
		case 2:   /* Led 2 */
			break;
		case 3:/* Led 3 */
			break;
		case 4:/* Led 4 */
			break;
		default:
			break;
	}
}

/*******************************************************************************
* Function Name  : CustomHID_Status_Out
* Description    : Joystick status OUT routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void CustomHID_Status_Out(void)
{
}

/*******************************************************************************
* Function Name  : CustomHID_Data_Setup
* Description    : Handle the data class specific requests.
* Input          : Request Nb.
* Output         : None.
* Return         : USB_UNSUPPORT or USB_SUCCESS.
*******************************************************************************/
RESULT CustomHID_Data_Setup(uint8_t RequestNo)
{
	u8 *(*CopyRoutine)(u16);

	CopyRoutine = NULL;
	if ((RequestNo == GET_DESCRIPTOR)
	  && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
	  && (pInformation->USBwIndex0 < 2))
	{

	if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
	{
		if (pInformation->USBwIndex0 == 0)
			CopyRoutine = KP_GetReportDescriptor;
		else
			CopyRoutine = Mouse_GetReportDescriptor;
	}
	else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
	{
		if (pInformation->USBwIndex0 == 0)
			CopyRoutine = KP_GetHIDDescriptor;
		else
			CopyRoutine = Mouse_GetHIDDescriptor;
	}

  } /* End of GET_DESCRIPTOR */

  /*** GET_PROTOCOL ***/
  else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
           && RequestNo == GET_PROTOCOL)
  {
    CopyRoutine = CustomHID_GetProtocolValue;
  }


  if (CopyRoutine == NULL)
  {
    return USB_UNSUPPORT;
  }

  pInformation->Ctrl_Info.CopyData = CopyRoutine;
  pInformation->Ctrl_Info.Usb_wOffset = 0;
  (*CopyRoutine)(0);
  return USB_SUCCESS;
}

/*******************************************************************************
* Function Name  : CustomHID_SetReport_Feature
* Description    : Set Feature request handling
* Input          : Length.
* Output         : None.
* Return         : Buffer
*******************************************************************************/
uint8_t *CustomHID_SetReport_Feature(uint16_t Length)
{
	if (Length == 0)
	{
		pInformation->Ctrl_Info.Usb_wLength = 2;
		return NULL;
	}
	else
	{
		return Report_Buf;
	}
}

/*******************************************************************************
* Function Name  : CustomHID_NoData_Setup
* Description    : handle the no data class specific requests
* Input          : Request Nb.
* Output         : None.
* Return         : USB_UNSUPPORT or USB_SUCCESS.
*******************************************************************************/
RESULT CustomHID_NoData_Setup(uint8_t RequestNo)
{
	if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
		&& (RequestNo == SET_PROTOCOL))
	{
		return CustomHID_SetProtocol();
	}

	else
	{
		return USB_UNSUPPORT;
	}
}

/*******************************************************************************
* Function Name  : CustomHID_GetDeviceDescriptor.
* Description    : Gets the device descriptor.
* Input          : Length
* Output         : None.
* Return         : The address of the device descriptor.
*******************************************************************************/
uint8_t *CustomHID_GetDeviceDescriptor(uint16_t Length)
{
	return Standard_GetDescriptorData(Length, &Device_Descriptor);
}

/*******************************************************************************
* Function Name  : CustomHID_GetConfigDescriptor.
* Description    : Gets the configuration descriptor.
* Input          : Length
* Output         : None.
* Return         : The address of the configuration descriptor.
*******************************************************************************/
uint8_t *CustomHID_GetConfigDescriptor(uint16_t Length)
{
	return Standard_GetDescriptorData(Length, &Config_Descriptor);
}

/*******************************************************************************
* Function Name  : CustomHID_GetStringDescriptor
* Description    : Gets the string descriptors according to the needed index
* Input          : Length
* Output         : None.
* Return         : The address of the string descriptors.
*******************************************************************************/
uint8_t *CustomHID_GetStringDescriptor(uint16_t Length)
{
	uint8_t wValue0 = pInformation->USBwValue0;
//	if (wValue0 >= 4)
//	{
//		return NULL;
//	}
//	else
//	{
		return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]);
//	}
}

/*******************************************************************************
* Function Name  : Joystick_GetReportDescriptor.
* Description    : Gets the HID report descriptor.
* Input          : Length
* Output         : None.
* Return         : The address of the configuration descriptor.
*******************************************************************************/
u8 *KP_GetReportDescriptor(u16 Length)
{
  return Standard_GetDescriptorData(Length, &KP_Report_Descriptor);
}

u8 *Mouse_GetReportDescriptor(u16 Length)
{
  return Standard_GetDescriptorData(Length, &Mouse_Report_Descriptor);
}

/*******************************************************************************
* Function Name  : Joystick_GetHIDDescriptor.
* Description    : Gets the HID descriptor.
* Input          : Length
* Output         : None.
* Return         : The address of the configuration descriptor.
*******************************************************************************/
u8 *KP_GetHIDDescriptor(u16 Length)
{
	return Standard_GetDescriptorData(Length, &KP_Hid_Descriptor);
}
u8 *Mouse_GetHIDDescriptor(u16 Length)
{
  return Standard_GetDescriptorData(Length, &Mouse_Hid_Descriptor);
}

/*******************************************************************************
* Function Name  : CustomHID_Get_Interface_Setting.
* Description    : tests the interface and the alternate setting according to the
*                  supported one.
* Input          : - Interface : interface number.
*                  - AlternateSetting : Alternate Setting number.
* Output         : None.
* Return         : USB_SUCCESS or USB_UNSUPPORT.
*******************************************************************************/
RESULT CustomHID_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting)
{
	if (AlternateSetting > 0)
	{
		return USB_UNSUPPORT;
	}
	else if (Interface > 0)
	{
		return USB_UNSUPPORT;
	}
	return USB_SUCCESS;
}

/*******************************************************************************
* Function Name  : CustomHID_SetProtocol
* Description    : Joystick Set Protocol request routine.
* Input          : None.
* Output         : None.
* Return         : USB SUCCESS.
*******************************************************************************/
RESULT CustomHID_SetProtocol(void)
{
	uint8_t wValue0 = pInformation->USBwValue0;
	ProtocolValue = wValue0;
	return USB_SUCCESS;
}

/*******************************************************************************
* Function Name  : CustomHID_GetProtocolValue
* Description    : get the protocol value
* Input          : Length.
* Output         : None.
* Return         : address of the protocol value.
*******************************************************************************/
uint8_t *CustomHID_GetProtocolValue(uint16_t Length)
{
	if (Length == 0)
	{
		pInformation->Ctrl_Info.Usb_wLength = 1;
		return NULL;
	}
	else
	{
		return (uint8_t *)(&ProtocolValue);
	}
}



//新增函数
/
uint8_t *Get_Max_Lun(uint16_t Length)
{
  if (Length == 0)
  {
    pInformation->Ctrl_Info.Usb_wLength = LUN_DATA_LENGTH;
    return 0;
  }
  else
  {
    return((uint8_t*)(&Max_Lun));
  }
}

void CustomHID_ClearFeature (void)
{
    if (CBW.dSignature != BOT_CBW_SIGNATURE)
    Bot_Abort(BOTH_DIR);
}
///

下面就该修改MSC相关的。
首先是文件mass_mal.c

#include "fatfs_flash_spi.h"
#include "stm32f10x_flash.h"
#include "mass_mal.h"
#include <stdio.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t Mass_Memory_Size[2];
uint32_t Mass_Block_Size[2];
uint32_t Mass_Block_Count[2];
__IO uint32_t Status = 0;
//#define  sFLASH_ID              0xEF3015     //W25X16
//#define  sFLASH_ID              0xEF4015	 //W25Q16
//#define  sFLASH_ID              0XEF4017    //W25Q64
//#define  sFLASH_ID              0XEF4018   //W25Q128
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name  : MAL_Init
* Description    : 初始化STM32上的媒体设备
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
uint16_t MAL_Init(uint8_t lun)
{
  uint16_t status = MAL_OK;

  switch (lun)
  {
    case 0:
		   FLASH_Unlock();
      break;
    default:
      return MAL_FAIL;
  }
  return status;
}
/*******************************************************************************
* Function Name  : MAL_Write
* Description    : Write sectors
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length) 
{ uint16_t i;
	switch (lun) 
{ 
case 0: 
 for(i=0;i<Transfer_Length;i+=FLASH_PAGE_SIZE) 
	
 {
	if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT)
	
 { 
	FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
	
 } 

FLASH_ErasePage(FLASH_START_ADDR + Memory_Offset + i);

}

for(i=0;i<Transfer_Length;i+=4) 

{
if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT)
	
{ 
	FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR| FLASH_FLAG_WRPRTERR);
	
} 

FLASH_ProgramWord(FLASH_START_ADDR + Memory_Offset + i , Writebuff[i>>2]); 
}

break;
default: return MAL_FAIL; 
} 
return MAL_OK;
}

/*******************************************************************************
* Function Name  : MAL_Read
* Description    : Read sectors
* Input          : None
* Output         : None
* Return         : Buffer pointer
*******************************************************************************/
uint16_t MAL_Read(uint8_t lun, uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length) 

{ uint16_t i;
	switch (lun)
		{ case 0:
      for(i=0;i<Transfer_Length;i+=4)
      { 
	     Readbuff[i>>2] = ((vu32*)(FLASH_START_ADDR + Memory_Offset))[i>>2]; 
       }
break;

default: return MAL_FAIL;

}
return MAL_OK;

}

/*******************************************************************************
* Function Name  : MAL_GetStatus
* Description    : Get status
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
uint16_t MAL_GetStatus (uint8_t lun) 
{ 
	if (lun == 0)
 { 
 Mass_Block_Count[0] = FLASH_SIZE/FLASH_PAGE_SIZE; 
 Mass_Block_Size[0] = FLASH_PAGE_SIZE; 
 Mass_Memory_Size[0] = FLASH_SIZE;
 return MAL_OK;
 } 
 
  return MAL_FAIL; }



/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

其余的文件只需要修改端点为前面设置的端点就好了。
当然要进行flash的操作还需要stm32f10x_flash文件。
这里还有一个fatfs_flash_spi.c文件

 /**
  ******************************************************************************
  * @file    bsp_xxx.c
  * @author  STMicroelectronics
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   spi flash 底层应用函数bsp 
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火 F103-指南者 STM32 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */
  
#include <fatfs_flash_spi.h>
#include "stm32f10x_spi.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;    
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

/**
  * @brief  SPI_FLASH初始化
  * @param  无
  * @retval 无
  */
uint8_t FLASH_SPI_disk_initialize(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
	
	/* 使能SPI时钟 */
	FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );
	
	/* 使能SPI引脚相关的时钟 */
 	FLASH_SPI_CS_APBxClock_FUN ( FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
																	FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
	
  /* 配置SPI的 CS引脚,普通IO即可 */
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
	
  /* 配置SPI的 SCK引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);

  /* 配置SPI的 MISO引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);

  /* 配置SPI的 MOSI引脚*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);

  /* 停止信号 FLASH: CS引脚高电平*/
  SPI_FLASH_CS_HIGH();

  /* SPI 模式配置 */
  // FLASH芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(FLASH_SPIx , &SPI_InitStructure);

  /* 使能 SPI  */
  SPI_Cmd(FLASH_SPIx , ENABLE);
	
	
	if(sFLASH_ID == SPI_FLASH_ReadID())			/*检测FLASH是否正常工作*/
	{
		return 0;	/* Clear STA_NOINIT flag */
	}else
	{
		return 1;
	}
}
 /**
  * @brief  擦除FLASH扇区
  * @param  SectorAddr:要擦除的扇区地址
  * @retval 无
  */
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();
  SPI_FLASH_WaitForWriteEnd();
  /* 擦除扇区 */
  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 发送扇区擦除指令*/
  SPI_FLASH_SendByte(W25X_SectorErase);
  /*发送擦除扇区地址的高位*/
  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  /* 发送擦除扇区地址的中位 */
  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  /* 发送擦除扇区地址的低位 */
  SPI_FLASH_SendByte(SectorAddr & 0xFF);
  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();
  /* 等待擦除完毕*/
  SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief  擦除FLASH扇区,整片擦除
  * @param  无
  * @retval 无
  */
void SPI_FLASH_BulkErase(void)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();

  /* 整块 Erase */
  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 发送整块擦除指令*/
  SPI_FLASH_SendByte(W25X_ChipErase);
  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();

  /* 等待擦除完毕*/
  SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer,要写入数据的指针
  * @param WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
  * @retval 无
  */
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  /* 发送FLASH写使能命令 */
  SPI_FLASH_WriteEnable();

  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();
  /* 写页写指令*/
  SPI_FLASH_SendByte(W25X_PageProgram);
  /*发送写地址的高位*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /*发送写地址的中位*/
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /*发送写地址的低位*/
  SPI_FLASH_SendByte(WriteAddr & 0xFF);

  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
     NumByteToWrite = SPI_FLASH_PerWritePageSize;
     FLASH_ERROR("SPI_FLASH_PageWrite too large!"); 
  }

  /* 写入数据*/
  while (NumByteToWrite--)
  {
    /* 发送当前要写入的字节数据 */
    SPI_FLASH_SendByte(*pBuffer);
    /* 指向下一字节数据 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();

  /* 等待写入完毕*/
  SPI_FLASH_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer,要写入数据的指针
  * @param  WriteAddr,写入地址
  * @param  NumByteToWrite,写入数据长度
  * @retval 无
  */
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
	
	/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
  Addr = WriteAddr % SPI_FLASH_PageSize;
	
	/*差count个数据值,刚好可以对齐到页地址*/
  count = SPI_FLASH_PageSize - Addr;
	/*计算出要写多少整数页*/
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
	/*mod运算求余,计算出剩余不满一页的字节数*/
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
	
	/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
  if (Addr == 0)
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0) 
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    { 
			/*先把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不满一页的数据,把它写完*/
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
	/* 若地址与 SPI_FLASH_PageSize 不对齐  */
  else 
  {
		/* NumByteToWrite < SPI_FLASH_PageSize */
    if (NumOfPage == 0)
    {
			/*当前页剩余的count个位置比NumOfSingle小,一页写不完*/
      if (NumOfSingle > count) 
      {
        temp = NumOfSingle - count;
				/*先写满当前页*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
				
        WriteAddr +=  count;
        pBuffer += count;
				/*再写剩余的数据*/
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /* NumByteToWrite > SPI_FLASH_PageSize */
    {
			/*地址不对齐多出的count分开处理,不加入这个运算*/
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
			
			/* 先写完count个数据,为的是让下一次要写的地址对齐 */
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
			
			/* 接下来就重复地址对齐的情况 */
      WriteAddr +=  count;
      pBuffer += count;
			/*把整数页都写了*/
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
			/*若有多余的不满一页的数据,把它写完*/
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

 /**
  * @brief  读取FLASH数据
  * @param 	pBuffer,存储读出数据的指针
  * @param   ReadAddr,读取地址
  * @param   NumByteToRead,读取数据长度
  * @retval 无
  */
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
  /* 选择FLASH: CS低电平 */
  SPI_FLASH_CS_LOW();

  /* 发送 读 指令 */
  SPI_FLASH_SendByte(W25X_ReadData);

  /* 发送 读 地址高位 */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* 发送 读 地址中位 */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* 发送 读 地址低位 */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
	
	/* 读取数据 */
  while (NumByteToRead--) /* while there is data to be read */
  {
    /* 读取一个字节*/
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* 指向下一个字节缓冲区 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI_FLASH_CS_HIGH();
}

 /**
  * @brief  读取FLASH ID
  * @param 	无
  * @retval FLASH ID
  */
u32 SPI_FLASH_ReadID(void)
{
  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

  /* 开始通讯:CS低电平 */
  SPI_FLASH_CS_LOW();

  /* 发送JEDEC指令,读取ID */
  SPI_FLASH_SendByte(W25X_JedecDeviceID);

  /* 读取一个字节数据 */
  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

  /* 读取一个字节数据 */
  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

  /* 读取一个字节数据 */
  Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

 /* 停止通讯:CS高电平 */
  SPI_FLASH_CS_HIGH();

  /*把数据组合起来,作为函数的返回值*/
	Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

  return Temp;
}
 /**
  * @brief  读取FLASH Device ID
  * @param 	无
  * @retval FLASH Device ID
  */
u32 SPI_FLASH_ReadDeviceID(void)
{
  u32 Temp = 0;

  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();

  /* Send "RDID " instruction */
  SPI_FLASH_SendByte(W25X_DeviceID);
  SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_SendByte(Dummy_Byte);
  
  /* Read a byte from the FLASH */
  Temp = SPI_FLASH_SendByte(Dummy_Byte);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();

  return Temp;
}
/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(u32 ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();

  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(W25X_ReadData);

  /* Send the 24-bit address of the address to read from -----------------------*/
  /* Send ReadAddr high nibble address byte */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
}


 /**
  * @brief  使用SPI读取一个字节的数据
  * @param  无
  * @retval 返回接收到的数据
  */
u8 SPI_FLASH_ReadByte(void)
{
  return (SPI_FLASH_SendByte(Dummy_Byte));
}

 /**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u8 SPI_FLASH_SendByte(u8 byte)
{
	 SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空,TXE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
	{
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
   }

  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(FLASH_SPIx , byte);

	SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空,RXNE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
  {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
   }

  /* 读取数据寄存器,获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(FLASH_SPIx );
}

 /**
  * @brief  使用SPI发送两个字节的数据
  * @param  byte:要发送的数据
  * @retval 返回接收到的数据
  */
u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
{
	  SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空,TXE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
	{
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);
   }
	
  /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(FLASH_SPIx , HalfWord);

	 SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空,RXNE事件 */
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
	 {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);
   }
  /* 读取数据寄存器,获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(FLASH_SPIx );
}

 /**
  * @brief  向FLASH发送 写使能 命令
  * @param  none
  * @retval none
  */
void SPI_FLASH_WriteEnable(void)
{
  /* 通讯开始:CS低 */
  SPI_FLASH_CS_LOW();

  /* 发送写使能命令*/
  SPI_FLASH_SendByte(W25X_WriteEnable);

  /*通讯结束:CS高 */
  SPI_FLASH_CS_HIGH();
}

/* WIP(busy)标志,FLASH内部正在写入 */
#define WIP_Flag                  0x01

 /**
  * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
  * @param  none
  * @retval none
  */
void SPI_FLASH_WaitForWriteEnd(void)
{
  u8 FLASH_Status = 0;

  /* 选择 FLASH: CS 低 */
  SPI_FLASH_CS_LOW();

  /* 发送 读状态寄存器 命令 */
  SPI_FLASH_SendByte(W25X_ReadStatusReg);

  /* 若FLASH忙碌,则等待 */
  do
  {
		/* 读取FLASH芯片的状态寄存器 */
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);	 
  }
  while ((FLASH_Status & WIP_Flag) == SET);  /* 正在写入标志 */

  /* 停止信号  FLASH: CS 高 */
  SPI_FLASH_CS_HIGH();
}


//进入掉电模式
void SPI_Flash_PowerDown(void)   
{ 
  /* 通讯开始:CS低 */
  SPI_FLASH_CS_LOW();

  /* 发送 掉电 命令 */
  SPI_FLASH_SendByte(W25X_PowerDown);

  /*通讯结束:CS高 */
  SPI_FLASH_CS_HIGH();
}   

//唤醒
void SPI_Flash_WAKEUP(void)   
{
  /*选择 FLASH: CS 低 */
  SPI_FLASH_CS_LOW();

  /* 发送 上电 命令 */
  SPI_FLASH_SendByte(W25X_ReleasePowerDown);

   /* 停止信号 FLASH: CS 高 */
  SPI_FLASH_CS_HIGH();
}   
   

/**
  * @brief  等待超时回调函数
  * @param  None.
  * @retval None.
  */
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* 等待超时后的处理,输出错误信息 */
  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
  return 0;
}

/*********************************************END OF FILE**********************/

结束文章来源地址https://www.toymoban.com/news/detail-488153.html

到了这里,关于USB复合设备(键盘鼠标U盘三合一)基于标准库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Si3262 一款低功耗刷卡+触摸+mcu 三合一SOC芯片

     Si3262是-款高度集成的低功耗soC芯片,其集成了基于RISC-V 核的低功耗MCU和工作在13.56MHz的非接触式读写器模块。 该芯片ACD模式下刷卡距离可达4-5cm(天线决定),适用于智能门锁,电子锁,柜锁,桑拿锁,物流锁等。 MCU模块具有低功耗、Low Pin Count、宽电压工作范围,集成了

    2024年02月09日
    浏览(45)
  • NexNoSQL Client:Elasticsearch、Redis、MongoDB三合一的可视化客户端管理工具

    工作中我们使用了Elasticsearch作为存储,来支持内容的搜索,Elasticsearch这个软件大家都耳熟能详,它是一个分布式、高扩展、高实时的搜索与数据分析引擎,不仅仅支持文本索引,还支持聚合操作,使用它既可以做数据搜索,还可以做报表分析,非常的方便。 在使用过程中我

    2024年02月15日
    浏览(152)
  • PHP分类信息网站源码系统 电脑+手机+微信端三合一 带完整前后端部署教程

    大家好啊!今天源码小编来给大家分享一款PHP分类信息网站类源码系统。这款源码系统是一套专业的信息发布类网站综合管理系统,适合各类地方信息和行业分类站点建站。随着这几年我们国家网民爆炸式的增 长,网络信息也随之越来越庞大,网民对信息需求从大而全转为专

    2024年02月04日
    浏览(47)
  • stm32 USB复合设备 cubeMX库一键生成 多路CDC串口 HID鼠标键盘 Composite Device

    最近有个需求,需要同时用usb键盘鼠标和虚拟串口等,因为平时没怎么研究过usb协议,所以自己写复合设备一直没有成功,然后正巧在github上看到了一个stm32的一个usb复合设备库,可以快速配置usb组合设备,并且支持超级多路串口 Gihub地址 https://github.com/alambe94/I-CUBE-USBD-Compo

    2024年02月09日
    浏览(62)
  • 基于XG24-EK2703A的BLE HID蓝牙键盘+鼠标复合设备功能开发(BLE+HID+FreeRTOS+Gecko SDK)

    👉 【Funpack3-1】基于XG24-EK2703A的BLE HID蓝牙键盘+鼠标复合设备 👉 Github: EmbeddedCamerata/XG24_ble_hid_keymouse 本项目基于Silicon Labs XG24-EK2703A开发板,通过HID协议实现了一个蓝牙键盘+鼠标复合设备,可通过按键实现上下翻页、发送字符功能。使用板载两个按键,当BTN0按下,向上翻页;

    2024年01月25日
    浏览(44)
  • stm32 USB HID+CDC 鼠标键盘串口 组合设备配置解析

    查阅网上的博客与代码,很多都是关于USB的鼠标配置、USB的键盘配置、USB的虚拟串口配置,稍微深入一点的会将鼠标键盘合在一起,但移植起来就会报很多错误,要么是检测不到,要么是警告,这很正常,因为不理解这些数字代表着什么。但只要理解每个数字代表什么意思,

    2024年02月13日
    浏览(41)
  • 手把手教你使用USB的CDC+MSC复合设备(基于stm32f407)

      最近对usb有点兴趣,感觉挺好玩的,于是买了本圈圈大神的经典著作-圈圈教你玩USB,里面使用51单片机+usb芯片对usb的基本知识潺潺道来,做了十个左右的常用案例实验,很有趣,建议大家看看。   趁热打铁,拿身边的开发板来练练手,探索一下复合设备的好玩方便的

    2024年02月13日
    浏览(61)
  • Android 9.0 禁止usb键盘和usb鼠标挂载

    在9.0的系统产品开发中,对于系统中usb鼠标和usb键盘的等外设输入设备挂载处理,系统是在inputflinger模块中处理的,在产品的需求中对于外设输入设备的usb鼠标和usb键盘的挂载是禁用的,所以需要从挂载入手,禁止挂载usb鼠标和usb键盘 在android系统中是由各个子系统分工协作

    2024年02月09日
    浏览(44)
  • 教你STM32做USB鼠标、键盘

    使用CubeMX软件傻瓜式的配置,一键生成USB的HID驱动。 ①、选择相对应的芯片  ②、配置时钟和Debug和debug      ③、配置USB    ④、生成代码          最好把这个也勾上,勾上以后每个外设配置不再都给你塞到main.c里,而是建一个.c.h,这样感觉舒服多了         USB协议

    2024年01月23日
    浏览(40)
  • 【流量分析】USB键盘与鼠标流量分析

    USB流量指的是USB设备接口的流量,攻击者能够通过监听usb接口流量获取键盘敲击键、鼠标移动与点击、存储设备的铭文传输通信、USB无线网卡网络传输内容等等。 在CTF中,USB流量分析主要以键盘和鼠标流量为主。 下面通过简单的讲解与例题的展示,分析键盘流量与鼠标流量

    2024年02月08日
    浏览(39)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包