github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_avr.go (about)

     1  //go:build avr && !avrtiny
     2  
     3  package machine
     4  
     5  import (
     6  	"device/avr"
     7  	"runtime/volatile"
     8  	"unsafe"
     9  )
    10  
    11  const deviceName = avr.DEVICE
    12  
    13  const (
    14  	PinInput PinMode = iota
    15  	PinInputPullup
    16  	PinOutput
    17  )
    18  
    19  // In all the AVRs I've looked at, the PIN/DDR/PORT registers followed a regular
    20  // pattern: PINx, DDRx, PORTx in this order without registers in between.
    21  // Therefore, if you know any of them, you can calculate the other two.
    22  //
    23  // For now, I've chosen to let the PORTx register be the one that is returned
    24  // for each specific chip and to calculate the others from that one. Setting an
    25  // output port (done using PORTx) is likely the most common operation and the
    26  // one that is the most time critical. For others, the PINx and DDRx register
    27  // can trivially be calculated using a subtraction.
    28  
    29  // Configure sets the pin to input or output.
    30  func (p Pin) Configure(config PinConfig) {
    31  	port, mask := p.getPortMask()
    32  	// The DDRx register can be found by subtracting one from the PORTx
    33  	// register, as this appears to be the case for many (most? all?) AVR chips.
    34  	ddr := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 1))
    35  	if config.Mode == PinOutput {
    36  		// set output bit
    37  		ddr.SetBits(mask)
    38  
    39  		// Note: if the pin was PinInputPullup before, it'll now be high.
    40  		// Otherwise it will be low.
    41  	} else {
    42  		// configure input: clear output bit
    43  		ddr.ClearBits(mask)
    44  
    45  		if config.Mode == PinInput {
    46  			// No pullup (floating).
    47  			// The transition may be one of the following:
    48  			//   output high -> input pullup -> input (safe: output high and input pullup are similar)
    49  			//   output low  -> input        -> input (safe: no extra transition)
    50  			port.ClearBits(mask)
    51  		} else {
    52  			// Pullup.
    53  			// The transition may be one of the following:
    54  			//   output high -> input pullup -> input pullup (safe: no extra transition)
    55  			//   output low  -> input        -> input pullup (possibly problematic)
    56  			// For the last transition (output low -> input -> input pullup),
    57  			// the transition may be problematic in some cases because there is
    58  			// an intermediate floating state (which may cause irratic
    59  			// interrupts, for example). If this is a problem, the application
    60  			// should set the pin high before configuring it as PinInputPullup.
    61  			// We can't do that here because setting it to high as an
    62  			// intermediate state may have other problems.
    63  			port.SetBits(mask)
    64  		}
    65  	}
    66  }
    67  
    68  // Get returns the current value of a GPIO pin when the pin is configured as an
    69  // input or as an output.
    70  func (p Pin) Get() bool {
    71  	port, mask := p.getPortMask()
    72  	// As noted above, the PINx register is always two registers below the PORTx
    73  	// register, so we can find it simply by subtracting two from the PORTx
    74  	// register address.
    75  	pin := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 2)) // PINA, PINB, etc
    76  	return (pin.Get() & mask) > 0
    77  }
    78  
    79  // Set changes the value of the GPIO pin. The pin must be configured as output.
    80  func (p Pin) Set(value bool) {
    81  	if value { // set bits
    82  		port, mask := p.PortMaskSet()
    83  		port.Set(mask)
    84  	} else { // clear bits
    85  		port, mask := p.PortMaskClear()
    86  		port.Set(mask)
    87  	}
    88  }
    89  
    90  // Return the register and mask to enable a given GPIO pin. This can be used to
    91  // implement bit-banged drivers.
    92  //
    93  // Warning: there are no separate pin set/clear registers on the AVR. The
    94  // returned mask is only valid as long as no other pin in the same port has been
    95  // changed.
    96  func (p Pin) PortMaskSet() (*volatile.Register8, uint8) {
    97  	port, mask := p.getPortMask()
    98  	return port, port.Get() | mask
    99  }
   100  
   101  // Return the register and mask to disable a given port. This can be used to
   102  // implement bit-banged drivers.
   103  //
   104  // Warning: there are no separate pin set/clear registers on the AVR. The
   105  // returned mask is only valid as long as no other pin in the same port has been
   106  // changed.
   107  func (p Pin) PortMaskClear() (*volatile.Register8, uint8) {
   108  	port, mask := p.getPortMask()
   109  	return port, port.Get() &^ mask
   110  }
   111  
   112  // InitADC initializes the registers needed for ADC.
   113  func InitADC() {
   114  	// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
   115  	avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
   116  
   117  	// enable a2d conversions
   118  	avr.ADCSRA.SetBits(avr.ADCSRA_ADEN)
   119  }
   120  
   121  // Configure configures a ADCPin to be able to be used to read data.
   122  func (a ADC) Configure(ADCConfig) {
   123  	return // no pin specific setup on AVR machine.
   124  }
   125  
   126  // Get returns the current value of a ADC pin, in the range 0..0xffff. The AVR
   127  // has an ADC of 10 bits precision so the lower 6 bits will be zero.
   128  func (a ADC) Get() uint16 {
   129  	// set the analog reference (high two bits of ADMUX) and select the
   130  	// channel (low 4 bits), masked to only turn on one ADC at a time.
   131  	// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
   132  	// bits. This has the same effect as shifting the return value left by 6
   133  	// bits.
   134  	avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (uint8(a.Pin) & 0x07))
   135  
   136  	// start the conversion
   137  	avr.ADCSRA.SetBits(avr.ADCSRA_ADSC)
   138  
   139  	// ADSC is cleared when the conversion finishes
   140  	for ok := true; ok; ok = avr.ADCSRA.HasBits(avr.ADCSRA_ADSC) {
   141  	}
   142  
   143  	return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
   144  }
   145  
   146  // linked from runtime.adjustMonotonicTimer
   147  func adjustMonotonicTimer()
   148  
   149  // linked from runtime.initMonotonicTimer
   150  func initMonotonicTimer()