Add core support for multiple UARTs (#152)

* machine/uart: add core support for multiple UARTs by allowing for multiple RingBuffers
* machine/uart: complete core support for multiple UARTs
* machine/uart: no need to store pointer to UART, better to treat like I2C and SPI
* machine/uart: increase ring buffer size to 128 bytes
* machine/uart: improve godocs comments and use comma-ok idiom for buffer Put/Get methods
Этот коммит содержится в:
Ron Evans 2019-01-25 22:09:13 +01:00 коммит произвёл GitHub
родитель d820c36c4f
коммит 4f4d7976c6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 216 добавлений и 77 удалений

Просмотреть файл

@ -7,27 +7,34 @@ import (
"time"
)
// change these to test a different UART or pins if available
var (
uart = machine.UART0
tx uint8 = machine.UART_TX_PIN
rx uint8 = machine.UART_RX_PIN
)
func main() {
machine.UART0.Configure(machine.UARTConfig{})
machine.UART0.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
uart.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()
if uart.Buffered() > 0 {
data, _ := uart.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"))
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
machine.UART0.WriteByte(data)
uart.WriteByte(data)
input[i] = data
i++
}

Просмотреть файл

@ -16,3 +16,9 @@ const (
ADC4 = 4 // Used by TWI for SDA
ADC5 = 5 // Used by TWI for SCL
)
// UART pins
const (
UART_TX_PIN = 1
UART_RX_PIN = 0
)

Просмотреть файл

@ -4,8 +4,8 @@ package machine
// GPIO Pins
const (
D0 = 11 // RX: SERCOM0/PAD[3]
D1 = 10 // TX: SERCOM0/PAD[2]
D0 = 11 // UART0 RX: SERCOM0/PAD[3]
D1 = 10 // UART0 TX: SERCOM0/PAD[2]
D2 = 14
D3 = 9
D4 = 8
@ -14,8 +14,8 @@ const (
D7 = 21
D8 = 6
D9 = 7
D10 = 18
D11 = 16
D10 = 18 // UART1 TX(1): SERCOM1/PAD[2] can be used for UART1 TX
D11 = 16 // UART1 TX(2): SERCOM1/PAD[0] can be used for UART1 TX
D12 = 19
D13 = 17
)

49
src/machine/buffer.go Обычный файл
Просмотреть файл

@ -0,0 +1,49 @@
package machine
const bufferSize = 128
//go:volatile
type volatileByte byte
// RingBuffer is ring buffer implementation inspired by post at
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
//
// It has some limitations currently due to how "volatile" variables that are
// members of a struct are not compiled correctly by TinyGo.
// See https://github.com/aykevl/tinygo/issues/151 for details.
type RingBuffer struct {
rxbuffer [bufferSize]volatileByte
head volatileByte
tail volatileByte
}
// NewRingBuffer returns a new ring buffer.
func NewRingBuffer() *RingBuffer {
return &RingBuffer{}
}
// Used returns how many bytes in buffer have been used.
func (rb *RingBuffer) Used() uint8 {
return uint8(rb.head - rb.tail)
}
// Put stores a byte in the buffer. If the buffer is already
// full, the method will return false.
func (rb *RingBuffer) Put(val byte) bool {
if rb.Used() != bufferSize {
rb.head++
rb.rxbuffer[rb.head%bufferSize] = volatileByte(val)
return true
}
return false
}
// Get returns a byte from the buffer. If the buffer is empty,
// the method will return a false as the second value.
func (rb *RingBuffer) Get() (byte, bool) {
if rb.Used() != 0 {
rb.tail++
return byte(rb.rxbuffer[rb.tail%bufferSize]), true
}
return 0, false
}

Просмотреть файл

@ -211,6 +211,11 @@ func (i2c I2C) readByte() byte {
return byte(*avr.TWDR)
}
// 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 {
@ -248,6 +253,6 @@ func handleUSART_RX() {
// 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))
UART0.Receive(byte(data))
}
}

Просмотреть файл

@ -297,10 +297,18 @@ func (p GPIO) setPinCfg(val sam.RegValue8) {
}
}
// UART
// UART on the SAMD21.
type UART struct {
Buffer *RingBuffer
Bus *sam.SERCOM_USART_Type
}
var (
// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
UART0 = &UART{}
UART0 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()}
// The second hardware serial port on the SAMD21. Uses the SERCOM1 interface.
UART1 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
)
const (
@ -322,20 +330,55 @@ func (uart UART) Configure(config UARTConfig) {
config.BaudRate = 115200
}
// enable pins
GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
GPIO{UART_RX_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
// determine pins
if config.TX == 0 {
// use default pins
config.TX = UART_TX_PIN
config.RX = UART_RX_PIN
}
// determine pads
var txpad, rxpad int
switch config.TX {
case UART_TX_PIN:
txpad = sercomTXPad2
case D10:
txpad = sercomTXPad2
case D11:
txpad = sercomTXPad0
default:
panic("Invalid TX pin for UART")
}
switch config.RX {
case UART_RX_PIN:
rxpad = sercomRXPad3
case D10:
rxpad = sercomRXPad2
case D11:
rxpad = sercomRXPad0
case D12:
rxpad = sercomRXPad3
case D13:
rxpad = sercomRXPad1
default:
panic("Invalid RX pin for UART")
}
// configure pins
GPIO{config.TX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
GPIO{config.RX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
// reset SERCOM0
sam.SERCOM0_USART.CTRLA |= sam.SERCOM_USART_CTRLA_SWRST
for (sam.SERCOM0_USART.CTRLA&sam.SERCOM_USART_CTRLA_SWRST) > 0 ||
(sam.SERCOM0_USART.SYNCBUSY&sam.SERCOM_USART_SYNCBUSY_SWRST) > 0 {
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_SWRST
for (uart.Bus.CTRLA&sam.SERCOM_USART_CTRLA_SWRST) > 0 ||
(uart.Bus.SYNCBUSY&sam.SERCOM_USART_SYNCBUSY_SWRST) > 0 {
}
// set UART mode/sample rate
// SERCOM_USART_CTRLA_MODE(mode) |
// SERCOM_USART_CTRLA_SAMPR(sampleRate);
sam.SERCOM0_USART.CTRLA = (sam.SERCOM_USART_CTRLA_MODE_USART_INT_CLK << sam.SERCOM_USART_CTRLA_MODE_Pos) |
uart.Bus.CTRLA = (sam.SERCOM_USART_CTRLA_MODE_USART_INT_CLK << sam.SERCOM_USART_CTRLA_MODE_Pos) |
(1 << sam.SERCOM_USART_CTRLA_SAMPR_Pos) // sample rate of 16x
// Set baud rate
@ -344,39 +387,44 @@ func (uart UART) Configure(config UARTConfig) {
// setup UART frame
// SERCOM_USART_CTRLA_FORM( (parityMode == SERCOM_NO_PARITY ? 0 : 1) ) |
// dataOrder << SERCOM_USART_CTRLA_DORD_Pos;
sam.SERCOM0_USART.CTRLA |= (0 << sam.SERCOM_USART_CTRLA_FORM_Pos) | // no parity
uart.Bus.CTRLA |= (0 << sam.SERCOM_USART_CTRLA_FORM_Pos) | // no parity
(lsbFirst << sam.SERCOM_USART_CTRLA_DORD_Pos) // data order
// set UART stop bits/parity
// SERCOM_USART_CTRLB_CHSIZE(charSize) |
// nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos |
// (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value
sam.SERCOM0_USART.CTRLB |= (0 << sam.SERCOM_USART_CTRLB_CHSIZE_Pos) | // 8 bits is 0
uart.Bus.CTRLB |= (0 << sam.SERCOM_USART_CTRLB_CHSIZE_Pos) | // 8 bits is 0
(0 << sam.SERCOM_USART_CTRLB_SBMODE_Pos) | // 1 stop bit is zero
(0 << sam.SERCOM_USART_CTRLB_PMODE_Pos) // no parity
// set UART pads. This is not same as pins...
// SERCOM_USART_CTRLA_TXPO(txPad) |
// SERCOM_USART_CTRLA_RXPO(rxPad);
sam.SERCOM0_USART.CTRLA |= (sercomTXPad2 << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
(sercomRXPad3 << sam.SERCOM_USART_CTRLA_RXPO_Pos)
uart.Bus.CTRLA |= sam.RegValue((txpad << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
(rxpad << sam.SERCOM_USART_CTRLA_RXPO_Pos))
// Enable Transceiver and Receiver
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
sam.SERCOM0_USART.CTRLB |= (sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
uart.Bus.CTRLB |= (sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
// Enable USART1 port.
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
sam.SERCOM0_USART.CTRLA |= sam.SERCOM_USART_CTRLA_ENABLE
for (sam.SERCOM0_USART.SYNCBUSY & sam.SERCOM_USART_SYNCBUSY_ENABLE) > 0 {
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_ENABLE
for (uart.Bus.SYNCBUSY & sam.SERCOM_USART_SYNCBUSY_ENABLE) > 0 {
}
// setup interrupt on receive
sam.SERCOM0_USART.INTENSET = sam.SERCOM_USART_INTENSET_RXC
uart.Bus.INTENSET = sam.SERCOM_USART_INTENSET_RXC
// Enable RX IRQ.
//arm.SetPriority(sam.IRQ_SERCOM0, 0xc0)
if config.TX == UART_TX_PIN {
// UART0
arm.EnableIRQ(sam.IRQ_SERCOM0)
} else {
// UART1
arm.EnableIRQ(sam.IRQ_SERCOM1)
}
}
// SetBaudRate sets the communication speed for the UART.
@ -389,24 +437,31 @@ func (uart UART) SetBaudRate(br uint32) {
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
sam.SERCOM0_USART.BAUD = sam.RegValue16(((baud % 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_FP_Pos) |
uart.Bus.BAUD = sam.RegValue16(((baud % 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_FP_Pos) |
((baud / 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_BAUD_Pos))
}
// WriteByte writes a byte of data to the UART.
func (uart UART) WriteByte(c byte) error {
// wait until ready to receive
for (sam.SERCOM0_USART.INTFLAG & sam.SERCOM_USART_INTFLAG_DRE) == 0 {
for (uart.Bus.INTFLAG & sam.SERCOM_USART_INTFLAG_DRE) == 0 {
}
sam.SERCOM0_USART.DATA = sam.RegValue16(c)
uart.Bus.DATA = sam.RegValue16(c)
return nil
}
//go:export SERCOM0_IRQHandler
func handleUART0() {
// should reset IRQ
bufferPut(byte((sam.SERCOM0_USART.DATA & 0xFF)))
sam.SERCOM0_USART.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
UART0.Receive(byte((UART0.Bus.DATA & 0xFF)))
UART0.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}
//go:export SERCOM1_IRQHandler
func handleUART1() {
// should reset IRQ
UART1.Receive(byte((UART1.Bus.DATA & 0xFF)))
UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}
// I2C on the SAMD21.

Просмотреть файл

@ -25,6 +25,12 @@ func (p GPIO) Get() bool {
return (val > 0)
}
// UART on the AVR is a dummy implementation. UART has not been implemented for ATtiny
// devices.
type UART struct {
Buffer *RingBuffer
}
// Configure is a dummy implementation. UART has not been implemented for ATtiny
// devices.
func (uart UART) Configure(config UARTConfig) {

Просмотреть файл

@ -92,5 +92,5 @@ var I2C0 = I2C{}
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = &UART{}
UART0 = UART{Buffer: NewRingBuffer()}
)

Просмотреть файл

@ -54,10 +54,15 @@ func (p GPIO) Get() bool {
return (port.IN>>pin)&1 != 0
}
// UART on the NRF.
type UART struct {
Buffer *RingBuffer
}
// UART
var (
// UART0 is the hardware serial port on the NRF.
UART0 = &UART{}
UART0 = UART{Buffer: NewRingBuffer()}
)
// Configure the UART.
@ -108,7 +113,7 @@ func (uart UART) WriteByte(c byte) error {
func (uart UART) handleInterrupt() {
if nrf.UART0.EVENTS_RXDRDY != 0 {
bufferPut(byte(nrf.UART0.RXD))
uart.Receive(byte(nrf.UART0.RXD))
nrf.UART0.EVENTS_RXDRDY = 0x0
}
}

Просмотреть файл

@ -100,11 +100,15 @@ func (p GPIO) Set(high bool) {
}
// UART
type UART struct {
Buffer *RingBuffer
}
var (
// USART1 is the first hardware serial port on the STM32.
// Both UART0 and UART1 refers to USART1.
UART0 = &UART{}
UART1 = UART0
// Both UART0 and UART1 refer to USART1.
UART0 = UART{Buffer: NewRingBuffer()}
UART1 = &UART0
)
// Configure the UART.
@ -160,7 +164,7 @@ func (uart UART) WriteByte(c byte) error {
//go:export USART1_IRQHandler
func handleUART1() {
bufferPut(byte((stm32.USART1.DR & 0xFF)))
UART1.Receive(byte((stm32.USART1.DR & 0xFF)))
}
// SPI on the STM32.

Просмотреть файл

@ -10,8 +10,19 @@ type UARTConfig struct {
RX uint8
}
type UART struct {
}
// To implement the UART interface for a board, you must declare a concrete type as follows:
//
// type UART struct {
// Buffer *RingBuffer
// }
//
// You can also add additional members to this struct depending on your implementation,
// but the *RingBuffer is required.
// When you are declaring your UARTs for your board, make sure that you also declare the
// RingBuffer using the NewRingBuffer() function when you declare your UART:
//
// UART{Buffer: NewRingBuffer()}
//
// Read from the RX buffer.
func (uart UART) Read(data []byte) (n int, err error) {
@ -47,41 +58,20 @@ func (uart UART) Write(data []byte) (n int, err error) {
// 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 {
buf, ok := uart.Buffer.Get()
if !ok {
return 0, errors.New("Buffer empty")
}
return bufferGet(), nil
return buf, nil
}
// Buffered returns the number of bytes currently stored in the RX buffer.
func (uart UART) Buffered() int {
return int(bufferUsed())
return int(uart.Buffer.Used())
}
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
// Receive handles adding data to the UART's data buffer.
// Usually called by the IRQ handler for a machine.
func (uart UART) Receive(data byte) {
uart.Buffer.Put(data)
}

Просмотреть файл

@ -297,7 +297,7 @@ func handleRTC() {
}
func initUARTClock() {
// Turn on clock to SERCOM0 for Serial
// Turn on clock to SERCOM0 for UART0
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_
// Use GCLK0 for SERCOM0 aka UART0
@ -308,6 +308,18 @@ func initUARTClock() {
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM1 for UART1
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM1_
// Use GCLK0 for SERCOM1 aka UART1
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
// GCLK_CLKCTRL_CLKEN ;
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func initI2CClock() {