Does anyone have any working example code showing how to use the ADC with more than 2 channels ?
Ultimately, I need to convert 7 channels every 100us. I'm using lib_a_series_support, but I can't figure out the correct sequencing of calls to work around the 5 word buffering limitation mentioned in the api header and here http://www.xcore.com/forum/viewtopic.ph ... &hilit=ADC
/**
* Minimum guaranteed buffer depth. Exceeding this number may cause lock-up on read_packet.
* Use multiple read commands for more than 5 enabled ADC channels
*/
#define XS1_MAX_SAMPLES_PER_PACKET 5
I can get it to work fine at samples_per_packet=2 and bits_per_sample = 16 or bits_per_sample = 8 and samples_per_packet = 4 but samples_per_packet =4 and bits_per_sample=16 causes the core to hang in inuchar.
I've tried converting 4x 16bit channels by calling at_adc_trigger_packet twice with samples_per_packet=2 but then the channel sequencing messes up and I no longer get the correct channels in order (after capturing last 50 readings from each channel - can see my "known channel voltage" jumping between channel buffers. This doesn't occur with the configurations that get all channels transferred in one packet.
Any ideas ? Here's the essense of the code that attempts to read 4 channels as 2 packets of 2 samples:-
enum {
numAdcChannels = 4
};
#define SMP_PER_PACKET 2
#define ADC_NUM_PACKETS 2
// moving average filter
#define ADC_MA_LENGTH 50
#define XMOS_ADC_TRIGGER_PERIOD_MS 5
#define ADC_PERIOD (XMOS_ADC_TRIGGER_PERIOD_MS * 100000 / ADC_MA_LENGTH) //Comvert to timer cycles
#define ADC_TRIG_MASK 0x04
out port trigger_port = on tile[0] : XS1_PORT_4B;
[[combinable]]
void adc_task(
chanend c_adc, /* chan to the analog tile ADC */
streaming chanend c_out ) /* output to controller task, passes scan events */
{
uint32_t adc_samps[numAdcChannels] = {0, 0, 0, 0 }; //The samples (7 lots of 32 bits (12 bit msb justified)
int32_t adc_time; //Used to time periodic triggers
timer t_adc_timer; //Timer for periodic ADC trigger
uint32_t curr_ch = 0;
at_adc_config_t adc_config = {{ 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; //initialise all ADC to off
for (uint16_t ch=0; ch < numAdcChannels; ch++)
{
adc_config.input_enable[ch] = 1; // Enable input
}
adc_config.bits_per_sample = ADC_16_BPS; // Samples will be placed in the MSB 12 bits of the half wor
adc_config.samples_per_packet = SMP_PER_PACKET; // 2x16-bit words per packet
adc_config.calibration_mode = 0; //Normal ADC operation - disable self calibration
at_adc_enable(adc_tile, c_adc, trigger_port, ADC_TRIG_MASK, adc_config);
t_adc_timer :> adc_time; //Set ADC timer for first loop tick
adc_time += ADC_PERIOD;
for (int pkt=0; pkt < ADC_NUM_PACKETS; pkt++)
{
at_adc_trigger_packet(trigger_port, ADC_TRIG_MASK, adc_config); //Fire the ADC!
}
while(1)
{
select
{
case t_adc_timer when timerafter(adc_time) :> void:
for (int pkt=0; pkt < ADC_NUM_PACKETS; pkt++)
{
at_adc_trigger_packet(trigger_port, ADC_TRIG_MASK, adc_config); //Trigger 1st ADC conversion
}
adc_time += ADC_PERIOD; //Setup time for next ADC rx event
break;
case at_adc_read_packet(c_adc, adc_config, &adc_samps[curr_ch]): //if data ready to be read from ADC
curr_ch += SMP_PER_PACKET;
if ( curr_ch == numAdcChannels)
{
curr_ch = 0;
// parse voltages
handle_adc_data( adc_samps, c_out );
}
break;
}//select
}//while 1
}
Using ADC with more than 2 channels
-
- Member
- Posts: 11
- Joined: Thu Nov 20, 2014 9:19 pm
-
- Member
- Posts: 11
- Joined: Thu Nov 20, 2014 9:19 pm
I figured it out & am including the solution here for anyone else who has problems with this. The answer was to NOT use a timer to trigger the ADC, but instead trigger the next conversion after each read...
[[combinable]]
void adc_task(
chanend c_adc, /* chan to the analog tile ADC */
streaming chanend c_out ) /* output to controller task, passes scan events */
{
uint32_t adc_samps[numAdcChannels]; //The samples (7 lots of 32 bits (12 bit msb justified)
uint32_t curr_ch = 0;
uint32_t adc_read_word;
at_adc_config_t adc_config = {{ 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; //initialise all ADC to off
for (uint16_t ch=0; ch < numAdcChannels; ch++)
{
adc_config.input_enable[ch] = 1; // Enable input
}
adc_config.bits_per_sample = ADC_16_BPS; // Samples will be placed in the MSB 12 bits of the half wor
adc_config.samples_per_packet = SMP_PER_PACKET; // 1x16-bit word per packet
adc_config.calibration_mode = 0; //Normal ADC operation - disable self calibration
at_adc_enable(adc_tile, c_adc, trigger_port, ADC_TRIG_MASK, adc_config);
memset(adcHistory, 0, numAdcChannels*ADC_MA_LENGTH*sizeof(uint16_t));
// trigger the first channel conversion
at_adc_trigger(trigger_port, ADC_TRIG_MASK);
while(1)
{
select
{
case at_adc_read(c_adc, adc_config, adc_read_word): // reading from next channel is available
adc_samps[ curr_ch++ ] = adc_read_word;
if ( curr_ch == numAdcChannels)
{
curr_ch = 0;
// parse new set of voltages
handle_adc_data( adc_samps, c_out );
}
// trigger next ch conversion
at_adc_trigger(trigger_port, ADC_TRIG_MASK);
break;
}//select
}//while 1
}
[[combinable]]
void adc_task(
chanend c_adc, /* chan to the analog tile ADC */
streaming chanend c_out ) /* output to controller task, passes scan events */
{
uint32_t adc_samps[numAdcChannels]; //The samples (7 lots of 32 bits (12 bit msb justified)
uint32_t curr_ch = 0;
uint32_t adc_read_word;
at_adc_config_t adc_config = {{ 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; //initialise all ADC to off
for (uint16_t ch=0; ch < numAdcChannels; ch++)
{
adc_config.input_enable[ch] = 1; // Enable input
}
adc_config.bits_per_sample = ADC_16_BPS; // Samples will be placed in the MSB 12 bits of the half wor
adc_config.samples_per_packet = SMP_PER_PACKET; // 1x16-bit word per packet
adc_config.calibration_mode = 0; //Normal ADC operation - disable self calibration
at_adc_enable(adc_tile, c_adc, trigger_port, ADC_TRIG_MASK, adc_config);
memset(adcHistory, 0, numAdcChannels*ADC_MA_LENGTH*sizeof(uint16_t));
// trigger the first channel conversion
at_adc_trigger(trigger_port, ADC_TRIG_MASK);
while(1)
{
select
{
case at_adc_read(c_adc, adc_config, adc_read_word): // reading from next channel is available
adc_samps[ curr_ch++ ] = adc_read_word;
if ( curr_ch == numAdcChannels)
{
curr_ch = 0;
// parse new set of voltages
handle_adc_data( adc_samps, c_out );
}
// trigger next ch conversion
at_adc_trigger(trigger_port, ADC_TRIG_MASK);
break;
}//select
}//while 1
}