2
\$\begingroup\$

beginner here in need for starting directions.

I have SAMD21 Xplained Pro Board. I would like to produce DC DAC output, based on 8 bits on the input.

Range of my DC output values should be around 1V-5V (possibly a little different).

For example, this is what I would like to have: INPUT 00000000 00000001 00000010 00000011 ... OUTPUT 0V 1V 1.1V 1.2V ...

This will probably not be the actual mapping, but something of the sort. I hope you get the idea.

I am working in Atmel Studio.

Is this at all doable? What is the best way to do it? Are there any example that do something like this in Atmel and with AVR (Microchip) MCUs, to get me started?

Any help appreciated.

\$\endgroup\$
  • \$\begingroup\$ Find an example for use of the DAC, hopefully in ASF which is bundled with Atmel Studio and also available as a standalone download. See existing DAC questions or any DAC theory reference to understand the proportionate mapping of the code word to the output voltage. You may want something like a lookup table (or a simple calculation) from your input values to the necessary code words to achieve your voltages. \$\endgroup\$ – Chris Stratton Nov 27 '18 at 16:33
  • \$\begingroup\$ Thanks for your comment. Examples in Atmel seems to be all made by ASF, which makes it difficult for me to interpret. (I am currently to figure out how that works). I was hoping to find some bare-metal DAC examples. In the datasheet (ww1.microchip.com/downloads/en/DeviceDoc/40001882A.pdf) chapter 35 is dedicated to DAC, but it is very difficult to use that as a starting point as I don't have any initial code samples. \$\endgroup\$ – DenR Nov 27 '18 at 17:02
3
\$\begingroup\$

Even if you don't want to use ASF (I don't even use Atmel Studio) you are still best off including and compiling with the Atmel packs that define all of the registers and pins, similar to the "io.h" files used with AVR. All example code here is using definitions/structures from the SAMD21 pack. ARM is significantly different than AVR and requires a lot more steps to get something started, but once the clocks are taken care of, it's similar to many other microcontrollers in that you are just setting register values.

Here's the samd21 datasheet, for reference.

There are numerous steps involved, and I can add more details to each upon request.

1. Configure System Clocks

This is extremely involved, but necessary to do anything...

2. Enable Power to modules (not all of these are necessary, just an example)

PM->APBBMASK.reg =
    PM_APBBMASK_DMAC |
    PM_APBBMASK_PORT |
    PM_APBBMASK_NVMCTRL;

PM->APBCMASK.reg = 
    PM_APBCMASK_ADC |    // This is for the ADC
    PM_APBCMASK_DAC |    // This is for the DAC
    PM_APBCMASK_TC3;     // This is for Timer/Counter 3

3. Configure ports and pins

DAC for SAMD21 is PA02, Port MUX B

4. Attach Generic Clock to the module

#define DAC_GCLKGEN    3    // examples using GLCK Generator 3
GCLK->CLKCTRL.reg = (DAC_GCLKGEN << 8) | GCLK_CLKCTRL_ID_DAC | GCLK_CLKCTRL_CLKEN;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);

5. Configure the module registers

DAC->CTRLB.reg = 0x40;  // use AVCC as the reference

6. Create some macros/functions

#define DAC_VREF        3.3f  // this is the value of AVCC, the reference voltage
#define DAC_MASK        0x3FF // DAC is 10 bit, so this is the maximum value
#define DAC_FACTOR      (DAC_MASK / DAC_VREF)  // For converting to voltage
#define DAC_ON()        DAC->CTRLB.reg = 0x43
#define DAC_OFF()       DAC->CTRLB.reg = 0x40

/**
 * Set the DAC output register with the value.
 *
 * @param value   10-bit value to be set
 */
static void
DAC_setRaw(const uint16_t value) {
    DAC_OFF();
    DAC->CTRLA.reg = DAC_CTRLA_ENABLE;
    while (1 == DAC->STATUS.bit.SYNCBUSY);
    DAC->DATA.reg = DAC_MASK & value;
    while (1 == DAC->STATUS.bit.SYNCBUSY);
    DAC_ON();
}

/**
 * Set the DAC output register with the value.
 * @note the 8-bit value is left shifted; the 4 lsb are 0.
 *
 * @param value   8-bit value to be set
 */
static inline void
DAC_setRaw8(const uint8_t value) {
    DAC_setRaw((uint16_t)(value) << 4);
}   

/**
 * Set the DAC to be the specified potential.
 *
 * @param potential   the potential to be set
 */
static inline void
DAC_set(const float potential) {
    DAC_setRaw((uint16_t)(potential * DAC_FACTOR);
}

7. Use the functions in your code

I don't know what your "inputs" actually are or where they are coming from, but you can use the DAC_setRaw() and DAC_setRaw8() functions to set using a raw number or the DAC_set() function to set using a voltage floating point number.

There is also an ADC input internally connected to the DAC you can use to check if it's actually running, but that's a story for another day...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.