Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serial USB not working on STM32u575 #339

Open
lefebvresam opened this issue Sep 4, 2024 · 19 comments
Open

Serial USB not working on STM32u575 #339

lefebvresam opened this issue Sep 4, 2024 · 19 comments

Comments

@lefebvresam
Copy link

I started some tests with the serial USB (OTG) on the STM32u575 to get a virtual com port on a Windows PC.

After fixing some issues with the clock config file (because it was never used and tested before), I have the following situation:

In main.cpp I include the driver:

#include "USBSerial.h"
USBSerial serial;

And I always get:

/home/sam/git/hmipanel/source/main.cpp:10:10: fatal error: USBSerial.h: No such file or directory

In the custom targets file I use:

        "device_has_add": [
            "USBDEVICE"
        ],

I'm not sure if I have to add also something in the "components" section?
Is this anywhere described?

I also tried to add this in the mbed_app.json:

    “requires”: [
        “bare-metal”,
        “drivers-usb”,
        “events”
    ]

But in that case I get:

CMake Error at mbed-os/tools/cmake/mbed_generate_configuration.cmake:123 (message):
  mbedtools configure failed! Cannot build this project.  Command was
  /home/sam/git/hmipanel/mbed-os/venv/bin/python3.10 -c import
  mbed_tools.cli.main; exit(mbed_tools.cli.main.cli()) -v configure -t
  GCC_ARM -m HMC20 --mbed-os-path
  /home/sam/git/hmipanel/mbed-os/tools/cmake/../..  --output-dir
  /home/sam/git/hmipanel/build/develop --program-path /home/sam/git/hmipanel
Call Stack (most recent call first):
  mbed-os/tools/cmake/app.cmake:24 (include)
  CMakeLists.txt:8 (include)
@wdx04
Copy link

wdx04 commented Sep 4, 2024

USB support for STM32U5 was not available in the official Mbed OS.
I have implemented some basic USB functionality for STM32U5. The code is not well tested but at least USBSerial is working on these targets. You may have a look at the code:
USB FS support on Nucleo-U545RE-Q, Nucleo-U575ZI-Q, B-U585I-IOT02A:
wdx04@77a7bad
USB HS support on Nucleo-U5A5ZJ-Q and STM32F723E-Disco:
wdx04@5a9b126

@lefebvresam
Copy link
Author

Is there a plan to create a merge request for adding those feature in the mbed-os master branch? I think this is a common use case. Later I also want to use the same usb connection for firmware upgrades. If I can get it working maybe we can create a branch for it?

@lefebvresam
Copy link
Author

And is there a way to use this virtual com port as the default uart (std::printf)?

@JohnK1987
Copy link
Member

And is there a way to use this virtual com port as the default uart (std::printf)?

Yes, it is but you have to use an app with auto control of DTR signal.

https://forums.mbed.com/t/hitchhikers-guide-to-printf-in-mbed-6/12492/1
https://forums.mbed.com/t/redirecting-printf-to-usb-serial-in-mbed-os-6/14279

@lefebvresam
Copy link
Author

I'm still struggeling with the error fatal error: USBSerial.h: No such file or directory. Should I have to add something in the CMakeLists somewhere in the targets folder? Next, I will use the demo of @wdx04 and change it for HSE use.

@wdx04
Copy link

wdx04 commented Sep 4, 2024

I'm still struggeling with the error fatal error: USBSerial.h: No such file or directory. Should I have to add something in the CMakeLists somewhere in the targets folder? Next, I will use the demo of @wdx04 and change it for HSE use.

Yes, you should link your application to the mbed-usb library:

target_link_libraries(mbed-app mbed-os mbed-usb)

@lefebvresam
Copy link
Author

lefebvresam commented Sep 4, 2024

Wow this looks better. Is there a reason that you need to link only this library and not others for SPI, IIC, storage, etc?
And where this is documented?

target_link_libraries(${APP_TARGET} PRIVATE
    mbed-os
    mbed-usb
    RA8875
    dmaflash
    widgets
    interpreter
    apprunlight
    appbutton
    appfogger
    commandinterface
)

@JohnK1987
Copy link
Member

JohnK1987 commented Sep 4, 2024

In general it is not a part of MbedOS core but it is an Mbed library - https://github.com/mbed-ce/mbed-os/wiki/MbedOS-configuration#mbed-libraries.

@lefebvresam
Copy link
Author

I created a branch for testing.
https://github.com/mbed-ce/mbed-os/tree/feature/stm32u575xG_usb-support

After some small modifications in the clock settings file it's already working but there is:

  • When I plug out and in the usb cable the COM port is not appearing again.
    I have to reset the MCU and reconnect the terminal.
  • When I reset the MCU the terminal connection is broken, which looks normal.
  • How can I change the baudrate?

@JohnK1987
Copy link
Member

It is VCP so the baudrate should be changed only on PC side.

@lefebvresam
Copy link
Author

lefebvresam commented Sep 4, 2024

Ah ok. I also see that the firnware is blocking until you open a terminal. I was searching on the internet for better examples how to dynamically load/unload the driver when connecting the cable, but examples are very poor. In the real world, the user will connect the USB cable and must see the logging information without resetting the device.

Is there any example how you can connect when cable is attached/detached without blocking the firmware?

@JohnK1987
Copy link
Member

My colleague used it like this

USBSerial serial(false);

template<typename... Args> void debugPrint(const char *msg, Args... args){
    if(!serial.configured()){
        serial.connect();            
    }
    
    if(serial.configured()){
        if(!serial.connected()){
            serial.init();    
        }   
    }
    
    if(serial.connected()){
        serial.printf(msg, args...);
    } 
    //else
    printf(msg, args...);
}

@lefebvresam
Copy link
Author

Looks nice. I will test this. In my case, the printf should be 'routed' to the usb (for debug in the field) and other serial port is used for commands (application board connector). I wil also check for a way to upload the FW trough the same usb cable without using a programmer. Do you suggest one of the upload methods described on mbed ce (like using cube programmer) or can I use the 'default' bootloader from ST and use a dedicated tool like you have with an arduino board?

@JohnK1987
Copy link
Member

I understand, and i believe you can achieve that via combination of the code above and the method mentioned in topic what i posted few post above via.

/** Targets may implement this to change stdin, stdout, stderr.
*
* If the application hasn't provided mbed_override_console, this is called
* to give the target a chance to specify a FileHandle for the console.
*
* If this is not provided or returns NULL, the console will be:
* - BufferedSerial if configuration option "platform.stdio-buffered-serial" is
* true and the target has DEVICE_SERIAL;
* - Raw HAL serial via serial_getc and serial_putc if
* "platform.stdio-buffered-serial" is false and the target has DEVICE_SERIAL;
* - stdout/stderr will be a sink and stdin will input a stream of 0s if the
* target does not have DEVICE_SERIAL.
*
* @param fd file descriptor - STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO
* @return pointer to FileHandle to override normal stream otherwise NULL
*/
FileHandle *mbed_target_override_console(int fd);

@lefebvresam
Copy link
Author

I will check it in detail tomorrow. Thanks for all this useful information.

@lefebvresam
Copy link
Author

My colleague used it like this

USBSerial serial(false);

template<typename... Args> void debugPrint(const char *msg, Args... args){
    if(!serial.configured()){
        serial.connect();            
    }
    
    if(serial.configured()){
        if(!serial.connected()){
            serial.init();    
        }   
    }
    
    if(serial.connected()){
        serial.printf(msg, args...);
    } 
    //else
    printf(msg, args...);
}

I couldn't resist to check this, seems to need the following setting in the USBPhy_STM32.cpp under USE_USB_OTG_FS:

hpcd.Init.vbus_sensing_enable = ENABLE;

@lefebvresam
Copy link
Author

lefebvresam commented Sep 6, 2024

Because the USB is automatically connecting when I enable vbus sensing with resistor divider I don't need the connect call.
I only use this to rewire the stdout:

class MyConsole : public USBSerial {
    // inherit all constructors
    using USBSerial::USBSerial;

    // override putc
    virtual int _putc(int c) override {
        USBSerial::_putc(c);
        return c;
    }
};

#ifdef STDOUT_USB
namespace mbed {
    FileHandle *mbed_override_console(int fd) {
        static MyConsole console(false); // non blocking
        return &console;
    }
}
#endif

However I was searching for a way to give a welcome text as soon as the terminal is connected, because you cannot connect the terminal first and startup the board secondly like you can do with a hardware serial port. Because it's USB the terminal connection breaks after each reset.

I tried something like this in the MyConsole class, but this is doing nothing:

virtual void callback_state_change(USBDevice::DeviceState new_state) override { // ISR?
        switch (new_state) {
            case Attached:
                printf("Attached\n");
                break;
            case Powered:
                printf("Powered\n");
                break;
            default:
                break;
        }
    };

Alternatively I could create a new thread and wait for connected().

Secondly, to create a firmware upload method over usb, how do you see that? Is it creating a virtual drive where you copy paste the hex file like you have with nucleo boards? Or is it a protocol? Or do you expose the sd card as a drive? Is there any example of this or are there some more hints?

@JohnK1987
Copy link
Member

However I was searching for a way to give a welcome text as soon as the terminal is connected, because you cannot connect the terminal first and startup the board secondly like you can do with a hardware serial port. Because it's USB the terminal connection breaks after each reset.

Yeah, that is strange but I am not USB expert so I am not able say what is wrong. However my logic says this is not normal.

About Bootloader:
I have some experiences with Bootloader under ARM Mbed and I did not tested this under MbedCE and I am also not sure this is worked feature at this moment, probably not.
So as an alternative you can try Mcuboot. Here is an example from the past - https://github.com/AGlass0fMilk/mbed-mcuboot-demo

Maybe some points from discussing about - ARMmbed#15156

About transfer and storage of binary:
I do not know, from my point of view it depends on many things. For example if your on-chip flash is enough big for two binary then you do not need any external memory probably but if you already have SD card for something else then why not.
I do not know about any protocol for transfer but you can do it by your self. A python script that will split whole binary to a chunks with crc and send it one by one over USB is possible i think.

@lefebvresam
Copy link
Author

lefebvresam commented Sep 9, 2024

This is working in my case:

class MyConsole : public USBSerial {
    
    public:
    MyConsole(bool b, RA8875& lcd) : USBSerial(b), _lcd(lcd),
        _thread(osPriorityBelowNormal, 1024, nullptr, "usbserial") {
            _thread.start(callback(this, &MyConsole::run));
        }
    
    ~MyConsole() {
        _thread.terminate();
    }

    // override putc
    virtual int _putc(int c) override {
        USBSerial::_putc(c);
        return c;
    }

    private:
    void run() {
        bool connected = false;
        bool previousconnected = false;
        int c;
        while(true) {
            connected = this->connected();
            if (!previousconnected && connected) {
                print_welcome();
                print_settings(_lcd);
                print_menu();
                this->printf("\n");
            }
            if (connected && this->readable()) {                
                int last;
                last = _getc();
                if (last != '\n' && last != '\r') {
                    c = last; // store the last char
                } else {
                    switch(c) {
                        case 's':
                            print_settings(_lcd);
                            break;
                        case 'i':
                            print_welcome();
                            break;
                        case 'h':
                            print_menu();
                            break;
                        default:
                            break;
                    }
                    this->printf("\n");
                    c = '\0';
                }
            }
            fflush(stdout);
            previousconnected = connected;
            ThisThread::sleep_for(100ms);
        }
    }

    Thread _thread;
    RA8875& _lcd;
};

#ifdef STDOUT_USB
namespace mbed {
    FileHandle *mbed_override_console(int fd) {
        static MyConsole console(false, lcd); // non blocking
        return &console;
    }
}
#endif

I did also some tests with available(), but the strange thing is that if you don't use _getc() the number always stays at 1. If you fastly type characters and to _getc() in a loop then the number increases and decreases by one after each _getc(). It looks like this counter is not 'the buffer' but maybe something that is filled after you do _getc() and new characters arrive. Because this is fuzzy, unclear, not specified and may be buggy I don't use it.

For the FW update let me further investigate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants