Playing with analog-to-digital converter on Arduino Due

Today I’m going to present some of more advanced capabilities of ADC built in ATSAM3X8E – the heart of Arduino Due.

I like the Arduino platform. It makes using complex microcontrollers much simpler and faster. Lets take for example the analog-to-digital converter. To configure it even on Atmega328 (Arduino Uno/Duemilanove) you must understand and set correct values in 4 registers. And it can be much more in complex device, like 14 in ATSAM3X8E (Arduino Due)!
In Arduino, for no matter which processor, all you need to do is:

val = analogRead(A0);

Playing with analog-to-digital converter on Arduino Due

It’s simple and useful. But there are situations, where you need to use more potential of your chip. Arduino allows you to do so – after all it’s just C++ with some additions.
In my project on Arduino Due I need to sample voltage continuously and as fast as possible. Lets try it the simplest way:

int input = A0;
int led = 13;
int val;
void setup()
{
  pinMode(input,INPUT);
  pinMode(led,OUTPUT);
}
void loop()
{
  digitalWrite(led,HIGH);
  val = analogRead(input);
  digitalWrite(led,LOW);
}

Anything the program does is reading ADC and toggling the led line, so I can measure how fast it happens.

There is nice square ~100kHz wave. So the sampling frequency is about 100kS/s. Not bad, but according to datasheet it can be much higher (1Ms/s). And I’m not doing anything else, just reading ADC and wasting all the processor’s time. There is also another problem, which is clearly visible when looking on a wave with oscilloscope with persistence:

Yes, time between samples is not always even. It wiggles a microsecond or less from time to time. It is due to other tasks that Arduino are performing in background, like counting the time. That’s bad. Sampling frequency should be constant. To unleash full potential of this chip, different approach is needed.

Good news is that Arduino let’s you use almost all the capabilities of microcontroller by using low level C/C++ programming. Bad news – this is quite hard, especially with complex ARM processors. Processors peripherals’ documentation is large (about 100 pages in datasheet and two application notes for ADC alone). And it’s barely enough to understand all the details. Another useful info are examples available in Atmel Studio and the Arduino libraries’ source (\%arduino%\hardware\arduino\sam\cores\arduino).

Using ADC, just like any peripheral, is done by setting appropriate values to related registers. It can be done directly in program, but then it’s quite susceptible to errors. 14 registers, 32 bits in each of them – it’s 448 places to make a mistake. But fortunately, Atmel provides some libraries to make the task easier. And they are bundled in Arduino (\%arduino%\hardware\arduino\sam\system\libsam\). With them, instead of writing to registers, you call corresponding functions. They are handling all the bit masking, shifting and similar stuff. So lets look at the next version of program:

int input = A0;
int led = 13;
int val;

void setup()
{
  pinMode(input,INPUT);
  pinMode(led,OUTPUT);
  // Setup all registers
  pmc_enable_periph_clk(ID_ADC); // To use peripheral, we must enable clock distributon to it
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); // initialize, set maximum posibble speed
  adc_disable_interrupt(ADC, 0xFFFFFFFF);
  adc_set_resolution(ADC, ADC_12_BITS);
  adc_configure_power_save(ADC, 0, 0); // Disable sleep
  adc_configure_timing(ADC, 0, ADC_SETTLING_TIME_3, 1); // Set timings - standard values
  adc_set_bias_current(ADC, 1); // Bias current - maximum performance over current consumption
  adc_stop_sequencer(ADC); // not using it
  adc_disable_tag(ADC); // it has to do with sequencer, not using it
  adc_disable_ts(ADC); // deisable temperature sensor
  adc_disable_channel_differential_input(ADC, ADC_CHANNEL_7);
  adc_configure_trigger(ADC, ADC_TRIG_SW, 1); // triggering from software, freerunning mode
  adc_disable_all_channel(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7); // just one channel enabled
  adc_start(ADC);
}

void loop() 
{
  while(1)
  {
    PIO_Set(PIOB,PIO_PB27B_TIOB0);
    while ((adc_get_status(ADC) & ADC_ISR_DRDY) != ADC_ISR_DRDY)
      {}; //Wait for end of conversion
    PIO_Clear(PIOB,PIO_PB27B_TIOB0);
    val = adc_get_latest_value(ADC); // Read ADC
  }
}

This program handles the ADC using Atmel SAM libraries. It initializes all ADC registers first. It’s not really necessary – most of these parameters are set to their default values. But it’s better to be sure – Arduino startup code plays with these registers too, and no one can be sure what will be there in next release.
In this example, I’ve set ADC to freerunning mode. In this mode, conversion has to be started just once, and ADC is running by itself after that. Every time conversion is complete, we have information in status register and next conversion starts immediately. So ADC is running in maximum possible speed and main program must only read converted values when they’re ready.

As you may notice, in this example I used ADC_CHANNEL_7 input. And if you think, it’s just A0 pin… you are just as wrong, as me! I wasted about an hour trying to find out why I read other value than I set at input. And then I checked Arduino schematic. Yes, analog pins A0..A7 are connected to analog inputs 0..7, but in reversed order! A0 is ADC channel 7, A1 – 6 and so on. Arduino, damn you!

To make this example faster, I exchanged Arduino’s slow digitalWrite function to faster direct register operations – PIO_Set and PIO_clear. I also used a while(1) loop inside the loop() function. It prevents some operations that Arduino are doing between loop() calls. It’s bad idea to do so – some Arduino functionality is lost, buts it’s only for this test.

 

For more detail: Playing with analog-to-digital converter on Arduino Due


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top