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  }