I recently started working on a project commissioned by my son to measure how fast he and his sister can run… “Easy,” I thought. All I would need would be two light barriers and I could calculate speed from the time travelled over a known distance. Turned out that a simpler and cheaper solution might be just to use two ultrasonic distance sensors commonly combined with an Arduino and these would work as my “gates”. I looked up a code snippet and came across this example and many others similar to it…

Here’s what I actually setup…

The basic premise is that you have a transponder that is triggered with a short (10us) pulse, a burst of ultrasound (ping) is emitted from the transmitter and then the receiver produces a high signal whose length is equal to the time of flight (ToF) for the ping. Note that this is the round trip time ie. the time taken for the outward and return journey of the sound. This code worked fine for me however it has a fairly serious limitation in that it blocks the micro’s execution loop while waiting for the received signal here…

```duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;
```

The cause of the problem is located in `pulseIn()` which is an Arduino library function that measures the time that an input is high for. This call gives us the ToF however it is blocking and stops the micro from doing anything else. This means that to have two sensors running at the same time would never work. You would have to do a measurement on one then the other continuously – not suitable for a fast-moving target. I should say at this point that the solution provided above is perfectly good for one sensor or if you are very new to microcontrollers and software etc. It does not fully harness the power of the Arduino however. What we need is to do some multitasking and microcontrollers typically use external and internal (timer) interrupts for this purpose. Doing a repeating task at a fixed period is accomplished with timer interrupts and I discovered a nice explanation of how to use the Arduino’s timer interrupts here.

To solve this problem I did the following…

#### Step 1

I used the example code to set up a 16-bit timer to give me a repeating interrupt at 41.75 Hz – the maximum possible ToF for a target at a maximum measurable range of 4m.

```void configureTimer1Interrupt(void)
{
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1  = 0; //initialize counter value to 0
// set compare match register for 41.75hz increments
OCR1A = 47903; // = 16E6 / (41.75*8) - 1 (must be <65536)
TCCR1B |= (1 << WGM12);   // turn on CTC mode
TCCR1B |= (1 << CS11);    // set prescaler to 8
TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
}
```

#### Step 2

I attached the timer interrupt to trigger the transmitter with a 10 us pulse

```ISR(TIMER1_COMPA_vect)
{   //ISR attached to timer1 that triggers the echo
digitalWrite(TRIG_PIN_IDX,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN_IDX, LOW);
}
```

#### Step 3

I attached the receiver channel to an external interrupt that would measure the length of the ToF pulse from the receiver.

```void handleEcho(void)
{
if (risingEdge) handleEchoRisingEdge();
else handleEchoFallingEdge();
}

void handleEchoRisingEdge(void)
{
echoInitialTime = micros();
}

void handleEchoFallingEdge(void)
{
uint32_t timeOfFlightRoundTrip = micros() - echoInitialTime;
distanceCm = (0.0344 * timeOfFlightRoundTrip) / 2.0;
}

void setup()
{
...
attachInterrupt(
digitalPinToInterrupt(ECHO_PIN_IDX),
handleEcho,
CHANGE);
...
}
```

Here’s what the actual output looked like…

# A little better

I wondered if I could do better and remove the sleep from step 2 by setting up PWM (pulse width modulated) output at the specified frequency with a very low duty cycle (10us / (1/41.75Hz) = 4.175E-4). This would in fact completely remove the need for the code in Step 2; no timer interrupt would be needed. I found an enlightening post here. This does indeed work. Here’s the code snippet…

```void configureTimer1Pwm(void)
{
pinMode(TRIG_PIN_IDX, OUTPUT);
TCCR1A = _BV(COM1A1) | _BV(COM1B1) |_BV(WGM10) | _BV(WGM11);
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS11);
OCR1A = 47903;
OCR1B = 19;
}
```

I’ve used timer1 again in “fast-PWM” mode to generate a PWM signal to drive the ultrasonic sensor. The frequency is controlled by the value in OCR1A (and prescaler) set by the clock select (CS) bits in the timer control register (TCCR1B). Effectively a counter counts up until it reaches the compare register A (OCR1A) and the output is switched on and resets. When the counter reaches compare register B, the output is turned off. You can follow the links for details of the calculations and have a look at my scrappy notes here.

I get the same output on the trigger pin as before but without the 10us sleep in `ISR(TIMER1_COMPA_vect)`. In fact, that function is no longer needed. The micro does all the signal generation that is required to drive the ultrasonic sensor. We don’t need any logic in the code anymore. All that’s required is to set up the PWM output correctly. I think that this is a preferable solution because it means that this cyclic task of driving the sensor is done at a low level and is more likely to be jitter-free and more importantly, we don’t block the micro’s execution loop.

Example code

## Arduino without the IDE – An intro to UNIX Make

Recently I’ve been having a go at make. Make is an ancient and powerful UNIX utility that we can use for automating a software build process. But why do we need this when the Arduino IDE does this for us? For me, this comes down to the following:

1. I wanted to have full control over the build process and all files that are included such as the Arduino cores; as the lifetester project nears maturity, I want to be in control of all files included and and what they contain.
2. I didn’t like the way that all tabs in the Arduino sketch are stitched together (see Arduino build process). This means that any global variables that you declare as static within a module are then brought into the same file and are no longer private.
3. Lastly, I like to use Sublime Text. I love the text highlighting and keyboard shortcuts. It really speeds up editing for me. Since discovering it, it’s been hard for me to accept anything else including the Arduino IDE.

I should say at this point that there is already a well developed makefile for the Arduino project here.  For me, it was impenetrable and so I went through the exercise of writing my own to get some idea how this mysterious tool works.If this interests you, then read on. Otherwise go to the link and check out a copy.

So what I was looking for a way to write C files and build them into a binary that I could upload onto the Atmega328 without the IDE. Compiling and linking C files is in UNIX (or Windows) is straightforward – just invoke the C compiler with cc. What does the Arduino IDE do then? All you have to do is turn on verbose in settings and you can see the commands issued in the console (and loads of other output too) at the bottom of the IDE. To demonstrate this, I saved a copy of `Blink.ino` as `MyBlinkTest.cpp` and copied the relevant files from the Arduino cores (`/usr/share/arduino/hardware`) and variants folders to the working directory. I called the following commands in the prompt…

```\$ avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c MyBlinkTest.cpp main.cpp new.cpp Stream.cpp wiring.c wiring_digital.c WString.cpp
\$ avr-gcc -mmcu=atmega328p main.o MyBlinkTest.o new.o Stream.o wiring.o wiring_digital.o WString.o -o MyBlinkTest.elf
\$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:MyBlinkTest.hex
```

…but it didn’t work. I found that I also needed to add “Arduino.h” to the top of MyBlinkTest.cpp. This is because the Arduino-specific commands such as `digitalWrite` etc. need to be defined and this header points to the functions where that happens. There were then a few places in the Arduino core code that I had to replace  instances of `<Header.h>` with `"Header.h"`. The angle braces were telling the compiler to search system directories rather than the working directory.  Then it worked!

Clearly, this approach is limited. We would have to write out all the c files and object files explicitly, and copy files from the core directory and then remember all the commands. Not really feasible and very messy especially as the project starts to grow.

Enter make…  What it does is automate these commands by manipulating text and punching it into the command line for us. You can see the process in the commands above which I’m going to breakdown next and implement them in a makefile. Before I go on, it’s worth going over the basics of make here. A makefile consists of a number of ‘rules’ that each create a ‘target’ based on ‘prerequisites’ (dependencies) and commands. They look something like this:

```target [target ...]: [component ...]
Tab ↹[command 1]
.
.
.
Tab ↹[command n]
```

In make, variables are called ‘macros’ and we write them as…

```MACRO=definition
```

and use them like this…

```\${MACRO}
```

This just means that whatever text we defined for that particular macro will be inserted where we specify.  So our hello world application would look like this…

```MACRO1=hello
MACRO2=world!

test:
@echo \${MACRO1} \${MACRO2}
```

Armed with this basic knowledge, let’s go through a makefile for an Arduino build – the classic blinking LED. You’ll find a copy of these files here if you’re interested in having a starting point for your own adventures with make.

## Step 1: Compilation

First, we call the avr-gcc compiler, with a list of c and cpp files that we want to compile into object (.o) files. But in the makefile however,  you’ll see that I haven’t specified a file list however. I’ve said that the target will be a group of object files based on all .c and .cpp files in the `DEPS` macro combined with `MyBlinkTest.cpp` (`blink.ino` renamed). Using the `VPATH` macro, I’ve pointed the compiler to the Arduino core and variants directories. This is where all the important under-the-hood stuff is stored for building Arduino sketches. Note the additional `-I` flag in the call to avr-gcc which tells the compiler where to look for header files.  You’ll also see that I’ve sent all the object files to a build directory that is created if it doesn’t exist. Last but not least, there’s an important make idiom in the compiler call in the use of `\$^`  which is an internal macro or automatic variable which expands to a space delimited string of prerequisites (‘implicit’ source): a list of all our .c and .cpp files.

```VPATH=/usr/share/arduino/hardware/arduino/cores/arduino
VARIANTS=/usr/share/arduino/hardware/arduino/variants/standard
BUILD_DIR=Build
CC=avr-gcc
MMCU=-mmcu=atmega328p
CFLAGS=-Os -DF_CPU=16000000UL \${MMCU}

\${BUILD_DIR}/*.o: \${DEPS}
mkdir -p Build/
\${CC} \${CFLAGS} -c \$^ -I \${VARIANTS} -I \${VPATH}
mv *.o \${BUILD_DIR}/
```

The linking step simply takes all the object files that we’ve generated and bundles them together in one .elf file. Again, I’ve used the implicit source variable (`\$^`) and the target variable `\$@`  which substitutes in the name of the target which in this case is `\${PROGRAM}.elf`  and evaluates to `MyBlinkTest.elf`.

```PROGRAM=MyBlinkTest

\${PROGRAM}.elf: \${BUILD_DIR}/*.o
\${CC} \${MMCU} \$^ -o \${BUILD_DIR}/\$@
```

### Step 3:  File conversion

```OBJCOPY=avr-objcopy

\${PROGRAM}.hex: \${BUILD_DIR}/\${PROGRAM}.elf
\${OBJCOPY} -O ihex -R .eeprom \$&lt; \${BUILD_DIR}/\$@
```

Using the `avr-objcopy` command the .elf file is converted into a standardised .hex format. `\$<`  is used here as it stands for the first prerequisite. There is only one so you get the idea.

```PORT=/dev/ttyACM0

avrdude -F -V -c arduino -p ATMEGA328P -P \${PORT} -b 115200 -U flash:w:\${BUILD_DIR}/\$&lt;
```

Heavy lifting done. Now it’s time to upload our beautiful code onto the Arduino with this last command which calls avrdude. Note that I’ve used `\$<`  again to substitute in the name of the prerequisite and a macro to hold the name of the port.

### Other things to note

• Tabs are tabs in make! Don’t indent by four spaces and expect that to be equivalent. Make only understands tabs. You have been warned.
• It’s worth defining the first rule (default target) as the one where all the important targets are specified. I’ve called this all.
• You can also selectively just compile or upload only if you have defined rules for this by simply calling `make compile` or `make upload` from the command line respectively.
• A clean rule is also a good idea. I’ve defined one here which just deletes everything in the build directory.
```all: \${BUILD_DIR}/*.o \${PROGRAM}.elf \${PROGRAM}.hex upload

# option to compile only without upload/install
compile: \${BUILD_DIR}/*.o \${PROGRAM}.elf \${PROGRAM}.hex

avrdude -F -V -c arduino -p ATMEGA328P -P \${PORT} -b 115200 -U flash:w:\${BUILD_DIR}/\$&lt;

clean:
rm -f \${BUILD_DIR}/*
```

## How long do solar cells live? (part 3)

Finally, after much tinkering, I’ve got a system that’s worth committing to a PCB. Here is a shot of the prototype system being tested out…

Above is a picture that I took as I was working on the system. At this point, two solar cells (under the work lamp) are illuminated and being driven at maximum power point (MPP) at the same time. As described previously, I used a current sensing circuit based on an inverting amplifier which is assembled on the long breadboard in the middle along with the DACs and ADCs needed to drive the circuit and collect data. On the neighbouring breadboard is a programmed ATMega328 chip which drives this process and is interfaced by I2C as a slave to another master ATMega328 on an Arduino UNO board. I needed another Arduino UNO board for programming the ATMega and for USB-Serial communication debugging when needed. There’s a neat article on this on the Arduino site here. Have a look at this schematic below for more detail of what I did exactly…

Unfortunately, the analog circuit that I was using was not quite doing the job. I noticed that although the output voltage from the DAC was as expected from the binary code that I was feeding into it, at the other end of the buffer amplifier (at the DUT terminal) it wasn’t. In particular, at Vin = 0V (short-circuit), the applied bias wasn’t 0V. It turns out that the buffer amplifier needs to work as a current sink in this case – current actually flows from ground to the buffer. To overcome this, in addition to +5V and 0V,  I also needed to supply -5V to the op-amp. To make sure that the output from the amplifier to the ADC, Vout, never went below 0V I used a precision rectifier circuit – it acts like an ideal diode; there’s no voltage drop at the output which is commonly associated with a regular diode. The simplified schematic is below and a full Fritzing file here.

Here’s what it does again in brief:

1. Under illumination, current flows from ground to the buffer amplifier.
2. Current flowing from ground to the buffer amplifier leads to a small (0 > Vx > -10mV) negative voltage across the sense resistor.
3. This voltage is fed into an inverting op-amp. It is inverted and amplified 350 times. A precision rectifier arrangement ensures that the output can never go below 0V. Gain and offset can be tuned by means of trimmer resistors.
4. The output is connected to an ADC for data logging and MPP tracking.

Below is some of the data that came out of this system…

The live MPPT data shows some fluctuation in voltage. Because of the hill climbing, perturb and observe algorithm used, the voltage is constantly being probed. You can also see a sharp step in the MPP data where I adjusted light intensity which is indicated by the increase in ADCx (current). Shortly afterwards (measurements are taken roughly every second), this is followed by DACx (applied voltage) as the MPPT system catches up which is expected. As a double check, I reset the lifetime tester to run another IV characteristic without changing the light intensity. This registered an MPP at DACx = 760 (0.38V) which was consistent with the MPP tracking data.

Having convinced myself that this system was working nicely, I decided it was time to design a PCB. More on that to come.

## …now for the digital-to-analogue converter

Now I’m dealing with the mirror case to the ADC that I just posted on. Instead of us reading an analogue voltage and converting that to a digital representation, we want an analogue voltage from a digital signal (DAC). In other words, I want the Arduino to give me a voltage from an int. But this is just like the AnalogWrite() function right?… Wrong!

1. AnalogWrite()  does not give us an analogue voltage. It gives us a pulse width modulated (PWM) digital signal ie. the output is either 0 or 5V and we change the ratio of on and off time to give us an analogue-like signal for driving motors for example. This is not truly analogue in that what we want is to be able to select any DC  voltage between 0 and 5V that we want. We could apply a filter to the PWM signal to convert it to DC and this would be a cheap and fast trick but we would lose speed and voltage.  Speed is a key figure of merit in a digital-to-analogue converter (DAC) systems as it determines the sampling rate.
2. Resolution – if we were to go with option 1 plus filter, with the Arduino, we’re limited to a resolution of 8 bits ie. the output voltage would be scaled between 0-255*Vout. On the other hand with an external DAC, we could choose our resolution up to 32 bits ie. 0-4294967296!

So being able to use a DAC is going to be an important tool in our kit for attacking lots of projects. These things are commonplace in A/V equipment and mobile phones but I have another project in mind for this. More on that later. First, let’s discuss how to use a DAC – in this case the 12bit MCP4822. Se pinout here…

If you’ve not done so already, have a look back at my previous post on using an ADC which covers the basics of SPI connections, bit math and relevant code.

Same as before, we’re going to setup an SPI connection between the DAC and Arduino but this time we transfer data over the master-out-slave-in (MOSI) line. The process will look like this:

1. Setup the SPI interface first given the settings in the chip datasheet.
2. Take user input from the serial connection and store it as text.
3. Separate the channel address, voltage and gain from the users command and store as char, int and int respectively.
4. Convert our int representation of voltage to two binary bytes.
5. Change the state of some of the leading bits to denote which channel of the DAC we’re addressing (A or B) and what gain we would like (1 or 2).
6. Take the CS pin LOW and transfer two bytes via SPI (MSB first). Then return the CS pin to HIGH again.
7. Print the result to the serial window.
8. Repeat

Above you can see an extract from the datasheet that illustrates the SPI protocol. So how do we actually set the voltage that we want? Well, the MCP4822 has two channels, two gain settings and a 12-bit DAC register meaning and the voltage is calculated as follows…

Vout = Gain*Vref*(D/4095)

…where the gain is either 1 or 2, Vref=2.04V and is the internal voltage reference of the DAC, and D is the binary input that we send to the unit over SPI. Having an internal reference voltage means that even though the supply voltage may change slightly, the output is going to be stable since Vref is a stabilised internal reference…Nice! So this means we can choose whether we want to scale our voltage over 0.00-2.04V or 0.00-4.08V by the gain setting, either large/small range low/high resolution depending on our application, by using the gain setting.

Here is an extract from the MCP4822 datasheet for further information. It shows how the output is calculated and the writing process with example input. You can see that the before our binary input (left of the most significant bit) there are some extra bits referred to as config bits which in my code are inside the most significant byte (named MSB in the following code). We need to concentrate on bit 15, the channel selector, bit 13, the gain selector and bit 12, the shutdown bit…

Now let’s discuss how the code could look. The basic process is given in the numbered list above but let’s elaborate here (You’ll find a complete copy of the code at the bottom of the post):

#### Setup the SPI interface

```  // set the CS as an output:
pinMode (DAC_CS, OUTPUT);
Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
inputString.reserve(200); // reserve 200 bytes for the inputString:
SPI.begin();```

#### Take user input from the serial connection

```  // read the incoming string:
// say what you got:
Serial.println(inputString);```

#### Separate the channel address, voltage and gain from the user’s command and store as char, int and char respectively

```//extract channel address
channel = inputString.charAt(0);
//and gain
gain = inputString.charAt(1);
//convert string to an int - binary voltage
n = inputString.substring(2).toInt();
//set DAC state
DAC_set(n, channel, gain, DAC_CS, errmsg);
//print errors if there are any
Serial.println(errmsg);

//clear input string ready for the next command
inputString = "";```

#### Convert our int representation of voltage to two binary bytes

```  //convert decimal input to binary stored in two bytes
MSB = (input >> 8) & 0xFF;  //most sig byte
LSB = input & 0xFF;         //least sig byte```

#### Set the config bits

You will see that setting config bits is done with bit manipulation. In essence, if you want to set bit 7 to a 1, you need to do an OR operation on your data with 10000000 (which is 0x80 in hex) – this is useful in setting the channel bit. If instead, you want to set the same bit to 0, you would do an AND operation with 01111111 (0x7F) so you would end up setting bit 7 to 0 and keeping any data that you already had. If you did, AND 00000000, all your bits would go to 0 of course.

``` //apply config bits to the front of MSB
if (DAC_sel=='a' || DAC_sel=='A')
MSB &= 0x7F; //writing a 0 to bit 7.
else if (DAC_sel=='b' || DAC_sel=='B')
MSB |= 0x80; //writing a 1 to bit 7.
else
errmsg += "DAC selection out of range. input A or B.";

if (Gain_sel=='l' || Gain_sel=='L')
MSB |= 0x20;
else if (Gain_sel=='h' || Gain_sel=='H')
MSB &= 0x1F;
else
errmsg += "Gain selection out of range. input H or L.";

//get out of shutdown mode to active state
MSB |= 0x10;```

#### Take the CS pin LOW and transfer two bytes via SPI (MSB first). Then return the CS pin to HIGH again.

```  //now write to DAC
// take the CS pin low to select the chip:
digitalWrite(CS_pin,LOW);
delay(10);
//  send in the address and value via SPI:
SPI.transfer(MSB);
SPI.transfer(LSB);
delay(10);
// take the CS pin high to de-select the chip:
digitalWrite(CS_pin,HIGH);```

#### Print the result to the serial window.

```  Serial.println("binary input to DAC: ");
Serial.print(MSB,BIN);
Serial.print(" ");
Serial.println(LSB,BIN);```

#### Repeat

In the serial monitor, you should see something like this…

```I received: AL500

binary input to DAC:
110001 11110100

binary input to DAC:
110000 0

binary input to DAC:
110011 11101000

binary input to DAC:
110000 0

binary input to DAC:
110011 11101000
```

You get the command back that you wrote in then the binary input to the DAC and any errors.  To check that the code and hardware were working, I measured the output from the DAC as a function of binary input. Here is what I got…

What follows is the final code that I used to check my DAC. It contains separate functions for setting the state of the DAC and reading user input from the Arduino Serial monitor. I hope this post is enough to give you the basics of DAC implementation using an Arduino. If you have any questions, please comment and I’ll do my best to answer. Note that this code is limited in terms of speed. I’ve included some short (10ms) delays in the code which would really slow the application of the DAC if we wanted to sample at high data rates. One to return to at a later date.

```/*
D. Mohamad 29/03/17 code to commumincate with MCP4822 12-bit DAC via SPI
pin assignments as follows...
Uno (master)  MCP4822 (slave)
CS    8             2
MOSI  11            4
SCK   13            3
Read output voltage with a multimeter Va/Vb on pin 8/6.
send commands via serial interface eg.
AL1000 would mean...
channel=A
gain=low
D=1000
Va = gain*Vref*D/4095
= 1*2.04*1000/4095 = 0.498V
see www.theonlineshed.com
*/

#include <SPI.h>

const int DAC_CS = 8; //Chip select pin for the DAC
String inputString = ""; //holds serial commands

void setup() {
// set the CS as an output:
pinMode (DAC_CS, OUTPUT);
Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps
inputString.reserve(200); // reserve 200 bytes for the inputString:
SPI.begin();
}

//function to set state of DAC - input value between 0-4095
void DAC_set(unsigned int input, char DAC_sel, char Gain_sel, int CS_pin, String &errmsg)
{
//DAC_sel choose which DAC channel you want to write to A or B
//Gain_sel choose your gain: H=2xVref and L=1xVref
byte MSB,LSB;//most sig, least sig bytes and config info

//clear error messages
errmsg="";

Serial.flush();  // in case of garbage serial data, flush the buffer
delay(10);

//only run the rest of the code if binary is in range.
if (input<0 || input >4095)
errmsg += "input out of range. 0-4095.";
else
{
//convert decimal input to binary stored in two bytes
MSB = (input >> 8) & 0xFF;  //most sig byte
LSB = input & 0xFF;         //least sig byte

//apply config bits to the front of MSB
if (DAC_sel=='a' || DAC_sel=='A')
MSB &= 0x7F; //writing a 0 to bit 7.
else if (DAC_sel=='b' || DAC_sel=='B')
MSB |= 0x80; //writing a 1 to bit 7.
else
errmsg += "DAC selection out of range. input A or B.";

if (Gain_sel=='l' || Gain_sel=='L')
MSB |= 0x20;
else if (Gain_sel=='h' || Gain_sel=='H')
MSB &= 0x1F;
else
errmsg += "Gain selection out of range. input H or L.";

//get out of shutdown mode to active state
MSB |= 0x10;

Serial.println("binary input to DAC: ");
Serial.print(MSB,BIN);
Serial.print(" ");
Serial.println(LSB,BIN);

//now write to DAC
// take the CS pin low to select the chip:
digitalWrite(CS_pin,LOW);
delay(10);
//  send in the address and value via SPI:
SPI.transfer(MSB);
SPI.transfer(LSB);
delay(10);
// take the CS pin high to de-select the chip:
digitalWrite(CS_pin,HIGH);
}
}

//function to read user command from the serial command window and set the DAC output
void serial_DAC_set(void)
{
unsigned int n; //number 2 bytes long unsigned
char channel, gain;
String errmsg;  //errors returned from DAC_set

// say what you got:
Serial.println(inputString);

channel = inputString.charAt(0);
//and gain
gain = inputString.charAt(1);
//convert string to an int - binary voltage
n = inputString.substring(2).toInt();
//set DAC state
DAC_set(n, channel, gain, DAC_CS, errmsg);
//print errors if there are any
Serial.println(errmsg);

//clear input string ready for the next command
inputString = "";
}

void loop()
{
//only run when data is available
if (Serial.available() > 0)
serial_DAC_set();
}```

## My guide on using an analogue-to-digital converter

In this example, I’ve wired up an Analogue-to-digital converter (ADC) and showed you some code that will get this device talking to an Arduino using the SPI connection. There’s a potentiometer here that you can use to change the input voltage and it’s read once per second. Note that ADCs are capable of reading at much higher rates than this – according to the data sheet, the ADS1286 can sample at up to 20kHz but higher rates are possible.

SPI stands for Serial Peripheral Interface and is a really impressive invention that allows ICs to talk to one another over short distances (on a PCB) using three wires: (1) clock, (2) master-out-slave-in (MOSI)/master-in-slave-out (MISO) and (3) chip select. In our case, since we’re dealing with receiving data from the slave (ADC), then we only use the MISO line. Wikipedia and Arduino have really good entries explaining this. However, I think that the timing diagram on Wikipedia is really helpful. Basically, when we want to talk to a chip over SPI, we send the chip select pin low, then send/read data over the MOSI/MISO data line. The whole thing is synchronised by clock pulses on the clock line. Each time the clock switches state, we end up reading another bit. The exact protocol of whether we (a) read bits on a low-high or high-low clock pulse and (b) whether bits are read on the leading or trailing edge of the clock is set in the SPI settings in the code (see below). Here is the exact operating sequence from the datasheet:

In the code below, you can see that the process goes like this:

1. Setup the SPI interface first given the settings discussed above and also set the clock speed as recommended by the manufacturers (It doesn’t seem to work if I don’t set this manually).
2. Take the CS pin LOW and read out two bytes via SPI (MSB first). Then return the CS pin to HIGH again.
3. Do some bit maths on these two bytes to convert them into a usable binary number.
4. Cast this binary number into an int.
5. Print the result to the serial window.
6. Repeat.

Here’s some example raw data that we might read from the unit. The whole thing looks like this…

So what we need to do is keep only the data inside the box and get rid of everthing else. The key line in the code is this one…

`ADCval = ((MSB & 0x3e)<<8 | LSB) >> 1;`

Notice that we do a bitwise AND first with the MSB and 0x3e which is hex for 00011111 (step 1). This effectively does an AND operation with each bit in the MSB  and this number. Therefore anything in the first three bits gets switched to a 0 because any bit AND 0 -> 0. Then we shift the MSB left 8 places (step 2) and do a bitwise OR with the LSB (step 3). This effectively moves the MSB into its correct position as the most significant data and stuffs the LSB onto the end. Lastly, we shift everything right one place to get rid of the junk hanging off the end (step 4). This effectively shifts it off the end. Note that this will only work because we have declared ADCval as an unsigned int. If not, instead of a 0 being moved in from the left as we shift everything right, a 1 would end up there instead.

And here’s the rest of the code which includes the steps for addressing the ADC.

```#include <SPI.h>
//pin connections
//12 MISO       6 (with 10k pull-up)
//10 CS         5
//13 SCK        7
const int CS = 8;

void setup() {
// set the CS as an output:
pinMode (CS, OUTPUT);
Serial.begin(9600);     // opens serial port, sets data rate to 9600 bps

SPI.begin();
SPI.beginTransaction(SPISettings(20000, MSBFIRST, SPI_MODE0));
}

{
byte MSB,LSB;

//now write to DAC
// take the CS pin low to select the chip:
digitalWrite(CS,LOW);
delay(10);
//  send in the address and value via SPI:

MSB = SPI.transfer(0x00); //most sig byte
LSB = SPI.transfer(0x00); //least sig byte

//print out the raw measurement
for (int i=7;i>=0;i--)
for (int i=7;i>=0;i--)
Serial.println();

//now get rid of the first three digits (most sig bits)
//combine with the LSB
//shift everything right one to get rid of junk least sig bit
ADCval = ((MSB & 0x3e)<<8 | LSB) >> 1;

//print out data again
for (int i=15;i>=0;i--)
Serial.println();

delay(10);
// take the CS pin high to de-select the chip:
digitalWrite(CS,HIGH);

}

void loop()
{
Serial.println(data);
delay(1000);
}```

Here’s an example of the kind of thing that you get on the serial monitor…

```1100110110101101
0000011001010110
1622
1100110110101010
0000011001010101
1621
1100110110101101
0000011001010110
1622
1100110110101101
0000011001010110
1622
1100110110101111
0000011001010111
1623
1100110110100111
0000011001010011
1619
1100011111001010
0000001101100101
869
1100010011110000
0000001001111000
632
1100000100101000
0000000000010100
20
1100000000000000
0000000000000000
0
1100000000000000
0000000000000000
0
```

…first, you get the raw data read from the ADC followed by the shifted bits after we do our maths. Then you get the decimal value representing the voltage which is Vin/Vref*4095.

I decided it might be a good idea to check the linearity of the device. I wanted to be able to relate the binary output from the ADC to a real voltage. Testing this is pretty easy.  I monitored the wiper terminal (ADC input) using a digital voltmeter and compared it to the ADC output from the serial window. Here is what I got…

As you can see, everything looks rosy. The response is linear to a high degree of accuracy and the gradient is 812 V^-1 = 4095 binary / 5.042V. I hope this post will help explain the basic process of how to implement an ADC. Get in touch if I’ve missed anything.