Standard UART Library - using in MC Audio project

New to XMOS and XCore? Get started here.
Post Reply
Dimdim
Junior Member
Posts: 7
Joined: Wed Jan 03, 2018 4:03 pm

Standard UART Library - using in MC Audio project

Post by Dimdim »

Hi guys, new guy here.

I'm doing a project involving an XUF216 doing USB to I2S duties.

I've adapted the mc reference design project to my needs with no problems, everything seems to be working as it should, I'm getting audio out and everything.

But then I attempted to include a UART interface and my newbeeness at this caught up with me.

The idea is to use a UART tx channel to send audio channel info to an external uC every time something worthwhile happens, like for example the sampling rate changes. To do that I see that i need to use AudioHwConfig() to send the relevant data through a serial port. I'd like that serial port to be at port 1A.

Looking at the lib_uart user's guide, I'm seeing that I need to do something like this:

port p_uart_tx = on tile[0] : XS1_PORT_1A;

int main() {
interface uart_tx_if i_tx;
output_gpio_if i_gpio_tx[1];
par {
on tile[0]: output_gpio(i_gpio_tx, 1, p_uart_tx, null);
on tile[0]: uart_tx(i_tx, null,115200, UART_PARITY_NONE, 8, 1,i_gpio_tx[0]);
on tile[0]: app(i_tx);
}
return 0;
}

and have an app() that looks like this:

void app(client uart_tx_if uart_tx) {
// For starters, just write a single byte to the UART
uart_tx.write(0xff);
}

It looks pretty simple, I suppose.

The problem is that I can't figure out where exactly to put the above code.

I've tried putting all of it in main.cx or audiohw.cx and pretty much all different combinations of parts of code and files, with no success.

I'm suspecting that I'm missing something pretty basic here.

Any kind of help would be most welcome.


User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

HI,
the main function of usb_audio (as you have found) is hidden in the sc_usb_Audio library rather than living in the app. This means you need to edit main.xc in sc_usb_Audio to add the output_gpio() and uart_tx() task calls, as well as the interfaces i_tx and i_gpio_tx. You will obviously need to declare your uart tx port on the correct tile too.

The client uart_tx_if uart_tx then needs passing from main through usb_audio_io() in main, through to audio() in audio.xc through to AudioHwConfig() in audiohw.xc. From there you can make your uart_tx.write() call. You will need to change the prototypes in the various headers too.

So it's a bit a of a faff due to the number of layers, but that's the proper way to do it. A better way, if uart_tx had an exit method, would have been a local par in audio_hw.xc with an exit call but unfortunately lib_uart doesn't support that (although lib_i2c does). One for the feature enhancement list.
Dimdim
Junior Member
Posts: 7
Joined: Wed Jan 03, 2018 4:03 pm

Post by Dimdim »

Ok, this is extremely helpful but it still presents me with a steep learning curve. I don't mind, but I'd appreciate a couple of pointers regarding the concepts of client and server interfaces and the method that must be followed in order to pass them through the various functions.

So far I've done this much:

In main.xc I have this (changes in bold):

------ <snip> --------

port p_uart_tx = on tile[0] : XS1_PORT_1P;

void app(client uart_tx_if uart_tx) {
// For starters, just write a single byte to the UART
uart_tx.write(0xff);
}


/* Main for USB Audio Applications */
int main()
{

interface uart_tx_if i_tx;
output_gpio_if i_gpio_tx[1];


chan c_mix_out;
#ifdef MIDI
chan c_midi;
#endif
#ifdef IAP

------ <snip> --------

USER_MAIN_DECLARATIONS

par
{
on tile[0]: output_gpio(i_gpio_tx, 1, p_uart_tx, null);
on tile[0]: uart_tx(i_tx, null,115200, UART_PARITY_NONE, 8, 1,i_gpio_tx[0]);
on tile[0]: app(i_tx);


on tile[XUD_TILE]:
par
{
#if (XUD_TILE == 0)
/* Check if USB is on the flash tile (tile 0) */
------ <snip> --------

Do I have it right so far? Am I right to be calling app(i_tx) in the PAR or should I end up calling it only from AudioHwConfig()?

Moving on to passing the client uart_tx_if uart_tx to usb_audio_io(). To do that do I do this:

void usb_audio_io(chanend c_aud_in, chanend ?c_adc,
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
chanend c_spdif_tx,
#endif
#ifdef MIXER
chanend c_mix_ctl,
#endif
chanend ?c_aud_cfg,
streaming chanend ?c_spdif_rx,
chanend ?c_adat_rx,
chanend ?c_clk_ctl,
chanend ?c_clk_int
#if (XUD_TILE != 0)
, server interface i_dfu dfuInterface
#endif
#if (NUM_PDM_MICS > 0)
, chanend c_pdm_pcm
#endif
, client interface output_gpio_if i_tx
)

Is this right or should I use some kind of server interface?

Again, thanks for your time.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

This all looks fine to me. The uart_tx task is the server so yes, the app side should be client.

You will indeed need to call the app from the main par{} in this case (as you have shown). Forking it from hwconfig would cause hwconfig to never return because it would wait for both threads to join.

You may notice that the three tasks you declare actually add up to only a single thread (logical core). This is because the io and uart servers are distributable so effectively inlined by the compiler.
Post Reply