aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_rp2_adc.go
blob: 13504d719f57fc9c177dbe8cd64f223911b7bfaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//go:build rp2040 || rp2350

package machine

import (
	"device/rp"
	"errors"
	"sync"
)

// ADCChannel is the ADC peripheral mux channel. 0-4.
type ADCChannel uint8

// ADC channels. Only ADC_TEMP_SENSOR is public. The other channels are accessed via Machine.ADC objects
const (
	adc0_CH ADCChannel = iota
	adc1_CH
	adc2_CH
	adc3_CH       // Note: GPIO29 not broken out on pico board
	adcTempSensor // Internal temperature sensor channel
)

// Used to serialise ADC sampling
var adcLock sync.Mutex

// ADC peripheral reference voltage (mV)
var adcAref uint32

// InitADC resets the ADC peripheral.
func InitADC() {
	rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC)
	rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC)
	for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) {
	}
	// enable ADC
	rp.ADC.CS.Set(rp.ADC_CS_EN)
	adcAref = 3300
	waitForReady()
}

// Configure sets the ADC pin to analog input mode.
func (a ADC) Configure(config ADCConfig) error {
	c, err := a.GetADCChannel()
	if err != nil {
		return err
	}
	return c.Configure(config)
}

// Get returns a one-shot ADC sample reading.
func (a ADC) Get() uint16 {
	if c, err := a.GetADCChannel(); err == nil {
		return c.getOnce()
	}
	// Not an ADC pin!
	return 0
}

// GetADCChannel returns the channel associated with the ADC pin.
func (a ADC) GetADCChannel() (c ADCChannel, err error) {
	err = nil
	switch a.Pin {
	case ADC0:
		c = adc0_CH
	case ADC1:
		c = adc1_CH
	case ADC2:
		c = adc2_CH
	case ADC3:
		c = adc3_CH
	default:
		err = errors.New("no ADC channel for pin value")
	}
	return c, err
}

// Configure sets the channel's associated pin to analog input mode.
// The powered on temperature sensor increases ADC_AVDD current by approximately 40 μA.
func (c ADCChannel) Configure(config ADCConfig) error {
	if config.Reference != 0 {
		adcAref = config.Reference
	}
	p, err := c.Pin()
	if err != nil {
		return err
	}
	p.Configure(PinConfig{Mode: PinAnalog})
	return nil
}

// getOnce returns a one-shot ADC sample reading from an ADC channel.
func (c ADCChannel) getOnce() uint16 {
	// Make it safe to sample multiple ADC channels in separate go routines.
	adcLock.Lock()
	rp.ADC.CS.ReplaceBits(uint32(c), 0b111, rp.ADC_CS_AINSEL_Pos)
	rp.ADC.CS.SetBits(rp.ADC_CS_START_ONCE)

	waitForReady()
	adcLock.Unlock()

	// rp2040 is a 12-bit ADC, scale raw reading to 16-bits.
	return uint16(rp.ADC.RESULT.Get()) << 4
}

// getVoltage does a one-shot sample and returns a millivolts reading.
// Integer portion is stored in the high 16 bits and fractional in the low 16 bits.
func (c ADCChannel) getVoltage() uint32 {
	return (adcAref << 16) / (1 << 12) * uint32(c.getOnce()>>4)
}

// ReadTemperature does a one-shot sample of the internal temperature sensor and returns a milli-celsius reading.
func ReadTemperature() (millicelsius int32) {
	if rp.ADC.CS.Get()&rp.ADC_CS_EN == 0 {
		InitADC()
	}

	// Enable temperature sensor bias source
	rp.ADC.CS.SetBits(rp.ADC_CS_TS_EN)

	// T = 27 - (ADC_voltage - 0.706)/0.001721
	return (27000<<16 - (int32(adcTempSensor.getVoltage())-706<<16)*581) >> 16
}

// waitForReady spins waiting for the ADC peripheral to become ready.
func waitForReady() {
	for !rp.ADC.CS.HasBits(rp.ADC_CS_READY) {
	}
}

// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one.
func (c ADCChannel) Pin() (p Pin, err error) {
	err = nil
	switch c {
	case adc0_CH:
		p = ADC0
	case adc1_CH:
		p = ADC1
	case adc2_CH:
		p = ADC2
	case adc3_CH:
		p = ADC3
	default:
		err = errors.New("no associated pin for channel")
	}
	return p, err
}