Skip to content

Commit 9d6df2b

Browse files
deadprogramaykevl
authored andcommitted
machine/samd21: implement ADC
Signed-off-by: Ron Evans <[email protected]>
1 parent 5939729 commit 9d6df2b

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

src/machine/machine_atsamd21.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,18 @@ func (p GPIO) Configure(config GPIOConfig) {
188188
}
189189
// enable port config
190190
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
191+
case GPIO_ANALOG:
192+
if p.Pin&1 > 0 {
193+
// odd pin, so save the even pins
194+
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
195+
p.setPMux(val | (GPIO_ANALOG << sam.PORT_PMUX0_PMUXO_Pos))
196+
} else {
197+
// even pin, so save the odd pins
198+
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
199+
p.setPMux(val | (GPIO_COM << sam.PORT_PMUX0_PMUXE_Pos))
200+
}
201+
// enable port config
202+
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
191203
}
192204
}
193205

@@ -258,6 +270,141 @@ func (p GPIO) setPinCfg(val sam.RegValue8) {
258270
setPinCfg(p.Pin, val)
259271
}
260272

273+
// InitADC initializes the ADC.
274+
func InitADC() {
275+
// ADC Bias Calibration
276+
// #define ADC_FUSES_BIASCAL_ADDR (NVMCTRL_OTP4 + 4)
277+
// #define ADC_FUSES_BIASCAL_Pos 3 /**< \brief (NVMCTRL_OTP4) ADC Bias Calibration */
278+
// #define ADC_FUSES_BIASCAL_Msk (0x7u << ADC_FUSES_BIASCAL_Pos)
279+
// #define ADC_FUSES_BIASCAL(value) ((ADC_FUSES_BIASCAL_Msk & ((value) << ADC_FUSES_BIASCAL_Pos)))
280+
// #define ADC_FUSES_LINEARITY_0_ADDR NVMCTRL_OTP4
281+
// #define ADC_FUSES_LINEARITY_0_Pos 27 /**< \brief (NVMCTRL_OTP4) ADC Linearity bits 4:0 */
282+
// #define ADC_FUSES_LINEARITY_0_Msk (0x1Fu << ADC_FUSES_LINEARITY_0_Pos)
283+
// #define ADC_FUSES_LINEARITY_0(value) ((ADC_FUSES_LINEARITY_0_Msk & ((value) << ADC_FUSES_LINEARITY_0_Pos)))
284+
// #define ADC_FUSES_LINEARITY_1_ADDR (NVMCTRL_OTP4 + 4)
285+
// #define ADC_FUSES_LINEARITY_1_Pos 0 /**< \brief (NVMCTRL_OTP4) ADC Linearity bits 7:5 */
286+
// #define ADC_FUSES_LINEARITY_1_Msk (0x7u << ADC_FUSES_LINEARITY_1_Pos)
287+
// #define ADC_FUSES_LINEARITY_1(value) ((ADC_FUSES_LINEARITY_1_Msk & ((value) << ADC_FUSES_LINEARITY_1_Pos)))
288+
289+
biasFuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
290+
bias := sam.RegValue16(uint16(biasFuse>>3) & uint16(0x7))
291+
292+
// ADC Linearity bits 4:0
293+
linearity0Fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020)))
294+
linearity := sam.RegValue16(uint16(linearity0Fuse>>27) & uint16(0x1f))
295+
296+
// ADC Linearity bits 7:5
297+
linearity1Fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
298+
linearity |= sam.RegValue16(uint16(linearity1Fuse)&uint16(0x7)) << 5
299+
300+
// set calibration
301+
sam.ADC.CALIB = (bias << 8) | linearity
302+
303+
// Wait for synchronization
304+
waitADCSync()
305+
306+
// Divide Clock by 32 with 12 bits resolution as default
307+
sam.ADC.CTRLB = (sam.ADC_CTRLB_PRESCALER_DIV32 << sam.ADC_CTRLB_PRESCALER_Pos) |
308+
(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
309+
310+
// Sampling Time Length
311+
sam.ADC.SAMPCTRL = 5
312+
313+
// Wait for synchronization
314+
waitADCSync()
315+
316+
// Use internal ground
317+
sam.ADC.INPUTCTRL = (sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
318+
319+
// Averaging (see datasheet table in AVGCTRL register description)
320+
sam.ADC.AVGCTRL = (sam.ADC_AVGCTRL_SAMPLENUM_1 << sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
321+
(0x0 << sam.ADC_AVGCTRL_ADJRES_Pos)
322+
323+
// Analog Reference is AREF pin (3.3v)
324+
sam.ADC.INPUTCTRL |= (sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos)
325+
326+
// 1/2 VDDANA = 0.5 * 3V3 = 1.65V
327+
sam.ADC.REFCTRL |= (sam.ADC_REFCTRL_REFSEL_INTVCC1 << sam.ADC_REFCTRL_REFSEL_Pos)
328+
}
329+
330+
// Configure configures a ADCPin to be able to be used to read data.
331+
func (a ADC) Configure() {
332+
GPIO{a.Pin}.Configure(GPIOConfig{Mode: GPIO_ANALOG})
333+
return
334+
}
335+
336+
// Get returns the current value of a ADC pin, in the range 0..0xffff.
337+
func (a ADC) Get() uint16 {
338+
ch := a.getADCChannel()
339+
340+
// Selection for the positive ADC input
341+
sam.ADC.INPUTCTRL &^= sam.ADC_INPUTCTRL_MUXPOS_Msk
342+
waitADCSync()
343+
sam.ADC.INPUTCTRL |= sam.RegValue(ch << sam.ADC_INPUTCTRL_MUXPOS_Pos)
344+
waitADCSync()
345+
346+
// Enable ADC
347+
sam.ADC.CTRLA |= sam.ADC_CTRLA_ENABLE
348+
waitADCSync()
349+
350+
// Start conversion
351+
sam.ADC.SWTRIG |= sam.ADC_SWTRIG_START
352+
waitADCSync()
353+
354+
// Clear the Data Ready flag
355+
sam.ADC.INTFLAG = sam.ADC_INTFLAG_RESRDY
356+
waitADCSync()
357+
358+
// Start conversion again, since first conversion after reference voltage changed is invalid.
359+
sam.ADC.SWTRIG |= sam.ADC_SWTRIG_START
360+
waitADCSync()
361+
362+
// Waiting for conversion to complete
363+
for (sam.ADC.INTFLAG & sam.ADC_INTFLAG_RESRDY) == 0 {
364+
}
365+
val := sam.ADC.RESULT
366+
367+
// Disable ADC
368+
sam.ADC.CTRLA &^= sam.ADC_CTRLA_ENABLE
369+
waitADCSync()
370+
371+
return uint16(val)
372+
}
373+
374+
func (a ADC) getADCChannel() uint8 {
375+
switch a.Pin {
376+
case PA02:
377+
return 0
378+
case PB08:
379+
return 2
380+
case PB09:
381+
return 3
382+
case PA04:
383+
return 4
384+
case PA05:
385+
return 5
386+
case PA06:
387+
return 6
388+
case PA07:
389+
return 7
390+
case PB02:
391+
return 10
392+
case PB03:
393+
return 11
394+
case PA09:
395+
return 17
396+
case PA11:
397+
return 19
398+
default:
399+
return 0
400+
}
401+
}
402+
403+
func waitADCSync() {
404+
for (sam.ADC.STATUS & sam.ADC_STATUS_SYNCBUSY) > 0 {
405+
}
406+
}
407+
261408
// UART on the SAMD21.
262409
type UART struct {
263410
Buffer *RingBuffer

src/runtime/runtime_atsamd21.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func init() {
2424
initRTC()
2525
initSERCOMClocks()
2626
initUSBClock()
27+
initADCClock()
2728

2829
// connect to USB CDC interface
2930
machine.UART0.Configure(machine.UARTConfig{})
@@ -355,3 +356,14 @@ func initUSBClock() {
355356
sam.GCLK_CLKCTRL_CLKEN)
356357
waitForSync()
357358
}
359+
360+
func initADCClock() {
361+
// Turn on clock for ADC
362+
sam.PM.APBCMASK |= sam.PM_APBCMASK_ADC_
363+
364+
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
365+
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
366+
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
367+
sam.GCLK_CLKCTRL_CLKEN)
368+
waitForSync()
369+
}

0 commit comments

Comments
 (0)