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()