Review: Gooligum Electronics PIC Training Course and Development Board
Introduction
[Updated 18/06/2013]
There are many types of microcontrollers on the market, and it would be fair to say one of the two most popular types is the Microchip PIC series. The PICs are great as there is a huge range of microcontrollers available across a broad range of prices. However learning how to get started with the PIC platform isn’t exactly simple. Not that we expect it to be, however a soft start is always better. There are some older books, however they can cost more than $100 – and are generally outdated. So where do you start?
It is with this problem in mind that led fellow Australian David Meiklejohn to develop and offer his PIC Training Course and Development Board to the marketplace via his company Gooligum Electronics.
In his words:
There is plenty of material available on PICs, which can make it daunting to get started. And some of the available material is dated, originally developed before modern “flash” PICs were available, or based on older devices that are no longer the best choice for new designs. Our approach is to introduce PIC programming and design in easy stages, based on a solid grounding in theory, creating a set of building blocks and techniques and giving you the confidence to draw on as we move up to more complex designs.
So in this article we’ll examine David’s course package. First of all, let’s look at the development board and inclusions. Almost everything you will need to complete all the lessons is included in the package, including the following PIC microcontrollers:
You can choose to purchase the board in kit form or pre-assembled. If you enjoy soldering, save the money and get the kit – it’s simple to assemble and a nice way to spend a few hours with a soldering iron.
Although the board includes all the electronic components and PICs – you will need are a computer capable of running Microchip MPLAB software, a Microchip PICkit3 (or -2) programming device and an IC extractor. If you’re building the kit, a typical soldering iron and so on will be required. Being the ultra-paranoid type, I bought a couple extra of each PIC to have as spares, however none were damaged in my experimenting. Just use common-sense when handling the PICs and you will be fine.
Assembly
Putting the kit board together wasn’t difficult at all. There isn’t any surface-mount parts to worry about, and the PCB is silk-screened very well:
The rest of the parts are shipped in antistatic bags, appropriately labelled and protected:
Assembly was straight forward, just start with the low-profile parts and work your way up. The assembly guide is useful to help with component placement. After working at a normal pace, it was ready in just over an hour:
The Hardware
Once assembled (or you’ve opened the packaging) the various sections of the board are obvious and clearly labelled – as they should be for an educational board. You will notice a large amount of jumper headers – they are required to bridge in and out various LEDs, select various input methods and so on. A large amount of jumper shunts is included with the board.
It might appear a little disconcerting at first, but all is revealed and explained as you progress through the lessons. The board has decent rubber feet, and is powered either by the PICkit3 programmer, or a regulated DC power source between 5 and 6V DC, such as from a plug-pack if you want to operate your board away from a PC.
However there is a wide range of functions, input and output devices on the board – and an adjustable oscillator, as shown in the following diagram:
The Lessons
There is some assumed knowledge, which is a reasonable understanding of basic electronics, some computer and mathematical savvy and the C programming language.
You can view the first group of lessons for free on the kit website, and these are included along with the additional lessons in the included CDROM. They’re in .pdf format and easy to read. The CDROM also includes all the code so you don’t have to transcribe it from the lessons. Students start with an absolute introduction to the system, and first learn how to program in assembly language in the first group of tutorials, followed by C in the second set.
This is great as you learn about the microcontroller itself, and basically start from the bottom. Although it’s no secret I enjoy using the Arduino system – it really does hide a lot of the actual hardware knowledge away from the end user which won’t be learned. With David’s system – you will learn.
If you scroll down to the bottom of this page, you can review the tutorial summaries. Finally here’s a quick demonstration of the 7-segment displays in action:
Update – 18/06/2013
David has continued publishing more tutorials for his customers every few months – including such topics as the EEPROM and pulse-width modulation. As part of the expanded lessons you can also get a pack which allows experimenting with electric motors that includes a small DC motor, the TI SN75441 h-bridge IC, N-channel and P-channel MOSFETS and more:
So after the initial purchase, you won’t be left on your own. Kudos to David for continuing to support and develop more material for his customers.
Where to from here?
Once you run through all the tutorials, and feel confident with your knowledge, the world of Microchip PIC will be open to you. Plus you now have a great development board for prototyping with 6 to 14-pin PIC microcontrollers. Don’t forget all the pins are brought out to the row of sockets next to the solderless breadboard, so general prototyping is a breeze.
Conclusion
For those who have mastered basic electronics, and have some C or C-like programming experience from using other development environments or PCs – this package is perfect for getting started with the Microchip PIC environment. Plus you’ll learn about assembly language – which is a good thing. I genuinely recommend this to anyone who wants to learn about PIC and/or move into more advanced microcontroller work. And as the entire package is cheaper than some books – you can’t go wrong. The training course is available directly from the Gooligum website.
Disclaimer - The Baseline and Mid-Range PIC Training Course and Development Board was a promotional consideration from Gooligum Electronics.
In the meanwhile have fun and keep checking into tronixstuff.com. Why not follow things on twitter, Google+, subscribe for email updates or RSS using the links on the right-hand column? And join our friendly Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other – and we can all learn something.
Review – Digilent chipKIT Uno32
In this review we consider a Digilent chipKIT Uno32 development board made available by element14.
Introduction
This is a development board that is based on the Arduino Uno, however uses a Microchip PIC32MX320F128 microcontroller instead of the Atmel ATmega328 we are used to:
Digilent’s decision to use the PIC32 introduces some interesting changes to the Uno format, and the largest change to take note of is the clock speed – 80 MHz instead of the Uno’s 16 MHz. That certainly took my attention, and we can see this demonstrated shortly.
When shipped the board arrives alone in a cardboard box, without a USB cable:
All documentation is found on the Digilent website. There is also a support forum to discuss libraries, IDE updates and so on. The board itself is quite familiar upon initial inspection:
You can see that Arduino shield will physically fit onto the board, and the extra I/O pins are accessed through the second rows of jumpers inside the board. With some crafty PCB creation skills you could make your own Uno32 shields, or consider one of the boards available from element14 or Digilent.
As for the other specifications of the Uno32:
- Clock speed – 80 MHz
- 128K flash program memory
- 16K SRAM data memory
- I/O pins – 42 (12 used as analogue inputs or digital I/O)
- Five PWM pins
- FTDI chip for USB interface
- Two user LEDs
- Same form factor as Arduino Uno boards, which allows physical shield compatibility
- Five interrupt pins
- On board real-time clock (external crystal required)
You will need a new IDE, and you can download Uno32-modified versions of the Arduino v22 and v23 IDE from here for Windows, MacOS and 32-bit Linux (no 64-bit…). The bootloader is preinstalled on the Uno32 and after installing the special IDE it works just as our normal Arduinos do in terms of editing and uploading sketches. The board also is compatible with the Microchip MPLAB IDE and PICkit3 in-circuit debugger if you want to use the Uno32 as a normal PIC32 development board. There is a row of holes between the USB socket and the DC socket that will need header pins soldered in for PICkit3 use.
Naturally you want to see the speed test. The following sketch was run on an Arduino Uno and the Uno32 boards using IDE v1.0 for the Uno and the MPIDE v23 for the Uno32:
void setup()
{
Serial.begin(9600);
}
unsigned long a,b,c,d,e;
void loop()
{
a=millis();
for (c=1; c<1000000; c++)
{
d=sq(c);
}
b=millis();
e=b-a;
Serial.print("Duration: ");
Serial.print(e);
Serial.println("ms");
do {} while (1>0);
}
And here are the results of running the sketch four times on each board (click image to enlarge):
Well that’s pretty impressive – over sixty times faster than the Arduino Uno. Therein lies the major reason to use this board over the Uno. The eagle-eyed among you may have also noticed the difference in the compiled binary sketch size – 6432 bytes for the Uno32 vs. 2540 bytes for the Arduino Uno. That’s interesting.
Nevertheless there are many things to take note of when moving from Arduino to Uno32, or in other words – you can’t just swap out an Arduino Uno for an Uno32, recompile and run your sketch at the faster speed. The Microchip PIC32 is very much a different beast to the Atmel AVRs we’re used to, so it is important that you understand the differences in hardware and software to take advantage of the Uno32. So let’s run through those now.
Power Differences
The Uno32 is a 3.3V board due to the PIC32. You can still power it via USB, or connect between 7~15 VDC to the power socket on the board. You can change a jumper and feed 5V directly into the board bypassing the 5V regulator. External power is regulated to 5V then to 3.3V. From a total of 1A current, the PIC32 uses 75mA, so you can draw up to 925mA from the 5V bus or 425mA from the 3.3V bus (or a mixture from both). It would pay to determine your current load before testing to avoid damaging the board, however the manual notes that the regulators will become hot at high current loads but do have thermal protection. Finally there is also a jumper that chooses between a 5V or 3.3V voltage feed to the shields. As always, consult the manual first.
I/O Differences
Although the PIC32 being a 3.3V part, the manual states that the digital I/O pins are 5V tolerant, so applying 5V to a digital input won’t damage the PIC32. Logic on the other hand is a different kettle of fish. According to the manual a digital ‘high’ when sourcing 12mA of current will only reach close to 3.3V. This may be too low in some situations so check your threshold voltages when choosing external parts. Furthermore, the analogue reference voltage (AREF) is restricted to 3.3V.
One stand-out difference is that you can only source 18mA from a digital pin, which is OK if you’re blinking some LEDs. However for logic output to keep the voltage range below 0.4V for ‘low’ and above 2.4V for ‘high’ the current must be restricted to -12~+7mA – another different limitaion. Finally, the maximum current you can source over all the I/O pins at once is 200mA.
There are two UARTs, number one where we expect it (D0/D1) and another on pins 39 and 40. I2C is on A4/A5 but needs to be activated with a jumper. Note that unlike an Arduino there aren’t any inbuilt pull-up resistors for the I2C bus, so add your own. There is also an SPI bus at the usual position (D10~13) and interestingly you can change the board between SPI master and slave via another set of jumpers. There are five pulse-width modulation outputs, however one is on D10 which is also part of the SPI bus. Finally there are five hardware interrupt pins.
Shield Compatibility
Arduino shields will physically fit onto the Uno32 – but you need to be aware of the I/O differences listed above, the voltage and current specification and also the software side of things. Again – do your research before making the commitment to the hardware.
Software Compatibility
The Uno32 is compatible with a variety of Arduino sketches, but not all. This in a large part is due to the libraries which will need to be sourced from the community or rewritten yourself if not provided with the MPIDE software. There is a community on the support forum which is contributing their own, such as the real-time clock library – but again, research needs to be done before use. When trying to use an existing Arduino sketch and hardware, you will need to spend some time checking for compatibility. Again – it’s much easier to design a new project around the Uno32 than rejig an existing one.
Open Source?
One of the things many people love about the Arduino ecosystem is that the entire system is open source hardware and software. Without causing a pro/con argument about software licensing you should note that not all of the software toolchain for the Uno32 is open, nor the USB or TCP/IP stack. There is some interesting discourse about this here.
Conclusion
A lot of work needs to be done to ensure compatibility with existing Arduino applications. The Uno32 is tempting due to the raw clock-speed increase, however the sketch/library and hardware differences may introduce a few road blocks. However, when designing a project from scratch and understand the licensing limitations, the Uno32 would be great as you know what you have to work with – a much faster board with much more I/O. And it is very inexpensive, less than ~$35. You can order your new Uno32 from element14.
Finally, if you’re looking for a very inexpensive PIC32 development board to use with Microchip MPLAB, the Uno32 is a great deal that can possibly interface with a wide variety of shields from the Arduino world.
Disclaimer – The Chipkit Uno32 board reviewed in this article was a promotional consideration made available by element14.
Have fun and keep checking into tronixstuff.com. Why not follow things on twitter, Google+, subscribe for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other – and we can all learn something.
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.
[Updated 10/01/2013]
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 - 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 in void 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 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 a word 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 function lowbyte() 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 - 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. Readers have reported that the IC doesn’t keep time if the USB and external power are both applied to the Arduino at the same time.
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 "SPI.h"
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.
Finally here is another example of reading the time data from the DS3234 (download):
/* Example 36.3 - SPI bus device demo using a Maxim IC DS3234 Accurate RTC http://tronixstuff.com/tutorials > chapter 36 | CC by-sa-nc | John Boxall */
#include "SPI.h" // necessary library // store hours, minutes, seconds, day of month, month, year byte h,m,s,d,mo,y;
void setup()
{
pinMode(10, OUTPUT);
SPI.begin(); // wake up the SPI bus.
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE1);
digitalWrite(10, LOW);
SPI.transfer(0x8E); // write to control register 8Eh (page 14 data sheet)
SPI.transfer(0x60); // oscillator on, 1Hz, alarms off (b01100000)
digitalWrite(10, HIGH);
Serial.begin(9600);
}
void readDS3234()
{
byte data=0;
int a,b=0;
digitalWrite(10, LOW);
// get seconds
SPI.transfer(0x00); // get seconds data from register 00h
data=SPI.transfer(0x00);
a = data & B00001111; // isolates 1's column
b = (data & B01110000)>>4; // isolates 10's digit
s=(b*10)+a; // calculate seconds!
// get minutes
SPI.transfer(0x01); // get minutes data from register 00h
data=SPI.transfer(0x00);
a = data & B00001111; // isolates 1's column
b = (data & B01110000)>>4; // isolates 10's digit
m=(b*10)+a; // calculate minutes!
// get hours
SPI.transfer(0x02); // get minutes data from register 00h
data=SPI.transfer(0x00);
a = data & B00001111; // isolates 1's column
b = (data & B00110000)>>4; // isolate upper nibble
if (b==B00000010) // 24 hr time
{
b=20;
} else
if (b==B00000001) // 12 hr time
{
b=10;
}
h = a + b; // calculate hours
digitalWrite(10, HIGH);
}
void loop()
{
readDS3234();
Serial.print(h, DEC);
Serial.print(":");
if (m<10)
{
Serial.print("0");
}
Serial.print(m, DEC);
Serial.print(":");
if (s<10)
{
Serial.print("0");
}
Serial.print(s, DEC);
Serial.println();
delay(500);
}
So there you have it – more about the world of the SPI bus and how to control the devices within.
In the meanwhile have fun and keep checking into tronixstuff.com. Why not follow things on twitter, Google+, subscribe for email updates or RSS using the links on the right-hand column? And join our friendly Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other – and we can all learn something.
Tutorial: Arduino and the I2C bus – Part One
This is part one of several tutorials on how to use the I2C bus with Arduino, and chapter twenty of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A tutorial on the Arduino universe. The first chapter is here, the complete series is detailed here.
[Updated 10/01/2013]
In this first of several tutorials we are going to investigate the I2C data bus, and how we can control devices using it with our Arduino systems. The I2C bus can be a complex interface to master, so I will do my best to simplify it for you. In this article we will learn the necessary theory, and then apply it by controlling a variety of devices. Furthermore it would be in your interest to have an understanding of the binary, binary-coded decimal and hexadecimal number systems.
But first of all, what is it?
I2C is an acronym for “Inter-Integrated Circuit”. In the late 1970s, Philips’ semiconductor division (now NXP) saw the need for simplifying and standardising the data lines that travel between various integrated circuits in their products. Their solution was the I2C bus. This reduced the number of wires to two (SDA – data, and SCL – clock). Here is a nice introductory video from NXP:
Why would we want to use I2C devices?
As there are literally thousands of components that use the I2C interface! And our Arduino boards can control them all. There are many applications, such a real-time clocks, digital potentiometers, temperature sensors, digital compasses, memory chips, FM radio circuits, I/O expanders, LCD controllers, amplifiers, and so on. And you can have more than one on the bus at any time, in fact the maximum number of I2C devices used at any one time is 112.
From a hardware perspective, the wiring is very easy. Those of you with an Arduino Duemilanove, Uno or 100% compatible board, you will be using pins A4 for SDA (data) and A5 for SCL (clock).
If you are using an Arduino Mega, SDA is pin 20 and SCL is 21, so note that shields with I2C need to be specifically for the Mega. If you have another type of board, check your data sheet or try the Arduino team’s hardware website. And finally, if you are using a bare DIP ATmega328-PU microcontroller, you will use pins 27 for SDA and 28 for SCL.
The bus wiring is simple:

If you are only using one I2C device, the pull-up resistors are (normally) not required, as the ATmega328 microcontroller in our Arduino has them built-in. However if you are running a string of devices, use two 10 kilo ohm resistors. Like anything, some testing on a breadboard or prototype circuit will determine their necessity. Sometimes you may see in a particular device’s data sheet the use of different value pull-up resistors – for example 4.7k ohm. If so, heed that advice. The maximum length of an I2C bus is around one metre, and is a function of the capacitance of the bus. This distance can be extended with the use of a special IC, which we will examine during the next I2C chapter.
Each device can be connected to the bus in any order, and devices can be masters or slaves. In our Arduino situation, the board is the master and the devices on the I2C bus are the slaves. We can write data to a device, or read data from a device. By now you should be thinking “how do we differentiate each device on the bus?”… Each device has a unique address. We use that address in the functions described later on to direct our read or write requests to the correct device. It is possible to use two devices with identical addresses on an I2C bus, but that will be discussed in a later article.
As like most devices, we make use of an Arduino library, in this case <wire.h>. Then use the function Wire.begin(); inside of void setup() and we’re ready to go.
Sending data from our Arduino to the I2C devices requires two things: the unique device address (we need this in hexadecimal) and at least one byte of data to send. For example, the address of the part in example 20.1 (below) is 00101111 (binary) which is 0X2F in hexadecimal. Then we want to set the wiper value, which is a value between 0 and 127, or 0×00 and 0x7F in hexadecimal. So to set the wiper to zero, we would use the following three functions:
Wire.beginTransmission(0x2F); // part address is 0x2F or 0101111b
This sends the device address down the SDA (data) line of the bus. It travels along the bus, and “notifies” the matching device that it has some data coming…
Wire.write(69); // sends 69 down the bus
This sends the byte of data to the device – into the device register (or memory of sorts), which is waiting for it with open arms. Any other devices on the bus will ignore this. Note that you can only perform one I2C operation at a time! Then when we have finished sending data to the device, we “end transmission”. This tells the device that we’re finished, and frees up the I2C bus for the next operation:
Wire.endTransmission();
Some devices may have more than one register, and require more bytes of data in each transmission. For example, the DS1307 real-time clock IC has eight registers to store timing data, each requiring eight bits of data (one byte):
However with the DS1307 - the entire lot need to be rewritten every time. So in this case we would use eight wire.send(); functions every time. Each device will interpret the byte of data sent to it, so you need the data sheet for your device to understand how to use it.
Receiving data from an I2C device into our Arduino requires two things: the unique device address (we need this in hexadecimal) and the number of bytes of data to accept from the device. Receiving data at this point is a two stage process. If you review the table above from the DS1307 data sheet, note that there is eight registers, or bytes of data in there. The first thing we need to do is have the I2C device start reading from the first register, which is done by sending a zero to the device:
Wire.beginTransmission(device_address);
Wire.write(0);
Wire.endTransmission();
Now the I2C device will send data from the first register when requested. We now need to ask the device for the data, and how many bytes we want. For example, if a device held three bytes of data, we would ask for three, and store each byte in its own variable (for example, we have three variables of type byte: a, b, and c. The first function to execute is:
Wire.requestFrom(device_address, 3);
Which tells the device to send three bytes of data back to the Arduino. We then immediately follow this with:
*a = Wire.read();
*b = Wire.read();
*c = Wire.read();
We do not need to use Wire.endTransmission() when reading data. Now that the requested data is in their respective variables, you can treat them like any ordinary byte variable.
For a more detailed explanation of the I2C bus, read this explanatory document by NXP. Now let’s use our I2C knowledge by controlling a range of devices…
Example 20.1
A new part for today, the Microchip MCP4018T digital linear potentiometer. The value of this model is 10 kilo ohms. Inside this tiny, tiny SMD part is a resistor array consisting of 127 elements and a wiper that we control by sending a value of between 0 and 127 (in hexadecimal) down the I2C bus. This is a volatile digital potentiometer, it forgets the wiper position when the power is removed. However naturally there is a compromise with using such a small part, it is only rated for 2.5 milliamps – but used in conjunction with op amps and so on. For more information, please consult the data sheet.
As this is an SMD part, for breadboard prototyping purposes it needed to be mounted on a breakout board. Here it is in raw form:
Above the IC is a breakout board. Consider that the graph paper is 5mm square! It is the incorrect size, but all I have. However soldering was bearable. Put a drop of solder on one pad of the breakout board, then hold the IC with tweezers in one hand, and reheat the solder with the other hand – then push the IC into place. A few more tiny blobs of solder over the remaining pins, and remove the excess with solder wick. Well … it worked for me:
Our example schematic is as follows:
As you can see, the part is simple to use, your signal enters pin 6 and the result of the voltage division is found on pin 5. Please note that this is not a replacement for a typical mechanical potentiometer, we can’t just hook this up as a volume or motor-speed control! Again, please read the data sheet.
Control is very simple, we only need to send one byte of data down, the hexadecimal reference point for the wiper, e.g.:
Wire.beginTransmission(0x2F); // part address is 0x2F or 0101111b
Wire.write(0x3F); //
Wire.endTransmission();
Here is a quick demonstration that moves the wiper across all points: (download)
/* Example 20.1 Microchip MCP4018 digital potentiometer demonstration sketch http://tronixstuff.com/tutorials > chapter 20 CC by-sa v3.0 */
int dt = 2000; // used for delay duration byte rval = 0x00; // used for value sent to potentiometer
#include "Wire.h" #define pot_address 0x2F // each I2C object has a unique bus address, the MCP4018 is 0x2F or 0101111 in binary
void setup()
{
Wire.begin();
Serial.begin(9600);
}
void potLoop()
// sends values of 0x00 to 0x7F to pot in order to change the resistance
// equates to 0~127
{
for (rval=0; rval<128; rval++)
{
Wire.beginTransmission(pot_address);
Wire.write(rval); //
Wire.endTransmission();
Serial.print(" sent - ");
Serial.println(rval, HEX);
delay(dt);
}
}
void loop()
{
potLoop();
}
and a video demonstration:
Example 20.2
Now we will read some data from an I2C device. Our test subject is the ST Microelectronics CN75 temperature sensor. Again, we have another SMD component, but the CN75 is the next stage larger than the part from example 20.1. Thankfully this makes the soldering process much easier, however still requiring some delicate handiwork:
First, a small blob of solder, then slide the IC into it. Once that has cooled, you can complete the rest and solder the header pins into the breakout board:
Our example schematic is as follows:
Pins 5, 6 and 7 determine the final three bits of the device address – in this case they are all set to GND, which sets the address to 1001000. This allows you to use multiple sensors on the same bus. Pin 3 is not used for basic temperature use, however it is an output for the thermostat functions, which we will examine in the next chapter.
As a thermometer it can return temperatures down to the nearest half of a degree Celsius. Although that may not be accurate enough, it was designed for automotive and thermostat use. For more details please read the data sheet. The CN75 stores the temperature data in two bytes, let’s call them A and B. So we use
Wire.requestFrom(cn75address, 2)
with the second paramater as 2, as we want two bytes of data. Which we then store using the following functions:
*a = Wire.read(); // first received byte stored here
*b = Wire.read(); // second received byte stored here
where *a and *b are variables of the type byte.
And as always, there is a twist to decoding the temperature from these bytes. Here are two example pieces of sample data:
Example bytes one: 00011001 10000000
Example bytes two: 11100111 00000000
The bits in each byte note particular values… the most significant bit (leftmost) of byte A determines whether it is below or above zero degrees – 1 for below zero. The remaining seven bits are the binary representation of the integer part of the temperature; if it is below zero, we subtract 128 from the value of the whole byte and multiply by -1. The most significant bit of byte B determines the fraction, either zero or half a degree. So as you will see in the following example sketch (download), there is some decision making done in showCN75data():
/* Example 20.2 ST Microelectronics CN75 Digital Temperature sensor demonstration sketch CC by-sa v3.0 */
#include "Wire.h" #define cn75address 0x48 // with pins 5~7 set to GND, the device address is 0x48
void setup()
{
Wire.begin(); // wake up I2C bus
Serial.begin(9600);
}
void getCN75data(byte *a, byte *b)
{
// move the register pointer back to the first register
Wire.beginTransmission(cn75address); // "Hey, CN75 @ 0x48! Message for you"
Wire.write(0); // "move your register pointer back to 00h"
Wire.endTransmission(); // "Thanks, goodbye..."
// now get the data from the CN75 Wire.requestFrom(cn75address, 2); // "Hey, CN75 @ 0x48 - please send me the contents of your first two registers" *a = Wire.read(); // first received byte stored here *b = Wire.read(); // second received byte stored here }
void showCN75data()
{
byte aa,bb;
float temperature=0;
getCN75data(&aa,&bb);
if (aa>127) // check for below zero degrees
{
temperature=((aa-128)*-1);
if (bb==128) // check for 0.5 fraction
{
temperature-=0.5;
}
}
else // it must be above zero degrees
{
temperature=aa;
if (bb==128) // check for 0.5 fraction
{
temperature+=0.5;
}
}
Serial.print("Temperature = ");
Serial.print(temperature,1);
Serial.println(" degrees C");
delay(1000);
}
void loop()
{
showCN75data();
}
And here is the result from the serial monitor:
Example 20.3
Now that we know how to read and write data to devices on the I2C bus – here is an example of doing both, with a very popular device – the Maxim DS1307 real-time clock IC. Before moving on, consider reading their good data sheet. For those of you new to the world of tronixstuff, we use this part quite often, for example with our Arduino RTC shield and modifications, or blinky – the one-eyed clock. It is an 8-pin DIP IC that allows timing with accuracy down to a few seconds a day:

Furthermore, it also has a programmable square-wave generator. Connection and use is quite simple:
However some external components are required: a 32.768 kHz crystal, a 3V battery for time retention when the power is off, and a 10k ohm pullup resistor is required if using as a square-wave generator, and 10k ohm pull-up resistors on the SCL and SDA lines. You can use the SQW and timing simultaneously. If we have a more detailed look at the register map for the DS1307:
We see that the first seven registers are for timing data, the eighth is the square-wave control, and then another eight RAM registers. In this chapter we will look at the first eight only. Hopefully you have noticed that various time parameters are represented by less than eight bits of data – the DS1307 uses binary-coded decimal. But don’t panic, we have some functions to do the conversions for us.
However, in general - remember that each bit in each register can only be zero or one – so how do we represent a register’s contents in hexadecimal? First, we need to find the binary representation, then convert that to hexadecimal. So, using the third register of the DS1307 as an example, and a time of 12:34 pm – we will read from left to right. Bit 7 is unused, so it is 0. Bit 6 determines whether the time kept is 12- or 24-hour time. So we’ll choose 1 for 12-hour time. Bit 5 (when bit 6 is 0) is the AM/PM indicator – choose 1 for PM. Bit 4 represents the left-most digit of the time, that is the 1 in 12:34 pm. So we’ll choose 1. Bits 3 to 0 represent the BCD version of 2 which is 0010.
So to store 12pm as hours we need to write 00110010 as hexadecimal into the hours register – which is 0×32.
Reading data from the DS1307 should be easy for you now, reset the register pointed, then request seven bytes of data and receive them into seven variables. The device address is 0×68. For example:
Wire.beginTransmission(0x68);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read();
*minute = bcdToDec(Wire.read();
*hour = bcdToDec(Wire.read();
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
At which point the time data will need to be converted to decimal numbers, which we will take care of in the example sketch later. Setting the time, or controlling the square-wave output is another long operation – you need to write seven variables to set the time or eight to change the square-wave output. For example, the time:
Wire.beginTransmission(0x68);
Wire.write(0);
Wire.write(decToBcd(second));
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour));
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
The decToBcd is a function defined in our example to convert the decimal numbers to BCD suitable for the DS1307.
You can also address each register individually. We will demonstrate doing this with an explanation of how to control the DS1037′s in built square-wave generator (download sketch):
/*
DS1307 Square-wave machine
Used to demonstrate the four different square-wave outputs from Maxim DS1307
See page nine of data sheet for more information
John Boxall - tronixstuff.wordpress.com
*/
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68 // each I2C object has a unique bus address, the DS1307 is 0x68
void setup()
{
Wire.begin();
}
void sqw1() // set to 1Hz
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x10); // sends 0x10 (hex) 00010000 (binary)
Wire.endTransmission();
}
void sqw2() // set to 4.096 kHz
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x11); // sends 0x11 (hex) 00010001 (binary)
Wire.endTransmission();
}
void sqw3() // set to 8.192 kHz
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x12); // sends 0x12 (hex) 00010010 (binary)
Wire.endTransmission();
}
void sqw4() // set to 32.768 kHz (the crystal frequency)
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x13); // sends 0x13 (hex) 00010011 (binary)
Wire.endTransmission();
}
void sqwOff()
// turns the SQW off
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x00); // turns the SQW pin off
Wire.endTransmission();
}
void loop()
{
sqw1();
delay(5000);
sqw2();
delay(5000);
sqw3();
delay(5000);
sqw4();
delay(5000);
sqwOff();
delay(5000);
}
Here is the SQW output in action – we measure the frequency using my very old Tek CFC-250:
For further DS1307 examples, I will not repeat myself and instead direct you to the list of many tronixstuff articles that make use of the DS1307.
So there you have it – hopefully an easy to understand introduction to the world of the I2C bus and how to control the devices within. Part two of the I2C tutorial has now been published, as well as an article about the NXP SAA1064 LED display driver IC and the Microchip MC23017 16-bit port expander IC.
Have fun and keep checking into tronixstuff.com. Why not follow things on twitter, Google+, subscribe for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other – and we can all learn something.

















































