Go Down

Topic: Strange issue with FHT conflicting with ADC (Read 416 times) previous topic - next topic

svante311

Nov 29, 2017, 10:15 pm Last Edit: Nov 29, 2017, 10:21 pm by svante311
Hi,

I have been playing with the FHT library and have run into an interesting issue with fht_mag_octave() disrupting the output of my ADC actions.  Running on a MEGA ADK.

The idea is that I'm using FHT to read in the microphone input into A0, but also occasionally checking A2 pot to adjust the volume/brightness of the of the overall FastLED strip.

Here is the code in question...  it's barebones.  Just the standard FHT loop and then, every 100 loops, it checks A2 for the brightness setting.

THE ISSUE IS...that the A2 value "brightness" does not read properly if I use fht_mag_octave().  If I comment out only the fht_mag_octave() line, it works perfectly.  I can also use fht_mag_log instead, and it works fine. This makes me think that there is something that fht_mag_octave() does something that interferes with the ADC registers.


Code: [Select]


#include <FastLED.h>

#define LED_PIN     6
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS 16
CRGB* const leds(NUM_LEDS);

#define LOG_OUT 1 // use the log output function
#define OCTAVE 1 //   // Group buckets into octaves  (use the log output function LOG_OUT 1)
#define OCT_NORM 0 // Don't normalise octave intensities by number of bins
#define FHT_N 128 // set to 128 point fht
#include <FHT.h> //

void setup() {
  Serial.begin(9600);
  delay(1000);
  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness (BRIGHTNESS);


  // TIMSK0 = 0; // turn off timer0 for lower jitter (commented this out b/c I think this interferes with FastLED)
  ADCSRA = 0xe5; // set the adc to free running mode (0b11100101), prescalar=32 for higher ADC clock freq
  ADMUX = 0x40 | (0 & 0x07); // use adc0 (0b1000000)
  DIDR0 = 0x01; // turn off the digital input for adc0

}

int counter = 0;

void loop() {

  while (1) { // reduces jitter

    cli();  // UDRE interrupt slows this way down on arduino1.0


    for (int i = 0 ; i < FHT_N ; i++) { // save 128 samples
      while (!(ADCSRA & 0x10)); // wait for adc to be ready  in binary 0010000 (look for ADC Interrupt flag to be set to 1)
      ADCSRA = 0xf5; // restart adc (in binary 11110101...http://www.robotplatform.com/knowledge/ADC/adc_tutorial_2.html)
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fht_input[i] = k; // put real data into bins

    }

    fht_window(); // window the data for better frequency response
    fht_reorder(); // reorder the data before doing the fht
    fht_run(); // process the data in the fht
    fht_mag_octave();
 //   fht_mag_log();

    // every 100th loop, adjust the volume accourding to the value on A2 (Pot)
    if (counter > 10) {

      ADMUX = 0x40 | (2 & 0x07); // set admux to look at Analogpin A2

      while (!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int brightness = (j << 8) | m; // form into an int
      
//      brightness = map(brightness, 0, 1023, 0, 255);
      Serial.print("b: ");
      Serial.println(brightness);

      FastLED.setBrightness (brightness);

      ADMUX = 0x40 | (0 & 0x07); // set admux back to look at A0 analog pin (to read the microphone input
      counter = 0;
    }
 
    counter++;
    sei();

  }
}



Here's the output as shown (basically, it's outputting nonsense, non-int values):
b: ⸮
b: ⸮
b:
b: 
b: ⸮
b: ⸮
b: 
b: ⸮
b: ⸮
b: 
b: ⸮
b: ⸮
b: ⸮
b: 
b: ⸮
b: ⸮
b:

b: ⸮
b: ⸮
b:



If I comment OUT the fht_mag_octave line and uncomment fht_mag_log() , here is the output (which looks right as I turn the Pot pin):
b: 0
b: 0
b: 0
b: 0
b: 0
b: 9
b: 35
b: 38
b: 48
b: 85
b: 143
b: 171
b: 188
b: 204
b: 224
b: 245
b: 280
b: 332


Any ideas?


svante311

ps.  to test this code, you don't need a real microphone on A0... just set up a Pot between Gnd and +5V with input to A2.

It should work the same on a regular Uno.

DVDdoug

Try a 1mS delay right-before reading A2.

The ADC requires some "settling" time (and there is only one multiplexed ADC in the ATmega chip).    I haven't attempted to understand your code, but of course there is a known sample rate for the FHT readings, but there may be no time between the "last" FHT reading and the A2 reading.

svante311

Thanks @DVD Doug for the suggestion,  I have tried adding a delay before and after that A2 read...up to 2000ms.  but that doesn't seem to work.

svante311

Solved here...  Basically there is a weird timing issue, but if I just read A2 twice in the same manner, the 2nd readout is fine.  Weird.

https://arduino.stackexchange.com/questions/47162/strange-issue-with-fht-library-for-sound-reactive-led-project/47196#47196

jremington

#5
Nov 30, 2017, 07:04 pm Last Edit: Nov 30, 2017, 07:10 pm by jremington
Quote
if I just read A2 twice in the same manner, the 2nd readout is fine.
This is a well known issue, and two readings (discarding the first) are always recommended after switching ADC channels.

The ATMega328 data sheet ADC section has the following comment:
Quote
In Free Running mode, always select the channel before starting the first conversion. The channel selection may be changed one ADC clock cycle after writing one to ADSC. However, the simplest method is to wait for the first conversion to complete, and then change the channel selection. Since the next conversion has already started automatically, the next result will reflect the previous channel selection. Subsequent conversions will reflect the new channel selection.

svante311

Thanks.  I figured that because I did the wait loop, and restarted the ADC, that would have taken care of it...but obviously not.

Grumpy_Mike

Where you went wrong is that code resets the A/D and then immediately reads it without having it finish the conversion.

svante311

@Gumpy_Mike  Thanks!  I just repurposed the standard FHT code and adjusted the ADMUX to look at A2.  Are you saying that I need to put in a delay after the ADCRSA=0xf5 before looking at the ADCL/ADCH?  Or, do I put a delay after adjusting the ADMUX value?


      ADMUX = 0x40 | (2 & 0x07); // set admux to look at Analogpin A2
--> DELAY HERE?
      while (!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
--> or DELAY HERE?
      byte m = ADCL; // fetch adc data
      byte j = ADCH;

Grumpy_Mike

No I am saying insert a "wait for adc to be ready" holding while loop after you have restarted the ADC. You only need the first "-> DELAY HERE" to be a delay if the input impedance of what is driving analogue pin 2 is higher than 10K. This delay allows the sample and hold capacitor to charge first before the conversion starts.

svante311

Thanks (again) @Grumpy_Mike.  I am using a 100k pot for A2...which is why it's slow to respond.  I'll switch to 10k pots instead.

Go Up