Arm Mbed and Pelion Device Management support forum

USBAudio Callbacks on DISCO_L476RG

Hi, how are the callbacks set up? Below is the usual pattern, however I am told I am trying to pass a callback of type void(Classname&) instead of just void(). How do I create the void callback? Can I really not use it with a class? What is the best way to make this a compound device?

#include "mbed.h"
#include "rtos.h"
#include "USBAudio.h"

#define SPK_FREQ	48000
#define MIC_FREQ	48000

#define SPK_CHAN	2
#define MIC_CHAN	2

#define SPK_PACKET_LENGTH	((SPK_FREQ/500) * SPK_CHAN)
#define MIC_PACKET_LENGTH	((MIC_FREQ/500) * MIC_CHAN)

class Application {
public:
	Application();
	void update_volume();
	void tx_done();
	void rx_done();
protected:
	USBAudio audio;
};

Application::Application() :
    audio(true, 48000, 2, 48000, 2, 10, 0x7bb8, 0x1111, 0x0000) {
    audio.attach(callback(this, &Application::update_volume));
    audio.attach_tx(callback(this, &Application::tx_done));
    audio.attach_rx(callback(this, &Application::rx_done));
}

void Application::update_volume() {
}

void Application::tx_done() {
}

void Application::rx_done() {
}

int main() {
	Application app;
}

Ok, it seems like the type may actually be function of USBAudio::AudioEvent. So:

class Application {
public:
	Application();
	void update_volume();
	void tx_done(USBAudio::AudioEvent ae);
	void rx_done(USBAudio::AudioEvent ae);
protected:
	USBAudio audio;
};

Gives:

Compile [100.0%]: main.cc
[Error] main.cc@26,23: cannot bind non-const lvalue reference of type 'mbed::Callback<void()>&' to an rvalue of type 'mbed::Callback<void()>'
[Error] main.cc@27,26: cannot bind non-const lvalue reference of type 'mbed::Callback<void(USBAudio::AudioEvent)>&' to an rvalue of type 'mbed::Callback<void(USBAudio::AudioEvent)>'
[Error] main.cc@28,26: cannot bind non-const lvalue reference of type 'mbed::Callback<void(USBAudio::AudioEvent)>&' to an rvalue of type 'mbed::Callback<void(USBAudio::AudioEvent)>'
[ERROR] ./main.cc: In constructor 'Application::Application()':
./main.cc:26:23: error: cannot bind non-const lvalue reference of type 'mbed::Callback<void()>&' to an rvalue of type 'mbed::Callback<void()>'
   26 |  audio.attach(callback(this, &Application::update_volume));
  |               ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    In file included from ./main.cc:3:
    ./mbed-os/drivers/USBAudio.h:237:41: note:   initializing argument 1 of 'void USBAudio::attach(mbed::Callback<void()>&)' 
 237 |     void attach(mbed::Callback<void()> &cb);
  |                 ~~~~~~~~~~~~~~~~~~~~~~~~^~
./main.cc:27:26: error: cannot bind non-const lvalue reference of type 'mbed::Callback<void(USBAudio::AudioEvent)>&' to an rvalue of type 'mbed::Callback<void(USBAudio::AudioEvent)>'
   27 |  audio.attach_tx(callback(this, &Application::tx_done));
  |                  ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ./main.cc:3:
./mbed-os/drivers/USBAudio.h:244:54: note:   initializing argument 1 of 'void USBAudio::attach_tx(mbed::Callback<void(USBAudio::AudioEvent)>&)'
  244 |     void attach_tx(mbed::Callback<void(AudioEvent)> &cb);
  |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
./main.cc:28:26: error: cannot bind non-const lvalue reference of type 'mbed::Callback<void(USBAudio::AudioEvent)>&' to an rvalue of type 'mbed::Callback<void(USBAudio::AudioEvent)>'
   28 |  audio.attach_rx(callback(this, &Application::rx_done));
  |                  ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ./main.cc:3:
./mbed-os/drivers/USBAudio.h:251:54: note:   initializing argument 1 of 'void USBAudio::attach_rx(mbed::Callback<void(USBAudio::AudioEvent)>&)'
  251 |     void attach_rx(mbed::Callback<void(AudioEvent)> &cb);
  |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

I eventually got this compiling. Is there a more modern version?

static void update_volume() {
}
   
int main() {
	USBAudio audio(true, 48000, 2, 48000, 2, 10, 0x7bb8, 0x1111, 0x0000);
	Callback<void(void)> cb(update_volume);
	audio.attach(cb);
}

I put the tone generator example onto the board after changing vid and pid. Previously I was using the following Python code to save samples from an audio device (with stuttering due to latency, I also have a C version that is far more verbose). However right now I receive “endvalid endpoint address 0x81.” Can anyone comment on the particulars of how the USBAudio device is set up? Otherwise this will be a lot of digging.

#!/usr/bin/env python3
import usb, numpy as np
import array, wave
from pprint import pprint

def main():
    ds = [d for d in
            usb.core.find(find_all=True, idVendor=0x0000, idProduct=0x0000)]
    d = ds[0]
    pprint(d)
    d.set_configuration(1)
    d.set_interface_altsetting(interface=1, alternate_setting=1)

    f = wave.open('out.wav', 'wb')
    f.setnchannels(2)
    f.setsampwidth(2)
    f.setframerate(48000)

    #t = np.array([], dtype='int16')
    t = array.array('B', [])

    try:
        while True:
            #r = np.ndarray.view(np.uint8(d.read(0x81, 200)), dtype='int16')
            #t = np.append(t, r)
            r = d.read(0x81, 200 * 1)
            if not r:
                print('Empty?')
            elif r[0] == 0:
                print('Zero?')
            else:
                t += r
    finally:
        for b in t:
            if t == 0:
                print('Zero in middle.')

        print('Saved.')
        f.writeframes(t)
        f.close()

if __name__ == '__main__':
    main()

Alright, I’m hoping this attracts some attention from mbed maintainers. I understand the paid maintainers are probably supporting business partners and don’t expect these issues to be addressed very quickly, especially as some of these issues probably are upstream of mbed, but this cycle of evaluations I did is interesting:

  1. Attempt USB work on NUCLEO_L432KC. Realize it has no mbed USB support. (Oops, I’d previously done USB work on it using CubeMX).
  2. Attempt USB work on DISCO_L476VG after consulting board support pages. Enumerates to USB host machine, but otherwise I can’t make heads or tails of the API, how to use it, and/or it doesn’t work. (I used the example so I think there is a library issue.)
  3. Attempt USB work on NUCLEO_L476RG. This doesn’t have mbed USB support and is the point where I realized I’d have to drop mbed. Some time ago I’d started with this product and gotten a ST-provided demonstration using a carrier board working. Unfortunately all of the demonstrations and the ST-provided libraries handle the USB controller in such a way as to make extending the device classes troublesome.

This is just the most recent round. Before, I did:

  1. Start with CubeMX on NUCLEO_L476RG and carrier because mbed wouldn’t run on my system and was hard to version.
  2. Get fed up with trying to understand the multiple layers of bouncing around in the ST HAL and USB stack.
  3. Move to libopencm3, which was a nonstarter due to poor support for my part and USB in general.
  4. Go back to ST’s HAL and make some minor progress, but feel it is taking too long.
  5. Attempt to use a more powerful Cortex-A part with a USB device controller with the USB gadget drivers. Made initial progress, but the audio classes had bugs and there were issues using multiple devices at the same time.

Around #5 I realized some of these issues were caused by inflexibility in the device mode controllers. The Cortex-A part had by far the most capable controller with the fewest number of restrictions but this still caused problems for the Linux kernel module developers.

My most recent inclination is to move to ZephyrOS. There would be more work doing high level USB stack management however it makes clear what must be done for low level USB support. Unfortunately the only part with extant support is Intel’s C1000 which has limited availability. Otherwise, I’ll be trying to find a Cortex-A part.