esp8266 / Arduino

ESP8266 core for Arduino
GNU Lesser General Public License v2.1
16.05k stars 13.33k forks source link

ADC is slow #696

Open madsci1016 opened 9 years ago

madsci1016 commented 9 years ago

I'm hard-pressed to find any documentation on what the ADC is actually capable of in terms of sampling rate. Currently, the staging release can only do about 2.5kHz. I've found others using the ADC at 20kHz and higher successfully, but not with this toolchain.

For example: http://www.esp8266.com/viewtopic.php?f=13&t=2134

and

https://hackaday.io/project/4318-vu-meter-esp8266-ws2812b

Any chance on improved ADC sample time?

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

igrr commented 9 years ago

You are welcome to investigate if there is a higher-speed version of ADC routine which works reliably under all three WiFi modes. If you find one which is a clear improvement, that would certainly be cool. You can experiment by putting ADC function into your sketch (or by adding a ".c" file to your sketch).

On Sun, Aug 16, 2015, 05:46 Bill Porter notifications@github.com wrote:

I'm hard-pressed to find any documentation on what the ADC is actually capable of in terms of sampling rate. Currently, the staging release can only do about 2.5kHz. I've found others using the ADC at 20kHz and higher successfully, but not with this toolchain.

For example: http://www.esp8266.com/viewtopic.php?f=13&t=2134

and

https://hackaday.io/project/4318-vu-meter-esp8266-ws2812b

Any chance on improved ADC sample time?

— Reply to this email directly or view it on GitHub https://github.com/esp8266/Arduino/issues/696.

pvvx commented 9 years ago

1..20 000 Hz 14 bit (send UDP) https://github.com/pvvx/esp8266web/blob/master/app/driver/wdrv.c http://esp8266.ru/forum/threads/razrabotka-biblioteki-malogo-webservera-na-esp8266.56/page-37#post-9203

madsci1016 commented 9 years ago

Yep that's one of the other examples I found, but I'd be damned if I can't figure out how to merge either example with the Arduino tool chain. Doesn't help that half the time the compiler just locks up and crashes instead or reporting an error...

madsci1016 commented 9 years ago

Well I've narrowed in on it somewhat. I can't compile because the function rom_i2c_writeReg_Mask() has to be linked to function in the boot ROM and I can't find where read_sar_dout() is defined.

I think that's the limit of my experience, any help would be appreciated.

NdK73 commented 6 years ago

Old thread, but maybe can be useful. You can use a simple

extern "C" {
// Not defined anywhere except ld script, but that does not specify parameter types... could be bytes or longs...
void rom_i2c_writeReg_Mask(int, int, int, int, int, int);
// Even more mysterious...
void read_sar_dout(void *);
}

and the compiler is happy. I was trying to tie ADC reading to an ISR, so that reading was "in background" instead of blocking, but I "randomly" got Exception 0 (illegal instruction). My ISR code was:

void setup()
{
...
  // Setup ISR to read ADC
  noInterrupts();
  timer0_isr_init();
  timer0_attachInterrupt(adcISR);
  next=ESP.getCycleCount()+microsecondsToClockCycles(1000);
  timer0_write(next);
  interrupts();
}

static volatile long next;
volatile int adc;
static volatile bool fresh=false;

static ICACHE_FLASH_ATTR void adcISR()
{
  static int s=0; // State machine
  next+=microsecondsToClockCycles(1000); // this gives 1kHz calls to ISR, ~ 333Hz ADC readings
  timer0_write(next); // Rearm

  // FSM conversion of code from http://www.esp8266.com/viewtopic.php?f=13&t=800&start=12
  switch(s) {
    case 0: // Start acquisition (no WiFi disable, ATM)
      s=1;
      rom_i2c_writeReg_Mask(108,2,0,5,5,1);
      SET_PERI_REG_MASK(0x60000D5C,0x00200000);
      break;
    case 1: // Check if complete (1)
      if(!(READ_PERI_REG(0x60000D50)&(0x7<<24))) {
        CLEAR_PERI_REG_MASK(0x60000D50,2);
        SET_PERI_REG_MASK(0x60000D50,2);
        s=2;
      }
      break;
    case 2: // Check if complete (2), then read
      if(!(READ_PERI_REG(0x60000D50)&(0x7<<24))) {
        uint16 sar_x[8];
        uint16 z=0;
        read_sar_dout(&sar_x[0]);
        for(int y = 0; y < 8; y++)
          z += sar_x[y];
        z += 8;
        z >>= 4;

        // ... uninteresting app specific code removed ...

        s=0;
        adc=z; // Makes read value available
        fresh=true; // Signal that it's ready (I know, there's a race between reading adc value and resetting fresh, but *currently* I don't care)
      }
      break;
  }
}

Comments (and improvement suggestions) welcome!

devyte commented 6 years ago

@NdK73 The random crashes could have been because the ISR needs to have the ICACHE_RAM_ATTR and not the FLASH one. No such thing as an old thread when an improvement is possible :)

NdK73 commented 6 years ago

Urgh! You're right. Too bad it keeps spitting out Exception 0 :( I'll need to simplify the code to understand where is the problem. Currently between my ADC routine, TFT SPI lib and WiFi there are probably too many interactions happening...

PS: using ICACHE_RAM_ATTR I could increase to 2ksps the sampling rate of my routine using analogRead() w/o triggering exceptions... Not bad, but quite sure it's possible to do something better...

Mar-Pfa commented 6 years ago

Hey! Did you find a solution to get the code running stable!? If yes, i'll be very interested as i'm working currently on the same topic and have some problems here :-/

NdK73 commented 6 years ago

Nope, sorry. Kept having random exceptions with wifi on :( I don't know the ESP8266 well enough to do something better... It's probably better to offload all the ADC sampling to a "coprocessor" accessed via I2C or SPI (SPI can be shared with flash chip, if lines are brought out like on some ESP-12 models: this way you have to use only one other GPIO for CS).

NdK73 commented 6 years ago

Just discovered a possible reason ICACHE_RAM_ATTR made no difference: according to comment in https://github.com/adafruit/Adafruit_NeoPixel/blob/master/esp8266.c the ISR should have been in its own .c file. Might be worth trying it.

debsahu commented 6 years ago

Maybe this is helpful: https://github.com/cnlohr/colorchord/tree/master/embedded8266/user/adc.c

NdK73 commented 6 years ago

Tks @debsahu . It seems to offer a bit more info than the source I used, but it's more or less the same. Still misses the prototypes for i2c_Reg_Mask functions, that are "internal" (inside the bin only blob), assuming they're inside one of the .h files it includes. Too bad the only reference in SDK I could find is a #define like the ones he used... :( I tried guessing the type of the parameters, but quite surely I got some wrong...

Tech-TX commented 3 years ago

Charles used the ESP-IOT-SDK, which has routines that are undoubtedly remapped or possibly missing in the Non-OS SDK. The physical registers are still there, but it may take decompiling the blob to see what the old routines were doing. My ESP assembly language skills just aren't up to the challenge. I can understand some of it, but I'm not fluent in it, and the decompiler doesn't do a very good job with the blob - there are numerous translation errors.

One issue we can't fix: if you're trying to use the ADC for audio or other precisely timed conversions , you'll get pops, clicks or other misfeatures in the output because WiFi has a higher interrupt priority than just about anything else, and you also have to yield() occasionally for core upkeep. Deal with it, or write your own SDK. The API for the fast ADC read is pretty worthless, as you have to disable ALL interrupts and timers:

To use system_adc_read_fast, Wi-Fi has to be disabled. And if ADC continuously sampling is needed, all interrupts have to be disabled, so PWM or NMI hardware timer can not be used when system_adc_read_fast is calling.

That means an external dedicated chip, otherwise you're using the pitiful SAR ADC in the ESP as a 'dedicated chip'. A cheap 8-bit flash ADC will have better linearity.