From 65ea74bd84bb19254b5b78473cd21c036ee5cf31 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Sun, 20 Jan 2019 20:34:13 +0100 Subject: [PATCH] machine/atsamd21: implements UART0 using the SERCOM0 interface Signed-off-by: Ron Evans --- src/machine/machine_atsamd21g18.go | 115 ++++++++++++++++++++++++++++- src/machine/uart.go | 2 +- src/runtime/runtime_atsamd21g18.go | 26 +++++-- 3 files changed, 135 insertions(+), 8 deletions(-) diff --git a/src/machine/machine_atsamd21g18.go b/src/machine/machine_atsamd21g18.go index e9dc8766..fd5a4af7 100644 --- a/src/machine/machine_atsamd21g18.go +++ b/src/machine/machine_atsamd21g18.go @@ -8,6 +8,7 @@ package machine import ( + "device/arm" "device/sam" ) @@ -54,7 +55,7 @@ func (p GPIO) Configure(config GPIOConfig) { p.setPMux(val | (GPIO_SERCOM << sam.PORT_PMUX0_PMUXE_Pos)) } // enable port config - p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR) + p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN) } } @@ -294,3 +295,115 @@ func (p GPIO) setPinCfg(val sam.RegValue8) { sam.PORT.PINCFG0_31 = val } } + +// UART +var ( + // The first hardware serial port on the SAMD21. Uses the SERCOM0 interface. + UART0 = &UART{} +) + +const ( + sampleRate16X = 16 + lsbFirst = 1 + sercomRXPad0 = 0 + sercomRXPad1 = 1 + sercomRXPad2 = 2 + sercomRXPad3 = 3 + sercomTXPad0 = 0 // Only for UART + sercomTXPad2 = 1 // Only for UART + sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3 +) + +// Configure the UART. +func (uart UART) Configure(config UARTConfig) { + // Default baud rate to 115200. + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + // enable pins + GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM}) + GPIO{UART_RX_PIN}.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 { + } + + // 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) | + (1 << sam.SERCOM_USART_CTRLA_SAMPR_Pos) // sample rate of 16x + + // Set baud rate + uart.SetBaudRate(config.BaudRate) + + // 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 + (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 + (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) + + // 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) + + // 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 { + } + + // setup interrupt on receive + sam.SERCOM0_USART.INTENSET = sam.SERCOM_USART_INTENSET_RXC + + // Enable RX IRQ. + //arm.SetPriority(sam.IRQ_SERCOM0, 0xc0) + arm.EnableIRQ(sam.IRQ_SERCOM0) +} + +// SetBaudRate sets the communication speed for the UART. +func (uart UART) SetBaudRate(br uint32) { + // Asynchronous fractional mode (Table 24-2 in datasheet) + // BAUD = fref / (sampleRateValue * fbaud) + // (multiply by 8, to calculate fractional piece) + // uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate); + baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br) + + // 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) | + ((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 { + } + sam.SERCOM0_USART.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 +} diff --git a/src/machine/uart.go b/src/machine/uart.go index 84f3f088..a621bc1c 100644 --- a/src/machine/uart.go +++ b/src/machine/uart.go @@ -1,4 +1,4 @@ -// +build avr nrf stm32 +// +build avr nrf sam stm32 package machine diff --git a/src/runtime/runtime_atsamd21g18.go b/src/runtime/runtime_atsamd21g18.go index cb300c56..4bbb1f68 100644 --- a/src/runtime/runtime_atsamd21g18.go +++ b/src/runtime/runtime_atsamd21g18.go @@ -5,6 +5,7 @@ package runtime import ( "device/arm" "device/sam" + "machine" "unsafe" ) @@ -21,16 +22,14 @@ func main() { func init() { initClocks() initRTC() + initUARTClock() - // Turn on clock to SERCOM0 for Serial - sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_ - - // TODO: connect to UART + // connect to UART + machine.UART0.Configure(machine.UARTConfig{}) } func putchar(c byte) { - // TODO: write byte to UART - //machine.UART0.WriteByte(c) + machine.UART0.WriteByte(c) } func initClocks() { @@ -216,6 +215,7 @@ func initRTC() { sam.RTC_MODE0.CTRL |= sam.RTC_MODE0_CTRL_ENABLE waitForSync() + arm.SetPriority(sam.IRQ_RTC, 0xc0) arm.EnableIRQ(sam.IRQ_RTC) } @@ -292,3 +292,17 @@ func handleRTC() { timerWakeup = true } + +func initUARTClock() { + // Turn on clock to SERCOM0 for Serial + sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_ + + // Use GCLK0 for SERCOM0 aka UART0 + // 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_SERCOM0_CORE << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + waitForSync() +}