Tuesday, June 21, 2011

Tutorial: Arduino timing methods with millis()

This is chapter thirty-seven of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – in what feels like an endless series of articles on the Arduino universe. The first chapter is here, the complete series is detailed here. Any files from tutorials will be found here.

Welcome back fellow arduidans!

In this article we introduce the millis(); function and put it to use to create various timing examples. Please ensure you are running version 13 or upwards of the Arduino IDE.

Millis? Nothing to do with lip-syncers… hopefully you recognised milli as being the numerical prefix for one-thousandths; that is multiplying a unit of measure by 0.001 (or ten to the power of negative 3). Interestingly our Arduino systems will count the number ofmilliseconds (thousands of a second) from the start of a sketch running until the count reaches the maximum number capable of being stored in the variable type unsigned long (a 32-bit [four byte] integer – that ranges from zero to (2^32)-1.

(2^32)-1, or 4294967295 milliseconds converts to 49.71027-odd days. The counter resets when the Arduino is reset, it reaches the maximum value or a new sketch is uploaded. To get the value of the counter at a particular juncture, just call the function – for example:

start=millis();

Where start is an unsigned long variable. Here is a very simple example (download sketch) to show you millis() in action:

Example 37.1

/* Example 37.1 - millis() demonstration http://tronixstuff.wordpress.com/tutorials > chapter 37 John Boxall | CC by-sa-nc */  unsigned long start, finished, elapsed;  void setup() { Serial.begin(9600); }  void loop() { Serial.println("Start..."); start=millis(); delay(1000); finished=millis(); Serial.println("Finished"); elapsed=finished-start; Serial.print(elapsed); Serial.println(" milliseconds elapsed"); Serial.println(); delay(500); }

The sketch stores the current millis count in start, then waits one second, then stores the value of millis again in finished. Finally it calculates the elapsed time of the delay.  In the following screen dump of the serial monitor, you can see that the duration was not always exactly 1000 milliseconds:

To put it simply, the millis function makes use of an internal counter within the ATmega microcontroller at the heart of your Arduino. This counter increments every clock cycle – which happens (in standard Arduino and compatibles) at a clock speed of 16 Mhz. This speed is controlled by the crystal on the Arduino board (the silver thing with T16.000 stamped on it):

Crystal accuracy can vary depending on external temperature, and the tolerance of the crystal itself. This in turn will affect the accuracy of your millis result. Anecdotal experience has reported the drift in timing accuracy can be around three or four seconds per twenty-four hour period. If you are using a board or your own version that is using a ceramic resonator instead of a crystal, note that they are not as accurate and will introduce the possibility of higher drift levels. If you need a much higher level of timing accuracy, consider specific timer ICs such as the Maxim DS3231.

Now we can make use of the millis  for various timing functions. As demonstrated in the previous example sketch, we can calculate elapsed time. To take this idea forward, let’s make a simple stopwatch. Doing so can be as simple or as complex as necessary, but for this case we will veer towards simple. On the hardware perspective, we will have two buttons – Start and Stop - with the 10k ohm pull-down resistors connected to digital pins 2 and 3 respectively.

When the user presses start the sketch will note the value for millis – then after stop is pressed, the sketch will again note the value formillis, calculate and display the elapsed time. The user can then press start to repeat the process, or stop for updated data. Here is the sketch (download):

Example 37.2

/* Example 37.2 – Super-basic stopwatch using millis(); http://tronixstuff.wordpress.com/tutorials > chapter 37 John Boxall | CC by-sa-nc */  unsigned long start, finished, elapsed;  void setup() { Serial.begin(9600); pinMode(2, INPUT); // start button pinMode(3, INPUT); // stop button Serial.println("Press 1 for Start/reset, 2 for elapsed time"); }  void displayResult() { float h,m,s,ms; unsigned long over; elapsed=finished-start; h=int(elapsed/3600000); over=elapsed%3600000; m=int(over/60000); over=over%60000; s=int(over/1000); ms=over%1000; Serial.print("Raw elapsed time: "); Serial.println(elapsed); Serial.print("Elapsed time: "); Serial.print(h,0); Serial.print("h "); Serial.print(m,0); Serial.print("m "); Serial.print(s,0); Serial.print("s "); Serial.print(ms,0); Serial.println("ms"); Serial.println(); }  void loop() { if (digitalRead(2)==HIGH) { start=millis(); delay(200); // for debounce Serial.println("Started..."); } if (digitalRead(3)==HIGH) { finished=millis(); delay(200); // for debounce displayResult(); } }

The calls to delay() are used to debounce the switches – these are optional and their use will depend on your hardware. Below is an example of the sketch’s serial monitor output – the stopwatch has started, and then button two pressed six times across periods of time:

If you had a sensor at the start and end of a fixed distance, speed could be calculated: speed = distance ÷ time.

You can also make a speedometer for a wheeled form of motion, for example a bicycle. At the present time I do not have a bicycle to mess about with, however we can describe the process to do so – it is quite simple. (Disclaimer – do so at your own risk etc.)  First of all, let’s review the necessary maths. You will need to know the circumference of the wheel taking into account the location of the sensor. (Sensor? For example – a reed switch and magnet. Consider the reed switch to be a normally-open button, and connect as usual with a 10k ohm pull-down resistor. Others may use a hall-effect sensor – each to their own). Remember from maths class:

(image licence)

To calculate the circumference – use the formula:

circumference = 2πr 

where r is the radius of the circle. However be careful when measuring your value for the radius. It should be the distance from the centre of the wheel to the sensor/magnet – not the edge of the wheel. Now that you have the wheel circumference, this value can be considered as our ‘fixed distance’, and therefore the speed can be calculated by measuring the elapsed time between of a full rotation.

Your sensor – once fitted – should act in the same method as a normally-open button that is pushed every rotation. Our sketch will measure the time elapsed between every pulse from the sensor. To do this, our example will have the sensor output connected to digital pin 2 – as it will trigger an interrupt to calculate the speed. (Interrupts? See chapter three). The sketch will otherwise be displaying the speed on a normal I2C-interface LCD module. The I2C interface is suggested as this requires only 4 wires from the Arduino board to the LCD – the less wires the better.

Here is the sketch for your perusal (download):

Example 37.3

/* Example 37.3 – Basic speedometer using millis(); http://tronixstuff.wordpress.com/tutorials > chapter 37 John Boxall | CC by-sa-nc */  #include "Wire.h" // for I2C bus LCD #include "LiquidCrystal_I2C.h" // for I2C bus LCD module - http://bit.ly/m7K5wt LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display  float start, finished; float elapsed, time; float circMetric=1.2; // wheel circumference relative to sensor position (in meters) float circImperial; // using 1 kilometer = 0.621371192 miles float speedk, speedm;    // holds calculated speed vales in metric and imperial  void setup() { attachInterrupt(0, speedCalc, HIGH); // interrupt called when sensors sends digital 2 high (every wheel rotation) start=millis(); // setup LCD lcd.init();      // initialize the lcd lcd.backlight(); // turn on LCD backlight lcd.clear(); lcd.println(" Wear a helmet! "); delay(3000); lcd.clear(); Serial.begin(115200); circImperial=circMetric*.62137; // convert metric to imperial for MPH calculations }  void speedCalc() { elapsed=millis()-start; start=millis(); speedk=(3600*circMetric)/elapsed; // km/h speedm=(3600*circImperial)/elapsed; // Miles per hour }  void loop() { lcd.setCursor(0,0); lcd.print(int(speedk)); lcd.print(" km/h "); lcd.print(int(speedm)); lcd.print(" MPH   "); lcd.setCursor(0,1); lcd.print(int(elapsed)); lcd.print(" ms/rev      "); delay(1000); // adjust for personal preference to minimise flicker }

There isn’t that much going on – every time the wheel completes one revolution the signal from the sensor will go from low to high – triggering an interrupt which calls the function speedCalc(). This takes a reading of millis() and then calculates the difference between the current reading and the previous reading – this value becomes the time to cover the distance (which is the circumference of the wheel relative to the sensor – stored in

float circMetric=1.2;

and is measured in metres). It finally calculates the speed in km/h and MPH. Between interrupts the sketch displays the updated speed data on the LCD as well as the raw time value for each revolution for curiosity’s sake. In real life I don’t think anyone would mount an LCD on a bicycle, perhaps an LED display would be more relevant. If you didn’t feel like making something from scratch, you could use a Digit Shield or something similar. In the meanwhile, you can see how this example works in the following short video clip. Instead of a bike wheel and reed switch/magnet combination, I have connected the square-wave output from a function generator to the interrupt pin to simulate the pulses from the sensor, so you can get an idea of how it works:

That just about sums up the use of millis() for the time being. There is also the micros(); function which counts microseconds.

So there you have it – another practical function that can allow more problems to be solved via the world of Arduino. As always, now it is up to you and your imagination to find something to control or get up to other shenanigans. If you have any suggestions with regards to parts for the next article, leave a comment below and we’ll look into it.  As always I hope you enjoyed reading this as much as I did writing it for you.

If you have any questions about the processes or details in this article, please ask in our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, there is the odd competition or give-away –  and we can all learn something. Or follow tronixstuff on twitter and facebook. High resolution images available on flickr.

Otherwise, have fun, stay safe, be good to each other – and make something!

Tuesday, June 14, 2011

Tutorial: Arduino and the SPI bus part II

This is chapter thirty-six of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A seemingly endless series of articles on the Arduino universe. The first chapter is here, the complete series is detailed here. Any files from tutorials will be found here.

Welcome back fellow arduidans!

This is the second of several chapters in which we are investigating the SPI data bus, and how we can control devices using it with our Arduino systems. If you have not done so already, please read part one of the SPI articles. Again we will learn the necessary theory, and then apply it by controlling a variety of devices. As always things will be kept as simple as possible.

First on our list today is the use of multiple SPI devices on the single bus. We briefly touched on this in part one, by showing how multiple devices are wired, for example:

Notice how the slave devices share the clock, MOSI and MISO lines – however they both have their own chip select line back to the master device. At this point a limitation of the SPI bus becomes prevalent – for each slave device we need another digital pin to control chip select for that device. If you were looking to control many devices, it would be better to consider finding I2C solutions to the problem. To implement multiple devices is very easy. Consider the example 34.1 from part one – we controlled a digital rheostat. Now we will repeat the example, but instead control four instead of one. For reference, here is the pinout diagram:

Doing so may sound complex, but it is not. We connect the SCK, MOSI and  MISO pins together, then to Arduino pins D13, D11, D12 respectively. Each CS pin is wired to a separate Arduino digital pin. In our example rheostats 1 to 4 connect to D10 through to D7 respectively. To show the resistance is changing on each rheostat, there is an LED between pin 5 and GND and a 470 ohm resistor between 5V and pin 6. Next, here is the sketch (download):

Example 36.1

/* Example 36.1 - Multiple SPI bus device demo using four Microchip MCP4162s [http://bit.ly/iwDmnd] http://tronixstuff.com/tutorials > chapter 36 | CC by-sa-nc | John Boxall */  #include "SPI.h" // necessary library int del=3; // used for various delays  int led1=10; // CS lines for each SPI device int led2=9; int led3=8; int led4=7;  void setup() { pinMode(led1, OUTPUT); pinMode(led2, OUTPUT); pinMode(led3, OUTPUT); pinMode(led4, OUTPUT); digitalWrite(led1, HIGH); digitalWrite(led2, HIGH); digitalWrite(led3, HIGH); digitalWrite(led4, HIGH); SPI.begin(); // wake up the SPI bus. SPI.setBitOrder(MSBFIRST); // our MCP4162s requires data to be sent MSB (most significant byte) first }  void setValue(int l, int value) // sends value 'value' to SPI device on CS digital out pin 'l' { digitalWrite(l, LOW); SPI.transfer(0); // send command byte SPI.transfer(value); // send value (0~255) digitalWrite(l, HIGH); }  void allOff() // sets all pots to max resistance { setValue(led1,255); setValue(led2,255); setValue(led3,255); setValue(led4,255); }  void pulse(int l) { allOff(); for (int a=255; a>=0; --a) { setValue(l,a); delay(del); } for (int a=0; a<256; a++) { setValue(l,a); delay(del); } }  void pulseAll() { allOff(); for (int a=255; a>=0; --a) { setValue(led1,a); setValue(led2,a); setValue(led3,a); setValue(led4,a); delay(del); } for (int a=0; a<256; a++) { setValue(led1,a); setValue(led2,a); setValue(led3,a); setValue(led4,a); delay(del); } }  void loop() { pulse(led1); pulse(led2); pulse(led3); pulse(led4); pulseAll(); }

Although the example sketch may be longer than necessary, it is quite simple. We have four SPI devices each controlling one LED, so to keep things easy to track we have defined led1~led4 to match the chip select digital out pins used for each SPI device. Then see the first four lines invoid setup(); these pins are set to output in order to function as required. Next – this is very important – we set the pins’ state to HIGH. You must do this to every chip select line! Otherwise more than one CS pins may be initially low in some instances and cause the first data sent from MOSI to travel along to two or more SPI devices. With LEDs this may not be an issue, but for motor controllers … well it could be.

The other point of interest is the function

void setValue(int l, int value)

We pass the value for the SPI device we want to control, and the value to send to the device. The value for l is the chip select value for the SPI device to control, and ranges from 10~7 – or as defined earlier, led1~4. The rest of the sketch is involved in controlling the LED’s brightness by varying the resistance of the rheostats. Now to see example 36.1 in action via the following video clip:


(If you are wondering what I have done to the Freetronics board in that video, it was to add a DS1307 real-time clock IC in the prototyping section).

Next on the agenda is a digital-to-analogue converter, to be referred to using the acronym DAC. What is a DAC? In simple terms, it accepts a numerical value between zero and a maximum value (digital) and outputs a voltage between the range of zero and a maximum relative to the input value (analogue). One could consider this to be the opposite of the what we use the function analogRead(); for. For our example we will use a Microchip MCP4921 (data sheet.pdf):

(Please note that this is a beginners’ tutorial and is somewhat simplified). This DAC has a 12-bit resolution. This means that it can accept a decimal number between 0 and 4095 – in binary this is 0 to 1111 1111 1111 (see why it is called 12-bit) – and the outpout voltage is divided into 4096 steps. The output voltage for this particular DAC can fall between 0 and just under the supply voltage (5V). So for each increase of 1 in the decimal input value, the DAC will output around 1.221 millivolts.

It is also possible to reduce the size of the voltage output steps by using a lower reference voltage. Then the DAC will consider the reference voltage to be the maximum output with a value of 4095. So (for example) if the reference voltage was 2.5V, each increase of 1 in the decimal input value, the DAC will output around 0.6105 millivolts. The minimum reference voltage possible is 0.8V, which offers a step of 200 microvolts (uV).

The output of a DAC can be used for many things, such as a function generator or the playback of audio recorded in a digital form. (For an excellent example of this, check out the adafruit waveshield). For now we will examine how to use the hardware, and monitoring output on an oscilloscope. First we need the pinouts:

By now these sorts of diagrams shouldn’t present any problems. In this example, we keep pin 5 permanently set to GND; pin 6 is where you feed in the reference voltage – we will set this to +5V; AVss is GND; and Vouta is the output signal pin – where the magic comes from :) The next thing to investigate is the MCP4921′s write command register:

Bits 0 to 11 are the 12 bits of the output value; bit 15 is an output selector (unused on the MPC4921); bit 14 controls the input buffer; bit 13 controls an inbuilt output amplifier; and bit 12 can shutdown the DAC. Unlike previous devices, the input data is spread across two bytes (or aword of data). Therefore a small amount of work needs to be done to format the data ready for the DAC. Let’s explain this through looking at the sketch for example 36.2 that follows. The purpose of the sketch is to go through all possible DAC values, from 0 to 4095, then back to 0 and so on.

First. note the variable outputvalue - it is a word, a 16-bit unsigned variable. This is perfect as we will be sending a word of data to the DAC. We put the increasing/decreasing value for a into outputValue. However as we can only send bytes of data at a time down the SPI bus, we will use the function highbyte() to separate the high side of the word (bits 15~8) into a byte variable called data.

We then use the bitwise AND and OR operators to set the parameter bits 15~12. Then this byte is sent to the SPI bus. Finally, the functionlowbyte() is used to send the low side of the word (bits 7~0) into data and thence down the SPI bus as well.

Now for our demonstration sketch (download):

Example 36.2

/* Example 36.2 - SPI bus device demo using a Microchip MCP4921 DAC [http://bit.ly/j3TSak] http://tronixstuff.com/tutorials > chapter 36 | CC by-sa-nc | John Boxall */  #include "SPI.h" // necessary library int del=0; // used for various delays word outputValue = 0; // a word is a 16-bit number byte data = 0; // and a byte is an 8-bit number void setup() { //set pin(s) to input and output pinMode(10, OUTPUT); SPI.begin(); // wake up the SPI bus. SPI.setBitOrder(MSBFIRST); }  void loop() { for (int a=0; a<=4095; a++) { outputValue = a; digitalWrite(10, LOW); data = highByte(outputValue); data = 0b00001111 & data; data = 0b00110000 | data; SPI.transfer(data); data = lowByte(outputValue); SPI.transfer(data); digitalWrite(10, HIGH); delay(del); } delay(del+25); for (int a=4095; a>=0; --a) { outputValue = a; digitalWrite(10, LOW); data = highByte(outputValue); data = 0b00001111 & data; data = 0b00110000 | data; SPI.transfer(data); data = lowByte(outputValue); SPI.transfer(data); digitalWrite(10, HIGH); delay(del); } delay(del+25); }

And a quick look at the DAC in action via an oscilloscope:

By now we have covered in detail how to send data to a device on the SPI bus. But how do we receive data from a device?

Doing so is quite simple, but some information is required about the particular device. For the rest of this chapter, we will use the Maxim DS3234 ”extremely accurate” real-time clock. Please download the data sheet (.pdf) now, as it will be referred to many times.

The DS3234 is not available in through-hole packaging, so we will be using one that comes pre-soldered onto a very convenient breakout board:

It only takes a few moments to solder in some header pins for breadboard use. The battery type is CR1220 (12 x 2.0mm, 3V); if you don’t have a battery you will need to short out the battery holder with some wire otherwise the IC will not work.

A device will have one or more registers where information is read from and written to. Look at page twelve of the DS3234 data sheet, there are twenty-three registers, each containing eight bits (one byte) of data. Please take note that each register has a read and write address. An example – to retrieve the contents of the register at location 08h (alarm minutes) and place it into the byte data we need to do the following:

digitalWrite(10, LOW); // select the DS3234 that has its CS line on digital 10 SPI.transfer(0x08); // tell the DS3234 device we're requesting data from the register at 08h data=SPI.transfer(0); // the DS3234 sends the data back and stores it in the byte data digitalWrite(10, HIGH);  // deselect the DS3234 if finished with it

Don’t forget to take note of  the function SPI.setBitOrder(MSBFIRST); in your sketch, as this also determines the bit order of the data coming from the device.

To write data to a specific address is also quite simple, for example:

digitalWrite(10, LOW); SPI.transfer(0x80); // tells the device which address to write to SPI.transfer(b00001010);   // you can send any representation of a byte digitalWrite(10, HIGH);

Up to this point, we have not concerned ourselves with what is called the SPI data mode. The mode determines how the SPI device interprets the ‘pulses’ of data going in and out of the device. For a well-defined explanation, please read this article. With some devices (and in our forthcoming example) the data mode needs to be defined. So we use:

SPI.setDataMode(SPI_MODE1);

to set the data mode, within void(setup);. To determine a device’s data mode, as always – consult the data sheet. With our DS3234 example, the mode is mentioned on page 1 under Features List.

Finally, let’s delve a little deeper into SPI via the DS3234. The interesting people at Sparkfun have already written a good demonstration sketch for the DS3234, so let’s have a look at that and deconstruct it a little to see what is going on. You can download the sketch below from here, then change the file extension from .c to .pde.

#include const int  cs=8; //chip select   void setup() { Serial.begin(9600); RTC_init(); //day(1-31), month(1-12), year(0-99), hour(0-23), minute(0-59), second(0-59) SetTimeDate(11,12,13,14,15,16); }  void loop() { Serial.println(ReadTimeDate()); delay(1000); } //===================================== int RTC_init(){ pinMode(cs,OUTPUT); // chip select // start the SPI library: SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE1); // both mode 1 & 3 should work //set control register digitalWrite(cs, LOW); SPI.transfer(0x8E); SPI.transfer(0x60); //60= disable Osciallator and Battery SQ wave @1hz, temp compensation, Alarms disabled digitalWrite(cs, HIGH); delay(10); } //===================================== int SetTimeDate(int d, int mo, int y, int h, int mi, int s){ int TimeDate [7]={s,mi,h,0,d,mo,y}; for(int i=0; i<=6;i++){ if(i==3) i++; int b= TimeDate[i]/10; int a= TimeDate[i]-b*10; if(i==2){ if (b==2) b=B00000010; else if (b==1) b=B00000001; } TimeDate[i]= a+(b<<4);  digitalWrite(cs, LOW); SPI.transfer(i+0x80); SPI.transfer(TimeDate[i]); digitalWrite(cs, HIGH); } } //===================================== String ReadTimeDate(){ String temp; int TimeDate [7]; //second,minute,hour,null,day,month,year for(int i=0; i<=6;i++){ if(i==3) i++; digitalWrite(cs, LOW); SPI.transfer(i+0x00); unsigned int n = SPI.transfer(0x00); digitalWrite(cs, HIGH); int a=n & B00001111; if(i==2){ int b=(n & B00110000)>>4; //24 hour mode if(b==B00000010) b=20; else if(b==B00000001) b=10; TimeDate[i]=a+b; } else if(i==4){ int b=(n & B00110000)>>4; TimeDate[i]=a+b*10; } else if(i==5){ int b=(n & B00010000)>>4; TimeDate[i]=a+b*10; } else if(i==6){ int b=(n & B11110000)>>4; TimeDate[i]=a+b*10; } else{ int b=(n & B01110000)>>4; TimeDate[i]=a+b*10; } } temp.concat(TimeDate[4]); temp.concat("/") ; temp.concat(TimeDate[5]); temp.concat("/") ; temp.concat(TimeDate[6]); temp.concat("     ") ; temp.concat(TimeDate[2]); temp.concat(":") ; temp.concat(TimeDate[1]); temp.concat(":") ; temp.concat(TimeDate[0]); return(temp); }

Don’t let the use of custom functions and loops put you off, they are there to save time. Looking in the function SetTimeDate();, you can see that the data is written to the registers 80h through to 86h (skipping 83h – day of week) in the way as described earlier (set CS low, send out address to write to, send out data, set CS high). You will also notice some bitwise arithmetic going on as well. This is done to convert data between binary-coded decimal and decimal numbers.

Why? Go back to page twelve of the DS3234 data sheet and look at (e.g.) register 00h/80h – seconds. The bits 7~4 are used to represent the ‘tens’ column of the value, and bits 3~0 represent the ‘ones’ column of the value. So some bit shifting is necessary to isolate the digit for each column in order to convert the data to decimal. For other ways to convert between BCD and decimal, see the examples using the Maxim DS1307 in chapter seven.

So there you have it – more about the world of the SPI bus and how to control the devices within. As always, now it is up to you and your imagination to find something to control or get up to other shenanigans. If you have any suggestions with regards to parts for the next article, leave a comment below and we’ll look into it.  As always I hope you enjoyed reading this as much as I did writing it for you.

If you have any questions about the processes or details in this article, please ask in our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, there is the odd competition or give-away –  and we can all learn something. Or follow tronixstuff on twitter and facebook. High resolution images available on flickr. Thanks to Sparkfun for their DS3234 demonstration sketch.

Otherwise, have fun, stay safe, be good to each other – and make something!