github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_adc.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "device/rp" 7 "errors" 8 "sync" 9 ) 10 11 // ADCChannel is the ADC peripheral mux channel. 0-4. 12 type ADCChannel uint8 13 14 // ADC channels. Only ADC_TEMP_SENSOR is public. The other channels are accessed via Machine.ADC objects 15 const ( 16 adc0_CH ADCChannel = iota 17 adc1_CH 18 adc2_CH 19 adc3_CH // Note: GPIO29 not broken out on pico board 20 adcTempSensor // Internal temperature sensor channel 21 ) 22 23 // Used to serialise ADC sampling 24 var adcLock sync.Mutex 25 26 // ADC peripheral reference voltage (mV) 27 var adcAref uint32 28 29 // InitADC resets the ADC peripheral. 30 func InitADC() { 31 rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC) 32 rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC) 33 for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) { 34 } 35 // enable ADC 36 rp.ADC.CS.Set(rp.ADC_CS_EN) 37 adcAref = 3300 38 waitForReady() 39 } 40 41 // Configure sets the ADC pin to analog input mode. 42 func (a ADC) Configure(config ADCConfig) error { 43 c, err := a.GetADCChannel() 44 if err != nil { 45 return err 46 } 47 return c.Configure(config) 48 } 49 50 // Get returns a one-shot ADC sample reading. 51 func (a ADC) Get() uint16 { 52 if c, err := a.GetADCChannel(); err == nil { 53 return c.getOnce() 54 } 55 // Not an ADC pin! 56 return 0 57 } 58 59 // GetADCChannel returns the channel associated with the ADC pin. 60 func (a ADC) GetADCChannel() (c ADCChannel, err error) { 61 err = nil 62 switch a.Pin { 63 case ADC0: 64 c = adc0_CH 65 case ADC1: 66 c = adc1_CH 67 case ADC2: 68 c = adc2_CH 69 case ADC3: 70 c = adc3_CH 71 default: 72 err = errors.New("no ADC channel for pin value") 73 } 74 return c, err 75 } 76 77 // Configure sets the channel's associated pin to analog input mode. 78 // The powered on temperature sensor increases ADC_AVDD current by approximately 40 μA. 79 func (c ADCChannel) Configure(config ADCConfig) error { 80 if config.Reference != 0 { 81 adcAref = config.Reference 82 } 83 p, err := c.Pin() 84 if err != nil { 85 return err 86 } 87 p.Configure(PinConfig{Mode: PinAnalog}) 88 return nil 89 } 90 91 // getOnce returns a one-shot ADC sample reading from an ADC channel. 92 func (c ADCChannel) getOnce() uint16 { 93 // Make it safe to sample multiple ADC channels in separate go routines. 94 adcLock.Lock() 95 rp.ADC.CS.ReplaceBits(uint32(c), 0b111, rp.ADC_CS_AINSEL_Pos) 96 rp.ADC.CS.SetBits(rp.ADC_CS_START_ONCE) 97 98 waitForReady() 99 adcLock.Unlock() 100 101 // rp2040 is a 12-bit ADC, scale raw reading to 16-bits. 102 return uint16(rp.ADC.RESULT.Get()) << 4 103 } 104 105 // getVoltage does a one-shot sample and returns a millivolts reading. 106 // Integer portion is stored in the high 16 bits and fractional in the low 16 bits. 107 func (c ADCChannel) getVoltage() uint32 { 108 return (adcAref << 16) / (1 << 12) * uint32(c.getOnce()>>4) 109 } 110 111 // ReadTemperature does a one-shot sample of the internal temperature sensor and returns a milli-celsius reading. 112 func ReadTemperature() (millicelsius int32) { 113 if rp.ADC.CS.Get()&rp.ADC_CS_EN == 0 { 114 InitADC() 115 } 116 117 // Enable temperature sensor bias source 118 rp.ADC.CS.SetBits(rp.ADC_CS_TS_EN) 119 120 // T = 27 - (ADC_voltage - 0.706)/0.001721 121 return (27000<<16 - (int32(adcTempSensor.getVoltage())-706<<16)*581) >> 16 122 } 123 124 // waitForReady spins waiting for the ADC peripheral to become ready. 125 func waitForReady() { 126 for !rp.ADC.CS.HasBits(rp.ADC_CS_READY) { 127 } 128 } 129 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 142 default: 143 err = errors.New("no associated pin for channel") 144 } 145 return p, err 146 }