===================================== Using the Atmel AT91EB55 Onboard Digital to Analog Converters (DACs) ===================================== -------------------- Written by: Josh Ibach-MacKeen Michael Ivey Group 9 Winter 2010 -------------------- Introduction ------------ The AT91M55800A (the ARM7-based processor on the AT91EB55 evaluation board) posseses two onboard Digital to Analog Converters (DACs); namely DAC0 and DAC1. These are documented in pages 204-213 of the AT91M55800A reference manual. This tutorial ("application note") will demonstrate how to intialize the DAC in both free-running mode as well as in a timer-contrained mode wherein the DAC output is upated in sync with the output of a timer channel. DAC Basics ---------- The M55800A's DACs have the following characteristics: - 0 to 2.5 V output range* - 8 or 10 bit resolution (programmatically selectable) ----> 8b => 256 output levels, 10b => 1024 output levels - ~5 microsecond settle time at any given output level * The Analog Voltage Reference is configurable by modifying the DAVREF module on the AT91EB55 board. This modification is beyond the scope of this tutorial. The API documentation for the DAC is contained in the "periph/dac" folder. dac.h contains a useful list of #defines (for picking timer channels, output mode, etc.) and lib_dac.c contains documentation for the API functions. It is important to note that the desired output value is written into a so-called Data Holding Register (DAC_DHR, see p.209 of M55800A ref.) in the DAC hardware, the contents of which are then transferred to the Data Output Register (DAC_DOR, p. 209) at which point the desired output is applied at the DAC output. In the timer-triggered (sync) mode, the DAC_DATRDY bit in the DAC Status Register (p. 210) is 0 when the contents of the DHR have not yet been transferred into the DOR. Writing to the DAC when the DATRDY bit is 0 will clobber the previous output value stored in the DHR. A note on 8b vs 10b mode: In 8 bit mode, only byte transfer is perfomed to move values into the DAC_DHR. 10 bit mode requires a half word transfer wihch consumes more time. A note on pinouts: DAC0 output is available on Pin 4 of the JPAD/DA header. DAC1 output is available on Pin 6 of the JPAD/DA header. Demonstration 1: Free-Running DAC Output ---------------------------------------- This demonstration will initialize a DAC as a random-access analog output. I.e. the data written to the DAC will be made available on its output as soon as possible. Consider the following code snippet: /******************************************/ #include "parts/m55800/eb55.h" u_int output; at91_dac_open(&DAC0_DESC,DAC_TTRGEN_DIS|DAC_10_BIT_RES); output=512; at91_dac_write(&DAC0_DESC,&output); /******************************************/ This code is all that is required to drive a value of 2.5V/1024 * 512 = 1.25 V onto the output line of DAC0. The at91_dac_open() function takes two arguments: a pointer to the DAC's address (available by default via DAC0_DESC) and a set of flags to indicate how the DAC is to operate. A list of these flags is available in dac.h. The flags used here disable synchronisation with a timer channel (DAC_TTRGEN_DIS) and enable 10 bit resolution on the DAC. The at91_dac_write() function also takes two arguments: a pointer to the DAC in memory and the ADDRESS of an output value. I.e. at91_dac_write(&DAC0_DESC,1024) will write whatever is stored at address 1024 at the moment, NOT the value 1024. In the free-running configuration, the DAC can be used similar in fashion to a GPIO in that it can be randomly accessed to provide asynchronous output. Demonstration 2: Timer Channel Synchronized ------------------------------------------- This demonstration ties the output changes on the DAC to clock pulses produced by a timer counter in Waveform mode. (For details on configuring the Timer Counters, see the Timer Counter tutorial.) Consider the following code snippet: /******************************************/ #include "parts/m55800/eb55.h" u_int output, drr; u_int tcreg[4]; at91_dac_open(&DAC0_DESC,DAC_TTRGEN_EN|DAC_10_BIT_RES|DAC_TRG_TIOA1); at91_tc_open(&TC1_DESC,TC_WAVE|TC_ACPC_TOGGLE_OUTPUT|TC_CLKS_MCK2|TC_CPCTRG,TRUE,FALSE); tcreg[RA]=0; tcreg[RB]=0; tcreg[RC]=266; at91_tc_write(&TC1_DESC,tcreg); at91_tc_trig_cmd(&TC1_DESC,TC_TRIG_CHANNEL); //Start the timer output=1023; for(;;) { drr=at91_dac_get_status(&DAC0_DESC); if(drr==1) { at91_dac_write(&DAC0_DESC,&output); output^=1023; } } /******************************************/ The timer channel has been configured to produce a square wave with a frequency of 60 kHz in the above example. (For details on this, consult the Timer Counter tutorial.) On each rising edge of the 60 kHz pulse, the DAC will transfer the contents of the DHR to the DOR. This corresponds to a 30 kHz square wave with an amplitude of 2.5 V being generated on the DAC0 output. (To verify this, hook an oscilloscope lead up to Pin 4 of the JPAD/DA header, with the ground connected to Pin 25 of the JPAD/DA header.) Note that the dac_open() function uses the flags DAC_TTRGEN_EN and DAC_TRG_TIOA1. These flags, respectively, indicate that the DAC should be synchronized with a timer and that the timer in this case is the output on Timer Channel 1, Output A. The "(if drr==1)" clause checks to see that the data previously written to the DAC has been moved to the output stage and therefore that it is safe to write new data into the DHR using the at91_dac_write() function. Note that it is also possible to have the DAC generate an interrupt when data is transferred from the DHR to the DOR. This can be done by enabling the DAC0 interrupt and setting bit 0 in the DAC_IER (Interrupt Enable Register.) Further detail is beyond the scope of this tutorial.