How do I use USB on MAX32620FTHR?

I hesitated to post this, since it requires admission of substantial ignorance on my part concerning both CMake and mbed-os.

I am attempting to get USB communication between a (mbed-os 6 bare-metal) program running on MAX32620FTHR and a MacBook Pro (Apple M1, macOS Ventura 13.0). Basically, what I want to do is figure out:

  1. A way to build up a buffer containing information regards the condition of switches and voltages measured by a program running on the FTHR,
  2. Transmit that buffer back to a host (in the initial case, that host would be my MacBook),
  3. Receive and display the contents of that buffer on my MacBook.

I am stuck on step #2 above. There is lots of I/O available on the FTHR, but USB seemed like it would be easiest, since the project will eventually use some serial communication between processors in the system (probably CAN bus; there are available adapters for CAN <==> USB).

The FTHR has a micro USB connector, and I saw some hints that it could even be programmed via that connector in lieu of using the DAPLINK facility of the MAX32625PICO, using an on-board bootloader (although I havenā€™t figured that one out, either).

When I just added #include ā€œUSBSerial.hā€ to my main.cpp, I just got an error that USBSerial.h could not be found. I tracked that down (I thinkā€¦) to a missing reference to mbed-os in the top-level CMakeLists.txt, and a possible missing USBDEVICE in mbed_lib.json. However, there was a hint that this was for the 32630, and not for the 32620, so Iā€™m confused.

Here is my top-level CMakeLists.txt:

cmake_minimum_required(VERSION 3.24)
cmake_policy(VERSION 3.24)
#enable firmware upload and remote debugging
set(PYOCD_UPLOAD_ENABLED TRUE)
set(PYOCD_TARGET_NAME max32620)
set(PYOCD_CLOCK_SPEED 4000k)
# Initialize Mbed OS build system. 
# Note: This block must be before the include of app.cmake
set(MBED_APP_JSON_PATH mbed_app.json)
include(mbed-os/tools/cmake/app.cmake)
add_subdirectory(mbed-os)
project(Example)
# add subdirectories and build targets here
add_executable(Example Example.cpp)
# Choose mbed-os or mbed-baremetal here
target_link_libraries(Example 
    PRIVATE
        mbed-baremetal
        mbed-usb
 )
mbed_set_post_build(Example)
mbed_finalize_build()

Here is my mbed_app.json:

{
    "target_overrides": {
         "*": {
            "platform.stdio-baud-rate": 115200,
            "platform.stdio-buffered-serial": 1,
            "target.printf_lib": "std",
            "target.device_has_add": ["USBDEVICE"]
        }
    }
}

I initially just got some warnings, but when I tried to instantiate a USBSerial object, I started getting build errors, such as:

[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBSerial.cpp:24: undefined reference to `get_usb_phy()ā€™

Am I barking up the wrong tree?

Am I just leaving out something important?

Is there a better or simpler way to get a serial communication working with the FTHR?

Hello Howard,

I suppose this question si related to MbedCE. That is important information I think.

Your CMakeLists.txt seems be ok. I do not know what impact has the PRIVATE keyword, but it is working also without it.
However from my point of view the USB DEVICE is not done very well for MAX32620 and it is not working.

Cheapest way could be UART => USB convertor based on FTDI chip, I think.

BR, Jan

Hmmmā€¦ thatā€™s too bad. Iā€™ll mention that to the hardware guy on Monday to see what he has to say.

BTW, would the MAX32630FTHR work with USB? We are actually going to be building our own custom board eventually, so we could just get whatever works now to test proof of concept stuff.

Would there be another board you would recommend over the 030? All I need for now is something that can read switch positions and voltages, and communicate them back to a host computer ā€“ donā€™t need WiFi or Bluetooth, but if it doesnā€™t cost much, I can just ignore those features. Ultimately, we will probably use CAN bus, not USB. I just thought (erroneously, it appears) that USB would be simple to use.

Unfortunately I have only ST boards. So my recommendation are based only on top of information what I collected here on the forum and from my personal tests of compilation.

I do not understand what you mean with ā€œover the030ā€, but I can recommended ST boards like Nucleo-F446RE, Nucleo-F767ZI, Nucleo-H743ZI2. Last two has also Ethernet interface.
From my personal view is ST the best option because all these boards are more powerful than MAX, Programmer/Debuger is onboard, are cheaper than any others, USB/Ethernet are working and STā€™s effort for Mbed is huge.

However I found something (I found something what @gerriko found :slight_smile: ) what you can try - TARGET Maxim get_usb_phy() issue due to incorrect target specified in USBPhy_Maxim.cpp Ā· Issue #15334 Ā· ARMmbed/mbed-os (github.com)

So visit Mbed-CE mbed-os/targets/Maxim/USBPhy_Maxim.cpp in your project and repair these typos on lines

Both need to be changed from TARGET_MAX32620 to TARGET_MAX32620C, after that the compilation is OK. We need real test now. Please let us know.

If that will work, then we could make PR to Mbed-CE.

BR, Jan

Actually, I mean the ā€˜20ā€™, as in MAX32620FTHR (basically a typo).

Thanks for the tips! Iā€™m too tired to look at them tonight, but I will check your suggestions Sunday afternoon and let you know what I find.

I see @JohnnyK found my similar issue and suggested solution on GitHub. As I was only able to test for myself, I will be intrigued to learn if this helped you too.

Hereā€™s what I got so far:

I changed TARGET_MAX32620 to TARGET_MAX32620C on lines 19 & 114 in
/Users/howardharkness/arm-workspace/mbed-os/targets/TARGET_Maxim/USBPhy_Maxim.cpp

Re-built after deleting build/ directory.

Build had several warnings:

[build] /Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_board.c: In function 'mbed_die':
[build] /Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_board.c:31:12: warning: unused variable 'led_err' [-Wunused-variable]
[build]    31 |     gpio_t led_err;
[build]       |            ^~~~~~~

[build] /Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_error.c:122:13: warning: 'mbed_error_is_handler' defined but not used [-Wunused-function]
[build]   122 | static bool mbed_error_is_handler(const mbed_error_ctx *ctx)
[build]       |             ^~~~~~~~~~~~~~~~~~~~~

Those warnings donā€™t appear to be serious, but I would have expected the following warning to result in a linker error:

[build] /Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_sdk_boot.c: In function '__wrap_main':
[build] /Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_sdk_boot.c:176:5: warning: implicit declaration of function 'mbed_error_initialize' [-Wimplicit-function-declaration]
[build]   176 |     mbed_error_initialize();
[build]       |     ^~~~~~~~~~~~~~~~~~~~~

Here is the offending snippet starting at line 173 in
/Users/howardharkness/arm-workspace/mbed-os/platform/source/mbed_sdk_boot.c

int __wrap_main(void)
{
    mbed_main();
    mbed_error_initialize(); // implicit declaration warning here
    return __real_main();
}

Some additional warnings:

[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp: In member function 'void USBDevice::_complete_set_configuration()':
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:405:57: warning: 'void* memset(void*, int, size_t)' clearing an object of non-trivial type 'struct USBDevice::endpoint_info_t'; use assignment or value-initialization instead [-Wclass-memaccess]
[build]   405 |         memset(_endpoint_info, 0, sizeof(_endpoint_info));
[build]       |                                                         ^
[build] In file included from /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:21:
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/include/usb/internal/USBDevice.h:550:12: note: 'struct USBDevice::endpoint_info_t' declared here
[build]   550 |     struct endpoint_info_t {
[build]       |            ^~~~~~~~~~~~~~~
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp: In constructor 'USBDevice::USBDevice(USBPhy*, uint16_t, uint16_t, uint16_t)':
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:1351:53: warning: 'void* memset(void*, int, size_t)' clearing an object of non-trivial type 'struct USBDevice::endpoint_info_t'; use assignment or value-initialization instead [-Wclass-memaccess]
[build]  1351 |     memset(_endpoint_info, 0, sizeof(_endpoint_info));
[build]       |                                                     ^
[build] In file included from /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:21:
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/include/usb/internal/USBDevice.h:550:12: note: 'struct USBDevice::endpoint_info_t' declared here
[build]   550 |     struct endpoint_info_t {
[build]       |            ^~~~~~~~~~~~~~~
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp: In member function 'void USBDevice::_change_state(USBDevice::DeviceState)':
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:1757:57: warning: 'void* memset(void*, int, size_t)' clearing an object of non-trivial type 'struct USBDevice::endpoint_info_t'; use assignment or value-initialization instead [-Wclass-memaccess]
[build]  1757 |         memset(_endpoint_info, 0, sizeof(_endpoint_info));
[build]       |                                                         ^
[build] In file included from /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/source/USBDevice.cpp:21:
[build] /Users/howardharkness/arm-workspace/mbed-os/drivers/usb/include/usb/internal/USBDevice.h:550:12: note: 'struct USBDevice::endpoint_info_t' declared here
[build]   550 |     struct endpoint_info_t {
[build]       |            ^~~~~~~~~~~~~~~

Here is my Example.cpp

#include "mbed.h"
#include "USBSerial.h"
#include "usb_phy_api.h"
#include "USBPhyHw.h"

DigitalOut rled(LED_RED,LED_ON);
DigitalOut gled(LED_GREEN,LED_ON);
DigitalOut bled(LED_BLUE,LED_ON);

// Virtual serial port over USB
USBSerial usb(false);

void blink(DigitalOut& led, const int times=1, const int period=250)
{
    for(int i=0; i < times; i++)
    {
        led = LED_ON;
        thread_sleep_for(period);
        led = LED_OFF;
        thread_sleep_for(period);
    }
}

int main()
{
    const int period=600; 
    const int blinks(2);

    usb.printf("Bare metal blinky example running on Mbed OS %d.%d.%d.\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);
    usb.printf("Modified to blink red, blue, green %d times each, followed by pause of %d ms before repeating\n", blinks, 2*blinks*period);

    thread_sleep_for(period * 2);
    rled = LED_OFF;
    bled = LED_OFF;
    gled = LED_OFF;
    thread_sleep_for(period * 2);

    while (true)
    {
        blink(rled, blinks, period);
        blink(bled, blinks, period);
        blink(gled, blinks, period);
        thread_sleep_for(2 * blinks * period);
    }
}

OS: MacOS Ventura 13.0
Mbed OS 6.16.0
IDE: VS Code
Python 3.10.7
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10) 10.3.1 20210824 (release)
git version 2.38.0
cmake 3.24

Iā€™m still working on how to read the usb on the MacOS side.

You set the blocking argument to False. That means, the code will not wait to any connection and will continue, but then you are also responsible for managing incoming connections.

For example the code below is from my good friend. He uses this for debuging over USB, but only when something is connected. You can use it for imagine what you need to do with your code.

// USBSerial non-blicking mode
USBSerial serial(false);

// debugPrint function 
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...);
        serial.printf("\r");
    } 
    //printf(msg, args...);
}

However best option is start with basic USBSerial example, with blocking mode.

#include "mbed.h"
#include "USBSerial.h"

//Virtual serial port over USB
USBSerial serial;

int main(void)
{
    while (1) {
        serial.printf("I am a virtual serial port\r\n");
        ThisThread::sleep_for(1000);
    }
}

The example program will stop in USBSerial constructor and will wait for a connection from Host side. This time you need check your device manager for a new device. Then just run your serial terminal app and connect to your new serial comport. However DTR signal must be enabled on Host side app, some apps do it by self and some not. I am not Apple user so I do not know how it works with Mac.

BR, Jan

I have been using USBSerial serial(true);, which blocks until the USB Serial connection is established, and for my applications this works fine.

But I did wonder why false did not work. So youā€™ve answered my question. Thanks.

1 Like

Is there some setting or #define that Iā€™m missing that is causing all of those warnings in the build?

Try to add content below to your mbed_app.json file

  "target.c_lib": "std",

Warnings 31,122,176 will remain, but the rest disappear.

BR, Jan

Use a micro USB cable to connect the MAX32620FTHR board to a suitable power source (no USB connectivity is required). Alternatively, you can power the board from a charged battery as long as you remember to turn it on by pressing the power/reset button next to the battery connector.

Regards,
Peter

I changed mbed_app.json to:

{
    "target_overrides": {
         "*": {
            "platform.stdio-baud-rate": 115200,
            "platform.stdio-buffered-serial": 1,
            "target.printf_lib": "std",
            "target.c_lib": "std",
            "target.device_has_add": ["USBDEVICE"]
        }
    }
}

But all of the warnings remained. I tried re-building with different build variants to make sure. Interestingly, the ā€œproductionā€ build variant had several additional ā€œunused variableā€ warnings.

The 176 error is the one that concerns me most. I suppose I could comment that line (mbed_error_initialize) out, but I donā€™t really want to do that unless/until I understand what it was intended to do and why it is unimplemented.

The problem is that I want to use USB for communication to the board. I have not had problems with powering it or running the ā€œBlinkyā€ example.

Warnings 31,122,176 are warnings not errors and do not have anything together with USB, but this topic is about USB. Whese warning are there also without USB.
Please try to focus to one thing and then to another one. So, is the USB working or not?

BR, Jan

The 176 warning was not there until I tried to include the USB code, which is why I thought it might be related.

The other warnings (unused variables) are not really of any concern, but the 176 warning is telling me that there is an unimplemented function ā€“ which should be an error, not a warning.

As for the USB working, I am seeing that the Example program stops at the first call to usb.printf, but Iā€™m not seeing any ā€œnew USB deviceā€ on the Mac side, and I did not see any USB traffic on Wireshark. Iā€™m currently working on that, but that may just be a matter of not knowing exactly where to look. Iā€™m relatively new to MacOS, and the latest update (Ventura) has a bunch of confusing gratuitous changes.

It seems to be in context of Bare-metal profile, not USB.
When you look here, then you will found definition of mbed_error_initialize(void). On line 230 is a configuration macro MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED, which is crash-capture-enabled from Platform/mbed_lib.json. This setting is 0/false in default and it is set to true only for few targets. So we can say this function is empty for your target = no impact for you.
The function seems to be Impicit because during switch to Bare-metal profile the .c file probably lose connection to platform/mbed_error.h file where its the declaration is.

BTW I tried that also with different target and it seems this error is here every time when GCC and bare-metal profile is used. We can probably say it is a Bug.

If you are using blocking mode how I recommended above, then this is late. The program must stops at constructor.

BR, Jan

I just checked that out, and you are correct. Thanks!

Oh right, I also added something that might be useful for this, as part of my Arduino Nano 33 BLE port. In your mbed_app.json, you can set:

"target.console-usb": true,
"target.console-uart": false

This will activate this piece of code and set it up so that printf() is redirected over the USB serial port by default.

Does this mean that I donā€™t need to declare a USBSerial object?