Inprove the speed of the "Mass storage" as u disk
a) USB transfer speed of MSC-BOT (Mass-Storage Class - Bulk-Only Transport)
MSC-BOT protocol has overhead to transfer data on both direction.
READ10/WRITE10 SCSI commands are carried by this BOT protocol.
CBW - Data transport (IN/OUT) - CSW
When Host runs OHCI or UHCI host controller (as usual on PCs),
each transport stage takes one or more USB frame,
because these host controllers delay interrupt of transfer completion until next SOF timing.
- CBW : 1 frame (1 ms)
- Data transport (4K Bytes) : 4 frames
- CSW : 1 frame
4K bytes / 6 ms = 667K Bytes/s
Therefore, your observation, 500KB-700KB/s, hits the practical max of full-speed MSC-BOT.
When the MSC device plugs in to a high-speed USB port (EHCI) over a USB2.0 hub,
you may see a little better performance. On EHCI, each transport stage
is assigned to micro-frame (125 us), instead of frame (1 ms).
b) Protocol overhead of SDIO
While host reads/writes file data on SD card, host accesses
to it in the unit of 4K bytes cluster. To read/write this size of data form a SD card,
multi-block read/write: READ_MULTIPLE_BLOCK (CMD18), WRITE_MULTIPLE_BLOCK (CMD25),
have less protocol overhead than repeated single-block ones,
though it requires a buffer of cluster size.
Put SET_WR_BLOCK_ERASE_COUNT (ACMD23: CMD55,CMD23)
before WRITE_MULTIPLE_BLOCK, to speed up multi-block write.
c) Concurrent processes of SDIO and USB
To run SDIO and USB concurrently, double buffer is applied.
That is, while a buffer is filled with SDIO, another buffer is sent over USB.
When both processes finish, the buffers are swapped and next processes start.
This scheme speeds up the throughput but it requires double-sized buffer.
To reduce the total buffer size, above double buffer is replaced with a cyclic buffer
in the unit of wMaxPacketSize (64 bytes). Every time 64 bytes are read out,
the firmware halts SDIO to check the buffer full. Next read of SDIO is delayed
until USB makes any room on the buffer. SDIO halts while SD clock pauses,
at any phase of multi-block read/write protocol. Similarly,
USB waits while the buffer is empty.
In this way, even 128 bytes buffer works.
Tsuneo
Hello,
I have the same problem (With Modified prop.c and sil.c files)
1/ prop.c:
/*Set Endp1 as a sent double buffer*/
SetEPType(ENDP1, EP_BULK);
SetEPDoubleBuff(ENDP1);
SetEPDblBuffAddr(ENDP1, ENDP1_BUFF0ADDR, ENDP1_BUFF1ADDR);
ClearDTOG_RX(ENDP1);
ToggleDTOG_RX(ENDP1);
ClearDTOG_TX(ENDP1);
SetEPTxStatus(ENDP1, EP_TX_VALID);
SetEPRxStatus(ENDP1, EP_RX_DIS);
/*Set Endp2 as a received double buffer*/
SetEPType(ENDP2, EP_BULK);
SetEPDoubleBuff(ENDP2);
SetEPDblBuffAddr(ENDP2, ENDP2_BUFF0ADDR, ENDP2_BUFF1ADDR);
SetEPDblBuffCount(ENDP2, EP_DBUF_OUT, BULK_MAX_PACKET_SIZE);
ClearDTOG_RX(ENDP2);
ClearDTOG_TX(ENDP2);
ToggleDTOG_TX(ENDP2);
SetEPRxStatus(ENDP2, EP_RX_VALID);
SetEPTxStatus(ENDP2, EP_TX_DIS);
2/ sil.c
uint32_t USB_SIL_Write(uint8_t bEpAddr, uint8_t* pBufferPointer, uint32_t wBufferSize)
{
/* Update the data length in the control register */
SetEPTxCount((bEpAddr & 0x7F), wBufferSize);
/*write to buffer 0*/
if (GetENDPOINT(ENDP2) & EP_DTOG_TX)
{
wBufferSize = GetEPDblBuf0Count(ENDP2);
UserToPMABufferCopy(pBufferPointer, ENDP2_BUFF0ADDR, wBufferSize);
}
else
{
/*write to buffer 1*/
wBufferSize = GetEPDblBuf1Count(ENDP2);
UserToPMABufferCopy(pBufferPointer, ENDP2_BUFF1ADDR, wBufferSize);
}
FreeUserBuffer(ENDP2, EP_DBUF_OUT);
return 0;
}
and :
uint32_t USB_SIL_Read(uint8_t bEpAddr, uint8_t* pBufferPointer)
{
uint32_t DataLength = 0;
/* Get the number of received data on the selected Endpoint */
DataLength = GetEPRxCount(bEpAddr & 0x7F);
/*read from buffer 0*/
if (GetENDPOINT(ENDP2) & EP_DTOG_TX)
{
DataLength = GetEPDblBuf0Count(ENDP2);
PMAToUserBufferCopy(pBufferPointer, ENDP2_BUFF0ADDR, DataLength);
}
else
{
/*read from buffer 1*/
DataLength = GetEPDblBuf1Count(ENDP2);
PMAToUserBufferCopy(pBufferPointer, ENDP2_BUFF1ADDR, DataLength);
}
FreeUserBuffer(ENDP2, EP_DBUF_OUT);
/* Return the number of received data */
return DataLength;
Could you please Help me
Abdul
A far more considered implementation, although still rather hackish, would look a bit like :
/* Initialize Endpoint 1 (TX) (IN) (USB_SIL_WRITE) (EP1_IN)*/
SetEPType(ENDP1, EP_BULK);
SetEPDoubleBuff(ENDP1);
SetEPDblBuffAddr(ENDP1, ENDP1_BUF0ADDR, ENDP1_BUF1ADDR);
SetEPDblBuffCount(ENDP1, EP_DBUF_IN, Device_Property.MaxPacketSize); // 0x40
ClearDTOG_TX(ENDP1); // USB PERIPHERAL
ClearDTOG_RX(ENDP1); // SW_BUF for APPLICATION
ToggleDTOG_RX(ENDP1); // NOT TX ie SW_BUF
SetEPTxStatus(ENDP1, EP_TX_NAK);
SetEPRxStatus(ENDP1, EP_RX_DIS); // NOT TX DISABLE
/* Initialize Endpoint 2 (RX) (OUT) (USB_SIL_Read) (EP2_OUT)*/
SetEPType(ENDP2, EP_BULK);
SetEPDoubleBuff(ENDP2);
SetEPDblBuffAddr(ENDP2, ENDP2_BUF0ADDR, ENDP2_BUF1ADDR);
SetEPDblBuffCount(ENDP2, EP_DBUF_OUT, Device_Property.MaxPacketSize); // 0x40
ClearDTOG_RX(ENDP2); // USB PERIPHERAL
ClearDTOG_TX(ENDP2); // SW_BUF for APPLICATION
ToggleDTOG_TX(ENDP2); // NOT RX ie SW_BUF
SetEPRxStatus(ENDP2, EP_RX_VALID);
SetEPTxStatus(ENDP2, EP_TX_DIS); // NOT RX DISABLE
/*******************************************************************************
* Function Name : USB_SIL_Write
* Description : Write a buffer of data to a selected endpoint.
* Input : - bEpAddr: The address of the non control endpoint.
* - pBufferPointer: The pointer to the buffer of data to be written
* to the endpoint.
* - wBufferSize: Number of data to be written (in bytes).
* Output : None.
* Return : Status.
*******************************************************************************/
uint32_t USB_SIL_Write(uint8_t bEpAddr, uint8_t* pBufferPointer, uint32_t wBufferSize)
{
// Hard coded to simplify
// Assumes (bEpAddr & 0x7F) == ENDP1 or EP1_IN?
if (GetENDPOINT(ENDP1) & EP_DTOG_RX) // NOT TX ie SW_BUF
{
UserToPMABufferCopy(pBufferPointer, ENDP1_BUF0ADDR, wBufferSize);
SetEpDblBuf0Count(ENDP1, wBufferSize);
}
else
{
UserToPMABufferCopy(pBufferPointer, ENDP1_BUF1ADDR, wBufferSize);
SetEpDblBuf1Count(ENDP1, wBufferSize);
}
FreeUserBuffer(ENDP1, EP_DBUF_IN); // Toggles EP_DTOG_RX / SW_BUF
return 0;
}
/*******************************************************************************
* Function Name : USB_SIL_Read
* Description : Write a buffer of data to a selected endpoint.
* Input : - bEpAddr: The address of the non control endpoint.
* - pBufferPointer: The pointer to which will be saved the
* received data buffer.
* Output : None.
* Return : Number of received data (in Bytes).
*******************************************************************************/
uint32_t USB_SIL_Read(uint8_t bEpAddr, uint8_t* pBufferPointer)
{
uint32_t DataLength = 0;
// Hard coded to simplify
// Assumes (bEpAddr & 0x7F) == ENDP2 or EP2_OUT?
if (GetENDPOINT(ENDP2) & EP_DTOG_TX) // NOT RX ie SW_BUF
{
DataLength = GetEPDblBuf0Count(ENDP2);
PMAToUserBufferCopy(pBufferPointer, ENDP2_BUF0ADDR, DataLength);
}
else
{
DataLength = GetEPDblBuf1Count(ENDP2);
PMAToUserBufferCopy(pBufferPointer, ENDP2_BUF1ADDR, DataLength);
}
FreeUserBuffer(ENDP2, EP_DBUF_OUT); // Toggles EP_DTOG_TX / SW_BUF
/* Return the number of received data */
return DataLength;
}
I don't think this addresses STALL conditions
Tanks you clive1, it's now clear,
please one more questions according to your correction made on my code
1/ I set in prop.c file in Mass_Storage_SetConfiguration following:
void Mass_Storage_SetConfiguration(void)
{
if (pInformation->Current_Configuration != 0)
{
/* Device configured */
bDeviceState = CONFIGURED;
ClearDTOG_RX(ENDP2);
ClearDTOG_TX(ENDP1);
ToggleDTOG_TX(ENDP2); /* reset value of the data toggle bits for the endpoint out*/
ClearDTOG_RX(ENDP1);
ClearDTOG_TX(ENDP1); /* clear the data toggle bits for the endpoint IN*/
Bot_State = BOT_IDLE; /* set the Bot state machine to the IDLE state */
}
}
and in MASS_NoData_Setup function:
RESULT MASS_NoData_Setup(uint8_t RequestNo)
{
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& (RequestNo == MASS_STORAGE_RESET) && (pInformation->USBwValue == 0)
&& (pInformation->USBwIndex == 0) && (pInformation->USBwLength == 0x00))
{
ClearDTOG_RX(ENDP2);
ClearDTOG_TX(ENDP2);
ToggleDTOG_TX(ENDP2); /* reset value of the data toggle bits for the endpoint out*/
ClearDTOG_RX(ENDP1);
ClearDTOG_TX(ENDP1); /* clear the data toggle bits for the endpoint IN*/
/*initialize the CBW signature to enable the clear feature*/
CBW.dSignature = BOT_CBW_SIGNATURE;
Bot_State = BOT_IDLE;
return USB_SUCCESS;
}
return USB_UNSUPPORT;
}
is it correct ? because I still have code 10 error
tanks
Abdul
Presumably if you wanted initial states correct it would be
ClearDTOG_TX(ENDP1);
ClearDTOG_RX(ENDP1);
ToggleDTOG_RX(ENDP1);
ClearDTOG_RX(ENDP2);
ClearDTOG_TX(ENDP2);
ToggleDTOG_TX(ENDP2);
Now that's not to say you wouldn't need to change other parts of the code.
It might take quite some effort, and understanding.
The current release MSC code running on an STM32F4 USB-FS (12Mbps) card
can read/write to an SD Card at 600/400 KBps.
I'm not sure what your target rate is?
Thanks clive for your Help, Yopu are Great;
I'm working on V3.3.0 USB lib from ST, My aim is to implement a double buffering in MSC demo.
1/ The bit toggling : is it the best way to measure Read/write speed ? I have to measure them before and after Double Bufferinh implementation to get #;
2/ Can you please tell me if it is correct about Buffering adress in usb_conf.h:
/* tx buffer base address for Double fuffering */
#define ENDP1_BUFF0ADDR (0x98)
#define ENDP1_BUFF1ADDR (0xD8)
/* Rx buffer base address for Double fuffering */
#define ENDP2_BUFF0ADDR (0x118)
#define ENDP2_BUFF1ADDR (0x158)
beacuse I have a strange behavi
* On Reset Led1 On and the Off (used for configuartion)
* it takes about more than 10 s to the device for appearing as Removable in windows
(not the case with simple buffer)
3/ in usb_bot.c is this correct ?
/*******************************************************************************
* Function Name : Bot_Abort
* Description : Stall the needed Endpoint according to the selected direction.
* Input : Endpoint direction IN, OUT or both directions
* Output : None.
* Return : None.
*******************************************************************************/
void Bot_Abort(uint8_t Direction)
{
switch (Direction)
{
case DIR_IN :
SetDouBleBuffEPStall(ENDP1, EP_DBUF_IN);
break;
case DIR_OUT :
SetDouBleBuffEPStall(ENDP2, EP_DBUF_OUT);
FreeUserBuffer(ENDP2, EP_DBUF_OUT);
break;
case BOTH_DIR :
SetDouBleBuffEPStall(ENDP1, EP_DBUF_IN);
SetDouBleBuffEPStall(ENDP2, EP_DBUF_OUT);
FreeUserBuffer(ENDP2, EP_DBUF_OUT);
break;
default:
break;
Tanks you very much
Abdul