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
Этот коммит содержится в:
родитель
11a402de95
коммит
9eb4a6268a
2 изменённых файлов: 158 добавлений и 25 удалений
49
src/examples/adc_rp2040/adc.go
Обычный файл
49
src/examples/adc_rp2040/adc.go
Обычный файл
|
@ -0,0 +1,49 @@
|
|||
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type celsius float32
|
||||
|
||||
func (c celsius) String() string {
|
||||
return fmt.Sprintf("%4.1f℃", c)
|
||||
}
|
||||
|
||||
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
|
||||
var adcReading [3]uint16
|
||||
|
||||
func readADC(a machine.ADC, w time.Duration, i int) {
|
||||
for {
|
||||
adcReading[i] = a.Get()
|
||||
time.Sleep(w)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
machine.InitADC()
|
||||
a0 := machine.ADC{machine.ADC0} // GPIO26 input
|
||||
a1 := machine.ADC{machine.ADC1} // GPIO27 input
|
||||
a2 := machine.ADC{machine.ADC2} // GPIO28 input
|
||||
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
|
||||
// Configure sets the GPIOs to PinAnalog mode
|
||||
a0.Configure(machine.ADCConfig{})
|
||||
a1.Configure(machine.ADCConfig{})
|
||||
a2.Configure(machine.ADCConfig{})
|
||||
// Configure powers on the temperature sensor
|
||||
t.Configure(machine.ADCConfig{})
|
||||
|
||||
// Safe to read concurrently
|
||||
go readADC(a0, 10*time.Millisecond, 0)
|
||||
go readADC(a1, 17*time.Millisecond, 1)
|
||||
go readADC(a2, 29*time.Millisecond, 2)
|
||||
|
||||
for {
|
||||
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
}
|
||||
}
|
|
@ -5,58 +5,142 @@ 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
|
||||
ADC_TEMP_SENSOR // 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() {
|
||||
// reset ADC
|
||||
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 configures a ADC pin to be able to be used to read data.
|
||||
func (a ADC) Configure(config ADCConfig) {
|
||||
switch a.Pin {
|
||||
case ADC0, ADC1, ADC2, ADC3:
|
||||
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||
default:
|
||||
// invalid ADC
|
||||
return
|
||||
// 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 {
|
||||
rp.ADC.CS.SetBits(uint32(a.getADCChannel()) << rp.ADC_CS_AINSEL_Pos)
|
||||
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 or powers on the temperature sensor for ADC_TEMP_SENSOR.
|
||||
// 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
|
||||
}
|
||||
if p, err := c.Pin(); err == nil {
|
||||
p.Configure(PinConfig{Mode: PinAnalog})
|
||||
}
|
||||
if c == ADC_TEMP_SENSOR {
|
||||
// Enable temperature sensor bias source
|
||||
rp.ADC.CS.SetBits(rp.ADC_CS_TS_EN)
|
||||
}
|
||||
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 uses 12-bit sampling, so scale to 16-bit
|
||||
return uint16(rp.ADC.RESULT.Get() << 4)
|
||||
// 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.
|
||||
// Only works on the ADC_TEMP_SENSOR channel. aka AINSEL=4. Other channels will return 0
|
||||
func (c ADCChannel) ReadTemperature() (millicelsius uint32) {
|
||||
if c != ADC_TEMP_SENSOR {
|
||||
return
|
||||
}
|
||||
// T = 27 - (ADC_voltage - 0.706)/0.001721
|
||||
return (27000<<16 - (c.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) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a ADC) getADCChannel() uint8 {
|
||||
switch a.Pin {
|
||||
case ADC0:
|
||||
return 0
|
||||
case ADC1:
|
||||
return 1
|
||||
case ADC2:
|
||||
return 2
|
||||
case ADC3:
|
||||
return 3
|
||||
// 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:
|
||||
return 0
|
||||
err = errors.New("no associated pin for channel")
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче