Add SPI support for Atmega based chips.
This is based on @Nerzal's #1398 PR, but is a bit of a refactor and expansion to support all the Atmega based chips present in tinygo.
Этот коммит содержится в:
		
							родитель
							
								
									9f5bd2c460
								
							
						
					
					
						коммит
						f4b4dd8d62
					
				
					 6 изменённых файлов: 249 добавлений и 2 удалений
				
			
		| 
						 | 
					@ -5,6 +5,8 @@ package machine
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"device/avr"
 | 
						"device/avr"
 | 
				
			||||||
	"runtime/interrupt"
 | 
						"runtime/interrupt"
 | 
				
			||||||
 | 
						"runtime/volatile"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// I2CConfig is used to store config info for I2C.
 | 
					// I2CConfig is used to store config info for I2C.
 | 
				
			||||||
| 
						 | 
					@ -157,3 +159,108 @@ func (uart UART) WriteByte(c byte) error {
 | 
				
			||||||
	avr.UDR0.Set(c) // send char
 | 
						avr.UDR0.Set(c) // send char
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPIConfig is used to store config info for SPI.
 | 
				
			||||||
 | 
					type SPIConfig struct {
 | 
				
			||||||
 | 
						Frequency uint32
 | 
				
			||||||
 | 
						LSBFirst  bool
 | 
				
			||||||
 | 
						Mode      uint8
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPI is for the Serial Peripheral Interface
 | 
				
			||||||
 | 
					// Data is taken from http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf page 169 and following
 | 
				
			||||||
 | 
					type SPI struct {
 | 
				
			||||||
 | 
						// The registers for the SPIx port set by the chip
 | 
				
			||||||
 | 
						spcr *volatile.Register8
 | 
				
			||||||
 | 
						spdr *volatile.Register8
 | 
				
			||||||
 | 
						spsr *volatile.Register8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The io pins for the SPIx port set by the chip
 | 
				
			||||||
 | 
						sck Pin
 | 
				
			||||||
 | 
						sdi Pin
 | 
				
			||||||
 | 
						sdo Pin
 | 
				
			||||||
 | 
						cs  Pin
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Configure is intended to setup the SPI interface.
 | 
				
			||||||
 | 
					func (s SPI) Configure(config SPIConfig) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is only here to help catch a bug with the configuration
 | 
				
			||||||
 | 
						// where a machine missed a value.
 | 
				
			||||||
 | 
						if s.spcr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
 | 
				
			||||||
 | 
							s.spsr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
 | 
				
			||||||
 | 
							s.spdr == (*volatile.Register8)(unsafe.Pointer(uintptr(0))) ||
 | 
				
			||||||
 | 
							s.sck == 0 || s.sdi == 0 || s.sdo == 0 || s.cs == 0 {
 | 
				
			||||||
 | 
							return errSPIInvalidMachineConfig
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make the defaults meaningful
 | 
				
			||||||
 | 
						if config.Frequency == 0 {
 | 
				
			||||||
 | 
							config.Frequency = 4000000
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Default all port configuration bits to 0 for simplicity
 | 
				
			||||||
 | 
						s.spcr.Set(0)
 | 
				
			||||||
 | 
						s.spsr.Set(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Setup pins output configuration
 | 
				
			||||||
 | 
						s.sck.Configure(PinConfig{Mode: PinOutput})
 | 
				
			||||||
 | 
						s.sdi.Configure(PinConfig{Mode: PinInput})
 | 
				
			||||||
 | 
						s.sdo.Configure(PinConfig{Mode: PinOutput})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Prevent CS glitches if the pin is enabled Low (0, default)
 | 
				
			||||||
 | 
						s.cs.High()
 | 
				
			||||||
 | 
						// If the CS pin is not configured as output the SPI port operates in
 | 
				
			||||||
 | 
						// slave mode.
 | 
				
			||||||
 | 
						s.cs.Configure(PinConfig{Mode: PinOutput})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frequencyDivider := CPUFrequency() / config.Frequency
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case frequencyDivider >= 128:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
 | 
				
			||||||
 | 
						case frequencyDivider >= 64:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_SPR1)
 | 
				
			||||||
 | 
						case frequencyDivider >= 32:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_SPR1)
 | 
				
			||||||
 | 
							s.spsr.SetBits(avr.SPSR_SPI2X)
 | 
				
			||||||
 | 
						case frequencyDivider >= 16:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_SPR0)
 | 
				
			||||||
 | 
						case frequencyDivider >= 8:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_SPR0)
 | 
				
			||||||
 | 
							s.spsr.SetBits(avr.SPSR_SPI2X)
 | 
				
			||||||
 | 
						case frequencyDivider >= 4:
 | 
				
			||||||
 | 
							// The clock is already set to all 0's.
 | 
				
			||||||
 | 
						default: // defaults to fastest which is /2
 | 
				
			||||||
 | 
							s.spsr.SetBits(avr.SPSR_SPI2X)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch config.Mode {
 | 
				
			||||||
 | 
						case Mode1:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_CPHA)
 | 
				
			||||||
 | 
						case Mode2:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_CPOL)
 | 
				
			||||||
 | 
						case Mode3:
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
 | 
				
			||||||
 | 
						default: // default is mode 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.LSBFirst {
 | 
				
			||||||
 | 
							s.spcr.SetBits(avr.SPCR_DORD)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// enable SPI, set controller, set clock rate
 | 
				
			||||||
 | 
						s.spcr.SetBits(avr.SPCR_SPE | avr.SPCR_MSTR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Transfer writes the byte into the register and returns the read content
 | 
				
			||||||
 | 
					func (s SPI) Transfer(b byte) (byte, error) {
 | 
				
			||||||
 | 
						s.spdr.Set(uint8(b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for !s.spsr.HasBits(avr.SPSR_SPIF) {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return byte(s.spdr.Get()), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,3 +69,13 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
 | 
				
			||||||
		return avr.PORTD, 1 << uint8(p-portD)
 | 
							return avr.PORTD, 1 << uint8(p-portD)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPI configuration
 | 
				
			||||||
 | 
					var SPI0 = SPI{
 | 
				
			||||||
 | 
						spcr: avr.SPCR,
 | 
				
			||||||
 | 
						spsr: avr.SPSR,
 | 
				
			||||||
 | 
						spdr: avr.SPDR,
 | 
				
			||||||
 | 
						sck:  PB7,
 | 
				
			||||||
 | 
						sdo:  PB5,
 | 
				
			||||||
 | 
						sdi:  PB6,
 | 
				
			||||||
 | 
						cs:   PB4}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,3 +126,13 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
 | 
				
			||||||
		return avr.PORTA, 255
 | 
							return avr.PORTA, 255
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPI configuration
 | 
				
			||||||
 | 
					var SPI0 = SPI{
 | 
				
			||||||
 | 
						spcr: avr.SPCR,
 | 
				
			||||||
 | 
						spdr: avr.SPDR,
 | 
				
			||||||
 | 
						spsr: avr.SPSR,
 | 
				
			||||||
 | 
						sck:  PB1,
 | 
				
			||||||
 | 
						sdo:  PB2,
 | 
				
			||||||
 | 
						sdi:  PB3,
 | 
				
			||||||
 | 
						cs:   PB0}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,3 +88,13 @@ func (pwm PWM) Set(value uint16) {
 | 
				
			||||||
		panic("Invalid PWM pin")
 | 
							panic("Invalid PWM pin")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPI configuration
 | 
				
			||||||
 | 
					var SPI0 = SPI{
 | 
				
			||||||
 | 
						spcr: avr.SPCR,
 | 
				
			||||||
 | 
						spdr: avr.SPDR,
 | 
				
			||||||
 | 
						spsr: avr.SPSR,
 | 
				
			||||||
 | 
						sck:  PB5,
 | 
				
			||||||
 | 
						sdo:  PB3,
 | 
				
			||||||
 | 
						sdi:  PB4,
 | 
				
			||||||
 | 
						cs:   PB2}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										109
									
								
								src/machine/machine_atmega328pb.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										109
									
								
								src/machine/machine_atmega328pb.go
									
										
									
									
									
										Обычный файл
									
								
							| 
						 | 
					@ -0,0 +1,109 @@
 | 
				
			||||||
 | 
					// +build avr,atmega328pb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package machine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"device/avr"
 | 
				
			||||||
 | 
						"runtime/volatile"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const irq_USART0_RX = avr.IRQ_USART0_RX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getPortMask returns the PORTx register and mask for the pin.
 | 
				
			||||||
 | 
					func (p Pin) getPortMask() (*volatile.Register8, uint8) {
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case p >= PB0 && p <= PB7: // port B
 | 
				
			||||||
 | 
							return avr.PORTB, 1 << uint8(p-portB)
 | 
				
			||||||
 | 
						case p >= PC0 && p <= PC7: // port C
 | 
				
			||||||
 | 
							return avr.PORTC, 1 << uint8(p-portC)
 | 
				
			||||||
 | 
						default: // port D
 | 
				
			||||||
 | 
							return avr.PORTD, 1 << uint8(p-portD)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitPWM initializes the registers needed for PWM.
 | 
				
			||||||
 | 
					func InitPWM() {
 | 
				
			||||||
 | 
						// use waveform generation
 | 
				
			||||||
 | 
						avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set timer 0 prescale factor to 64
 | 
				
			||||||
 | 
						avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set timer 1 prescale factor to 64
 | 
				
			||||||
 | 
						avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// put timer 1 in 8-bit phase correct pwm mode
 | 
				
			||||||
 | 
						avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set timer 2 prescale factor to 64
 | 
				
			||||||
 | 
						avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// configure timer 2 for phase correct pwm (8-bit)
 | 
				
			||||||
 | 
						avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Configure configures a PWM pin for output.
 | 
				
			||||||
 | 
					func (pwm PWM) Configure() error {
 | 
				
			||||||
 | 
						switch pwm.Pin / 8 {
 | 
				
			||||||
 | 
						case 0: // port B
 | 
				
			||||||
 | 
							avr.DDRB.SetBits(1 << uint8(pwm.Pin))
 | 
				
			||||||
 | 
						case 2: // port D
 | 
				
			||||||
 | 
							avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
 | 
				
			||||||
 | 
					// 8-bit value ranging from 0 to 255.
 | 
				
			||||||
 | 
					func (pwm PWM) Set(value uint16) {
 | 
				
			||||||
 | 
						value8 := uint8(value >> 8)
 | 
				
			||||||
 | 
						switch pwm.Pin {
 | 
				
			||||||
 | 
						case PD3:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 2, channel B
 | 
				
			||||||
 | 
							avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
 | 
				
			||||||
 | 
							avr.OCR2B.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						case PD5:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 0, channel B
 | 
				
			||||||
 | 
							avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
 | 
				
			||||||
 | 
							avr.OCR0B.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						case PD6:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 0, channel A
 | 
				
			||||||
 | 
							avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
 | 
				
			||||||
 | 
							avr.OCR0A.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						case PB1:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 1, channel A
 | 
				
			||||||
 | 
							avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
 | 
				
			||||||
 | 
							// this is a 16-bit value, but we only currently allow the low order bits to be set
 | 
				
			||||||
 | 
							avr.OCR1AL.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						case PB2:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 1, channel B
 | 
				
			||||||
 | 
							avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
 | 
				
			||||||
 | 
							// this is a 16-bit value, but we only currently allow the low order bits to be set
 | 
				
			||||||
 | 
							avr.OCR1BL.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						case PB3:
 | 
				
			||||||
 | 
							// connect pwm to pin on timer 2, channel A
 | 
				
			||||||
 | 
							avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
 | 
				
			||||||
 | 
							avr.OCR2A.Set(value8) // set pwm duty
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							panic("Invalid PWM pin")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SPI configuration
 | 
				
			||||||
 | 
					var SPI0 = SPI{
 | 
				
			||||||
 | 
						spcr: avr.SPCR0,
 | 
				
			||||||
 | 
						spdr: avr.SPDR0,
 | 
				
			||||||
 | 
						spsr: avr.SPSR0,
 | 
				
			||||||
 | 
						sck:  PB5,
 | 
				
			||||||
 | 
						sdo:  PB3,
 | 
				
			||||||
 | 
						sdi:  PB4,
 | 
				
			||||||
 | 
						cs:   PB2}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var SPI1 = SPI{
 | 
				
			||||||
 | 
						spcr: avr.SPCR1,
 | 
				
			||||||
 | 
						spdr: avr.SPDR1,
 | 
				
			||||||
 | 
						spsr: avr.SPSR1,
 | 
				
			||||||
 | 
						sck:  PC1,
 | 
				
			||||||
 | 
						sdo:  PE3,
 | 
				
			||||||
 | 
						sdi:  PC0,
 | 
				
			||||||
 | 
						cs:   PE2}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
// +build !baremetal sam stm32,!stm32f7x2 fe310 k210
 | 
					// +build !baremetal sam stm32,!stm32f7x2 fe310 k210 atmega
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package machine
 | 
					package machine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,8 @@ const (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
 | 
						ErrTxInvalidSliceSize      = errors.New("SPI write and read slices must be same size")
 | 
				
			||||||
 | 
						errSPIInvalidMachineConfig = errors.New("SPI port was not configured properly by the machine")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
 | 
					// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче