You are on page 1of 5

CPE 329: Digital-to-Analog Conversion

Interfacing the ATmega 328 and the Ardunio Uno to a DAC.

Revisions
Created by John Oliver 10.12.2010 Edited by John Oliver 03.20.2012

Objective
Understand how to use a digital-to-analog converter and how to interface it to an ATmega328 microcontroller.

Introduction
This document will discuss digital-to-analog converters (DAC) and walk-through the setup of a DAC with the ATmega328. In particular, this document uses a National Semiconductor DAC121S101, 12-bit D/A converter, which are found on Digilent Inc.s DA-2 PMOD board. Other devices that use SPI communication protocols will follow a similar procedure, and will be discussed later. This document assumes you have completed the ATmega SPI tutorial (the previous assignment). A DAC is a device that converts a digital (usually binary) code to an analog signal (current, voltage, or electric charge). An analog-to-digital converter (ADC) performs the reverse operation. Signals are easily stored and transmitted in digital form, but a DAC is needed for the signal to be recognized by human senses or other non-digital systems. Digital-to-analog conversion can degrade a signal, so be careful about choosing the DAC with the correct properties (not a problem in CPE 329, but can be challenging when you are working in industry). Due to cost and the need for matched components, DACs are almost exclusively manufactured on integrated circuits (ICs). There are many DAC architectures which have different advantages and disadvantages. The suitability of a particular DAC for an application is determined by a variety of measurements including speed and resolution. The two most common DAC types are DACs built with a pulse-width modulator method, and oversampling/interpolating DACs such as the delta-sigma DAC.

Experiment
We will use the ATmega as an SPI master - the microcontroller will initiate all communications with the DAC. Reminder, the SPI for the ATmega328 is muxed with the pins of PORTB. The PMOD board has 5 connections of use to use, and should be connected to the ATmega328 as shown below:
ATmega328 PMOD DA-2

PB2 (SS) PB3 (MOSI) PB5 (SCK) GND VDD

1. SYNC 2. DINA 4. SCK GND VDD

Once the connections have been made, we need to program the ATmega to write values to the DAC. Similar to the previous tutorial on the SPI, the program falls into two phases, SPI initialization and writing data to the SPI. To initialize the SPI, we need to make sure that we setup the SPI in a configuration that will operate well with the DAC. The DAC expects the following configurations for the SPI Control Register:
SPCR DORD MSTR CPOL CPHA SPR1 SPR0

0 MSB transmitted first 1 Master enabled 0 active high clock 1 sample on falling edge 0 freq / 4 0

It is also good practice to clear the SPI status register before using the SPI:
SPSR SPIF WCOL SPI2X

0 clear pending interrupt 0 clear write collision flag 0 regular rate SCK

Please familiarize yourself with the different control and status bits for the SPI prior to doing the lab by reading the SPI register description in the data sheet. Now that initialization is done, we need to perform a write. The SPI on the ATmega328 is an 8b SPI port, but we need to write 12b of data to the DAC. So each write is actually going to be comprised to two different writes to the DAC. To perform writes to the DAC, we first need to make !SS active (go low), then put the high-byte of our 12b value into the SPI's data register SPDR. In this example, we will poll the status register to see when this byte has been transmitted - waiting in a busy loop until the byte transfer has been completed. Alternatively, you can setup the SPI to generate an interrupt when the byte has been transferred. Once the high-byte has been transferred, we can then proceed to the lowbyte. We wait for the low-byte to be transferred, disable the slave select and we are done! The example code at the end of this document demonstrates this communications process.

Assignment:
2

1. The code below creates a square wave that is about 1.6V peak-to-peak with a 0.66 V DC offset with a 20 uS period. Re-write the example program to create a squarewave that has a 1V peak-to-peak with a 1V DC offset with the same period. Get a scope capture of the output of the DAC. 2. Create a triangle wave using a minimum of 10 different points per period of the triangle wave. Get a scope capture of the output of the DAC. 3. Submit an electronic document via Polylearn that has your names, your waveforms from the scope captures and your code for your triangle wave.

#define F_CPU 16000000 #define MOSI 3 #define SCK 5 #define SS 2 #include <avr/io.h> #include <util/delay.h> void Initialize_SPI_Master(void); void Transmit_SPI_Master(int Data);

//define internal CLK speed // PB pin 3 // PB pin 5 // PB pin 2

int main(void) { int count = 0; DDRB = 1<<MOSI | 1<<SCK | 1<<SS; Initialize_SPI_Master(); while(1) { Transmit_SPI_Master(0xAAA); _delay_us(10); Transmit_SPI_Master(0x333); _delay_us(10); } // end while return 0; // end main // make MOSI, SCK and SS outputs

//0xAAA / 0xFFF = 2739 / 4096 = ~ 2.2V

//0x333 / 0xFFF = 819 / 4096 = ~ 0.66

void Initialize_SPI_Master(void) { SPCR = (0<<SPIE) | (1<<SPE) | (0<<DORD) | (1<<MSTR) | (0<<CPOL) | (1<<CPHA) | (0<<SPR1) | (0<<SPR0) ; SPSR = (0<<SPIF) | (0<<WCOL) | (0<<SPI2X) ; PORTB = 1 << SS;

//No interrupts //SPI enabled //shifted out LSB //master //rising leading edge //sample leading edge //clock speed //SPI interrupt flag //Write collision flag //Doubles SPI clock // make sure SS is high

} void Transmit_SPI_Master(int Data) { PORTB = 0 << SS; // assert the slave select SPDR = (Data >> 8) & 0xF; // Start transmission, send high byte first while (!(SPSR & (1<<SPIF)));// Wait for transmission complete SPDR = 0xFF & Data; // send low byte next while (!(SPSR & (1<<SPIF)));// Wait for transmission complete PORTB = 1 << SS; // de-assert slave select }

Notes
The Digilent PMOD DA2 is super expensive (~$30). Another DAC that could be used is the Microchip MCP4921, which comes in a convenient DIP package that is easy for us to use on a breadboard and is only $2. Below is the waveform for writing to the MCP4921. You can see that this 12b DAC requires 16b of data to be written, where the top nibble are some control bits. Also note that from the waveform that the clock edge polarity should be set to leading edge. The datasheet for this DAC is located at: (http://ww1.microchip.com/downloads/en/devicedoc/21897b.pdf)

To initialize the SPI for communicating with the MCP 4921, follow the same initialization code as in the code provided with this lab, with the following exceptions: The clock polarity (CPOL) should should be set so that the SCK should idle low and the clock phase (CPHA) should be set so that data is sampled on the rising edge of SCK. Below is a write function to show how a 16b write can be accomplished.
void Transmit_SPI_Master(int Data) { PORTB = 0 << SS; //Assert slave select SPDR = ((Data >> 8) & 0xF) | 0x70; //Attach configuration Bits onto MSB while (!(SPSR & (1<<SPIF))); SPDR = 0xFF & Data; while (!(SPSR & (1<<SPIF))); PORTB = 1 << SS; }

You might also like