Translation missing: en.general.accessibility.skip_to_content

Lightino Lesson 3: Hold it! Stop your pattern from spinning!

You will have noticed that the patterns you've made so far spin round, because we haven't told the Lightino when to start during a rotation. This is what the sensor is for. We write to the LEDs with a digitalWrite() function, so you could probably guess that we read from the sensor with digitalRead(). This normally returns HIGH, but returns LOW if it sees something passing over the sensor.

Good Interruptions!

In our sketch, we could simply wait for the sensor to go LOW before starting the pattern, but there's a better method using "interrupts". Let me explain.

At any time on your PC you might hit a key on the keyboard, or move the mouse, or some data might arrive from the hard disk or the Internet. If it had to spend all its time watching for those things it'd never get anything else done! Instead, the hardware detects these events and when one of them happens it causes the processor to save where it's got to and what it's doing, execute a small piece of code called an Interrupt Service Routine (or ISR) to save away the incoming data and make a note to itself that there's some data to process when it's got a moment, then resume what it was doing.

The Arduino IDE provides functions for managing interrupts, but unfortunately not on the input that the Lightino sensor is connected to. So we have to rely on one or two magic incantations invented by someone who knows a lot more about the processor chip than even I do!

In your sketch...

Right at the beginning of the sketch we need to include the following line:

#include <avr/pgmspace.h>

This calls up definitions of the terms used in the magic incantations.

Somewhere before setup() we will need to declare some variables. These are only the sort of magic you're already used to.

// Sensor timings in microseconds

unsigned long curTime;    // Most recent sensor detection

unsigned long lastTime;   // Previous sensor detection time

unsigned long revTime;    // Time for last full rotation

unsigned long dwellTime;  // Time between LED changes (based on rotation speed)

int dwellDegrees = 4;     // Degrees of rotation between pixels

 

int revolutions;          // Total number of revolutions, in case we want to know

boolean flag = false;     // Set by the ISR

boolean overrun = false;  // Set if we didn't respond to flag being set soon enough

 

Some of these are declared as type unsigned long, which means they're stored as 32 bits rather than 16 bits, so allowing values up to around 4 billion. We know they'll never be negative so we don't need to waste a bit to say they're not.

Then in setup() we need to include these lines of deep magic:

 // Interrupt setting

 PCMSK0 = bit (PCINT6); //Use PCINT6 = PB6

 PCICR |= (1<<PCIE0);   //Enable Pin change interrupt

 sei();

This tells the chip to raise an interrupt whenever a change is detected on pin 10 (known to the chip as PB6), to which the sensor is connected.

More magic goes at the end of the sketch:

ISR(PCINT0_vect){  // Sensor input change detected

 if (!digitalRead(SENSOR)){

   overrun = flag; // If flag is still set from last time, indicate an over-run

   flag = true;

   revolutions += 1;

 }

}

This is the interrupt service routine, called whenever an interrupt is raised by a change in the sensor output. If the sensor now reads false it sets a flag for the code in loop() to act on, but first sets the boolean overrun if loop() hadn't got around to resetting the flag yet.

At the start of loop() we need to add:

 if (overrun) flag = false; // Last rotation over-ran? Wait for next

 if (flag){  

   flag=false;

   curTime = micros();

   revTime =  curTime - lastTime;

   lastTime = curTime;

   if (revTime < 20000 || revTime > 300000) revTime = 25000;

   dwellTime = (revTime * 3 / 360);

The function micros() returns the number of microseconds (millionths of a second) since the chip was last reset. This can be a big number, which is why we had to declare the variables we use here as type long.

First, we see if overrun is set, in which case we'd better wait another revolution and for flag to be set again.

Then we wait for flag to be set (remember, loop() is called repeatedly). We record the current time in microseconds and subtract the time last time we came here to get the time of a revolution. We check it's a sensible number, which it won't be on the first revolution. Finally, we calculate how long a LED must be lit to draw a single pixel, which we choose to be the time taken to spin through an angle of 3⁰.

Put it all together, compile it and upload it, and see if your hearts now stop spinning!

Continue to Lesson 4 by clicking here!

If you're stuck try reading the Help Sheet!

Previous article Comparing the BBC Micro:Bit V1 and V2, what is different?