diff --git a/src/machine/board_pca10040.go b/src/machine/board_pca10040.go index b5ddaa34..39161165 100644 --- a/src/machine/board_pca10040.go +++ b/src/machine/board_pca10040.go @@ -19,3 +19,9 @@ const ( BUTTON3 = 15 BUTTON4 = 16 ) + +// UART pins for NRF52840-DK +const ( + UART_TX_PIN = 6 + UART_RX_PIN = 8 +) diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index 0fcbf59a..ff02d052 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -4,7 +4,6 @@ package machine import ( "device/avr" - "errors" ) type GPIOMode uint8 @@ -274,14 +273,6 @@ func (i2c I2C) ReadByte() byte { } // UART - -type UARTConfig struct { - Baudrate uint32 -} - -type UART struct { -} - var ( // UART0 is the hardware serial port on the AVR. UART0 = &UART{} @@ -289,14 +280,14 @@ var ( // 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 + 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) + ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1) *avr.UBRR0H = avr.RegValue(ps >> 8) *avr.UBRR0L = avr.RegValue(ps & 0xff) @@ -304,47 +295,6 @@ func (uart UART) Configure(config UARTConfig) { *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. @@ -354,38 +304,6 @@ func (uart UART) WriteByte(c byte) error { 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. diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 81511d0a..6f58d366 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -3,6 +3,7 @@ package machine import ( + "device/arm" "device/nrf" ) @@ -35,3 +36,63 @@ func (p GPIO) Set(high bool) { func (p GPIO) Get() bool { return (nrf.P0.IN>>p.Pin)&1 != 0 } + +// UART +var ( + // UART0 is the hardware serial port on the NRF. + UART0 = &UART{} +) + +// Configure the UART. +func (uart UART) Configure(config UARTConfig) { + // Default baud rate to 115200. + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + uart.SetBaudRate(config.BaudRate) + + // Set TX and RX pins from board. + nrf.UART0.PSELTXD = UART_TX_PIN + nrf.UART0.PSELRXD = UART_RX_PIN + + nrf.UART0.ENABLE = nrf.UART_ENABLE_ENABLE_Enabled + nrf.UART0.TASKS_STARTTX = 1 + nrf.UART0.TASKS_STARTRX = 1 + nrf.UART0.INTENSET = nrf.UART_INTENSET_RXDRDY_Msk + + // Enable RX IRQ. + arm.EnableIRQ(nrf.IRQ_UARTE0_UART0) +} + +// SetBaudRate sets the communication speed for the UART. +func (uart UART) SetBaudRate(br uint32) { + // Magic: calculate 'baudrate' register from the input number. + // Every value listed in the datasheet will be converted to the + // correct register value, except for 192600. I suspect the value + // listed in the nrf52 datasheet (0x0EBED000) is incorrectly rounded + // and should be 0x0EBEE000, as the nrf51 datasheet lists the + // nonrounded value 0x0EBEDFA4. + // Some background: + // https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values/2046#2046 + rate := uint32((uint64(br/400)*uint64(400*0xffffffff/16000000) + 0x800) & 0xffffff000) + + nrf.UART0.BAUDRATE = nrf.RegValue(rate) +} + +// WriteByte writes a byte of data to the UART. +func (uart UART) WriteByte(c byte) error { + nrf.UART0.EVENTS_TXDRDY = 0 + nrf.UART0.TXD = nrf.RegValue(c) + for nrf.UART0.EVENTS_TXDRDY == 0 { + } + return nil +} + +//go:export UARTE0_UART0_IRQHandler +func handleUART0() { + if nrf.UART0.EVENTS_RXDRDY != 0 { + bufferPut(byte(nrf.UART0.RXD)) + nrf.UART0.EVENTS_RXDRDY = 0x0 + } +} diff --git a/src/machine/uart.go b/src/machine/uart.go new file mode 100644 index 00000000..566094fb --- /dev/null +++ b/src/machine/uart.go @@ -0,0 +1,85 @@ +// +build avr nrf + +package machine + +import "errors" + +type UARTConfig struct { + BaudRate uint32 +} + +type UART struct { +} + +// 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 +} + +// Buffered returns the number of bytes currently 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 +} diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 8ec08404..61755ba0 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -31,6 +31,7 @@ func initUART() { nrf.UART0.BAUDRATE = nrf.UART_BAUDRATE_BAUDRATE_Baud115200 nrf.UART0.TASKS_STARTTX = 1 nrf.UART0.PSELTXD = 6 // pin 6 for NRF52840-DK + nrf.UART0.PSELRXD = 8 // pin 8 for NRF52840-DK } func initLFCLK() {