Skip to content

Commit 9eb4a62

Browse files
authored
Pico adc input ch support (#2737)
machine/rp2040: ADC changes, including * Add rp2040 ADC mux channel support. Internal temp sensor reading and fix for incorrect setting of CS.AINSEL reg bits * Reset ADC ref voltage in InitADC
1 parent 11a402d commit 9eb4a62

File tree

2 files changed

+157
-24
lines changed

2 files changed

+157
-24
lines changed

src/examples/adc_rp2040/adc.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
2+
3+
package main
4+
5+
import (
6+
"fmt"
7+
"machine"
8+
"time"
9+
)
10+
11+
type celsius float32
12+
13+
func (c celsius) String() string {
14+
return fmt.Sprintf("%4.1f℃", c)
15+
}
16+
17+
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
18+
var adcReading [3]uint16
19+
20+
func readADC(a machine.ADC, w time.Duration, i int) {
21+
for {
22+
adcReading[i] = a.Get()
23+
time.Sleep(w)
24+
}
25+
}
26+
27+
func main() {
28+
machine.InitADC()
29+
a0 := machine.ADC{machine.ADC0} // GPIO26 input
30+
a1 := machine.ADC{machine.ADC1} // GPIO27 input
31+
a2 := machine.ADC{machine.ADC2} // GPIO28 input
32+
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
33+
// Configure sets the GPIOs to PinAnalog mode
34+
a0.Configure(machine.ADCConfig{})
35+
a1.Configure(machine.ADCConfig{})
36+
a2.Configure(machine.ADCConfig{})
37+
// Configure powers on the temperature sensor
38+
t.Configure(machine.ADCConfig{})
39+
40+
// Safe to read concurrently
41+
go readADC(a0, 10*time.Millisecond, 0)
42+
go readADC(a1, 17*time.Millisecond, 1)
43+
go readADC(a2, 29*time.Millisecond, 2)
44+
45+
for {
46+
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
47+
time.Sleep(1000 * time.Millisecond)
48+
}
49+
}

src/machine/machine_rp2040_adc.go

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,142 @@ package machine
55

66
import (
77
"device/rp"
8+
"errors"
9+
"sync"
810
)
911

12+
// ADCChannel is the ADC peripheral mux channel. 0-4.
13+
type ADCChannel uint8
14+
15+
// ADC channels. Only ADC_TEMP_SENSOR is public. The other channels are accessed via Machine.ADC objects
16+
const (
17+
adc0_CH ADCChannel = iota
18+
adc1_CH
19+
adc2_CH
20+
adc3_CH // Note: GPIO29 not broken out on pico board
21+
ADC_TEMP_SENSOR // Internal temperature sensor channel
22+
)
23+
24+
// Used to serialise ADC sampling
25+
var adcLock sync.Mutex
26+
27+
// ADC peripheral reference voltage (mV)
28+
var adcAref uint32
29+
30+
// InitADC resets the ADC peripheral.
1031
func InitADC() {
11-
// reset ADC
1232
rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC)
1333
rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC)
1434
for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) {
1535
}
16-
1736
// enable ADC
1837
rp.ADC.CS.Set(rp.ADC_CS_EN)
19-
38+
adcAref = 3300
2039
waitForReady()
2140
}
2241

23-
// Configure configures a ADC pin to be able to be used to read data.
24-
func (a ADC) Configure(config ADCConfig) {
42+
// Configure sets the ADC pin to analog input mode.
43+
func (a ADC) Configure(config ADCConfig) error {
44+
c, err := a.GetADCChannel()
45+
if err != nil {
46+
return err
47+
}
48+
return c.Configure(config)
49+
}
50+
51+
// Get returns a one-shot ADC sample reading.
52+
func (a ADC) Get() uint16 {
53+
if c, err := a.GetADCChannel(); err == nil {
54+
return c.getOnce()
55+
}
56+
// Not an ADC pin!
57+
return 0
58+
}
59+
60+
// GetADCChannel returns the channel associated with the ADC pin.
61+
func (a ADC) GetADCChannel() (c ADCChannel, err error) {
62+
err = nil
2563
switch a.Pin {
26-
case ADC0, ADC1, ADC2, ADC3:
27-
a.Pin.Configure(PinConfig{Mode: PinAnalog})
64+
case ADC0:
65+
c = adc0_CH
66+
case ADC1:
67+
c = adc1_CH
68+
case ADC2:
69+
c = adc2_CH
70+
case ADC3:
71+
c = adc3_CH
2872
default:
29-
// invalid ADC
30-
return
73+
err = errors.New("no ADC channel for pin value")
3174
}
75+
return c, err
3276
}
3377

34-
func (a ADC) Get() uint16 {
35-
rp.ADC.CS.SetBits(uint32(a.getADCChannel()) << rp.ADC_CS_AINSEL_Pos)
78+
// Configure sets the channel's associated pin to analog input mode or powers on the temperature sensor for ADC_TEMP_SENSOR.
79+
// The powered on temperature sensor increases ADC_AVDD current by approximately 40 μA.
80+
func (c ADCChannel) Configure(config ADCConfig) error {
81+
if config.Reference != 0 {
82+
adcAref = config.Reference
83+
}
84+
if p, err := c.Pin(); err == nil {
85+
p.Configure(PinConfig{Mode: PinAnalog})
86+
}
87+
if c == ADC_TEMP_SENSOR {
88+
// Enable temperature sensor bias source
89+
rp.ADC.CS.SetBits(rp.ADC_CS_TS_EN)
90+
}
91+
return nil
92+
}
93+
94+
// getOnce returns a one-shot ADC sample reading from an ADC channel.
95+
func (c ADCChannel) getOnce() uint16 {
96+
// Make it safe to sample multiple ADC channels in separate go routines.
97+
adcLock.Lock()
98+
rp.ADC.CS.ReplaceBits(uint32(c), 0b111, rp.ADC_CS_AINSEL_Pos)
3699
rp.ADC.CS.SetBits(rp.ADC_CS_START_ONCE)
37100

38101
waitForReady()
102+
adcLock.Unlock()
103+
104+
// rp2040 is a 12-bit ADC, scale raw reading to 16-bits.
105+
return uint16(rp.ADC.RESULT.Get()) << 4
106+
}
39107

40-
// rp2040 uses 12-bit sampling, so scale to 16-bit
41-
return uint16(rp.ADC.RESULT.Get() << 4)
108+
// getVoltage does a one-shot sample and returns a millivolts reading.
109+
// Integer portion is stored in the high 16 bits and fractional in the low 16 bits.
110+
func (c ADCChannel) getVoltage() uint32 {
111+
return (adcAref << 16) / (1 << 12) * uint32(c.getOnce()>>4)
42112
}
43113

114+
// ReadTemperature does a one-shot sample of the internal temperature sensor and returns a milli-celsius reading.
115+
// Only works on the ADC_TEMP_SENSOR channel. aka AINSEL=4. Other channels will return 0
116+
func (c ADCChannel) ReadTemperature() (millicelsius uint32) {
117+
if c != ADC_TEMP_SENSOR {
118+
return
119+
}
120+
// T = 27 - (ADC_voltage - 0.706)/0.001721
121+
return (27000<<16 - (c.getVoltage()-706<<16)*581) >> 16
122+
}
123+
124+
// waitForReady spins waiting for the ADC peripheral to become ready.
44125
func waitForReady() {
45126
for !rp.ADC.CS.HasBits(rp.ADC_CS_READY) {
46127
}
47128
}
48129

49-
func (a ADC) getADCChannel() uint8 {
50-
switch a.Pin {
51-
case ADC0:
52-
return 0
53-
case ADC1:
54-
return 1
55-
case ADC2:
56-
return 2
57-
case ADC3:
58-
return 3
130+
// The Pin method returns the GPIO Pin associated with the ADC mux channel, if it has one.
131+
func (c ADCChannel) Pin() (p Pin, err error) {
132+
err = nil
133+
switch c {
134+
case adc0_CH:
135+
p = ADC0
136+
case adc1_CH:
137+
p = ADC1
138+
case adc2_CH:
139+
p = ADC2
140+
case adc3_CH:
141+
p = ADC3
59142
default:
60-
return 0
143+
err = errors.New("no associated pin for channel")
61144
}
145+
return p, err
62146
}

0 commit comments

Comments
 (0)