Микроконтроллеры семейства STM32

Пробуйте, нам потом расскажете про совместимость.
Код в пн выложу.

хорошо, придётся чуток поправить скетч, распайка EEPROM разная

F411:


Generic EEPROM properties footprint
Name 	Unknown
Reference 	U3
Manufacturer 	Unknown
Part 	Generic EEPROM
Marking 	Unknown
Datasheet 	Unavailable
Package 	SOP 8 pins
Description 	Generic I2C EEPROM
Generic EEPROM pins footprint
# 	Name 	Function 	Connected to
1 	- 	/CS 	PA4
2 	- 	DO 	PB4
3 	- 	/WP 	+3.3V rail
4 	- 	GND 	Ground plane
5 	- 	DI 	PA7
6 	- 	CLK 	PA5
7 	- 	/HOLD 	+3.3V rail
8 	- 	VCC 	+3.3V rail

F401:

Generic EEPROM properties footprint
Name 	Unknown
Reference 	U3
Manufacturer 	Unknown
Part 	Generic EEPROM
Marking 	Unknown
Datasheet 	Unavailable
Package 	SOP 8 pins
Description 	Generic I2C EEPROM
Generic EEPROM pins footprint
# 	Name 	Function 	Connected to
1 	- 	/CS 	PA4
2 	- 	DO 	PA6
3 	- 	/WP 	+3.3V rail
4 	- 	GND 	Ground plane
5 	- 	DI 	PA7
6 	- 	CLK 	PA5
7 	- 	/HOLD 	+3.3V rail
8 	- 	VCC 	+3.3V rail

У меня на плате для 401/411 одна и та же разводка пинов, а на WEACT другая

РВ4 Во второй строке банально ошибка. Распиновка 401 и 411 одинаковая.

у него WEACT модуль, интернета пишет, что разная, на моих чёрных совпадает

Так просто не бывает, чтобы clk PA5, DI PA7, и при этом DO - PB4?

видимо бывает

Разница не в WEACT, а в буковках после 401/411. CE это 48 ножков, а RE - 64. Оттуда и несовпадения(возможно).

Ну нахрена ты))
Всю малину испортил.

вот на 401
вот 411

:grinning_face: И?

Мля, ты опять тупака включаешь?
F401CCU6 и F411CЕU6 это пин то пин !
Тебе, выбранному, прислали 401RET
и у тебя опять “проблемы”.

Включай катушку и спать.

что и?

Исходник кода / используемая литература:

Полный архив проекта:

Подключенный диск рекомендую форматировать с 4КБ кластером, чтоб он совпадал с сектором флэш памяти.

  • скорость записи примерно 87 килобайт в секунду.
  • скорость чтения не смог нормально заметить, слишком быстро прочитывается 8 МБ флэшка.

Основной модуль работы USB MSC из двух файлов:

Спойлер
/*
 * f4usbmsc.h
 *
 *  Created on: Jul 17, 2025
 *      Author: seleznev_a
 */

#ifndef F4USBMSC_H_
#define F4USBMSC_H_

#include <stdint.h>

void f411ceu6initUSBMSC(void);
void f411usbMSCintr(void); // основная движуха модуля
void f411usbMSCprocessInData(void); // обрабатываем входящий поток данных

constexpr unsigned char epUSBcontrol = 0x00; // точка последовательного порта USB управления
constexpr unsigned char epUSBdataOut = 0x01; // данных out (OUT endpoint 1)
constexpr unsigned char epUSBdataIn = 0x02; // данных in (IN endpoint 2)

#define USB_OTG_DEV  ((USB_OTG_DeviceTypeDef *)((uint32_t)USB_OTG_FS_PERIPH_BASE + USB_OTG_DEVICE_BASE))
#define USB_INEP(i)  ((USB_OTG_INEndpointTypeDef *)(( uint32_t)USB_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + (i) * USB_OTG_EP_REG_SIZE))
#define USB_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USB_OTG_FS_PERIPH_BASE + USB_OTG_OUT_ENDPOINT_BASE + (i) * USB_OTG_EP_REG_SIZE))
#define USB_FIFO_BASE *(__IO uint32_t *)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE)
#define USB_FIFO(i)  *(__IO uint32_t *)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE))

#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX_SIZE                   64U

#define RX_FIFO_SIZE     128
#define TX_FIFO_EP0_SIZE 128
#define TX_FIFO_EP1_SIZE 128
#define TX_FIFO_EP2_SIZE 128
#define TX_FIFO_EP3_SIZE 128

#define  REQ_DEVICE                0x00U
#define  REQ_INTERFACE             0x01U
#define  REQ_ENDPOINT              0x02U

#define  DESC_DEVICE               0x01U
#define  DESC_CONFIG               0x02U
#define  DESC_STRING               0x03U
#define  DESC_INTERFACE            0x04U
#define  DESC_ENDPOINT             0x05U
#define  DESC_DEVICE_QUALIFIER     0x06U
#define  DESC_OTHER_CONFIG         0x07U
#define  DESC_IAD                  0x0BU
#define  DESC_BOS                  0x0FU

#define  DESC_STR_LANGID           0x00U
#define  DESC_STR_MFC              0x01U
#define  DESC_STR_PRODUCT          0x02U
#define  DESC_STR_SERIAL           0x03U
#define  DESC_STR_CONFIG           0x04U
#define  DESC_STR_INTERFACE        0x05U
#define  REQ_DEVICE                0x00U
#define  REQ_INTERFACE             0x01U
#define  REQ_ENDPOINT              0x02U

#define  GET_STATUS                0x00U
#define  CLEAR_FEATURE             0x01U
#define  SET_FEATURE               0x03U
#define  SET_ADDRESS               0x05U
#define  GET_DESCRIPTOR            0x06U
#define  SET_DESCRIPTOR            0x07U
#define  GET_CONFIGURATION         0x08U
#define  SET_CONFIGURATION         0x09U
#define  GET_INTERFACE             0x0AU
#define  SET_INTERFACE             0x0BU
#define  SYNCH_FRAME               0x0CU

#define  REQ_STANDARD              0x00U
#define  REQ_CLASS                 0x20U
#define  REQ_VENDOR                0x40U
#define  REQ_MASK                  0x60U

#define EP0_OUT_INT                0x00010000
#define EP1_OUT_INT                0x00020000
#define EP2_OUT_INT                0x00040000

#define EP0_IN_INT                 0x0001
#define EP1_IN_INT                 0x0002
#define EP2_IN_INT                 0x0004

const uint8_t desc_device[] =
{
 0x12,                       /*bLength */
 0x01U,                      /*bDescriptorType*/
 0x00,                       /*bcdUSB */
 0x02,
 0x00,                       /*bDeviceClass*/
 0x00,                       /*bDeviceSubClass*/
 0x00,                       /*bDeviceProtocol*/
 0x40,                        /*bMaxPacketSize*/
 0x83, //LOBYTE(USBD_VID),   /*idVendor*/
 0x04, //HIBYTE(USBD_VID)    /*idVendor*/
 0x2A, //LOBYTE(USBD_PID_FS) /*idProduct*/
 0x57, //HIBYTE(USBD_PID_FS) /*idProduct*/
 0x00,                       /*bcdDevice rel. 2.00*/
 0x02,
 0x01,                      /*Index of manufacturer  string*/
 0x02,                      /*Index of product string*/
 0x03,                      /*Index of serial number string*/
 0x01                          /*bNumConfigurations*/
};

const uint8_t desc_config[]=
{
 /* Configuration Descriptor */
 0x09,                                       /* bLength: Configuration Descriptor size */
 0x02,                                      /* bDescriptorType: Configuration */
 0x20,                                        /* wTotalLength:no of returned bytes */
 0x00,
 0x01,                                       /* bNumInterfaces: 2 interface */
 0x01,                                       /* bConfigurationValue: Configuration value */
 0x00,                                       /* iConfiguration: Index of string descriptor describing the configuration */
 0x80,                                       /* bmAttributes: (Bus-powered Device) */
 0x64,                                      /* MaxPower 200 mA */
  /*---------------------------------------------------------------------------*/
 /* Interface Descriptor */
 0x09,                                       /* bLength: Interface Descriptor size */
 0x04,                                      /* bDescriptorType: Interface */ /* Interface descriptor type */
 0x00,                                       /* bInterfaceNumber: Number of Interface */
 0x00,                                       /* bAlternateSetting: Alternate setting */
 0x02,                                       /* bNumEndpoints: 2 endpoints used */
 0x08,                                       /* bInterfaceClass: (Mass Storage Device Class) */
 0x06,                                       /* bInterfaceSubClass: (Transparent SCSI subclass) */
 0x50,                                       /* bInterfaceProtocol: (Bulk only transport) */
 0x00,                                       /* iInterface: */
 /* Endpoint OUT Descriptor */
 0x07,                                       /* bLength: Endpoint Descriptor size */
 0x05,                                      /* bDescriptorType: Endpoint */
 0x01,                                      /* bEndpointAddress (OUT endpoint 1)*/
 0x02,                                       /* bmAttributes: Bulk (Transfer: Bulk / Synch: None / Usage: Data)*/
 64U,                                        /* wMaxPacketSize: */
 0x00,											/**/
 0x00,                                       /* bInterval: ignore for Bulk transfer */
 /* Endpoint IN Descriptor */
 0x07,                                       /* bLength: Endpoint Descriptor size */
 0x05,                                      /* bDescriptorType: Endpoint */
 0x82,                                      /* bEndpointAddress (IN endpoint 2)*/
 0x02,                                       /* bmAttributes: Bulk (Transfer: Bulk / Synch: None / Usage: Data)*/
 64U,                                        /* wMaxPacketSize: */
 0x00,											/**/
 0x00                                       /* bInterval: ignore for Bulk transfer */
};

const uint8_t desc_lang[] =
{
 0x04, // äëèíà äåñêðèïòîðà
 0x03, // òèï äåñêðèïòîðà - string desc
 0x09, // N áàéò èíäåòèôèêàòîð ÿçûêà
 0x04  /* LangID = 0x0409: U.S. English */
};

/*! @brief UFI Commands code*/
#define USB_DEVICE_MSC_INQUIRY_COMMAND (0x12U)         // Consume device information
#define USB_DEVICE_MSC_READ_10_COMMAND (0x28U)         // Host reads binary data in the storage medium
#define USB_DEVICE_MSC_READ_12_COMMAND (0xA8U)         // Same 28h
#define USB_DEVICE_MSC_REQUEST_SENSE_COMMAND (0x03U)   // The request device returns the execution results and status data to the host
#define USB_DEVICE_MSC_TEST_UNIT_READY_COMMAND (0x00U) // Whether the request device report is in the Ready state
#define USB_DEVICE_MSC_WRITE_10_COMMAND (0x2AU) // Write binary data from the host to the medium
#define USB_DEVICE_MSC_WRITE_12_COMMAND (0xAAU) // The same 2AH
#define USB_DEVICE_MSC_PREVENT_ALLOW_MEDIUM_REM_COMMAND (0x1EU) // Write protection
#define USB_DEVICE_MSC_FORMAT_UNIT_COMMAND (0x04U) // Formatal storage unit
#define USB_DEVICE_MSC_READ_CAPACITY_10_COMMAND (0x25U) // Requires the device to return the current capacity
#define USB_DEVICE_MSC_READ_CAPACITY_16_COMMAND (0x9EU) // Same 9EH
#define USB_DEVICE_MSC_READ_FORMAT_CAPACITIES_COMMAND (0x23U) // Query current capacity and available space
#define USB_DEVICE_MSC_MODE_SENSE_10_COMMAND (0x5AU)   // Transfer parameters to host
#define USB_DEVICE_MSC_MODE_SENSE_6_COMMAND (0x1AU)    // Tong 1AH
#define USB_DEVICE_MSC_MODE_SELECT_10_COMMAND (0x55U)  // Allow host setting parameters to external devices
#define USB_DEVICE_MSC_MODE_SELECT_6_COMMAND (0x15U)   // Same 55H
#define USB_DEVICE_MSC_SEND_DIAGNOSTIC_COMMAND (0x1DU) // Execute the firmware reset and perform diagnosis
#define USB_DEVICE_MSC_VERIFY_COMMAND (0x2FU) // Verify data in storage
#define USB_DEVICE_MSC_START_STOP_UNIT_COMMAND (0x1BU) //Start Stop Load Unload

constexpr unsigned long BaitUSBC = 0x43425355; // Магическое число 0x43425355 (оно же 'USBC').
constexpr unsigned long BaitUSBS = 0x53425355; // Магическое число 0x53425355 (оно же 'USBS').

/*! @brief Command Block Wrapper(CBW) */
typedef struct __attribute__((__packed__)) _usb_device_msc_cbw
{
    uint32_t signature;          /*!< Byte 0-3 dCBWSignature*/
    uint32_t tag;                /*!< Byte 4-7 dCBWTag*/
    uint32_t dataTransferLength; /*!< Byte 8-11 dCBWDataTransferLength*/
    uint8_t flags;               /*!< Byte 12 bmCBWFlags*/
    uint8_t logicalUnitNumber;   /*!< Byte 13 bCBWLUN*/
    uint8_t cbLength;            /*!< Byte 14 bCBWCBLength*/
    uint8_t cbwcb[16];           /*!< Byte 15-30 CBWCB, CBWCB is used to store UFI command*/
} usb_device_msc_cbw_t;

/*! @brief Command Status Wrapper(CSW) */
typedef struct __attribute__((__packed__)) _usb_device_msc_csw
{
    uint32_t signature;   /*!< Byte 0-3 dCSWSignature*/
    uint32_t tag;         /*!< Byte 4-7 dCSWTag*/
    uint32_t dataResidue; /*!< Byte 8-11 dCSWDataResidue*/
    uint8_t cswStatus;    /*!< Byte 12 bCSWStatus*/
} usb_device_msc_csw_t;

typedef struct __attribute__((__packed__)) {
    uint32_t tag;
    uint32_t len;
    uint8_t status;
    uint8_t lun;
} cbw_info_t;

/*! @brief  UFI inquiry data format structure*/
typedef struct __attribute__((__packed__)) _usb_device_inquiry_data_fromat_struct
{
    uint8_t peripheralDeviceType; /*!< Peripheral Device Type*/
    uint8_t rmb;                  /*!< Removable Media Bit*/
    uint8_t versions;             /*!< ISO Version, ECMA Version, ANSI Version*/
    uint8_t responseDataFormat;   /*!< Response Data Format*/
    uint8_t additionalLength;     /*!< The Additional Length field shall specify the length in bytes of the parameters*/
    uint8_t reserved[3];          /*!< reserved*/
    uint8_t vendorInformatin[8];  /*!< Vendor Identification*/
    uint8_t productId[16];        /*!< Product Identification*/
    uint8_t productVersionLevel[4]; /*!< Product Revision Level*/
} usb_device_inquiry_data_fromat_struct_t;

/*! @brief  UFI inquiry data format structure*/
typedef struct __attribute__((__packed__)) _usb_device_inquiry_data_fromat_structPAGE0
{
    uint8_t peripheralDeviceType; /*!< Peripheral Device Type*/
    uint8_t rmb;                  /*!< Removable Media Bit*/
    uint8_t versions;             /*!< ISO Version, ECMA Version, ANSI Version*/
    uint8_t reserved[3];          /*!< reserved*/
} usb_device_inquiry_data_fromat_structPAGE0_t;

typedef struct __attribute__((__packed__)) _usb_device_capacity_data_format_struct
{
    uint32_t lastNumBlock;
    uint32_t sizeOneBlock;
} _usb_device_capacity_data_format_struct_t;

typedef struct __attribute__((__packed__)) _usb_device_capacity_data_format_command
{
    uint8_t reserved[3];          /*!< reserved*/
    uint8_t capListLen; // capacity list lenghts
    uint32_t countNumBlock;
    uint8_t descType;
    //uint32_t sizeOneBlock;
    uint8_t sizeOneBlock[3];
} _usb_device_capacity_data_format_command_t;

struct __attribute__((__packed__)) scsi_cbw_10_t{
  uint8_t opcode; //READ(10) / WRITE(10)
  uint8_t cdb_info1;
  uint32_t block_address;
  uint8_t cdb_info2;
  uint16_t length;
  uint8_t control;
};

constexpr unsigned short countQcontrol0send = 16;
constexpr unsigned short countQdata2send = 1152;

struct itemQsend {
	unsigned char newSendBuf[MAX_SIZE]; // с новыми данными
	unsigned char newSendLen = 0; // длина новых отправляемых данных
};

struct sendQcontrol {
	unsigned short countItem = 0;
	unsigned short currentItem = 0;
	itemQsend items[countQcontrol0send];
	bool inSend = false;
};

struct sendQdata {
	unsigned short countItem = 0;
	unsigned short currentItem = 0;
	itemQsend items[countQdata2send];
	bool inSend = false;
};

#endif /* F4USBMSC_H_ */
Спойлер
/*
 * f4usbmsc.cpp
 *
 *  Created on: Jul 17, 2025
 *      Author: seleznev_a
 */

#include "f4usbmsc.h"
#include "f4w25flash.h"
#include "stm32f4xx.h"
#include <string.h>
#include "f4base.h"

uint8_t bufRx[72];
uint8_t epNumLastRx = 0;
volatile uint16_t countRx = 0;

const char desc_vendor[] = "STM32 Mass Storage";
const char desc_product[] = "MSC Config fill";
const char desc_serial[] = "A3D65C8E08B2";

sendQcontrol massEP0txControl;
sendQdata massEP2txData;

void f411usbMSCsend(const uint8_t ep, const uint8_t *buf, const unsigned char len) {
	if ((massEP0txControl.inSend) || (massEP0txControl.countItem) || (massEP2txData.inSend) || (massEP2txData.countItem)) {
		if (ep) {
			massEP2txData.items[massEP2txData.currentItem].newSendLen = len;
			memcpy((void*)&massEP2txData.items[massEP2txData.currentItem].newSendBuf[0], (const unsigned char*)buf, len);
			++massEP2txData.currentItem;
			if (massEP2txData.currentItem >= countQdata2send) massEP2txData.currentItem = 0;
			++massEP2txData.countItem;
		} else {
			massEP0txControl.items[massEP0txControl.currentItem].newSendLen = len;
			memcpy((void*)&massEP0txControl.items[massEP0txControl.currentItem].newSendBuf[0], (const unsigned char*)buf, len);
			++massEP0txControl.currentItem;
			if (massEP0txControl.currentItem >= countQcontrol0send) massEP0txControl.currentItem = 0;
			++massEP0txControl.countItem;
		}
	} else {
		if (ep)	massEP2txData.inSend = true; else massEP0txControl.inSend = true;
		uint16_t i;
		uint32_t *ptr = (uint32_t *)buf;
		USB_INEP(ep)->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len;       // Set outbound txlen
		USB_INEP(ep)->DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;   // Enable endpoint, clear NAK bit
		for(i = 0; i < ((len + 3) / 4); i++) {
			USB_FIFO(ep) = __UNALIGNED_UINT32_READ(ptr);                         // Copy data
	        ptr++;
	    }
	}
}

/* This function reads received data from the buffer */
uint16_t f411usbMSCreadData(const uint8_t ep, uint8_t *buf) {
	uint16_t i, len = 0;
	if(ep == epNumLastRx) {
		if(countRx) {                                      // If read data
			for(i = 0; i < countRx; i++) {
				*buf++ = bufRx[i];                        // Copy data
            }
            len = countRx;
            countRx = 0;
            epNumLastRx = 0;
        }
	}
	return len;
}

/* This function is sending data */
void f411usbMSCsendData(const uint8_t ep, const uint8_t *buf, const unsigned short len) {
	if (len == MAX_SIZE) {
		f411usbMSCsend(ep, buf, MAX_SIZE);
	} else if (len > MAX_SIZE) {
		unsigned char * pData = (unsigned char *)buf;
		unsigned short parts = len / MAX_SIZE;
		for(unsigned short i = 0; i < parts; ++i) {
			f411usbMSCsend(ep, pData, MAX_SIZE);
			pData += MAX_SIZE;
		}
		unsigned short ost = len % MAX_SIZE;
		if (ost) f411usbMSCsend(ep, pData, (unsigned char)(ost & 0x00FF));
	} else {
		f411usbMSCsend(ep, buf, (unsigned char)(len & 0x00FF));
	}
}

void f411usbMSCsendFIFO(const uint8_t ep) {
	unsigned char * buf;
	unsigned short len;
	if (ep) {
	    if (massEP2txData.countItem <= massEP2txData.currentItem) { // если позиция отправки больше количества отправляемых байт
	    	buf = (unsigned char*)&massEP2txData.items[massEP2txData.currentItem-massEP2txData.countItem].newSendBuf[0]; // берем первый вошедший байт // кладем байт в регистр МК для непосредственно отправки
	    	len = massEP2txData.items[massEP2txData.currentItem-massEP2txData.countItem].newSendLen; // берем первый вошедший байт // кладем байт в регистр МК для непосредственно отправки
	    } else { // если хвост кольцевого буфера выходит за границы размеров
	    	buf = (unsigned char*)&massEP2txData.items[countQdata2send-(massEP2txData.countItem-massEP2txData.currentItem)].newSendBuf[0]; // берем байт по другой логике // кладем байт в регистр МК для непосредственно отправки
	    	len = massEP2txData.items[countQdata2send-(massEP2txData.countItem-massEP2txData.currentItem)].newSendLen; // берем байт по другой логике // кладем байт в регистр МК для непосредственно отправки
	    }
		--massEP2txData.countItem;
		massEP2txData.inSend = true;
	} else {
	    if (massEP0txControl.countItem <= massEP0txControl.currentItem) { // если позиция отправки больше количества отправляемых байт
	    	buf = (unsigned char*)&massEP0txControl.items[massEP0txControl.currentItem-massEP0txControl.countItem].newSendBuf[0]; // берем первый вошедший байт // кладем байт в регистр МК для непосредственно отправки
	    	len = massEP0txControl.items[massEP0txControl.currentItem-massEP0txControl.countItem].newSendLen; // берем первый вошедший байт // кладем байт в регистр МК для непосредственно отправки
	    } else { // если хвост кольцевого буфера выходит за границы размеров
	    	buf = (unsigned char*)&massEP0txControl.items[countQcontrol0send-(massEP0txControl.countItem-massEP0txControl.currentItem)].newSendBuf[0]; // берем байт по другой логике // кладем байт в регистр МК для непосредственно отправки
	    	len = massEP0txControl.items[countQcontrol0send-(massEP0txControl.countItem-massEP0txControl.currentItem)].newSendLen; // берем байт по другой логике // кладем байт в регистр МК для непосредственно отправки
	    }
		--massEP0txControl.countItem;
		massEP0txControl.inSend = true;
	}
	uint16_t i;
	uint32_t *ptr = (uint32_t *)buf;
	USB_INEP(ep)->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len;       // Set outbound txlen
	USB_INEP(ep)->DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;   // Enable endpoint, clear NAK bit
	for(i = 0; i < ((len + 3) / 4); i++) {
		USB_FIFO(ep) = __UNALIGNED_UINT32_READ(ptr);                         // Copy data
        ptr++;
    }
}

void f411usbMSCsendEnd(const uint8_t ep) {
	while(USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH);
	USB_OTG_FS->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | ((ep & 0x0F) << 6);
	while(USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH);
	if (ep) {
		massEP2txData.inSend = false;
		if (massEP2txData.countItem) {
			f411usbMSCsendFIFO(ep);
		}
	} else {
		massEP0txControl.inSend = false;
		if (massEP0txControl.countItem) {
			f411usbMSCsendFIFO(ep);
		}
	}
}

void f411usbMSCread(const uint8_t ep, const uint8_t *buf, const unsigned short len) {
	int16_t i;
	uint32_t   *ptr = (uint32_t *)buf;
	for(i = 0; i < ((len + 3) / 4); i++) {
         __UNALIGNED_UINT32_WRITE(ptr, USB_FIFO(ep));         // Read data
         ptr++;
    }
}

void f411usbMSCflushTx(void) {
	while(USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH);
	USB_OTG_FS->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | (1 << 10);
	while(USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH);
}

void f411ceu6initUSBMSC(void) {
	/* Enable clock */
	RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN;
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
	/* Port config */
	GPIOA->AFR[1] |= (10 << GPIO_AFRH_AFSEL12_Pos) | (10 << GPIO_AFRH_AFSEL11_Pos); // PA12 -> OTG_FS_DP, PA11 -> OTG_FS_DM
	GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR12 | GPIO_OSPEEDER_OSPEEDR11;            // PA12 PA11 Very high speed
	GPIOA->MODER |= GPIO_MODER_MODE12_1 | GPIO_MODER_MODE11_1;                      // Alternate function
	/* Core */
	while (!(USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL)) {};
	USB_OTG_FS->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
	while (USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_CSRST) {};
	USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_PWRDWN | USB_OTG_GCCFG_NOVBUSSENS; // Exit Power Down mode
	USB_OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD | (0x06 << USB_OTG_GUSBCFG_TRDT_Pos) | USB_OTG_GUSBCFG_PHYSEL | (17 << USB_OTG_GUSBCFG_TOCAL_Pos); // Set to Device mode
	delay_ms(30UL);
	USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_IEPINT |   // Enable USB IN TX endpoint interrupt
    USB_OTG_GINTMSK_OEPINT |   // Enable USB OUT RX endpoint interrupt
    USB_OTG_GINTMSK_RXFLVLM |  // USB reciving
    USB_OTG_GINTMSK_USBRST;    // Reset interrupt
	USB_OTG_FS->GAHBCFG = USB_OTG_GAHBCFG_GINT;       // On USB general interrupt
	/* Device */
	USB_OTG_DEV->DCFG |= USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD_1 | USB_OTG_DCFG_DSPD_0; //Full speed, STALL for all OUT requests
	/* Buffers */
	USB_OTG_FS->GRXFSIZ = RX_FIFO_SIZE; // size is in 32-bit words !
	USB_OTG_FS->DIEPTXF0_HNPTXFSIZ = (TX_FIFO_EP0_SIZE << 16) | RX_FIFO_SIZE;              // Set the position and size of the transmit buffer
	USB_OTG_FS->DIEPTXF[1] = (TX_FIFO_EP1_SIZE << 16) | (RX_FIFO_SIZE + TX_FIFO_EP0_SIZE); // Set the position and size of the transmit buffer
	USB_OTG_FS->DIEPTXF[2] = (TX_FIFO_EP2_SIZE << 16) | (RX_FIFO_SIZE + TX_FIFO_EP0_SIZE + TX_FIFO_EP1_SIZE);
	/* Interrupt */ // не будем пока включать прерывания, в основном цикле будем все обрабатывать
	/*NVIC_SetPriority(OTG_FS_IRQn, 1);
 	NVIC_EnableIRQ(OTG_FS_IRQn);*/
	// reset usb
    USB_OTG_DEV->DCTL &= ~USB_OTG_DCTL_RWUSIG;      // Wakeup signal disable //    Из состояния suspended может быть осуществлен выход по инициативе самого устройства. В этом случае программа MCU устанавливает бит сигнализации remote wakeup (бит RWUSIG в регистре OTG_FS_DCTL), и очистит его в интервале от 1 до 15 мс.
    f411usbMSCflushTx();                                      // Clear tx buffer // очистка буферов передачи
    for(uint8_t i = 0U; i < 4; i++) {                 // Clear any pending EP flags
    	USB_INEP(i)->DIEPINT = 0xFB7FU;
    	USB_INEP(i)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
        USB_INEP(i)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
        USB_OUTEP(i)->DOEPINT = 0xFB7FU;
        USB_OUTEP(i)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
        USB_OUTEP(i)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
    }
    USB_OTG_DEV->DAINTMSK |= 0x10001U;              // EP0 OUT, EP0 IN Interupt
    USB_OTG_DEV->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | // Enable setup-done irq
    		USB_OTG_DOEPMSK_EPDM  | // Enable EP-disabled irq
			USB_OTG_DOEPMSK_XFRCM;  // Enable tx-done irq
    USB_OTG_DEV->DIEPMSK |= USB_OTG_DIEPMSK_TOM   | // Timeout irq
    			USB_OTG_DIEPMSK_XFRCM |
                USB_OTG_DIEPMSK_EPDM;
    // Setup EP0 to receive SETUP packets
    USB_OUTEP(0)->DOEPTSIZ = (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
    			USB_OTG_DOEPTSIZ_STUPCNT | // Allow 3 setup pkt
                (3 * 8);
    USB_OUTEP(0)->DOEPCTL = USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;   // Enable endpoint, Clear NAK bit
    USB_OTG_DEV->DCFG &= ~USB_OTG_DCFG_DAD;         // Clear address
    USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_USBRST;   // Clear flag
    // clear fifo tx buf
   	massEP0txControl.countItem = 0;
   	massEP0txControl.currentItem = 0;
   	massEP0txControl.inSend = false;
   	massEP2txData.countItem = 0;
   	massEP2txData.currentItem = 0;
   	massEP2txData.inSend = false;
}

void f411usbMSCflushRx(void) {
	USB_OTG_FS->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH;
	while (USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH);
}

void f411usbMSCstallTx(uint8_t ep) {
	USB_INEP(ep)->DIEPCTL |= USB_OTG_DIEPCTL_STALL;
}

void f411usbMSCgetDesc(uint16_t wValue, uint16_t wLength) {
	uint8_t *pbuf;
	uint8_t len = 0;
	switch(wValue >> 8) {
    	case DESC_DEVICE:                  // Request device descriptor
    		pbuf = (uint8_t*)desc_device;
            len = sizeof(desc_device);
            break;
        case DESC_CONFIG:                  // Request configuration descriptor
            pbuf = (uint8_t*) desc_config;
            len = sizeof(desc_config);
            break;
        case DESC_STRING:                  // Request string descriptor
            switch(wValue & 0xFF) {         // Request string descriptor
            	case DESC_STR_LANGID:  // Lang
                	pbuf = (uint8_t*) desc_lang;
                    len = sizeof(desc_lang);
                    break;
                case DESC_STR_MFC:     // Manufacturer
                    pbuf = (uint8_t*) desc_vendor,
                    len = sizeof(desc_vendor);
                    break;
                case DESC_STR_PRODUCT: // Product
                    pbuf = (uint8_t*) desc_product;
                    len = sizeof(desc_product);
                    break;
                case DESC_STR_SERIAL:  // SerialNumber
                    pbuf = (uint8_t*) desc_serial;
                    len = sizeof(desc_serial);
                    break;
                case DESC_STR_CONFIG:  // Config
                    pbuf = (uint8_t*) desc_config;
                    len = sizeof(desc_config);
                    break;
                case DESC_STR_INTERFACE: break;// Interface
                    default: break;
            }
            break;
        case DESC_OTHER_CONFIG:
        	f411usbMSCstallTx(epUSBcontrol); // we don't know how to handle this request
        	break;
    }
	if (len) {
		f411usbMSCsendData(epUSBcontrol, pbuf, MIN(len,wLength));
    }
}

void f411usbMSCsetAddr(uint8_t addr) {
	USB_OTG_DEV->DCFG |= ((uint32_t)addr << 4);
	f411usbMSCsend(epUSBcontrol, 0, 0);
}

void f411usbMSCsetConfig(void) {
	/* Open EP1 OUT */
	USB_OTG_DEV->DAINTMSK |= (1 << 17);                          // Enable Interupt
	USB_OUTEP(epUSBdataOut)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA |             // Endpoint enable
                            USB_OTG_DOEPCTL_SD0PID_SEVNFRM |    // Set DATA0
                            USB_OTG_DOEPCTL_CNAK |              // Clear NAK
                            USB_OTG_DOEPCTL_EPTYP_1 |           // Endpoint type bulk
                            USB_OTG_DOEPCTL_USBAEP |            // Active endpoint
                            MAX_SIZE;                           // Max packet size
	/* Open EP2 IN  */
	USB_OTG_DEV->DAINTMSK |=   (1 << 2);                           // Enable Interupt
	USB_INEP(epUSBdataIn)->DIEPCTL |= USB_OTG_DIEPCTL_EPENA |             // Endpoint enable
							USB_OTG_DIEPCTL_SD0PID_SEVNFRM |    // Set DATA0
							USB_OTG_DIEPCTL_TXFNUM_1 |   // TxFIFO number // These bits specify the FIFO number associated with this endpoint. Each active IN endpoint must be programmed to a separate FIFO number. This field is valid only for IN endpoints
                           USB_OTG_DIEPCTL_EPTYP_1 |            // Endpoint type bulk
                           USB_OTG_DIEPCTL_USBAEP |             // Active endpoint
                           MAX_SIZE;                            // Max packet size
	f411usbMSCsend(epUSBcontrol, 0, 0);
}

void f411usbMSCsetup(uint8_t *buf) { // обработка пакета setup
	const uint8_t bmRequestType = buf[0]; // которое содержит характеристики запроса в интерфейсе USB.
		/*Бит 7: Направление передачи данных в DATA фазе:
			0 = от хоста к устройству
			1 = от устройства к хосту
		Биты 6...5: Тип запроса
			0 = стандартный запрос USB
			1 = стандартный запрос для определенного класса устройств USB
			2 = пользовательский запрос
			3 = зарезервировано
		Биты 4...0: Кому адресован запрос
			0 = устройству
			1 = интерфейсу
			2 = конечной точке
			3 = другое
		4...31 = зарезервировано*/
 	 	/* Bits specifying characteristics of request.
		Valid values are 10100001 or 00100001
		only based on the following description:
		7 Data transfer direction
		0 = Host to device
		1 = Device to host
		6..5 Type
		1 = Class
		4..0 Recipient
		1 = Interface*/
	const uint8_t bRequest = buf[1]; // уникальный код запроса (например, 0 = GET_STATUS, 5 = SET_ADDRESS, 6 = GET_DESCRIPTOR и т. д.)
		/*Здесь следует пояснить, зачем нужны биты 4..0 поля bmRequestType. Например, для получения статуса хост посылает запрос GET_STATUS (bRequest = 0). Значение бит 4...0 поля bmRequestType определяет, статус чего именно хочет получить хост: 0 – статус устройства, 1 – статус интерфейса, 2 – статус контрольной точки и т. д.). Таким образом, «тип запроса» определяется битами 4..0 поля bmRequestType и полем bRequest.*/
		/*A specific request*/
	const uint16_t wValue = buf[2] | ((uint16_t)buf[3] << 8); // Numeric expression specifying word-size	field (varies according to request.)
		/*данное поле может использоваться для передачи параметров запроса. Например, для запроса SET_ADDRESS данное поле содержит адрес, который нужно присвоить устройству. Если параметры запроса не помещаются в 2 байта wValue, то хост их передает в DATA фазе.*/
	__attribute__((unused)) const uint16_t wIndex = buf[4] | ((uint16_t)buf[5] << 8); // Index or offset specifying word-size field (varies according to request.)
		/*значение данного поля зависит от типа запроса. Например, для запроса GET_STATUS к конечной точке, данное поле содержит индекс конечной точки, статус которой хост хочет получить.*/
	const uint16_t wLength = buf[6] | ((uint16_t)buf[7] << 8); // Numeric expressions specifying number of bytes to transfer in the data phase
	switch(bmRequestType & 0x1FU) { // кому адресован запрос
    	case REQ_DEVICE: // запрос адресован устройству
    		switch(bmRequestType & REQ_MASK) { // тип запроса
            	case REQ_CLASS: break; // стандартный запрос для определенного класса устройств USB
                case REQ_VENDOR: break; // пользовательский запрос
                case REQ_STANDARD: // стандартный запрос USB
                	switch(bRequest) { // разбираем стандартный запрос
                    	case GET_DESCRIPTOR: // хост запросил дескриптор
                    		f411usbMSCgetDesc(wValue, wLength); // отправляем хосту дискриптор
                        	break;
                        case SET_ADDRESS: // хост присвоил адрес устройству
                        	f411usbMSCsetAddr(buf[2]);
                        	break;
                        case SET_CONFIGURATION: // хост отправил команду сконфигурировать устроство
                        	f411usbMSCsetConfig();
                        	break;
                        case GET_CONFIGURATION: break; // хост хочет получить конфиг
                        case GET_STATUS: break; // хост хочет получить статус
                        case SET_FEATURE: break;
                        case CLEAR_FEATURE: break;
                        default: break;
                    }
                    break;
            }
            break;
        case REQ_INTERFACE: // запрос адресован интерфейсу
    		switch(bmRequestType & REQ_MASK) { // тип запроса
        		case REQ_CLASS: // стандартный запрос для определенного класса устройств USB
                	switch(bRequest) { // запрос
                		case 0xFE: // при первом обмене перед подачей команды идет запрос A1 FE, далее по каналу данных - команда Запоминающему устройству USB
                			break;
                		default:break;
                	}
        			break;
    			default:break;
    		}
        	uint8_t data[8];
        	memset((void *)&data[0], '\0', 8);
        	f411usbMSCsendData(epUSBcontrol, data, wLength); // отправляем хосту пустой конфиг интерфеса 8 байт
            if(bRequest == 34) countRx = 0; // не нашел в документации что за специфический такой bRequest что мы очищаем количество принятых данных
            break;
        case REQ_ENDPOINT: break; // запрос адресован конечной точке
    }
}

unsigned long swapUINT32(unsigned long inD) {
	unsigned long outD = (inD & 0x000000FF) << 24;
	outD |= (inD & 0x0000FF00) << 8;
	outD |= (inD & 0x00FF0000) >> 8;
	outD |= (inD & 0xFF000000) >> 24;
	return outD;
}

unsigned short swapUINT16(unsigned short inD) {
	unsigned short outD = (inD & 0x00FF) << 8;
	outD |= (inD & 0xFF00) >> 8;
	return outD;
}

void f411usbMSCresponseUSBSpacket(uint32_t inTag) {
	_usb_device_msc_csw respCsw;
	respCsw.signature = BaitUSBS;
	respCsw.tag = inTag;
	respCsw.dataResidue = 0UL;
	respCsw.cswStatus = 0x00;
	f411usbMSCsendData(epUSBdataIn, (unsigned char *)&respCsw, sizeof(_usb_device_msc_csw));
}

void writeInputEDdataToBlockFlash(const unsigned char * inDATAep, const unsigned long firstBlock = 0, const unsigned short countBlocks = 0) {
	static unsigned short needWriteBlocks = 0;
	static unsigned long currentBlock = 0UL;
	static unsigned char currentEpdInBlock = 0;
	static unsigned char oneBlockData[_one_item_capacity_drive];
	if ((inDATAep) && (!countBlocks) && (needWriteBlocks)) { // continou write
		memcpy((void *)&oneBlockData[currentEpdInBlock*MAX_SIZE], inDATAep, MAX_SIZE);
		++currentEpdInBlock;
		if (currentEpdInBlock >= (_one_item_capacity_drive/MAX_SIZE)) {
			currentEpdInBlock =0;
			--needWriteBlocks;
			w25writeSector(currentBlock, (unsigned char *)&oneBlockData[0]);
			++currentBlock;
		}
	} else { // new write
		currentBlock = firstBlock;
		currentEpdInBlock = 0;
		needWriteBlocks = countBlocks;
	}
}

void f411usbMSCUFIcommand(_usb_device_msc_cbw * inCBW) {
	switch (inCBW->cbwcb[0]) {
		case USB_DEVICE_MSC_INQUIRY_COMMAND: { // запрос подробностей по данному LUN'у // (0x12U)         // Consume device information
			static bool reqPage80 = false;
			if (reqPage80) {
				_usb_device_inquiry_data_fromat_struct outINQUIRY;
				outINQUIRY.peripheralDeviceType = 0x00; // Direct-access device (floppy)
				outINQUIRY.rmb = 0x80; // Removable Media Bit: this shall be set to one to indicate removable media
				outINQUIRY.versions = 0x00; // ISO/ECMA: These fields shall be zero for the UFI device
				outINQUIRY.responseDataFormat = 0x01; // Response Data Format: a value of 01h shall be used for UFI device
				outINQUIRY.additionalLength = 0x1F; // The Additional Length field shall specify the length in bytes of the parameters. If the Allocation Length of the			Command Packet is too small to transfer all of the parameters, the Additional Length shall not be adjusted to			reflect the truncation. The UFI device shall set this field to 1Fh.
				memcpy((void *)&outINQUIRY.vendorInformatin[0], (const void *)&desc_vendor[0], 8);
				outINQUIRY.vendorInformatin[7] = '\0';
				memcpy((void *)&outINQUIRY.productId, (const void *)&desc_product[0], sizeof(desc_product));
				outINQUIRY.productId[15] = '\0';
				memcpy((void *)&outINQUIRY.productVersionLevel[0], (const void *)"5.7", 3);
				outINQUIRY.productVersionLevel[3] = '2';
				f411usbMSCsendData(epUSBdataIn, (unsigned char *)&outINQUIRY, sizeof(_usb_device_inquiry_data_fromat_struct));
			} else {
				_usb_device_inquiry_data_fromat_structPAGE0 outINQUIRYp0;
				outINQUIRYp0.peripheralDeviceType = 0x00; // Direct-access device (floppy)
				outINQUIRYp0.rmb = 0x00; // Removable Media Bit: this shall be set to one to indicate removable media
				outINQUIRYp0.reserved[0] = sizeof(_usb_device_inquiry_data_fromat_structPAGE0)-4; // The Additional Length field shall specify the length in bytes of the parameters. If the Allocation Length of the			Command Packet is too small to transfer all of the parameters, the Additional Length shall not be adjusted to			reflect the truncation. The UFI device shall set this field to 1Fh.
				f411usbMSCsendData(epUSBdataIn, (unsigned char *)&outINQUIRYp0, sizeof(_usb_device_inquiry_data_fromat_structPAGE0));
				reqPage80 = true;
			}
			break;
		}
		case USB_DEVICE_MSC_READ_FORMAT_CAPACITIES_COMMAND: { // определение емкости (windows-specific) // (0x23U) // Query current capacity and available space
			_usb_device_capacity_data_format_struct outCAPACITY;
			outCAPACITY.lastNumBlock = swapUINT32(_count_items_drive - 1);
			outCAPACITY.sizeOneBlock = swapUINT32(_one_item_capacity_drive);
			f411usbMSCsendData(epUSBdataIn, (unsigned char *)&outCAPACITY, sizeof(_usb_device_capacity_data_format_struct));
			break;
		}
		case USB_DEVICE_MSC_MODE_SENSE_6_COMMAND: // еще немного подробностей по LUN'у // 0x1A
		case USB_DEVICE_MSC_MODE_SENSE_10_COMMAND: /* operation code :0x5A*/ {
			/* USB Mass storage sense 6 Data */
			constexpr unsigned char MODE_SENSE6_LEN = 0x04;
			uint8_t MSC_Mode_Sense6_data[MODE_SENSE6_LEN] =	{
			  0x03,     /* MODE DATA LENGTH. The number of bytes that follow. */
			  0x00,     /* MEDIUM TYPE. 00h for SBC devices. */
			  0x00,     /* DEVICE-SPECIFIC PARAMETER. For SBC devices:
			             *   bit 7: WP. Set to 1 if the media is write-protected.
			             *   bits 6..5: reserved
			             *   bit 4: DPOFUA. Set to 1 if the device supports the DPO and FUA bits
			             *   bits 3..0: reserved */
			  0x00      /* BLOCK DESCRIPTOR LENGTH */};
			f411usbMSCsendData(epUSBdataIn, (unsigned char *)&MSC_Mode_Sense6_data[0], MODE_SENSE6_LEN);
			break;
		}
		case USB_DEVICE_MSC_READ_CAPACITY_10_COMMAND: // запрос емкости // (0x25U) // Requires the device to return the current capacity
		case USB_DEVICE_MSC_READ_CAPACITY_16_COMMAND: /*operation code : 0x9E*/ {
			_usb_device_capacity_data_format_struct currCAPACITY;
			currCAPACITY.lastNumBlock = swapUINT32(_count_items_drive - 1);
			currCAPACITY.sizeOneBlock = swapUINT32(_one_item_capacity_drive);
			f411usbMSCsendData(epUSBdataIn, (unsigned char *)&currCAPACITY, sizeof(_usb_device_capacity_data_format_struct));
			break;
		}
		case USB_DEVICE_MSC_READ_10_COMMAND: // чтение // 0x28
		case USB_DEVICE_MSC_READ_12_COMMAND: /*operation code : 0xA8 */ {
			scsi_cbw_10_t * pCbw10 = (scsi_cbw_10_t *)&inCBW->cbwcb[0];
			if (inCBW->cbLength == sizeof(scsi_cbw_10_t)) {
				unsigned long startBlock = swapUINT32(pCbw10->block_address);
				unsigned short needRead = swapUINT16(pCbw10->length);
				for(unsigned short i = 0; i < needRead; ++i) {
					unsigned char rB[_one_item_capacity_drive];
					w25readSector(startBlock+i, (unsigned char *)&rB[0]);
					f411usbMSCsendData(epUSBdataIn, (unsigned char *)&rB[0], _one_item_capacity_drive);
				}
			}
			break;
		}
		case USB_DEVICE_MSC_TEST_UNIT_READY_COMMAND: { // 0x00
			break;
		}
		case USB_DEVICE_MSC_PREVENT_ALLOW_MEDIUM_REM_COMMAND: { // (0x1EU) // Write protection // Команда "PREVENT ALLOW MEDIUM REMOVAL" разрешает или запрещает извлечение носителя из устройства.
			break;
		}
		case USB_DEVICE_MSC_REQUEST_SENSE_COMMAND: { // 0x03 //
			break;
		}
		case USB_DEVICE_MSC_WRITE_10_COMMAND: // 0x2A
		case USB_DEVICE_MSC_WRITE_12_COMMAND: { // (0xAAU) // The same 2AH
			scsi_cbw_10_t * pCbw10 = (scsi_cbw_10_t *)&inCBW->cbwcb[0];
			if (inCBW->cbLength == sizeof(scsi_cbw_10_t)) {
				unsigned long startBlock = swapUINT32(pCbw10->block_address);
				unsigned short needRead = swapUINT16(pCbw10->length);
				writeInputEDdataToBlockFlash(nullptr, startBlock, needRead);
			}
			break;
		}
		default:{}
	}
}

void f411usbMSCprocessInData(void) { // обрабатываем входящий поток данных
	unsigned char inData[4*MAX_SIZE];
	unsigned short inLen = f411usbMSCreadData(epUSBdataOut, (unsigned char*)&inData[0]);
	if (inLen) {
		_usb_device_msc_cbw * pCbw = (_usb_device_msc_cbw *)&inData[0];
		if ((pCbw->signature == BaitUSBC) && (inLen == sizeof(_usb_device_msc_cbw))) {
			f411usbMSCUFIcommand(pCbw);
			f411usbMSCresponseUSBSpacket(pCbw->tag);
		} else {
			if (inLen == MAX_SIZE) writeInputEDdataToBlockFlash((unsigned char *)&inData[0]);
		}
	}
}

void f411usbMSCintr(void) { // основная движуха модуля
	if(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_RXFLVL)  { // RX level interrupt // Приложение получает прерывание заполненности приема (Rx-FIFO non-empty, бит RXFLVL в регистре OTG_FS_GINTSTS), пока имеется как минимум 1 пакет, доступный для выборки из FIFO.
		uint32_t status = USB_OTG_FS->GRXSTSP; // Приложение считывает информацию о пакете из регистра статуса чтения и выборки (GRXSTSP)
		uint16_t len = (status & USB_OTG_GRXSTSP_BCNT) >> 4; // Read len // BCNT (биты 14:4): Byte CouNT. Показывает количество байт данных принятого пакета IN.
        if(((status & USB_OTG_GRXSTSP_PKTSTS) >> 17) == 0x02) { // Data // PKTSTS (биты 20:17): PacKeT STatuS. Показывает статус принятого пакета.
        	/*0001: Global OUT NAK (срабатывает прерывание).
			0010: принят пакет данных OUT.
			0011: завершена транзакция OUT (срабатывает прерывание).
			0100: завершена транзакция SETUP (срабатывает прерывание).
			0110: принят пакет данных SETUP.
				Другие значения: зарезервировано.*/
           if(len) { // если есть что принять
        	   f411usbMSCread(status & USB_OTG_GRXSTSP_EPNUM, &bufRx[countRx] , len); // Read data // считываем // EPNUM (биты 3:0): EndPoint NUMber (биты 3:0): CHannel NUMber. Показывает номер конечной точки, которой принадлежит текущий принятый пакет.
               epNumLastRx = status & USB_OTG_GRXSTSP_EPNUM;                // Save last endpoint num // сохраняем точку
               countRx += len;                                              // Save all len // сохраняем общее количество принятых данных
           }
        }
        if(((status & USB_OTG_GRXSTSP_PKTSTS) >> 17) == 0x06) { // Setup data packet received //0110: принят пакет данных SETUP.
        	f411usbMSCread(status & USB_OTG_GRXSTSP_EPNUM, bufRx, 0x08);                    // Read setup packet // прочитали 8 байт пакета setup
        }
    }
	if (USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_OEPINT) { // OUT -> RX endpoint interrupt // OEPINT (бит 19): OUT EndPoint INTerrupt. Ядро установит этот бит, чтобы показать ожидающее прерывания на одной из конечных точек OUT (режим устройства).
        uint32_t epNum; // номер конечной точки
        uint32_t epInt; // причина прерывание конечной точки
        epNum = USB_OTG_DEV->DAINT; // Приложение должно прочитать регистр OTG_FS_DAINT, чтобы определить точный номер конечной точки OUT, на которой произошло прерывание,
        epNum &= USB_OTG_DEV->DAINTMSK; // наложим маску на прерывания
        if(epNum & EP0_OUT_INT) {                       // EP0 OUT RX Interrupt // конечная точка канала управления USB Serial device
             epInt = USB_OUTEP(epUSBcontrol)->DOEPINT; // и затем прочитать соответствующий регистр OTG_FS_DOEPINTx для точного определения причины прерывания.
             epInt &= USB_OTG_DEV->DOEPMSK; // наложим маску
             if(epInt & USB_OTG_DOEPINT_STUP) { // STUP (бит 3): SETUP phase done. Применимо только к конечным точкам control OUT. Показывает, что фаза SETUP управляющей конечной точки (control endpoint) завершена и для текущей передачи управления больше не были получены пакеты установки с обратной передачей (back-toback SETUP packets). На этом прерывании приложение может декодировать принятый пакет данных SETUP.
            	 f411usbMSCsetup(bufRx);    // обработка пакета                        // Parse setup packet
             }
             USB_OUTEP(epUSBcontrol)->DOEPINT = epInt;              // Clear flag // чистим флаг причины прерывания
             USB_OUTEP(epUSBcontrol)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK | // CNAK (бит 26): Clear NAK. Запись в этот бит очистит бит NAK для конечной точки.
             USB_OTG_DOEPCTL_EPENA; // Enable endpoint, Clear NAK bit // EPENA (бит 31): EndPoint ENAble. Приложение устанавливает этот бит, чтобы запустить передачу на конечной точке 0. Ядро очистит этот бит перед установкой любого из следующих прерываний этой конечной точки: – завершена фаза SETUP  – конечная точка запрещена  – транзакция завершена
        }
        if(epNum & EP1_OUT_INT) {                        // EP1 OUT RX Interrupt // конечная точка данных USB Serial device
             epInt = USB_OUTEP(epUSBdataOut)->DOEPINT; // и затем прочитать соответствующий регистр OTG_FS_DOEPINTx для точного определения причины прерывания.
             epInt &= USB_OTG_DEV->DOEPMSK; // наложим маску на прерывания
             USB_OUTEP(epUSBdataOut)->DOEPINT = epInt;               // Clear flag // чистим флаг причины прерывания
             USB_OUTEP(epUSBdataOut)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK | // CNAK (бит 26): Clear NAK. Запись в этот бит очистит бит NAK для конечной точки.
             USB_OTG_DOEPCTL_EPENA; // Enable endpoint, Clear NAK bit // EPENA (бит 31): EndPoint ENAble. Приложение устанавливает этот бит, чтобы запустить передачу на конечной точке 0. Ядро очистит этот бит перед установкой любого из следующих прерываний этой конечной точки: – завершена фаза SETUP  – конечная точка запрещена  – транзакция завершена
        }
     }
	if (USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_IEPINT) {   // IN -> TX endpoint interrupt // Bit 18 IEPINT: IN endpoint interrupt
        uint32_t epNum;
        uint32_t epInt;
        epNum = USB_OTG_DEV->DAINT;
        epNum &= USB_OTG_DEV->DAINTMSK;
        if(epNum & EP0_IN_INT) {                          // EP0 IN TX Interrupt
             epInt = USB_INEP(epUSBcontrol)->DIEPINT;
             epInt &= USB_OTG_DEV->DIEPMSK;
             if(epInt & USB_OTG_DIEPINT_XFRC) {
            	 f411usbMSCsendEnd(epUSBcontrol);                               // If data left to send
             }
             USB_INEP(epUSBcontrol)->DIEPINT = epInt;                // Clear flag
        }
        if(epNum & EP2_IN_INT) {                          // EP2 IN TX Interrupt
        	epInt = USB_INEP(epUSBdataIn)->DIEPINT;
        	epInt &= USB_OTG_DEV->DIEPMSK;
        	if(epInt & USB_OTG_DIEPINT_XFRC) {
        		f411usbMSCsendEnd(epUSBdataIn);                               // If data left to send
        	}
        	USB_INEP(epUSBdataIn)->DIEPINT = epInt;                // Clear flag
        }
    }
	if(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_USBRST) {   // Reset Int // The core sets this bit to indicate that a reset is detected on the USB // хост прислал команду сбросить устройство
        USB_OTG_DEV->DCTL &= ~USB_OTG_DCTL_RWUSIG;      // Wakeup signal disable //    Из состояния suspended может быть осуществлен выход по инициативе самого устройства. В этом случае программа MCU устанавливает бит сигнализации remote wakeup (бит RWUSIG в регистре OTG_FS_DCTL), и очистит его в интервале от 1 до 15 мс.
        f411usbMSCflushTx();                                      // Clear tx buffer // очистка буферов передачи
        for(uint8_t i = 0U; i < 4; i++) {                 // Clear any pending EP flags
        	USB_INEP(i)->DIEPINT = 0xFB7FU;
        	USB_INEP(i)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
            USB_INEP(i)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
            USB_OUTEP(i)->DOEPINT = 0xFB7FU;
            USB_OUTEP(i)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
            USB_OUTEP(i)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
        }
        USB_OTG_DEV->DAINTMSK |= 0x10001U;              // EP0 OUT, EP0 IN Interupt
        USB_OTG_DEV->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | // Enable setup-done irq
        		USB_OTG_DOEPMSK_EPDM  | // Enable EP-disabled irq
				USB_OTG_DOEPMSK_XFRCM;  // Enable tx-done irq
        USB_OTG_DEV->DIEPMSK |= USB_OTG_DIEPMSK_TOM   | // Timeout irq
        			USB_OTG_DIEPMSK_XFRCM |
                    USB_OTG_DIEPMSK_EPDM;
        // Setup EP0 to receive SETUP packets
        USB_OUTEP(0)->DOEPTSIZ = (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
        			USB_OTG_DOEPTSIZ_STUPCNT | // Allow 3 setup pkt
                    (3 * 8);
        USB_OUTEP(0)->DOEPCTL = USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;   // Enable endpoint, Clear NAK bit
        USB_OTG_DEV->DCFG &= ~USB_OTG_DCFG_DAD;         // Clear address
        USB_OTG_FS->GINTSTS = USB_OTG_GINTSTS_USBRST;   // Clear flag
        // clear fifo tx buf
        // clear fifo tx buf
       	massEP0txControl.countItem = 0;
       	massEP0txControl.currentItem = 0;
       	massEP0txControl.inSend = false;
       	massEP2txData.countItem = 0;
       	massEP2txData.currentItem = 0;
       	massEP2txData.inSend = false;
    }
	if(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_ENUMDNE) { // На прерывании Enumeration Done (завершение энумерации, бит ENUMDNE в регистре OTG_FS_GINTSTS), прочитайте регистр OTG_FS_DSTS, чтобы определить скорость энумерации.
		USB_OTG_FS->GINTSTS |= USB_OTG_GINTSTS_ENUMDNE; // Clear the flag
	}
	if(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_MMIS) { //  Если происходит недопустимый доступ, то генерируется прерывание несовпадения (mismatch interrupt), и это будет отражено в регистре прерываний ядра (бит MMIS в регистре OTG_FS_GINTSTS).
		USB_OTG_FS->GINTSTS |= USB_OTG_GINTSTS_MMIS;
	}
	if(USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_SOF) { // В режиме устройства USB прерывание начала фрейма (start of frame, SOF) генерируется всякий раз, когда по шине USB принят токен SOF (бит SOF в регистре OTH_FS_GINTSTS).
		USB_OTG_FS->GINTSTS |= USB_OTG_GINTSTS_SOF;     // Clear the flag (rc_w1)
	}
}