How to get USBSerial on NUCLEO_F429ZI to work reliably?

Hi,

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.

What did I miss? Please advise.

Thanks in advance.

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:

USBSerial 	( 	bool  	connect_blocking = true,
		uint16_t  	vendor_id = 0x1f00,
		uint16_t  	product_id = 0x2012,
		uint16_t  	product_release = 0x0001 
	) 	

However, in USBSerial header file, it looks like this:

USBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, bool connect_blocking = true): USBCDC(vendor_id, product_id, product_release, connect_blocking){
        settingsChangedCallback = 0;
    };

Once the initialization line of code is changed to the following:

USBSerial serial(0x1f00, 0x2012, 0x0001, false);

The example code seems to be working fine.

Hello Zhiyong Li,

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.

BR, Jan

Hi Jan,

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.

Hello Zhiyong Li,

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);
}

I hope it will help you.
BR, Jan

Hi @JohnnyK,

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.