I have designed system based on STM32L151. It uses usb for powering/charging and communicating to laptop. My system uses Embedded File System (SPI, 8MB) without RTX, USB(CDC Class), given by ST. I have typical problem and did not find any answer on net and from ST. As long I do not any request from PC on USB to system, things are OK, i.e. I can store and send the file from system to laptop on USB. But if I send any request from Laptop like configuration/reading of file etc, sending file to PC does not work. While digging, I found that lower level fopen function generate hard fault exception, hinting wrong memory access (data address invalid). It happens only when there is req from host, else device can send file without any problem to host by system keypad operation. I suspect that when EP_OUT is called, it is damaging the RAM area, which used by fopen function, like file information etc. I am new to USB, much can not do myself. I have 2K Stack and 1K heap, no of files opened at a time are 2. Is anybody, faced problem like this, if yes, how they solved. Is there any simple example to handle similar case, I do not need serial handling in system. Thanks in advance.
> USB(CDC Class), given by ST
Do you mean Virtual_COM_Port example on this ST library?
STM32F10x and STM32L1xx USB full-speed device library v3.4.0 www.st.com/.../stm32_usb-fs-device_lib.zip
> I suspect that when EP_OUT is called, it is damaging the RAM area, which used by fopen function, like file information etc
1) The USB packet memory appears on this address space, 0x4000 6000 - 0x4000 63FF In the map file, generated by compile/link, do you see any variable (array) in this address range?
2) When the device receives a packet at the bulk out endpoint, EP3_OUT_Callback() (usb_endp.c) is called. In this routine, the packet is copied to USB_Rx_Buffer[] array. And USB_To_USART_Send_Data() is called with this array. Trace this process to find the RAM overwrite problem, if any.
For example, a) USB_Rx_Buffer[] array may be too short to hold the packet. This array is declared in usb_endp.c, uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];
And the VIRTUAL_COM_PORT_DATA_SIZE macro is defined in Virtual_COM_Port\inc\usb_desc.h #define VIRTUAL_COM_PORT_DATA_SIZE 64 Did you change this value?
b) Maybe, you've modified USB_To_USART_Send_Data() routine for your application. Is there any bug on this implementation?
Tsuneo
1. Yes, I uses Virtual_COM_Port Example supplied with ST along with USB FS Device Library V3.4.0 along with peripheral library V1.1.1 2. I checked map and did not find any variable in USB PMA area (0x4000 6000 - 0x4000 63FF) 3. Traced the EP3_OUT_Callback while writing to Rcv Buffer, did not find any possibility of over write. 4. I did not make any changes in header and source except, usb_endp.c and added usb.c file to handle the traffic (to/fro) on USB. I did not change VIRTUAL_COM_PORT_DATA_SIZE also. 5. I have removed the functions USB_To_UART and UART_To_USB to suit my application. Because my application is not USB to UART bridge, but it uses USB for receive and communicate from Laptop. I checked in debugger/reviewed and did not find any problem in my added functions. 6. I used the Flash file also from Keil example (ffs.c and retarget.c). I am sending the data in SOF frame only, earlier, tried in call back also(there was also similar error), finally to make things much simpler I used SOF for transmission. Below is screen of asm file of fopen function gives error, I see the R4 get upper 16 bits zero. Is it possible to attach file in post and then I can more detail/debugger output. Below is source Code, Error comes when the ReadFile is called from Laptop. I have removed the flash file due to size, it is also taken from examples. _________________________________ usb_endp.c __________________________________ #define VCOMPORT_IN_FRAME_INTERVAL 5 void EP1_IN_Callback (void) { if(USB_PMA_FLG) { USB_PMA_FLG = 0; } } // end of EP1_IN_Callback
void EP3_OUT_Callback(void) { uint16_t USB_Rx_Cnt; USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, &USB_aubRxBuffer[USB_ubRxWrIdx]); USB_ubRxCount += USB_Rx_Cnt; USB_ubRxWrIdx += USB_Rx_Cnt; USB_ubRxWrIdx &= USB_DATA_BUF_MASK; SetEPRxValid(ENDP3); }
------------------------------- usb.c -------------------------------- #define USB_DATA_BUF_SIZE 64 #define USB_DATA_BUF_MASK (USB_DATA_BUF_SIZE - 1) USB_EXTERN ubyte USB_aubRxBuffer[USB_DATA_BUF_SIZE]; USB_EXTERN ubyte USB_aubTxBuffer[USB_DATA_BUF_SIZE];
USB_EXTERN ubyte USB_ubTxCount; USB_EXTERN ubyte USB_ubRxCount; USB_EXTERN ubyte USB_ubRxWrIdx; USB_EXTERN ubyte USB_ubRxRdIdx;
USB_EXTERN breg USB_brFlags; #define USB_PMA_FLG USB_brFlags.bits.bit0 // = 0, usb pma is free #define USB_TXMT_FLG USB_brFlags.bits.bit1 // = 1, usb has data to send #define USB_RCVD_FLG USB_brFlags.bits.bit2 // = 1, usb has data to process
static ubyte usb_ubProcIdx; static ubyte usb_aubProcBuffer[USB_DATA_BUF_SIZE];
/******************************************************************************/ /* Static functions */ /******************************************************************************/ static void usb_vCDCReadBuffer(void) { ubyte ubChar;
ubyte ubIdx; ubyte ubEchoIdx;
ubyte ubBuffCount; char aubEchoBuffer[16];
// Read the data size in USB rcv buffer ubBuffCount = USB_ubRxCount;
if(ubBuffCount) // If Data, process them { USB_ubRxCount -= ubBuffCount; // USB_ubRxCount is INT variable
for(ubEchoIdx = 0, ubIdx = 0; ubIdx < ubBuffCount; ubIdx++) { ubChar = USB_aubRxBuffer[USB_ubRxRdIdx++];
switch(ubChar) { case CNTLQ: /* ignore Control S/Q */ case CNTLS: break;
case DEL: case BACKSPACE: if(usb_ubProcIdx) { usb_ubProcIdx--; aubEchoBuffer[ubEchoIdx++] = BACKSPACE; aubEchoBuffer[ubEchoIdx++] = ' '; aubEchoBuffer[ubEchoIdx++] = BACKSPACE; } break;
case ESC: usb_aubProcBuffer[usb_ubProcIdx] = 0; return;
case CR: case LF: aubEchoBuffer[ubEchoIdx++] = CR; aubEchoBuffer[ubEchoIdx++] = LF; USB_ubRxWrIdx = 0; USB_ubRxRdIdx = 0; USB_RCVD_FLG = 1; break;
default: aubEchoBuffer[ubEchoIdx++] = ubChar; usb_aubProcBuffer[usb_ubProcIdx++] = ubChar; break; } // end of switch for char rcvd } // end of for loop for rcvd bytes USB_vTxmtBuffer(aubEchoBuffer, ubEchoIdx); }
usb_aubProcBuffer[usb_ubProcIdx] = 0; }
USB_EXTERN void USB_vTxmtBuffer(char *pString, uword uwSize) { // check the TX free, which indicate the usb is idle and free to load while((USB_PMA_FLG) || (USB_TXMT_FLG)); // wait for tx to free;
USB_ubTxCount = (ubyte)uwSize; if(USB_ubTxCount > VIRTUAL_COM_PORT_DATA_SIZE) // check with possible max size { USB_ubTxCount = VIRTUAL_COM_PORT_DATA_SIZE; // control the size to PMA }
LIB_vMemCopy((char *)USB_aubTxBuffer, pString, USB_ubTxCount); // data may have zeros USB_TXMT_FLG = 1; // So SOF will take buffer to send }
USB_EXTERN void USB_vTxmtString(char *pString) { uword ulLength;
while((USB_PMA_FLG) || (USB_TXMT_FLG)); // wait for tx to free;
ulLength = strlen(pString); // Calculate the Tx length
if(ulLength > VIRTUAL_COM_PORT_DATA_SIZE) // check with possible max size { USB_ubTxCount = VIRTUAL_COM_PORT_DATA_SIZE; // control the size to PMA } else { USB_ubTxCount = (ubyte)ulLength; }
strncpy((char *)USB_aubTxBuffer, pString, USB_ubTxCount); USB_TXMT_FLG = 1; // So SOF will take buffer to send }
USB_EXTERN void USB_vSOFTxfer(void) { if((USB_PMA_FLG == 0) && (USB_TXMT_FLG == 1)) // PMA buffer is free { UserToPMABufferCopy(USB_aubTxBuffer, ENDP1_TXADDR, USB_ubTxCount); SetEPTxCount(ENDP1, USB_ubTxCount); SetEPTxValid(ENDP1);
USB_TXMT_FLG = 0; // USB Tx buffer free USB_PMA_FLG = 1; // make PMA busy } }
Hello,
Thanks for your suggestions and spent time. Finally the problem has been fixed. It was not null pointer problem or usb library, but corruption of Stack and Memory area by a for loop. In USB receive function, I used to stripped all the unwanted bytes in received message including CR and LF(keeping Printable characters). In the file system functions, before using the file name and command, convert the received message to upper case using one general function (taken from KEIL example code, char *get_entry(char *cp, char **pNext)). In this function, for (sp = cp; *sp != ' ' && *sp != CR && *sp != LF; sp++) { *sp = (char)toupper (*sp); /* convert entry to uppercase */ } breaks with space, LF or CR, which were not part of command to read the file (for argument part, i.e. File Name) - READ FILE_NAME. This for loop create problem in RAM by converting to them upper case till that it does not find either of above three characters. After including the LF/CR in message, it terminate as expected and problem solved. Sometimes changes or optimisation leads blunders as I have optimised the receive function but missed the impact in conversion. Still I feel that the conversion should have null also as break condition so that any String can also be send for conversion without impact with fully occupied RAM.
Is there any way to use Circular buffer implementation for CDC/USB to speed the transmission and save RAM. Tx Can be manage, but how to manage Rx Because Library gives counts and take start address of buffer. Does KEIL USB library uses Circular buffer? How to increase the size of Tx/Rx Packet and Place of changes in files.
> Is there any way to use Circular buffer implementation for CDC/USB to speed the transmission and save RAM.
On the ST CDC example, EP1_IN_Callback() splits data on large buffer, USART_Rx_Buffer, into 64 bytes (or less) packet. You may extend this implemntation into a cyclic buffer. A couple of problems you may meet with are,
1-1) word (two-bytes) access of USB RAM The data on the cyclic buffer is copied to the USB RAM using UserToPMABufferCopy() When the data on the buffer spans from the end of cyclic buffer to the top of the buffer (buffer wrap around), you may need a trick to do this job, for example, - copy the data into a temporary buffer once, and apply UserToPMABufferCopy() to this temporary buffer. The access to the USB RAM is restricted to word access. If the first portion of the data has odd number, you need to bind it with the later portion before UserToPMABufferCopy() is applied.
1-2) CDC bulk IN transfer requires short-packet termination. A large size transfer is split into 64 bytes (or less) packets on the USB line. When a transfer ends with a 64 bytes packet, bulk IN endpoint requires a ZLP (Zero-Length Packet), to indicate transfer termination.
// send ZLP SetEPTxCount(ENDP1, 0); SetEPTxValid(ENDP1);
1-3) Start trigger of transfer. EP1_IN_Callback() is called just when a transaction (packet) finishes. To start a sequence of transactions, your firmware has to call EP1_IN_Callback() explicitly.
On EP3_OUT_Callback(), you'll implement another cyclic buffer, like EP1_IN_Callback() Here are another problems on this implementation.
2-1) word access PMAToUserBufferCopy() (in USB_SIL_Read()) expects a target buffer, alligned in word boundary. You'll need some trick to copy the packet into odd address.
2-2) Flow control When the cyclic buffer is full, the copy from the USB RAM to the buffer is deferred until the data on the buffer is consumed. Your firmware has to give chances to poll this condition in SOF interrupt.
> Does KEIL USB library uses Circular buffer? Keil doesn't have USB CDC example for STM32L15x (nor STM32F103x, which has almost same USB peripheral)
> How to increase the size of Tx/Rx Packet and Place of changes in files.
On the device side, firmware exchanges 64 bytes (or less) packets over the USB peripheral. To move large-size transfer, your firmware has to split/gather the packets into full transfer. On the PC side, PC host controller does this job. Your PC application doesn't need to be aware of the packet size, at all. PC application can send/ receive large size transfer in single write/read call.