Author Topic: How can I speed up this simple Arduino sketch?  (Read 10446 times)

0 Members and 1 Guest are viewing this topic.

Offline Thane of CawdorTopic starter

  • Regular Contributor
  • *
  • Posts: 96
How can I speed up this simple Arduino sketch?
« on: October 01, 2014, 02:38:20 pm »
Hi everyone,

I am experimenting on a short sketch based mainly off the Adafruit OLED library and I have added a some extra lines to make an oscilloscope-like display. At very low frequencies (sub 10Hz range) the display shows clean waveforms. However, at higher frequencies update rate of the display becomes an issue and the lines merge into each other. Are there any parts of the code which I can modify to speed up the rate at which data is shown on  the OLED display?  :-/O


The code:
Code: [Select]
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{
  B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

int x = 0;
int y = 0;

int ax = 0;
int ay = 64;

void setup()   {
        #if FASTADC
        sbi(ADCSRA,ADPS2);
        cbi(ADCSRA,ADPS1);
        cbi(ADCSRA,ADPS0);
        #endif 
  Serial.begin(9600);

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C addr 0x3D (for the 128x64)
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(500);
}

void loop()  {

  while (x<=128) {

    x++;
   
    display.drawLine(ax, ay, x, y, WHITE);//(xpos-start,ypos-start,xpos-end,ypos-end)
 
    ay = y;
   
    ax = x;
   
    y = 63-(((analogRead(A0)) / 17)); //inverts signal to show 0V as bottom of screen and scales down analogue input
 
    display.display();
  } 

  while (x >128) {
 
    display.clearDisplay();//refresh and clear display once line reaches OLED width
   
    x = 0;
   
    ax = 0;
  }
}



Thanks
 

Offline HackedFridgeMagnet

  • Super Contributor
  • ***
  • Posts: 2035
  • Country: au
Re: How can I speed up this simple Arduino sketch?
« Reply #1 on: October 01, 2014, 02:56:37 pm »
I am guessing display.display() takes a while, and that it blats the whole image.

edit: bad code removed.
yes separate out the acquisitions from the display as someone mentioned below.
« Last Edit: October 02, 2014, 01:07:48 am by HackedFridgeMagnet »
 

Online mikerj

  • Super Contributor
  • ***
  • Posts: 3338
  • Country: gb
Re: How can I speed up this simple Arduino sketch?
« Reply #2 on: October 01, 2014, 03:05:54 pm »
You don't really want to be reading the ADC and updating the display at the same time.  Split the function into two operations:

1) Grab 128 samples from the ADC and store into a buffer.  Store the raw sample value rather than scaling on the fly.
2) Apply any scaling to the sample values and send the data to the display.

This way your sample rate is not dependent on the time taken to draw lines on the screen, and the nasty divide by 17 in the loop.  Instead the lengthy processing tasks will simply determine the refresh rate of the display.

The fastest sample rate will be achievable in a tight loop as above, but for slower/variable sample rates consider using a timer to trigger the ADC and loading the buffer in the ADC interrupt.
« Last Edit: October 01, 2014, 03:08:50 pm by mikerj »
 

Offline Thane of CawdorTopic starter

  • Regular Contributor
  • *
  • Posts: 96
Re: How can I speed up this simple Arduino sketch?
« Reply #3 on: October 01, 2014, 03:26:47 pm »
Thanks for the responses,

Quote
Grab 128 samples from the ADC and store into a buffer
  I haven't used the Arduino environment for too long and how would you store values in a buffer? Is there a specific way to do this?


 

Online mikerj

  • Super Contributor
  • ***
  • Posts: 3338
  • Country: gb
Re: How can I speed up this simple Arduino sketch?
« Reply #4 on: October 01, 2014, 04:52:16 pm »
Something like this.  This is completely untested, probably loads of syntax errors!

Code: [Select]

#include <stdint.h>

#define ADC_BUFF_LEN 128

static uint16_t adcBuffer[ADC_BUFF_LEN];

void loop()  {

  // Read ADC samples into buffer
  for(x=0;x<ADC_BUFF_LEN;x++) {
    adcBuffer[x] = analogRead(A0);
  }
 
  display.clearDisplay();

  // Write samples to the display
  for(x=0;x<ADC_BUFF_LEN;x++) {

    y = 63-(adcBuffer[x] / 17); //inverts signal to show 0V as bottom of screen and scales down analogue input

    if(x==0) // Prevent the first sample drawing a line up the edge of the display
    {
      ax = x;
      ay = y;
    }

    display.drawLine(ax, ay, x, y, WHITE);  //(xpos-start,ypos-start,xpos-end,ypos-end)

    // Save current coordinates for next loop iteration
    ay = y;
    ax = x;
  }

  // Refresh display
  display.display();

}

 

Offline nixfu

  • Supporter
  • ****
  • Posts: 346
  • Country: us
Re: How can I speed up this simple Arduino sketch?
« Reply #5 on: October 02, 2014, 12:57:20 am »
Use a faster processor such as the $19 Teensy 3.1 that runs at 96mhz instead of 8/16mhz of a regular arduino.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #6 on: October 02, 2014, 01:40:23 am »
It is not clear to me what your issues are.

The title seems to suggest that it is an execution time / speed issue; The body of your message, however, seems to suggest that it is a triggering or time division issue.

There are solutions to each of the problems but they are quite different.
================================
https://dannyelectronics.wordpress.com/
 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6289
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #7 on: October 02, 2014, 02:10:23 am »
You can see near line 157 in the file below a 128 point graph that is drawn 10 times a second. The dispaly is a 128x64 1.3" oled connected via SPI and max speed (4Mhz IIRQ) to a 16Mhz atmega328p (Arduino mini pro). 

https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/display.cpp

It is done using the u8glib.  This is an excellent graphics library (also for Arduino) and the developer is very responsive on the Arduino forums (display section). You can ask him what performance to expect from his library.
 

Offline Rick Law

  • Super Contributor
  • ***
  • Posts: 3487
  • Country: us
Re: How can I speed up this simple Arduino sketch?
« Reply #8 on: October 02, 2014, 02:47:20 am »
It is not clear to me what your issues are.

The title seems to suggest that it is an execution time / speed issue; The body of your message, however, seems to suggest that it is a triggering or time division issue.

There are solutions to each of the problems but they are quite different.

From working with the TFT screen, I think it may also be contention with screen memory.  At high rate, for the TFT screen I used, the screen is fighting with the MCU for memory access.

Perhaps the OLED screen has that issue too.
« Last Edit: October 02, 2014, 02:48:51 am by Rick Law »
 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6289
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #9 on: October 02, 2014, 03:19:36 am »
OP, are you using i2c? What clock  speed? Typically they are slow. Also, color mode makes things even slower.
« Last Edit: October 02, 2014, 03:22:09 am by zapta »
 

Offline Thane of CawdorTopic starter

  • Regular Contributor
  • *
  • Posts: 96
Re: How can I speed up this simple Arduino sketch?
« Reply #10 on: October 02, 2014, 03:31:59 am »
Hi,

Quote
are you using i2c? What clock  speed? Typically they are slow. Also, color mode makes things even slower.

It's an I2C OLED with only one colour (white) and I'm using a clock speed of 16MHz.

Quote
execution time / speed issue; The body of your message, however, seems to suggest that it is a triggering or time division issue.

I am still quite uncertain, but I was under the impression that the execution time was causing the time division issue.  :-//

Quote
Something like this.

Thanks, I'll try to integrate it into the sketch and test it out
« Last Edit: October 02, 2014, 03:34:11 am by Thane of Cawdor »
 

Offline Thane of CawdorTopic starter

  • Regular Contributor
  • *
  • Posts: 96
Re: How can I speed up this simple Arduino sketch?
« Reply #11 on: October 02, 2014, 05:08:24 am »
Something like this.  This is completely untested, probably loads of syntax errors!

Code: [Select]

#include <stdint.h>

#define ADC_BUFF_LEN 128

static uint16_t adcBuffer[ADC_BUFF_LEN];

void loop()  {

  // Read ADC samples into buffer
  for(x=0;x<ADC_BUFF_LEN;x++) {
    adcBuffer[x] = analogRead(A0);
  }
 
  display.clearDisplay();

  // Write samples to the display
  for(x=0;x<ADC_BUFF_LEN;x++) {

    y = 63-(adcBuffer[x] / 17); //inverts signal to show 0V as bottom of screen and scales down analogue input

    if(x==0) // Prevent the first sample drawing a line up the edge of the display
    {
      ax = x;
      ay = y;
    }

    display.drawLine(ax, ay, x, y, WHITE);  //(xpos-start,ypos-start,xpos-end,ypos-end)

    // Save current coordinates for next loop iteration
    ay = y;
    ax = x;
  }

  // Refresh display
  display.display();

}


That works just fine.  :D  Now it's going super fast. I think I'll try out your method of controlling the sample rate by using a timer to trigger the ADC. Would it be possible to use the millis/micros function to do the timing i.e. sample ADC each time a certain value is reached? or would I be better off using delays?

Thanks
 

Offline zapta

  • Super Contributor
  • ***
  • Posts: 6289
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #12 on: October 02, 2014, 07:58:10 am »
It's an I2C OLED with only one colour (white) and I'm using a clock speed of 16MHz.

One color is good.

Are you sure that your I2C clock is 16Mhz (not to be confuses with the MCU clock)?  I would bet it's not.

If you have an oscilloscope look at the I2C clock signal, it's possible that the time is spend delivering the data to the display.

Edit: I am looking at the datasheet of the ssd1306, max I2C clock speed is 400khz (2.5usec).  Page 54 here https://www.adafruit.com/datasheets/SSD1306.pdf .  With 3 and 4 wire SPI it can go 10Mhz (100ns). That's ~x25 faster communication.
« Last Edit: October 02, 2014, 08:04:36 am by zapta »
 

Online mikerj

  • Super Contributor
  • ***
  • Posts: 3338
  • Country: gb
Re: How can I speed up this simple Arduino sketch?
« Reply #13 on: October 02, 2014, 08:04:23 am »
Simply putting a software delay in the ADC loop will slow down the sample rate, but this is a very crude way of doing things.  You are effectively throwing away CPU that could be used doing other things, such as drawing the previous waveform.

If you look at the ADCSRB register, you will notice that an ADC conversion can be automatically triggered by a number of events without any software overhead.  Most of these events are timer related; either a timer overflow or a output compare match event.  You could set up a timer with an output compare function to fire this event at very precise, and easily adjustable time intervals.  This would then start an ADC conversion, and when the ADC conversion has completed you could use the ADC interrupt to load the new sample into the buffer.  When the buffer is full you can disable the ADC interrupt and raise a flag (just set a variable to '1') so that the main loop knows it's time to update the display.

If you want to be clever, you can use two buffers (you have probably heard the term "double buffered" if you have been programming for long).  This way you alternate between the buffers being filled by the ADC, so that the display can be updated from one buffer whilst the other buffer is being filled.

At some point you will notice that the waveform display isn't very stable because you have no triggering, you simply start sampling at some random point in the waveform.  You might want to consider using the analog comparator to provide a trigger function, or this could also be done in the ADC interrupt.  Remember to keep your interrupt handlers as short and fast as possible, no divide or floating point operations for instance.

To do these things you are probably going to have to step outside of the basic Arduino libraries and start accessing hardware registers directly.
 

Offline Thane of CawdorTopic starter

  • Regular Contributor
  • *
  • Posts: 96
Re: How can I speed up this simple Arduino sketch?
« Reply #14 on: October 02, 2014, 08:24:57 am »
I haven't been programming for very long and I have a few questions in regard to the info you provided:

Firstly, what is the ADCSRB register and how would I look at it?

Secondly, how would I check whether the buffer is full? Would I just place a 'flag' at the end of the "for" loop?


Thanks again
 

Offline Rerouter

  • Super Contributor
  • ***
  • Posts: 4700
  • Country: au
  • Question Everything... Except This Statement
Re: How can I speed up this simple Arduino sketch?
« Reply #15 on: October 02, 2014, 08:37:46 am »
Try adding
Code: [Select]
#define TWI_FREQ_LCD 400000L
TWBR = ((CPU_FREQ / TWI_FREQ_LCD) - 16) / 2;

This will change the I2C clock rate to 400Khz, with most devices still happy to run at 800Khz, The atmega328 doesnt like running much faster, but i've played on other platforms with sensors which where happy up to 1.6Mhz when they where only rated at 400Khz)
 

Offline TopLoser

  • Supporter
  • ****
  • Posts: 1925
  • Country: fr
Re: How can I speed up this simple Arduino sketch?
« Reply #16 on: October 02, 2014, 08:43:17 am »
 

Online mikerj

  • Super Contributor
  • ***
  • Posts: 3338
  • Country: gb
Re: How can I speed up this simple Arduino sketch?
« Reply #17 on: October 02, 2014, 03:47:28 pm »
I haven't been programming for very long and I have a few questions in regard to the info you provided:

Firstly, what is the ADCSRB register and how would I look at it?

Secondly, how would I check whether the buffer is full? Would I just place a 'flag' at the end of the "for" loop?


Thanks again

The first thing you need to do is to download the datasheet for the ATMega device which is used on your Arduino board.  Next you need to read all the relevant sections on the peripherals you wish to use, these will explain the register names and how they function.  This isn't very exciting, but without a good understanding of the device you are programming your efforts will be doomed.

Once you have a plan, you can access special function registers directly from within your Arduino code e.g.
Code: [Select]
ADCSRA |= ADATE;  // Set ADATE bit to enable auto triggering
ADCSRB |= (ADTS1 | ADTS0); // Select Timer0 Compare Match A to trigger the ADC

If you are filling the buffer from an ADC interrupt then you won't have a 'for' loop.  Every time the interrupt fires you would grab the latest ADC value and copy it to the next location in the buffer.  This means you will need an index variable which is incremented on every interrupt.  You know when you have reached the end of the buffer because the index variable will be larger than the last location in your buffer.

This all means you will have to wean yourself off using some of the high level Arduino functions, they are designed for simplicity and ease of use, but not performance.  In this case you won't be calling analogRead() any more, since this function triggers an ADC conversion, waits for the conversion to finish and then returns the result.  The new scheme has the timer triggering the ADC, and when the conversion is complete the ADC will fire an interrupt for you, providing you have configured it correctly.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #18 on: October 03, 2014, 08:24:01 pm »
The fastest adc on this mcu is to run the adc continuously and manage the output in an isr. If you were to use a naked isr, the cost would be your own context management + code to manage the output -> 10 ticks?

Otherwise, 20 or so ticks should do. At 20MIPS, that's far faster than what the adc hardware can actually do.
================================
https://dannyelectronics.wordpress.com/
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: How can I speed up this simple Arduino sketch?
« Reply #19 on: October 03, 2014, 08:24:55 pm »
I would also left aligned the adc results as 8-bit display resolution is likely more than enough.
================================
https://dannyelectronics.wordpress.com/
 

Offline IconicPCB

  • Super Contributor
  • ***
  • Posts: 1546
  • Country: au
Re: How can I speed up this simple Arduino sketch?
« Reply #20 on: October 08, 2014, 03:49:45 am »
Free run the ADC
 

Offline poorchava

  • Super Contributor
  • ***
  • Posts: 1673
  • Country: pl
  • Troll Cave Electronics!
Re: How can I speed up this simple Arduino sketch?
« Reply #21 on: October 08, 2014, 06:39:36 am »
IIRC (haven't used AVR's in ages) ADC in simpler AVRs can do up to 200ksps at full resolution, but they will also work in low msps range with reduced resolutions. I remember running one at something like 800ksps, it gave about 7 usable data bits from what i remember.
I love the smell of FR4 in the morning!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf