Wednesday, June 19, 2013

Code efficiencies on #Arduino

Let's talk a minute about code efficiency. I’ve written code before for devices that had small memory models and Arduino is no exception. Every spare byte of memory used can be the difference between success or failure. This isn’t a problem with your PC because it employs a virtual memory model. When you open the 20th tab on your web browser and your PC runs out of memory, your PC simply takes some programs that it is not currently working on and the data that they have in memory and writes them to a swap file on your hard disk. When those programs are needed again, something else is removed from memory and they are swapped back in. The user doesn’t, usually, even notice this happens except that their pc will get slower the more that this has to happen, because reading from the hard disk is much slower than reading from memory. So, yes, if you have enough going on that your PC is swapping things in and out constantly, your PC will keep slowing down. That advertisement that has the dollar bills flying from a piggy bank is still running, being swapped in and out, even though you haven’t looked at that web page in 20 minutes. Just close it. 

Anyway, this is handled by your computer’s operating system (OS). This can be Window’s, iOS, Lynix, etc. Your Arduino doesn’t have an OS. It has very little beyond the boot loader and your last uploaded sketch. Even if an OS was written for it, without a hard disk, there’s no where to write a swap file. That’s not to say it couldn’t be done. Some clever person, or group, may write a simple OS that will implement virtual memory using an SD card or something. But, today, as far as I know, that doesn’t exist.

That is why I have to worry about code efficiency. Having said all of that, our “Shooting Star” game is really in danger of running out of memory on my Arduino mega compatible microcontroller which boasts 256KB of flash memory. Only 8KB is used by the bootloader. In times when PCs are talking about gigabytes of memory ¼ of a megabyte seems terribly small, but these sketches are tiny. Since this game is going to be our first complete project, not including experimentation, I would like to build a permanent version with a nice case that my kids can play with and, more importantly, as a keepsake of this milestone of our journey. I have no intention of using the power, and size, of this microcontroller board for this little keepsake. My plan is to spend $3.00 on an ATtiny84 which has 512 Bytes of memory. Yes, Bytes, roughly ½ KB and the bootloader will steal some of that! It’s like comparing a drop of rain to a lake.  Add a 9 volt battery adapter, a little pcb board, power regulator, a bit more resilient push-button, power switch, and a case. Without actually pricing out everything, I suspect that we can put it together for under $25 and a ton of new experiences (flashing bootloaders, pcb layout, soldering, etc.)
ed: I apparently mis-read the specs for the ATtiny84, it can actually have up to 8k of Flash memory.  It has a 512 byte eprom and 512 byte sram.    

So where am I going to find these inefficiencies in my program? Everywhere, you’d be surprised. Let’s start with my displayLed() function. I thought I was being pretty efficient by assuming that only 1 LED would be lit at a time. What I had though was a lot of code:


void displayLed(unsigned long iVal){
  digitalWrite(latchPin, LOW);            //Pull latch LOW to start sending data
  if (iVal > 255){
    shiftOut(dataPin,clockPin,MSBFIRST,0);
    shiftOut(dataPin,clockPin,MSBFIRST,iVal/256);
  }
  else
  {
    shiftOut(dataPin,clockPin,MSBFIRST,iVal);
    shiftOut(dataPin,clockPin,MSBFIRST,0);
  }  
  digitalWrite(latchPin, HIGH);           //Pull latch HIGH to stop sending data
}

As with most discoveries, this was not a Eureka moment. I was thinking, after the “caught” LED blinked, wouldn’t it be nice to have it split and fly off in both directions? By my displayLed() function can’t handle 2 LEDs. Technically, it can, but only in the right 8 LEDs. So I turned to Excel, as I often do, to figure out the math. I had already figured out that the left 8 leds can be calculated by “int(a1/256)” and realized that the right bits are actually the remainder of that calculation “mod(a1,256).” I came up with this using those 2 formulas:
1
0
1
00000000
00000001
2
0
2
00000000
00000010
4
0
4
00000000
00000100
8
0
8
00000000
00001000
16
0
16
00000000
00010000
32
0
32
00000000
00100000
64
0
64
00000000
01000000
128
0
128
00000000
10000000
256
1
0
00000001
00000000
512
2
0
00000010
00000000
1024
4
0
00000100
00000000
2048
8
0
00001000
00000000
4096
16
0
00010000
00000000
8192
32
0
00100000
00000000
16384
64
0
01000000
00000000
32768
128
0
10000000
00000000
If I want the 3rd LED from each end lit, I would add 8192 + 4 getting 01000000 00000010 in binary, but more importantly, I’d get 32 for the left register and 4 for the right register. Now back to the displayLed() function. With the math worked out, I can get rid of 2 lines of code and the entire “if” structure:

void displayLed(unsigned long iVal){
//This will work for 16 leds only and light any value between 0 and 65535
  digitalWrite(latchPin, LOW);         //Pull latch LOW to start sending data
  shiftOut(dataPin,clockPin,MSBFIRST,iVal%256);//Right 8 Leds
  shiftOut(dataPin,clockPin,MSBFIRST,int(iVal/256));//Left 8 Leds
  digitalWrite(latchPin, HIGH);        //Pull latch HIGH to stop sending data
}

I admit, I had to look up modulo, which isn’t a function, but an operator “%”)

2 comments :

  1. Glad to see you are continuing to progress!

    Quick note, the Tiny85 has 512 bytes of SRAM for runtime memory, but 8K of flash for storing programs which is quite a bit, really. (This is a Harvard architecture so program and memory/data are in separate spaces).

    I'm not sure how modulo is implemented in assembly (there isn't a native modulo instruction I don't think) but you might find left and right shift would be helpful here.

    Let's say you want to light the nth LED from left and right. I believe you could just do:

    iVal = (0x8000>>n) | (0x0001<<n);

    Which would result in 10 0000 0000 0100 (0x24 or 32+4)

    Hope this helps! :)

    ReplyDelete
  2. Mike, thanks for the advice. The only reason that I chose the Tiny84 over the Tiny85 is the number of i/o pins. I need 6 for the LCD panel, 3 for the LEDs, and one for the button and the Tiny85 only has 6 I/O pins.

    ReplyDelete