@@ -5,58 +5,142 @@ package machine
5
5
6
6
import (
7
7
"device/rp"
8
+ "errors"
9
+ "sync"
8
10
)
9
11
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.
10
31
func InitADC () {
11
- // reset ADC
12
32
rp .RESETS .RESET .SetBits (rp .RESETS_RESET_ADC )
13
33
rp .RESETS .RESET .ClearBits (rp .RESETS_RESET_ADC )
14
34
for ! rp .RESETS .RESET_DONE .HasBits (rp .RESETS_RESET_ADC ) {
15
35
}
16
-
17
36
// enable ADC
18
37
rp .ADC .CS .Set (rp .ADC_CS_EN )
19
-
38
+ adcAref = 3300
20
39
waitForReady ()
21
40
}
22
41
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
25
63
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
28
72
default :
29
- // invalid ADC
30
- return
73
+ err = errors .New ("no ADC channel for pin value" )
31
74
}
75
+ return c , err
32
76
}
33
77
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 )
36
99
rp .ADC .CS .SetBits (rp .ADC_CS_START_ONCE )
37
100
38
101
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
+ }
39
107
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 )
42
112
}
43
113
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.
44
125
func waitForReady () {
45
126
for ! rp .ADC .CS .HasBits (rp .ADC_CS_READY ) {
46
127
}
47
128
}
48
129
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
59
142
default :
60
- return 0
143
+ err = errors . New ( "no associated pin for channel" )
61
144
}
145
+ return p , err
62
146
}
0 commit comments