I have been having trouble with USBSerial/CDC for a while. After the following code is compiled and flashed into F429ZI, and a terminal program like Accessport is connected to the COM port of the board, I don’t always see the expected text “I am a virtual serial port”. And the echo worked once then Accessport crashed.
#include "mbed.h"
#include "USBSerial.h"
//Virtual serial port over USB
USBSerial serial;
void serialCb() {
serial._putc(serial._getc());
}
int main(void) {
serial.init();
serial.connect();
serial.attach(&serialCb);
while(1)
{
serial.printf("I am a virtual serial port\r\n");
wait(1.0);
}
}
This happened on two computers. So I think it is unlikely a problem of computer or USB port.
Part of the problem seems to be blocking and documentation. The other part of the problem is, once a board is reset by either pressing reset button or flashing new firmware, terminal program such as Accessport has to connect the same port with a different baud rate.
In API documentation, USBSerial initialization is documented as:
I do not know, but it looks like you use an old example (old MbedOS), because that is not constructor of the current USB driver. And it is more like this old unsupported version. So the documentation is OK from my point of view.
The USBSerial is set to blocking in the default and that mean, the USBSerial object will wait (the code will not continue) until a connection is established. The methods like init() and connect() not need to be called.
When you set it to non-blocking mode, then the methods init() and connect() must be used and, it must be handled by you.
Thanks for your response. Could you point to a good running example for newer version of driver? I think I am still missing something. When the order of parameters passed to USBSerial constructor is adjusted to match newer version of driver, Accessport on PC will crash after sending a 1 byte.
#include "mbed.h"
#include "USBSerial.h"
//Virtual serial port over USB
USBSerial serial(false, 0x1f00, 0x2012, 0x0001);
void serialCb() {
serial._putc(serial._getc());
}
int main(void) {
serial.init();
serial.connect();
serial.attach(&serialCb);
while(1)
{
serial.printf("I am a virtual serial port\r\n");
ThisThread::sleep_for(1000);
}
}
As of now, in both MBed-5.14 and 5.15, I will need to try to connect the board with different baud rate a few times before I start to see the text “I am a virtual serial port”. But once I start to send random byte to the board and expect echo back, Accessport will crash.
I made some tests and I had same result (also same board) like you.
With some debugging I found the issue seems to be about the buffer was not cleaned.
You can verify that when you add following code to to your loop.
int n = serial.available();
if(n) printf("n_bytes %d\n",n);
In the STDIO (ST-link output) you will see number of bytes what are ready to read. After the first byte is readed, the number of bytes are still two.
So you need repeat the putc(getc()) until the buffer is empty.
#include "mbed.h"
#include "USBSerial.h"
DigitalOut led(LED1);
DigitalOut led2(LED2);
//Virtual serial port over USB
USBSerial serial(false);
int main(void) {
printf("USB\n");
serial.init();
serial.connect();
while(1){
// visual check if a byte is in buffer
led2 = serial.readable();
// check how many bytes are in buffer
int n = serial.available();
// print the number of bytes ready to read if not empty
if(n) printf("n_bytes %d\n",n);
while(serial.readable()){
serial._putc(serial._getc());
}
// live led
led = !led;
ThisThread::sleep_for(100ms);
}
}
If you want to use the Interrupts your probably need to manage it via EventQueue because of the mutex, which is presented in these methods. How we know, the mutex is not allowed in interrupt context.
#include "mbed.h"
#include "USBSerial.h"
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;
DigitalOut led(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
//Virtual serial port over USB
USBSerial serial(false);
void usb_read(void);
void serialCb();
int main(void) {
printf("USB\n");
t.start(callback(&queue, &EventQueue::dispatch_forever));
serial.init();
serial.connect();
serial.attach(&serialCb);
while(1){
led = !led;
ThisThread::sleep_for(500ms);
}
}
void usb_read(void)
{
// check how many bytes are in buffer
int n = serial.available();
if(n) printf("n_bytes %d\n",n);
while(serial.readable()){
serial._putc(serial._getc());
led3 = !led3;
}
}
void serialCb() {
queue.call(usb_read);
}
Thanks for the detailed example. Once “500ms” is replaced with"500", everything works. LOL
I believe the main issue I missed is DTR. Once hardware flow control with DTR is selected in terminal program, my old code seems to work to a large degree.