XMOS "service"

Technical questions regarding the XTC tools and programming with XMOS.
egawtry
Member
Posts: 12
Joined: Thu Aug 18, 2011 9:51 pm

XMOS "service"

Post by egawtry »

Hello,

I am trying to use the su1 ADC and am getting really confused. The example code (sc_periph) does not even remotely match that in the Audio example (app_usb_aud_su1). I found two short paragraphs in section 3.7 of the XC Programming Guide calling the method a "service", but that does not tell me much.

Anyone know what is going on?

Thank You,
-Erik


User avatar
sethu_jangala
XCore Expert
Posts: 589
Joined: Wed Feb 29, 2012 10:03 am

Post by sethu_jangala »

The example in the sc_periph use service for interfacing with functions pre-programmed into the non-volatile memory of an xCORE. The service declaration is available in the XN file of the su1 device.

Code: Select all

<Service Proto="xs1_su_adc_service(chanend c_adc)">
<Chanend Identifier="c_adc" end="2" remote="5"/>
egawtry
Member
Posts: 12
Joined: Thu Aug 18, 2011 9:51 pm

Post by egawtry »

That still doesn't explain the service. I will assume that it somehow fills in the channel passed.

Questions:
1. I see that the only other part of that is the stub in audio.xc which takes that channel output and stuffs it into a global variable. How is it initialized?
2. How do I probe the multiple ADC channels? The code appears to only work with one ADC pin.
3. Related to #1, how do I initialize it before the service starts?
4. Where the #$!! is the documentation? All I can find is 3.19.3 in the USB Audio Design Guide and 3.7 in the XC Programming Guide.

Thank you for any responses,
-Erik
peter
XCore Addict
Posts: 230
Joined: Wed Mar 10, 2010 12:46 pm

Post by peter »

Apologies for the lack of documentation. The ADC itself is documented in the xCORE-USB data sheet of each part (https://www.xmos.com/support/documentat ... =datasheet). However, that documentation does not necessarily provide enough information to use it.

The sc_periph library provides an abstraction layer to make it easy to use the ADCs. The best place to look for how this works is the app_adc_demo_u example application in http://github.com/xcore/sc_periph

Hopefully looking at how that works will answer your questions. Firstly it configures and enables the ADC:

Code: Select all

    adc_config_t adc_config = { { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
In this case only ADC 0 is enabled:

Code: Select all

    adc_config.input_enable[0] = 1;
The accuracy of the ADCs is set to 32 bits per sample. It can be configured to 8, 16 or 32 bits per sample. Note that the accuracy of the ADC is only 12-bit anyway, so the values returned will have no increased accuracy if using 32-bit instead of 16-bit samples.

Code: Select all

    adc_config.bits_per_sample = ADC_32_BPS;
The ADC will deliver blocks of samples (packets). The data delivered is always a sequence with one sample per active ADC.

Code: Select all

    adc_config.samples_per_packet = 1;
If you were to enable ADC 0 and ADC 1 and keep the samples_per_packet then each packet will alternate returning data for ADC 0, then ADC 1, then ADC 0, and so on. If you configure samples_per_packet to 2, then each packet will contain a sample from ADC 0 and one from ADC 1. We'll see how this is read in a minute.

The ADC has a calibration mode where the ADC is connected to a 0.8V reference voltage.

Code: Select all

    adc_config.calibration_mode = 0;
And finally, one call to enable the ADCs. This call takes care of the fact that there the ADC requires a number of calibration samples before it is ready for use.

Code: Select all

    adc_enable(xs1_su, c, trigger_port, adc_config);
After that the library provides two ways of using the ADC:
- adc_trigger / adc_read
- adc_trigger_packet / adc_read_packet

The example app uses the second of these. It starts the process by triggering a packet of data to be read:

Code: Select all

   adc_trigger_packet(trigger_port, adc_config);
And then the rest of the application is handling the data from that trigger and periodically printing the current ADC value:

Code: Select all

    while (1)
    {
        unsigned data[1];

        select
        {
            case print_timer when timerafter(print_time) :> void:
                if (new_value != current_value)
                {
                    debug_printf("ADC value: 0x%x\n", new_value);
                    current_value = new_value;
                }
                print_time += PRINT_PERIOD;
                break;

            case adc_read_packet(c, adc_config, data):
                new_value = data[0];
                adc_trigger_packet(trigger_port, adc_config);
                break;
        }
    }
Each time a packet is received the next packet is triggered.

There is only one ADC trigger pin and it will cause the next active ADC to read a sample. They work in a round-robin manner.
egawtry
Member
Posts: 12
Joined: Thu Aug 18, 2011 9:51 pm

Post by egawtry »

Thank you, I got it working.