2 * Copyright © 2020 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include <ao_dac-samd21.h>
22 #include <ao_tcc-samd21.h>
24 /* Max DAC output value. We're using left-adjusted values */
25 #define SNEK_DAC_MAX 65535
27 #ifdef SNEK_SAMD21_DAC_TIMER
29 * If there's a timer available, we can use that
30 * to implement the 'tone' function
35 #define NSINE (sizeof(sine) / sizeof(sine[0]))
37 static uint16_t current_power;
38 static uint16_t power;
39 static uint32_t phase;
40 static uint32_t phase_step;
41 static volatile bool dac_running;
43 #define _paste2(x,y) x ## y
44 #define _paste3(x,y,z) x ## y ## z
45 #define paste2(x,y) _paste2(x,y)
46 #define paste3(x,y,z) _paste3(x,y,z)
47 #define SAMD21_TCC paste2(samd21_tcc, SNEK_SAMD21_DAC_TIMER)
48 #define SAMD21_TCC_ISR paste3(samd21_tcc, SNEK_SAMD21_DAC_TIMER, _isr)
50 #define AO_DAC_RATE 24000
52 #define UINT_TO_FIXED(u) ((uint32_t) (u) << 16)
53 #define FIXED_TO_UINT(u) ((u) >> 16)
58 uint32_t intflag = SAMD21_TCC.intflag;
59 SAMD21_TCC.intflag = intflag;
60 if (intflag & (1 << SAMD21_TCC_INTFLAG_OVF)) {
62 samd21_dac.data = ((uint32_t) sine[FIXED_TO_UINT(phase)] * current_power) >> 16;
63 if ((phase += phase_step) >= UINT_TO_FIXED(NSINE)) {
64 phase -= UINT_TO_FIXED(NSINE);
66 current_power = power;
68 /* Stop output at zero crossing when no longer outputing tone */
72 SAMD21_TCC.intenclr = (1 << SAMD21_TCC_INTFLAG_OVF);
80 ao_dac_set_hz(float hz)
82 /* samples/second = AC_DAC_RATE
86 * samples/cycle = AC_DAC_RATE / hz
90 * step/sample = step/cycle * cycle/samples
91 * = TWO_PI * hz / AC_DAC_RATE;
93 uint32_t new_phase_step = (float) UINT_TO_FIXED(NSINE) * hz / (float) AO_DAC_RATE;
97 phase_step = new_phase_step;
98 SAMD21_TCC.intenset = (1 << SAMD21_TCC_INTFLAG_OVF);
105 ao_dac_timer_init(void)
107 /* Adjust timer to interrupt once per sample period */
108 SAMD21_TCC.per = AO_HCLK / AO_DAC_RATE;
110 /* Enable timer interrupts */
111 samd21_nvic_set_enable(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS));
112 samd21_nvic_set_priority(paste3(SAMD21_NVIC_ISR_TCC, SNEK_SAMD21_DAC_TIMER, _POS), 3);
115 #define ao_dac_timer_init()
121 while (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY))
126 ao_dac_set(uint16_t new_power)
128 #if SNEK_DAC_MAX != SNEK_PWM_MAX
129 new_power = (uint16_t) ((uint32_t) new_power * SNEK_DAC_MAX) / SNEK_PWM_MAX;
133 #ifdef SNEK_SAMD21_DAC_TIMER
136 * When not generating a tone, just set the DAC
137 * output to the requested level
140 current_power = new_power;
141 samd21_dac.data = new_power;
144 samd21_dac.data = new_power;
153 samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_DAC);
155 /* enable the device */
156 samd21_pm.apbcmask |= (1 << SAMD21_PM_APBCMASK_DAC);
159 samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_SWRST);
161 while ((samd21_dac.ctrla & (1 << SAMD21_DAC_CTRLA_SWRST)) != 0 ||
162 (samd21_dac.status & (1 << SAMD21_DAC_STATUS_SYNCBUSY)) != 0)
165 /* Configure using VDD as reference */
166 samd21_dac.ctrlb = ((1 << SAMD21_DAC_CTRLB_EOEN) |
167 (0 << SAMD21_DAC_CTRLB_IOEN) |
168 (1 << SAMD21_DAC_CTRLB_LEFTADJ) |
169 (0 << SAMD21_DAC_CTRLB_VPD) |
170 (1 << SAMD21_DAC_CTRLB_BDWP) |
171 (SAMD21_DAC_CTRLB_REFSEL_VDDANA << SAMD21_DAC_CTRLB_REFSEL));
175 samd21_dac.ctrla = (1 << SAMD21_DAC_CTRLA_ENABLE);