3

Is it possible to directly set the registers that control the ADC in an Arduino Yun (micro-controller side is essentially a Arduino Leonardo) from a sketch or is there a specific file somewhere you must edit?

I am working on a project that collects a signal digitizes it and sends it through a digital filter. I looked up the Datasheet for the micro-controller on the Yun and wrote the code setting the registers associated with the ADC portion of the micro controller. The code setting the registers was something like REGNAME = 0x01;. Yet when tested several of the associated registers were unaffected. Pointers on the process to set the ADC manually without using the analogRead() function would be appreciated.

Edit: My original code looked something like this:

void setup(){
    // register to turn on ADC
    REG1 = REG1 | 0x01;

    //register to set sample rate and set ADC to Free Running Mode
    REG2 = REG2 & 0x30;

    // register to set other settings
    REG3 = REG3 | 0xF1;
}

void loop(){
    // code to take samples from ADC
    // and send them through a digital filter here.
}
  • Which part of the datasheet are you having trouble with? – Ignacio Vazquez-Abrams Jan 7 '16 at 9:22
  • The problem wasnt the datasheet. when I set the registers in the code they were not set on the microcontroller when I tested it. – Josh Nabours Jan 7 '16 at 9:22
  • 1
    The Arduino libraries do things to the hardware. The key is to avoid using them. – Ignacio Vazquez-Abrams Jan 7 '16 at 9:32
  • 2
    Do you call analogRead after setting the registers? analogRead does change the ADCSRB register. So if you are setting ADC High Speed Mode (ADHSM), that will be overwritten. The clock prescaler is only set in the init function, so you should be able to overwrite this in the setup function – Gerben Jan 7 '16 at 10:06
  • 1
    There are several issues here. The first is understanding the difference between || and |, && and & in C/C++. Your should then take a look at the code for analogRead() to understand how the ADC is initialed. github.com/arduino/Arduino/blob/master/hardware/arduino/avr/…. This type of modification requires a lot of detailed insight into the underlaying implementation. You may need to implement your own version of analogRead(). – Mikael Patel Jan 8 '16 at 9:35
2

I can see no problem with your pseudo-code. You should probably post a complete testable code in order to get better feedback.

That being said, here are a few suggestions:

  1. You should completely set the ADC registers to the values that suit your application, instead of just changing a few bits here and there. This is because you should not care about how the Arduino core library initializes those registers, especially if this is a prototype for a final product that will not use Arduino core. For example, instead of
REG1 = REG1 | 0x01;

you can write something like this:

REG1 = _BV(FOO)   // use foo mode...
     | _BV(BAR);  // ...in the bar setting

where _BV(x) is a macro from avr-libc that expands to (1<<(x)), and the bit names are those of the datasheet, for better readability.

  1. You may want to run the digital filter in the ADC interrupt service routine. If you can split your processing into a fast filter (processing every sample) and a slow filter (running on decimated samples), then do only the fast one in the ISR and leave the slow one to the main loop. I say this from experience: when I tried to do all the processing in loop() I always lost samples, which did not happen when I moved the filter to the ISR.

  2. But then, make sure your filter is optimized to be fast. Avoid floating point operations in the ISR if at all possible.

I have written a program that looks very similar to what you are trying to do: it samples an analog input at 9.6 kS/s and runs an homodyne detection filter in order to extract the signal power in a very narrow band around 1 kHz. The ADC is set to free running mode. In the ADC ISR, I retrieve the sample, run it through the filter, and store the result into volatile variables. In loop(), I read these back from time to time. Here is how I set the ADC:

static void configure_adc()
{
    ADMUX  = _BV(REFS0)  // ref = AVCC
           | _BV(ADLAR)  // left adjust result
           | analog_in;  // input channel
    ADCSRB = 0;          // free running mode
    ADCSRA = _BV(ADEN)   // enable
           | _BV(ADSC)   // start conversion
           | _BV(ADATE)  // auto trigger enable
           | _BV(ADIF)   // clear interrupt flag
           | _BV(ADIE)   // interrupt enable
           | 7;          // prescaler = 128
}

The complete program is available here: homodyne.ino. It was initially intended for an Arduino Uno, but it runs unmodified on a Leonardo clone.

1

Since your project is about acquiring signals I guess that the sample frequency is important for you. In fact you need to know exactly how much time passes from one sample to another. This cannot be achieved by simple reading the ADC in your loop(). The easiest implementation is to use an interrupt that occurs every n seconds (e.g. every overflow of timerX).

I'm going to write a pseudo-code for this:

void SetTimer()
{  TISREG |= 1<<Prescaler_bit   //set the prescaler
   TICNT = 0   //sdt the tarting value
   TIINT |= 1<<OVF_Interrupt_enable
   sei() //enable global interrupt
}

ISR(TIM1_OVF_vect)   //happens every [prescaler*2^bit_timer/F_CPU] seconds
{
   while( !(ADCSREG &= (1<<bit_is_ready)) ) ; //usually there is a bit that is 1 while the ADC has read the data, and 0 while it is doing the conversions

//imagine the ADC stores the 3 MSBits in the ADCHI
//and the LSBits in the  ADCLO

value_high = (ADCHI & 0b00000111);  
value_low = ADCLO;

value = (value_high << 7) | value_low;

}
  • At high sampling rates, the best option is to set the ADC in the so called "free running mode". If you read the question carefully, you will notice that's exactly what the OP is doing. – Edgar Bonet Mar 31 '16 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.