Fast, small, and simple Arduino SPI RAM chip routines.

The 8-pin chip, 23LC1024, is a good candidate for adding extra RAM to simple Arduinos such as the Uno, Nano, Mini.  It provides 128K of RAM (the ATmega328 chip that these Arduinos use only has 2K of internal RAM).

The chip works at 5V (anything from 2.5V to 5.5V) and uses SPI so it only uses 4 pins of the Arduino to talk to it.  You can buy the chips on eBay for less than £5 each.

I wanted some fast simple routines to copy any arbitrary number of bytes between ‘normal’ RAM and the SPI RAM.  I didn’t want to link in any huge libraries so I wrote these small routines that hit the 328 chip’s SPI registers direct.

There are only three functions:

spiRam::start(); // sets up the SPI interface – call once from your setup() routine

spiRam::writeSpi(*ptr, address, length); // copies to SPI RAM

spiRam::readSpi(*ptr, address, length); // copies from SPI RAM

*ptr is an unsigned byte (uint8_t) pointer – but you can cast any other data type address to that easily – see example.

address is an unsigned long (uint32_t) as addresses in the SPI RAM range from 0 to 131071 (2^17 – 1).

length is an unsigned int (uint16_t) – but will never be bigger than 2048 as the 328 chip only has 2K of RAM.

Standard connections (you can use a different pin than D10 for the chip select – but there’s not much point unless you want to use more than one RAM chip to give more than 128K RAM – see notes in the .h file).

Chip pin Arduino pin
1 D10
2 D12
3 5V
4 Gnd
5 D11
6 D13
7 5V
8 5V

Here’s the sketch: SpiRam23LC1024.zip Unzip it inside your Arduino folder. You want to end up with the SpiRam23LC1024 folder in your Arduino folder with the SpiRam23LC1024.ino and the two other files inside.  Then it should compile and upload okay.  To use it with your own code just move the .h and .cpp files inside your folder where your .ino file is and add the line

#include “spiRam.h”

up at the top of your sketch.

Because the routines are interrupt driven your code could be doing something else while the transfers are taking place – but be careful if you’re not sure – it’s easy to start destroying or changing contents before they’ve finished being transferred if you’re careless. The routines are very fast anyhow (9 milliseconds to transfer 1024 bytes in either direction), so as standard there is a ‘busy wait loop’ until the transfers are complete so you don’t need to worry about checking from inside your code.

 

Untangle, Planarity.

I’ve been writing some code (in Java) to solve this fun puzzle.  I’ve played the puzzle for many years – it’s part of the excellent and free Simon Tatham’s Portable Puzzle Collection which has been available on many platforms for ages and has recently been ported to Android too.  Untangle was inspired by Planarity, written by John Tantalo.

My first effort modelled the puzzle as a physical system.  The links between points are elastic bands, where the attractive force is proportional to the cube of their length.  The points have an electric charge which produces a repulsive force between all points in line with Coulomb’s inverse square law.  The whole thing is submerged in a viscous fluid so that things don’t move too fast.

Untangle17

It solves many puzzles just using those rules but sometimes, especially with puzzles with a large number of vertices, it gets stuck in ‘a knot’ or takes too long to stabilize.  Also, if it happens to ‘choose’ an outer face with a large number of vertices, then some inner vertices can be pushed out by the repulsive force so that they cross the ‘perimeter line’.

I’m now working on a faster and guaranteed-to-work algorithm – though it may not be as interesting to watch in action.  Stay tuned for an update…

ATmega328P-PU 12MHz bootloader

It took me an embarrassingly long time to get a 328 with a 12MHz crystal working so that it would allow a normal (serial / FTDI) upload from the Arduino IDE.

There are a few older guides around but I didn’t find one that used the newer optiboot bootloader.

If you don’t want to compile the bootloader yourself, you can download the compiled optiboot_at328mega_12.hex file from this link (right-click and and Save (link) as…) just copy it to your optiboot folder before editing your “boards.txt” file as described below.

If you want to compile the hex file yourself here’s how:

Edit the Makefile in your optiboot folder. On my (Windows 7 64bit) machine using Arduino 1.0.5-r2 this folder was C:\Program Files (x86)\Arduino\hardware\arduino\bootloaders\optiboot

Add this section. I put it in after the existing atmega328 sections and before the Sanguino section  (note that the -W1, … shouldn’t be on a separate line – it’s a continuation of the LDSECTIONS line, but this blogging software seems to want to wrap it onto a new line):

atmega328_12: TARGET = atmega328
atmega328_12: MCU_TARGET = atmega328p
atmega328_12: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=115200'
atmega328_12: AVR_FREQ = 12000000L
atmega328_12: LDSECTIONS  = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
atmega328_12: $(PROGRAM)_atmega328_12.hex
atmega328_12: $(PROGRAM)_atmega328_12.lst

atmega328_12_isp: atmega328
atmega328_12_isp: TARGET = atmega328
atmega328_12_isp: MCU_TARGET = atmega328p
# 512 byte boot, SPIEN
atmega328_12_isp: HFUSE = DE
# Low power xtal (12MHz) 16KCK/14CK+65ms
atmega328_12_isp: LFUSE = FF
# 2.7V brownout
atmega328_12_isp: EFUSE = 05
atmega328_12_isp: isp

This is just a copy of the atmega328. section with:

  • the name changed from atmega328. to atmega328_12.
  • the f_cpu parameter changed from 16000000L to 12000000L
  • the two (PROGRAM) lines changed so that the .hex and .lst filenames also have the “_12” addition
  • the comment about the 16MHz crystal frequency changed to 12MHz

Run a command shell as administrator and navigate to the optiboot folder.  Enter the following command to compile the new 12MHz optiboot bootloader:

omake atmega328_12

Now edit your boards.txt file.  On my machine this was located at C:\Program Files (x86)\Arduino\hardware\arduino  Add this new section at the end – again this is just a copy of the ‘uno’ section with the few obvious edits for frequency and bootloader file.

##############################################################

atmega328bb12.name=ATmega328 12MHz crystal
atmega328bb12.upload.protocol=arduino
atmega328bb12.upload.maximum_size=32256
atmega328bb12.upload.speed=115200
atmega328bb12.bootloader.low_fuses=0xff
atmega328bb12.bootloader.high_fuses=0xde
atmega328bb12.bootloader.extended_fuses=0x05
atmega328bb12.bootloader.path=optiboot
atmega328bb12.bootloader.file=optiboot_atmega328_12.hex
atmega328bb12.bootloader.unlock_bits=0x3F
atmega328bb12.bootloader.lock_bits=0x0F
atmega328bb12.build.mcu=atmega328p
atmega328bb12.build.f_cpu=12000000L
atmega328bb12.build.core=arduino
atmega328bb12.build.variant=standard

Now launch Arduino, select “ATmega328 12MHz crystal” as your board and “Burn Bootloader” from the Tools menu.

That’s it, and you should be up and running with the normal hardware serial port working correctly with the ‘Serial’ commands and serial uploading from the Arduino IDE also working correctly.  I’ve seen comments that not all of the timing functions (microsecond delays etc.) work accurately when using a 12MHz crystal and the standard Arduino libraries.  I’ve not investigated that yet; and so far I’ve not tried using software serial with a 12MHz crystal – that also may require some tweaking…

 

 

Controlling the Raspberry Pi camera from C

I wanted to be able to stop and start the camera driven by events instead of just calling raspivid to record for a preset time.

My code is really just a simple wrapper around raspivid using the SIGUSR1 option to stop the video under program control rather than after a preset time.

The example main() function starts the video, sleeps for five seconds and then stops it.  For demo purposes I included the options for black and white inverted video.  To record with the normal raspivid defaults you just call startVideo with an empty options string:

startVideo(“filename.h264”, “”);

Obviously you can put any of the normal raspivid options in the string – but you should avoid -t, -n, -o, and -s as the code fills those in for you.  If you want to enable preview/monitoring then make the obvious change to remove the -n (no preview) option.

Save code as, say, video.c and compile with gcc -o video video.c

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static pid_t pid = 0;

void startVideo(char *filename, char *options) {
    if ((pid = fork()) == 0) {
        char **cmd;

        // count tokens in options string
        int count = 0;
        char *copy;
        copy = strdup(options);
        if (strtok(copy, " \t") != NULL) {
            count = 1;
            while (strtok(NULL, " \t") != NULL)
                count++;
        }

        cmd = malloc((count + 8) * sizeof(char **));
        free(copy);

        // if any tokens in options, 
        // copy them to cmd starting at positon[1]
        if (count) {
            int i;
            copy = strdup(options);
            cmd[1] = strtok(copy, " \t");
            for (i = 2; i <= count; i++)
                cmd[i] = strtok(NULL, " \t");
        }

        // add default options
        cmd[0] = "raspivid"; // executable name
        cmd[count + 1] = "-n"; // no preview
        cmd[count + 2] = "-t"; // default time (overridden by -s)
                               // but needed for clean exit
        cmd[count + 3] = "10"; // 10 millisecond (minimum) time for -t
        cmd[count + 4] = "-s"; // enable USR1 signal to stop recording
        cmd[count + 5] = "-o"; // output file specifer
        cmd[count + 6] = filename;
        cmd[count + 7] = (char *)0; // terminator
        execv("/usr/bin/raspivid", cmd);
    }
}

void stopVideo(void) {
    if (pid) {
        kill(pid, 10); // seems to stop with two signals separated
                       // by 1 second if started with -t 10 parameter
        sleep(1);
        kill(pid, 10);
    }
}

int main(int argc, char **argv) {
    printf("Recording video for 5 secs...");
    // example options give an upside-down black and white video
    startVideo("temp.h264", "-cfx 128:128 -rot 180"); 
    fflush(stdout);
    sleep(5);
    stopVideo();
    printf("\nVideo stopped - exiting in 2 secs.\n");
    sleep(2);
    return 0;
}

Decoding 6 servo channel inputs with an Arduino UNO

Standard radio control receivers drive their connected servos by outputting a train of pulses.  The pulses usually repeat at a 50 Hz rate but the pulse width is the important thing – the standard is a 1.5 ms pulse width for a servo at its center position varying from about 1.0 ms up to 2.0 ms for servo’s nominal travel range. The pulses are positive going, usually about 5V in amplitude.

Searching on the web finds lots of examples of code to read the pulse widths for several servo channels, but for my application the Arduino is looking after several sensors besides the servo signals and also running software serial ports.  The examples I found all gave too much jitter and inaccuracy.

I wired the six channels to the Arduino digital pins 2 to 7.  Pins 0 and 1 are used by the UART (serial communication to PC or other device) so it’s best to avoid those.  Conveniently this means that all six channels are contained on a single Arduino input port, (Port D) so this makes the code to capture the pulse widths very clean.

We enable an interrupt that occurs when any of the 6 pins change state. To do this we set one bit for each of the 6 channels in the PCMSK register.  Then to allow changing input states on Port D to generate interrupts there is just one bit in the PCICR register to set:

PCMSK2 |= 0xFC;
PCICR |= 0x04;

The interrupt handler, declared as ISR(PCINT2_vect) checks each of the six pins. If a pin has changed state since the previous interrupt, we do one of two things:  If the pin has gone high we just remember the current time; if the pin has gone low we store the pulse width by subtracting the remembered time from the current one.  The Arduino has a function that returns microseconds (with a resolution of 4 microseconds on the UNO) convenient for this task.  The microsecond timer wraps around back to zero roughly every 70 minutes but this doesn’t cause any problems – by doing the subtractions using unsigned long integers the pulse widths are still correct even when a ‘wrap around’ occurs.  Here’s the complete program including interrupt handler and simple test output that just prints the current pulse widths to the serial port (PC).

volatile uint8_t prev; // remembers state of input bits from previous interrupt
volatile uint32_t risingEdge[6]; // time of last rising edge for each channel
volatile uint32_t uSec[6]; // the latest measured pulse width for each channel

ISR(PCINT2_vect) { // one or more of pins 2~7 have changed state
  uint32_t now = micros();
  uint8_t curr = PIND; // current state of the 6 input bits
  uint8_t changed = curr ^ prev;
  int channel = 0;
  for (uint8_t mask = 0x04; mask; mask <<= 1) {
    if (changed & mask) { // this pin has changed state
      if (curr & mask) { // +ve edge so remember time
        risingEdge[channel] = now;
      }
      else { // -ve edge so store pulse width
        uSec[channel] = now - risingEdge[channel];
      }
    }
    channel++;
  }
  prev = curr;
}

void setup() {
  Serial.begin(9600);

  for (int pin = 2; pin <= 7; pin++) { // enable pins 2 to 7 as our 6 input bits
    pinMode(pin, INPUT);
  }

  PCMSK2 |= 0xFC; // set the mask to allow those 6 pins to generate interrupts
  PCICR |= 0x04;  // enable interupt for port D
}

void loop() {
  Serial.flush();
  for (int channel = 0; channel < 6; channel++) {
    Serial.print(uSec[channel]);
    Serial.print("\t");
  }
  Serial.println();
}

 

Flash Rubik Cube Simulator

Snapshot of Flash Rubik Cube Simulator

Above is a snapshot image.  Here’s the real thing.

It works for any size of cube from 2 x 2 x 2 up to 11 x 11 x 11.  Actually, 11 is an arbitrary limit – the same code would work for any size of cube.  Use the little + and – buttons up in the top left hand corner to select the size of the cube.

Turning a face or slicing a layer is pretty intuitive – just click somewhere on the cube, hold down the mouse button and drag.

Turning the whole cube around is the same but you have to double-click, hold the mouse button down on the second click and then drag.

There’s a bug in the turning of the whole cube which causes the orientation to jump occasionally.  I think I can fix it by using quaternions to do the whole-cube rotation, but with the demise of Flash I’m thinking this would be a good project to migrate to HTML5 – and then I could patch in the quaternion stuff at the same time.

You can also use the keyboard to turn layers (there’s a sort of agreed way of doing this among speed-cubers, and the simulation mostly uses the agreed keys),

The Minimum Attack Problem

Place five queens on a chess board so that every square on the board is attacked.  It’s an old and famous problem; there are lots of solutions and it’s pretty easy to find one.  Try it for yourself and see!

Now try using just three queens and two rooks.  Not so easy this time; the solution is unique if you ignore trivial rotations and reflections of the whole board.  What about other attacking forces? Four queens and two pawns maybe?

The image above is a snapshot of my solver.  Click here to go to the solver itself. I wrote it over ten years ago when Java applets were flavor of the month.  Most modern computers and tablets don’t have Java available by default and most tablets won’t let you install Java at all; you can still install Java as a free plug-in in most PC browsers though.