From 4c8a725d78ad975714bf2386b98c556593ba6591 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Wed, 26 Sep 2018 12:34:04 +0200 Subject: [PATCH] avr: implement UART interface Signed-off-by: Ron Evans --- src/examples/echo/echo.go | 37 +++++++++++ src/machine/machine_avr.go | 126 +++++++++++++++++++++++++++++++++++++ src/runtime/runtime_avr.go | 5 +- 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/examples/echo/echo.go diff --git a/src/examples/echo/echo.go b/src/examples/echo/echo.go new file mode 100644 index 0000000000..20cae14564 --- /dev/null +++ b/src/examples/echo/echo.go @@ -0,0 +1,37 @@ +// This is a echo console running on the device UART. +// Connect using default baudrate for this hardware, 8-N-1 with your terminal program. +package main + +import ( + "machine" + "time" +) + +func main() { + machine.UART0.Configure(machine.UARTConfig{}) + machine.UART0.Write([]byte("Echo console enabled. Type something then press enter:\r\n")) + + input := make([]byte, 64) + i := 0 + for { + if machine.UART0.Buffered() > 0 { + data, _ := machine.UART0.ReadByte() + + switch data { + case 13: + // return key + machine.UART0.Write([]byte("\r\n")) + machine.UART0.Write([]byte("You typed: ")) + machine.UART0.Write(input[:i]) + machine.UART0.Write([]byte("\r\n")) + i = 0 + default: + // just echo the character + machine.UART0.WriteByte(data) + input[i] = data + i++ + } + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index 2cc5282b93..0fcbf59afa 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -4,6 +4,7 @@ package machine import ( "device/avr" + "errors" ) type GPIOMode uint8 @@ -271,3 +272,128 @@ func (i2c I2C) ReadByte() byte { return byte(*avr.TWDR) } + +// UART + +type UARTConfig struct { + Baudrate uint32 +} + +type UART struct { +} + +var ( + // UART0 is the hardware serial port on the AVR. + UART0 = &UART{} +) + +// 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 + } + + // 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 := ((CPU_FREQUENCY+config.Baudrate*8)/(config.Baudrate*16) - 1) + *avr.UBRR0H = avr.RegValue(ps >> 8) + *avr.UBRR0L = avr.RegValue(ps & 0xff) + + // enable RX interrupt + *avr.UCSR0B |= avr.UCSR0B_RXCIE0 +} + +// Read from the RX buffer. +func (uart UART) Read(data []byte) (n int, err error) { + // check if RX buffer is empty + size := uart.Buffered() + if size == 0 { + return 0, nil + } + + // Make sure we do not read more from buffer than the data slice can hold. + if len(data) < size { + size = len(data) + } + + // only read number of bytes used from buffer + for i := 0; i < size; i++ { + v, _ := uart.ReadByte() + data[i] = v + } + + return size, nil +} + +// Write data to the UART. +func (uart UART) Write(data []byte) (n int, err error) { + for _, v := range data { + uart.WriteByte(v) + } + return len(data), nil +} + +// ReadByte reads a single byte from the RX buffer. +// If there is no data in the buffer, returns an error. +func (uart UART) ReadByte() (byte, error) { + // check if RX buffer is empty + if uart.Buffered() == 0 { + return 0, errors.New("Buffer empty") + } + + return bufferGet(), nil +} + +// 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 & avr.UCSR0A_UDRE0) == 0 { + } + *avr.UDR0 = avr.RegValue(c) // send char + return nil +} + +// Buffered returns the number of bytes current stored in the RX buffer. +func (uart UART) Buffered() int { + return int(bufferUsed()) +} + +const bufferSize = 64 + +// Minimal ring buffer implementation inspired by post at +// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php + +//go:volatile +type volatileByte byte + +var rxbuffer [bufferSize]volatileByte +var head volatileByte +var tail volatileByte + +func bufferUsed() uint8 { return uint8(head - tail) } +func bufferPut(val byte) { + if bufferUsed() != bufferSize { + head++ + rxbuffer[head%bufferSize] = volatileByte(val) + } +} +func bufferGet() byte { + if bufferUsed() != 0 { + tail++ + return byte(rxbuffer[tail%bufferSize]) + } + return 0 +} + +//go:interrupt USART_RX_vect +func handleUSART_RX() { + // Read register to clear it. + data := *avr.UDR0 + + // Ensure no error. + if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { + // Put data from UDR register into buffer. + bufferPut(byte(data)) + } +} diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index 20ceea04f7..6c369ff0b1 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -54,9 +54,10 @@ func init() { } func initUART() { - // Initialize UART at 115200 baud when running at 16MHz. + // Initialize UART at 9600 baud when running at 16MHz. *avr.UBRR0H = 0 - *avr.UBRR0L = 8 + *avr.UBRR0L = 0x67 + *avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 // enable RX and TX *avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00 // 8-bits data }