Пробуйте, нам потом расскажете про совместимость.
Код в пн выложу.
хорошо, придётся чуток поправить скетч, распайка 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
РВ4 Во второй строке банально ошибка. Распиновка 401 и 411 одинаковая.
у него WEACT модуль, интернета пишет, что разная, на моих чёрных совпадает
Так просто не бывает, чтобы clk PA5, DI PA7, и при этом DO - PB4?
Разница не в WEACT, а в буковках после 401/411. CE это 48 ножков, а RE - 64. Оттуда и несовпадения(возможно).
Ну нахрена ты))
Всю малину испортил.
Мля, ты опять тупака включаешь?
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)
}
}


