1 // Copyright 2011 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (ol.gillet@gmail.com)
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 
16 #ifndef AVRLIBX_IO_DAC_H_
17 #define AVRLIBX_IO_DAC_H_
18 
19 #include <avr/io.h>
20 
21 #include "avrlibx/avrlibx.h"
22 #include "avrlibx/io/gpio.h"
23 
24 namespace avrlibx {
25 
26 template<typename Port>
27 struct DACWrapper { };
28 
29 #define WRAP_DAC(port) \
30 template<> \
31 struct DACWrapper<Port ## port> { \
32   static inline DAC_t& dac() { return DAC ## port; } \
33   static inline uint8_t status() { return DAC ## port ## _STATUS; } \
34   template<int channel> \
35   struct Channel { \
36     static inline void Write(uint16_t value) { \
37       if (channel == 0) { \
38         DAC ## port ## _CH0DATA = value; \
39       } else { \
40         DAC ## port ## _CH1DATA = value; \
41       } \
42     } \
43     static volatile void* dma_data() { return data(); } \
44     static volatile void* data() { \
45       if (channel == 0) { \
46         return &(DAC ## port ## _CH0DATA); \
47       } else { \
48         return &(DAC ## port ## _CH1DATA); \
49       } \
50     } \
51   }; \
52   typedef uint16_t Value; \
53 };
54 
55 WRAP_DAC(B)
56 
57 enum DACReference {
58   DAC_REF_INTERNAL_1V = 0,
59   DAC_REF_AVCC = 1,
60   DAC_REF_PORTA_AREF = 2,
61   DAC_REF_PORTB_AREF = 3
62 };
63 
64 template<
65     typename Port,
66     bool ch0 = true,
67     bool ch1 = false,
68     DACReference reference = DAC_REF_INTERNAL_1V>
69 struct DAC {
70   typedef DACWrapper<Port> dac;
71 
InitDAC72   static inline void Init() {
73     uint8_t ctrl_a = DAC_ENABLE_bm;
74     if (ch1) {
75       ctrl_a |= DAC_CH1EN_bm;
76     }
77     if (ch0) {
78       ctrl_a |= DAC_CH0EN_bm;
79     }
80     dac::dac().CTRLB = ch0 && !ch1 ? 0 : DAC_CHSEL1_bm;
81     dac::dac().CTRLC = reference << DAC_REFSEL_gp | 0;  // Right-adjust
82     set_conversion_time(0x06);  // 96 CLK between conversions
83     set_refresh_time(0x01);  // Very fast auto refresh for accurate timing.
84     dac::dac().CTRLA = ctrl_a;
85   }
86 
set_conversion_timeDAC87   static inline void set_conversion_time(uint8_t conversion_time) {
88     dac::dac().TIMCTRL = (dac::dac().TIMCTRL & ~DAC_CONINTVAL_gm) \
89         | (conversion_time << DAC_CONINTVAL_gp);
90   }
91 
set_refresh_timeDAC92   static inline void set_refresh_time(uint8_t refresh_time) {
93     dac::dac().TIMCTRL = (dac::dac().TIMCTRL & ~DAC_REFRESH_gm) \
94         | (refresh_time << DAC_REFRESH_gp);
95   }
96 
97   template<int channel>
98   struct Channel {
writableDAC::Channel99     static inline uint8_t writable() {
100       if (channel == 0) {
101         return dac::status() & DAC_CH0DRE_bm;
102       } else {
103         return dac::status() & DAC_CH1DRE_bm;
104       }
105     }
106 
WriteDAC::Channel107     static inline void Write(uint16_t value) {
108       dac::template Channel<channel>::Write(value);
109     }
110 
dma_dataDAC::Channel111     static inline volatile void* dma_data() {
112       return dac::template Channel<channel>::dma_data();
113     }
114 
115     typedef uint16_t Value;
116   };
117 
writableDAC118   static uint8_t writable() {
119     uint8_t mask = DAC_CH0DRE_bm | DAC_CH1DRE_bm;
120     return dac::status() & mask == mask;
121   }
122 
WaitDAC123   static inline void Wait() {
124     while (!writable());
125   }
126 
127   Channel<0> channel_0;
128   Channel<1> channel_1;
129 };
130 
131 }  // namespace avrlibx
132 
133 #endif   // AVRLIBX_IO_DAC_H_
134