The USB topic
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
It is an example of badly designed web site, you usually have better luck using Google than trying to navigate it. The old site was ugly like sin too but at least you could find things.
Giovanni
Giovanni
Re: The USB topic
I am trying serial over USB at version 2.3.4 on STM32L-Discovery.
There are two problems.
One is after call palSetPadMode(), like below
palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
USB can't work if not to reset GPIOA->MODER to input.
GPIOA->MODER = GPIOA->MODER & 0xFC3FFFFF;
The other problem is OUT data endpoint can't handle burst data more than 64 bytes.
I test it use windows HyperTerminal and send a 64 bytes string.
I don't know why but I will keep tracing code.
There are two problems.
One is after call palSetPadMode(), like below
palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
USB can't work if not to reset GPIOA->MODER to input.
GPIOA->MODER = GPIOA->MODER & 0xFC3FFFFF;
The other problem is OUT data endpoint can't handle burst data more than 64 bytes.
I test it use windows HyperTerminal and send a 64 bytes string.
I don't know why but I will keep tracing code.
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
Hi,
It is possible that the USB peripheral requires input pins, not alternate, something to verify on the RM. Try to set them as input without pullups instead of alternate.
About the 64 bytes, the packet maximum size is programmed to 64 bytes, you should be able to write more than 64 bytes on the simulated serial port however.
Giovanni
It is possible that the USB peripheral requires input pins, not alternate, something to verify on the RM. Try to set them as input without pullups instead of alternate.
About the 64 bytes, the packet maximum size is programmed to 64 bytes, you should be able to write more than 64 bytes on the simulated serial port however.
Giovanni
Re: The USB topic
Hi Giovanni:
I trace the code and got the problem.
If send > 64 bytes data from PC to device quickly(like paste a string or send file to HyperTerminal), the USB RX interrupt will call sduDataReceived() two times very quickly.
But the second time, ChSysIQIsEmptyI(&sdup->iqueue) will return FALSE because MCU can't handle iqueue data so quickly and usbStartReceiveI(usbp, ep) will not be called.
This result make the USB RX can't receive data at next time forever.
Thanks.
I trace the code and got the problem.
If send > 64 bytes data from PC to device quickly(like paste a string or send file to HyperTerminal), the USB RX interrupt will call sduDataReceived() two times very quickly.
But the second time, ChSysIQIsEmptyI(&sdup->iqueue) will return FALSE because MCU can't handle iqueue data so quickly and usbStartReceiveI(usbp, ep) will not be called.
This result make the USB RX can't receive data at next time forever.
Thanks.
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
Thanks for finding this, I will try to fix this issue before next release.
Giovanni
Giovanni
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
Just a note.
It is right that the packet cannot be read because the queue is not empty yet, it is supposed to work that way, however sbStartReceiveI(usbp, ep) should be called when the input queue is emptied from within the function inotify(), that should restart the endpoint.
Is inotify() triggered in your application? if not, the only scenario I can imagine is that the application does not empty the input queue and the endpoint is not restarted because of this.
Giovanni
It is right that the packet cannot be read because the queue is not empty yet, it is supposed to work that way, however sbStartReceiveI(usbp, ep) should be called when the input queue is emptied from within the function inotify(), that should restart the endpoint.
Is inotify() triggered in your application? if not, the only scenario I can imagine is that the application does not empty the input queue and the endpoint is not restarted because of this.
Giovanni
Re: The USB topic
My test program is take data from USB RX and put to RS232
I am not sure this coding is right or not, could it empty iqueue data ?
Code: Select all
while (TRUE) {
if (SDU1.vmt->getwouldblock((void *)&SDU1) == FALSE)
{
tx_cnt = SDU1.iqueue.q_counter;
SDU1.vmt->read((void *)&SDU1, tx_buf, tx_cnt);
uartStartSend(&UARTD1, tx_cnt, tx_buf);
}
}
I am not sure this coding is right or not, could it empty iqueue data ?
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
Hi,
There are several problems with that code:
You can use a macro for this: sdGetWouldBlock().
This could be replaced with: sdReadTimeout(,,,TIME_IMMEDIATE), this returns all the data contained in the queue without waiting. In general you don't need to access the inner fields of the various drivers, there should always be a function or a macro that is the official interface (internal details are not API and can change).
The critical point is this one:
That function is an asynchronous call, if you call it while the previous uartStartSend() is still active the the behavior is unpredictable a crash or a lock are not impossible, see the state diagram of the UART driver. Probably you need a buffered output here, you should use the serial driver instead of the UART driver.
Anyway if you want to stress test the USB driver just do the emptying of the input queue, add the serial part later.
Giovanni
There are several problems with that code:
Code: Select all
SDU1.vmt->getwouldblock((void *)&SDU1)You can use a macro for this: sdGetWouldBlock().
Code: Select all
tx_cnt = SDU1.iqueue.q_counter;
SDU1.vmt->read((void *)&SDU1, tx_buf, tx_cnt);This could be replaced with: sdReadTimeout(,,,TIME_IMMEDIATE), this returns all the data contained in the queue without waiting. In general you don't need to access the inner fields of the various drivers, there should always be a function or a macro that is the official interface (internal details are not API and can change).
The critical point is this one:
Code: Select all
uartStartSend(&UARTD1, tx_cnt, tx_buf)That function is an asynchronous call, if you call it while the previous uartStartSend() is still active the the behavior is unpredictable a crash or a lock are not impossible, see the state diagram of the UART driver. Probably you need a buffered output here, you should use the serial driver instead of the UART driver.
Anyway if you want to stress test the USB driver just do the emptying of the input queue, add the serial part later.
Giovanni
Re: The USB topic
So, finally I found what was wrong with my app. Yes, it was the hardware that I screwd up, even after checking (literally) more than five times.
Anyway, I have a suggestion for the demo code for using USB_CDC. For the descriptor strings, I managed to get a way that allows me to type the strings directly on code instead of using that ugly form interlaced with zeros. Not only that, but I suppose my form is easier to handle when non-ASCII characters are present.
So, instead of:
I used:
The only possible problem I see is portability. This worked for what I'm using, which is GCC (CodeSourcery 4.5.2), but I have no idea if that works on other compilers.
Besides, instead of using 56 and 56/2, we could also use a more straightforward number and let the compiler do the math, such as 27+1 and (27+1)*2
Anyway, I have a suggestion for the demo code for using USB_CDC. For the descriptor strings, I managed to get a way that allows me to type the strings directly on code instead of using that ugly form interlaced with zeros. Not only that, but I suppose my form is easier to handle when non-ASCII characters are present.
So, instead of:
Code: Select all
static const uint8_t vcom_string2[] = {
USB_DESC_BYTE(56), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,
'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,
'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,
'o', 0, 'r', 0, 't', 0
};
I used:
Code: Select all
static const struct {uint8_t l;uint8_t t; char16_t s[56/2];} vcom_string2 = {
USB_DESC_BYTE(56), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
u"ChibiOS/RT Virtual COM Port"
};
The only possible problem I see is portability. This worked for what I'm using, which is GCC (CodeSourcery 4.5.2), but I have no idea if that works on other compilers.
Besides, instead of using 56 and 56/2, we could also use a more straightforward number and let the compiler do the math, such as 27+1 and (27+1)*2
- Giovanni
- Site Admin
- Posts: 14891
- Joined: Wed May 27, 2009 8:48 am
- Has thanked: 1202 times
- Been thanked: 996 times
Re: The USB topic
I think it is a C++11 extension, probably GCC supports it as a custom extension to the C language. It is OK to use in applications but I can't use it in the standard demo.
Giovanni
Giovanni