mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
90b42799a2
This makes it possible to assign I2C objects (machine.I2C0, machine.I2C1, etc.) without needing to take a pointer. This is important especially in the future when I2C may be driven using DMA and the machine.I2C type needs to store some state.
280 lines
7.2 KiB
Go
280 lines
7.2 KiB
Go
// +build avr,atmega
|
|
|
|
package machine
|
|
|
|
import (
|
|
"device/avr"
|
|
"runtime/interrupt"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
// I2C on AVR.
|
|
type I2C struct {
|
|
}
|
|
|
|
// I2C0 is the only I2C interface on most AVRs.
|
|
var I2C0 *I2C = nil
|
|
|
|
// I2CConfig is used to store config info for I2C.
|
|
type I2CConfig struct {
|
|
Frequency uint32
|
|
}
|
|
|
|
// Configure is intended to setup the I2C interface.
|
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
|
// Default I2C bus speed is 100 kHz.
|
|
if config.Frequency == 0 {
|
|
config.Frequency = TWI_FREQ_100KHZ
|
|
}
|
|
|
|
// Activate internal pullups for twi.
|
|
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))
|
|
|
|
// Initialize twi prescaler and bit rate.
|
|
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
|
|
|
// twi bit rate formula from atmega128 manual pg. 204:
|
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
|
// NOTE: TWBR should be 10 or higher for controller mode.
|
|
// It is 72 for a 16mhz board with 100kHz TWI
|
|
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
|
|
|
// Enable twi module.
|
|
avr.TWCR.Set(avr.TWCR_TWEN)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Tx does a single I2C transaction at the specified address.
|
|
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
|
if len(w) != 0 {
|
|
i2c.start(uint8(addr), true) // start transmission for writing
|
|
for _, b := range w {
|
|
i2c.writeByte(b)
|
|
}
|
|
}
|
|
if len(r) != 0 {
|
|
i2c.start(uint8(addr), false) // re-start transmission for reading
|
|
for i := range r { // read each char
|
|
r[i] = i2c.readByte()
|
|
}
|
|
}
|
|
if len(w) != 0 || len(r) != 0 {
|
|
// Stop the transmission after it has been started.
|
|
i2c.stop()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// start starts an I2C communication session.
|
|
func (i2c *I2C) start(address uint8, write bool) {
|
|
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
|
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
|
|
|
// Wait till start condition is transmitted.
|
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
|
}
|
|
|
|
// Write 7-bit shifted peripheral address.
|
|
address <<= 1
|
|
if !write {
|
|
address |= 1 // set read flag
|
|
}
|
|
i2c.writeByte(address)
|
|
}
|
|
|
|
// stop ends an I2C communication session.
|
|
func (i2c *I2C) stop() {
|
|
// Send stop condition.
|
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
|
|
|
// Wait for stop condition to be executed on bus.
|
|
for !avr.TWCR.HasBits(avr.TWCR_TWSTO) {
|
|
}
|
|
}
|
|
|
|
// writeByte writes a single byte to the I2C bus.
|
|
func (i2c *I2C) writeByte(data byte) {
|
|
// Write data to register.
|
|
avr.TWDR.Set(data)
|
|
|
|
// Clear TWI interrupt flag and enable TWI.
|
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
|
|
|
|
// Wait till data is transmitted.
|
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
|
}
|
|
}
|
|
|
|
// readByte reads a single byte from the I2C bus.
|
|
func (i2c *I2C) readByte() byte {
|
|
// Clear TWI interrupt flag and enable TWI.
|
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
|
|
|
// Wait till read request is transmitted.
|
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
|
}
|
|
|
|
return byte(avr.TWDR.Get())
|
|
}
|
|
|
|
// UART
|
|
var (
|
|
// UART0 is the hardware serial port on the AVR.
|
|
UART0 = UART{Buffer: NewRingBuffer()}
|
|
)
|
|
|
|
// UART on the AVR.
|
|
type UART struct {
|
|
Buffer *RingBuffer
|
|
}
|
|
|
|
// Configure the UART on the AVR. Defaults to 9600 baud on Arduino.
|
|
func (uart UART) Configure(config UARTConfig) {
|
|
if config.BaudRate == 0 {
|
|
config.BaudRate = 9600
|
|
}
|
|
|
|
// Register the UART interrupt.
|
|
interrupt.New(irq_USART0_RX, func(intr interrupt.Interrupt) {
|
|
// Read register to clear it.
|
|
data := avr.UDR0.Get()
|
|
|
|
// Ensure no error.
|
|
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
|
// Put data from UDR register into buffer.
|
|
UART0.Receive(byte(data))
|
|
}
|
|
})
|
|
|
|
// Set baud rate based on prescale formula from
|
|
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
|
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
|
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
|
avr.UBRR0H.Set(uint8(ps >> 8))
|
|
avr.UBRR0L.Set(uint8(ps & 0xff))
|
|
|
|
// enable RX, TX and RX interrupt
|
|
avr.UCSR0B.Set(avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0)
|
|
|
|
// 8-bits data
|
|
avr.UCSR0C.Set(avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00)
|
|
}
|
|
|
|
// WriteByte writes a byte of data to the UART.
|
|
func (uart UART) WriteByte(c byte) error {
|
|
// Wait until UART buffer is not busy.
|
|
for !avr.UCSR0A.HasBits(avr.UCSR0A_UDRE0) {
|
|
}
|
|
avr.UDR0.Set(c) // send char
|
|
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
|
|
}
|