Browse Source

teensy40: add UART support

pull/1441/head
ardnew 4 years ago
committed by Ron Evans
parent
commit
9aa50853b8
  1. 100
      src/machine/board_teensy40.go
  2. 317
      src/machine/machine_mimxrt1062_uart.go
  3. 2
      src/machine/uart.go
  4. 10
      src/runtime/runtime_mimxrt1062.go

100
src/machine/board_teensy40.go

@ -2,6 +2,11 @@
package machine
import (
"device/nxp"
"runtime/interrupt"
)
// Digital pins
const (
// = Pin // [Pad]: Alt Func 0 Alt Func 1 Alt Func 2 Alt Func 3 Alt Func 4 Alt Func 5 Alt Func 6 Alt Func 7 Alt Func 8 Alt Func 9
@ -83,6 +88,17 @@ const (
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
)
func init() {
// register any interrupt handlers for this board's peripherals
UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, UART4.handleInterrupt)
UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, UART5.handleInterrupt)
UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, UART6.handleInterrupt)
UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, UART7.handleInterrupt)
}
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
@ -119,8 +135,88 @@ const (
UART7_TX_PIN = D29
)
// #==================================================================#
// | SPI |
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART6,
muxRX: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPUART6_RX_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT2,
sel: &nxp.IOMUXC.LPUART6_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D1 (PA2 [AD_B0_02])
mux: nxp.IOMUXC_LPUART6_TX_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT2,
sel: &nxp.IOMUXC.LPUART6_TX_SELECT_INPUT,
},
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART4,
muxRX: muxSelect{ // D7 (PB17 [B1_01])
mux: nxp.IOMUXC_LPUART4_RX_SELECT_INPUT_DAISY_GPIO_B1_01_ALT2,
sel: &nxp.IOMUXC.LPUART4_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D8 (PB16 [B1_00])
mux: nxp.IOMUXC_LPUART4_TX_SELECT_INPUT_DAISY_GPIO_B1_00_ALT2,
sel: &nxp.IOMUXC.LPUART4_TX_SELECT_INPUT,
},
}
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART2,
muxRX: muxSelect{ // D15 (PA19 [AD_B1_03])
mux: nxp.IOMUXC_LPUART2_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_03_ALT2,
sel: &nxp.IOMUXC.LPUART2_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D14 (PA18 [AD_B1_02])
mux: nxp.IOMUXC_LPUART2_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_02_ALT2,
sel: &nxp.IOMUXC.LPUART2_TX_SELECT_INPUT,
},
}
UART4 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART3,
muxRX: muxSelect{ // D16 (PA23 [AD_B1_07])
mux: nxp.IOMUXC_LPUART3_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_07_ALT2,
sel: &nxp.IOMUXC.LPUART3_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D17 (PA22 [AD_B1_06])
mux: nxp.IOMUXC_LPUART3_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_06_ALT2,
sel: &nxp.IOMUXC.LPUART3_TX_SELECT_INPUT,
},
}
UART5 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART8,
muxRX: muxSelect{ // D21 (PA27 [AD_B1_11])
mux: nxp.IOMUXC_LPUART8_RX_SELECT_INPUT_DAISY_GPIO_AD_B1_11_ALT2,
sel: &nxp.IOMUXC.LPUART8_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D20 (PA26 [AD_B1_10])
mux: nxp.IOMUXC_LPUART8_TX_SELECT_INPUT_DAISY_GPIO_AD_B1_10_ALT2,
sel: &nxp.IOMUXC.LPUART8_TX_SELECT_INPUT,
},
}
UART6 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART1,
// LPUART1 not connected via IOMUXC
// RX: D24 (PA12 [AD_B0_12])
// TX: D25 (PA13 [AD_B0_13])
}
UART7 = UART{
Buffer: NewRingBuffer(),
Bus: nxp.LPUART7,
muxRX: muxSelect{ // D28 (PC18 [EMC_32])
mux: nxp.IOMUXC_LPUART7_RX_SELECT_INPUT_DAISY_GPIO_EMC_32_ALT2,
sel: &nxp.IOMUXC.LPUART7_RX_SELECT_INPUT,
},
muxTX: muxSelect{ // D29 (PD31 [EMC_31])
mux: nxp.IOMUXC_LPUART7_TX_SELECT_INPUT_DAISY_GPIO_EMC_31_ALT2,
sel: &nxp.IOMUXC.LPUART7_TX_SELECT_INPUT,
},
}
)
// #===========#==========#===============#===========================#
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
// #===========#==========#===============#=================-=========#

317
src/machine/machine_mimxrt1062_uart.go

@ -0,0 +1,317 @@
// +build mimxrt1062
package machine
import (
"device/nxp"
"runtime/interrupt"
"runtime/volatile"
)
// UART peripheral abstraction layer for the MIMXRT1062
type UART struct {
Bus *nxp.LPUART_Type
Buffer *RingBuffer
Interrupt interrupt.Interrupt
// these hold the input selector ("daisy chain") values that select which pins
// are connected to the LPUART device, and should be defined where the UART
// instance is declared. see the godoc comments on type muxSelect for more
// details.
muxRX, muxTX muxSelect
// these are copied from UARTConfig, during (*UART).Configure(UARTConfig), and
// should be considered read-only for internal reference (i.e., modifying them
// will have no desirable effect).
rx, tx Pin
baud uint32
// auxiliary state data used internally
configured bool
msbFirst bool
transmitting volatile.Register32
txBuffer *RingBuffer
}
func (uart *UART) isTransmitting() bool { return uart.transmitting.Get() != 0 }
func (uart *UART) startTransmitting() { uart.transmitting.Set(1) }
func (uart *UART) stopTransmitting() { uart.transmitting.Set(0) }
func (uart *UART) resetTransmitting() {
uart.stopTransmitting()
uart.Bus.GLOBAL.SetBits(nxp.LPUART_GLOBAL_RST)
uart.Bus.GLOBAL.ClearBits(nxp.LPUART_GLOBAL_RST)
}
// Configure initializes a UART with the given UARTConfig and other default
// settings.
func (uart *UART) Configure(config UARTConfig) {
const defaultUartFreq = 115200
// use default baud rate if not specified
if config.BaudRate == 0 {
config.BaudRate = defaultUartFreq
}
uart.baud = config.BaudRate
// use default UART pins if not specified
if config.RX == 0 && config.TX == 0 {
config.RX = UART_RX_PIN
config.TX = UART_TX_PIN
}
uart.rx = config.RX
uart.tx = config.TX
// configure the mux and pad control registers
uart.rx.Configure(PinConfig{Mode: PinModeUARTRX})
uart.tx.Configure(PinConfig{Mode: PinModeUARTTX})
// configure the mux input selector
uart.muxRX.connect()
uart.muxTX.connect()
// reset all internal logic and registers
uart.resetTransmitting()
// determine the baud rate and over-sample divisors
sbr, osr := uart.getBaudRateDivisor(uart.baud)
// for now we assume some configuration. in particular:
// Data bits -> 8-bit
// Parity bit -> None (parity bit generation disabled)
// Stop bits -> 1 stop bit
// MSB first -> false
// RX idle type -> idle count starts after start bit
// RX idle config -> 1 idle character
// RX RTS enabled -> false
// TX CTS enabled -> false
// set the baud rate, over-sample configuration, stop bits
baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk) |
((nxp.LPUART_BAUD_SBNS_SBNS_0 << nxp.LPUART_BAUD_SBNS_Pos) & nxp.LPUART_BAUD_SBNS_Msk)
if osr <= 8 {
// if OSR less than or equal to 8, we must enable sampling on both edges
baudBits |= nxp.LPUART_BAUD_BOTHEDGE
}
uart.Bus.BAUD.Set(baudBits)
uart.Bus.PINCFG.Set(0) // disable triggers
// use 8 data bits, disable parity, use 1 idle char, and idle count starts
// after start bit
ctrlBits := uint32(((nxp.LPUART_CTRL_M_M_0 << nxp.LPUART_CTRL_M_Pos) & nxp.LPUART_CTRL_M_Msk) |
((nxp.LPUART_CTRL_PE_PE_0 << nxp.LPUART_CTRL_PE_Pos) & nxp.LPUART_CTRL_PE_Msk) |
((nxp.LPUART_CTRL_ILT_ILT_0 << nxp.LPUART_CTRL_ILT_Pos) & nxp.LPUART_CTRL_ILT_Msk) |
((nxp.LPUART_CTRL_IDLECFG_IDLECFG_0 << nxp.LPUART_CTRL_IDLECFG_Pos) & nxp.LPUART_CTRL_IDLECFG_Msk))
uart.Bus.CTRL.Set(ctrlBits)
rxSize, txSize := uart.getFIFOSize()
rxWater := rxSize >> 1
if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
}
txWater := txSize >> 1
if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
}
uart.Bus.WATER.Set(
((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
// enable TX/RX FIFOs
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE)
// flush TX/RX FIFOs
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
uart.Bus.MODIR.SetBits( // set the CTS configuration/TX CTS source
((nxp.LPUART_MODIR_TXCTSC_TXCTSC_0 << nxp.LPUART_MODIR_TXCTSC_Pos) & nxp.LPUART_MODIR_TXCTSC_Msk) |
((nxp.LPUART_MODIR_TXCTSSRC_TXCTSSRC_0 << nxp.LPUART_MODIR_TXCTSSRC_Pos) & nxp.LPUART_MODIR_TXCTSSRC_Msk))
// clear all status flags
stat := uint32(nxp.LPUART_STAT_RXEDGIF_Msk | nxp.LPUART_STAT_IDLE_Msk | nxp.LPUART_STAT_OR_Msk |
nxp.LPUART_STAT_NF_Msk | nxp.LPUART_STAT_FE_Msk | nxp.LPUART_STAT_PF_Msk |
nxp.LPUART_STAT_LBKDIF_Msk | nxp.LPUART_STAT_MA1F_Msk | nxp.LPUART_STAT_MA2F_Msk)
// set data bits order
if uart.msbFirst {
stat |= nxp.LPUART_STAT_MSBF
} else {
stat &^= nxp.LPUART_STAT_MSBF
}
uart.Bus.STAT.SetBits(stat)
// enable RX/TX functions
uart.Bus.CTRL.SetBits(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE)
// enable RX IRQ
uart.Interrupt.SetPriority(0xc0)
uart.Interrupt.Enable()
uart.configured = true
}
func (uart *UART) Disable() {
// first ensure the device is enabled
if uart.configured {
// wait for any buffered data to send
uart.Flush()
// stop trapping RX interrupts
uart.Interrupt.Disable()
// reset all internal registers
uart.resetTransmitting()
// disable RX/TX functions
uart.Bus.CTRL.ClearBits(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE)
// put pins back into GPIO mode
uart.rx.Configure(PinConfig{Mode: PinInputPullUp})
uart.tx.Configure(PinConfig{Mode: PinInputPullUp})
}
uart.configured = false
}
// Flush blocks the calling goroutine until all data in the output buffer has
// been written out.
func (uart *UART) Flush() {
for uart.isTransmitting() {
}
}
func (uart *UART) WriteByte(c byte) error {
if nil == uart.txBuffer {
uart.txBuffer = NewRingBuffer()
}
uart.startTransmitting()
for !uart.txBuffer.Put(c) {
}
uart.Bus.CTRL.SetBits(nxp.LPUART_CTRL_TIE)
return nil
}
// getBaudRateDivisor finds the greatest over-sampling factor (4..32) and
// corresponding baud rate divisor (1..8191) that best partition a given baud
// rate into equal intervals.
//
// This is an integral (i.e. non-floating point) port of the logic at the
// beginning of:
// void HardwareSerial::begin(uint32_t baud, uint16_t format)
// (from Teensyduino: `cores/teensy4/HardwareSerial.cpp`)
//
// We don't want to risk using floating point here in the machine package in
// case it gets called before the FPU or interrupts are ready (e.g., init()).
func (uart *UART) getBaudRateDivisor(baudRate uint32) (sbr uint32, osr uint32) {
const clock = 24000000 // UART is muxed to 24 MHz OSC
err := uint32(0xFFFFFFFF)
sbr, osr = 0, 0
for o := uint32(4); o <= 32; o++ {
s := ((clock*10)/(baudRate*o) + 5) / 10
if s == 0 {
s = 1
}
b := clock / (s * o)
var e uint32
if b > baudRate {
e = b - baudRate
} else {
e = baudRate - b
}
if e <= err {
err = e
osr = o
sbr = s
}
}
return sbr, osr
}
func (uart *UART) getFIFOSize() (rx, tx uint32) {
fifo := uart.Bus.FIFO.Get()
rx = uint32(1) << ((fifo & nxp.LPUART_FIFO_RXFIFOSIZE_Msk) >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
if rx > 1 {
rx <<= 1
}
tx = uint32(1) << ((fifo & nxp.LPUART_FIFO_TXFIFOSIZE_Msk) >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
if tx > 1 {
tx <<= 1
}
return rx, tx
}
func (uart *UART) getStatus() uint32 {
return uart.Bus.STAT.Get() |
((uart.Bus.FIFO.Get() & uint32(nxp.LPUART_FIFO_TXEMPT_Msk|nxp.LPUART_FIFO_RXEMPT_Msk|
nxp.LPUART_FIFO_TXOF_Msk|nxp.LPUART_FIFO_RXUF_Msk)) >> 16)
}
func (uart *UART) getEnabledInterrupts() uint32 {
return ((uart.Bus.BAUD.Get() & uint32(nxp.LPUART_BAUD_LBKDIE_Msk|nxp.LPUART_BAUD_RXEDGIE_Msk)) >> 8) |
((uart.Bus.FIFO.Get() & uint32(nxp.LPUART_FIFO_TXOFE_Msk|nxp.LPUART_FIFO_RXUFE_Msk)) >> 8) |
(uart.Bus.CTRL.Get() & uint32(0xFF0C000))
}
func (uart *UART) disableInterrupts(mask uint32) {
uart.Bus.BAUD.ClearBits((mask << 8) & uint32(nxp.LPUART_BAUD_LBKDIE_Msk|nxp.LPUART_BAUD_RXEDGIE_Msk))
uart.Bus.FIFO.Set((uart.Bus.FIFO.Get() & ^uint32(nxp.LPUART_FIFO_TXOF_Msk|nxp.LPUART_FIFO_RXUF_Msk)) &
^uint32((mask<<8)&(nxp.LPUART_FIFO_TXOFE_Msk|nxp.LPUART_FIFO_RXUFE_Msk)))
mask &= uint32(0xFFFFFF00)
uart.Bus.CTRL.ClearBits(mask)
}
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
stat := uart.getStatus()
inte := uart.getEnabledInterrupts()
_, txSize := uart.getFIFOSize()
// check for and clear overrun, otherwise RX will not work
if (stat & uint32(nxp.LPUART_STAT_OR)) != 0 {
uart.Bus.STAT.Set((uart.Bus.STAT.Get() & uint32(0x3FE00000)) | nxp.LPUART_STAT_OR)
}
// idle or receive data register is full
if (stat & uint32(nxp.LPUART_STAT_RDRF|nxp.LPUART_STAT_IDLE)) != 0 {
count := (uart.Bus.WATER.Get() & uint32(nxp.LPUART_WATER_RXCOUNT_Msk)) >> nxp.LPUART_WATER_RXCOUNT_Pos
for ; count > 0; count-- {
// read up to 8 bits of data at a time
// TODO: 7, 9, and 10-bit support?
uart.Buffer.Put(uint8(uart.Bus.DATA.Get() & uint32(0xFF)))
}
// if it was an IDLE status, clear the flag
if (stat & uint32(nxp.LPUART_STAT_IDLE)) != 0 {
uart.Bus.STAT.SetBits(nxp.LPUART_STAT_IDLE)
}
// disable idle line interrupts
uart.disableInterrupts(nxp.LPUART_CTRL_RIE | nxp.LPUART_CTRL_ORIE)
}
// check if we have data to write
if ((inte & nxp.LPUART_CTRL_TIE) != 0) && ((stat & nxp.LPUART_STAT_TDRE) != 0) {
for ((uart.Bus.WATER.Get() & uint32(nxp.LPUART_WATER_TXCOUNT_Msk)) >> nxp.LPUART_WATER_TXCOUNT_Pos) < txSize {
if b, ok := uart.txBuffer.Get(); ok {
uart.Bus.DATA.Set(uint32(b))
} else {
break
}
}
if uart.Bus.STAT.HasBits(nxp.LPUART_STAT_TDRE) {
uart.Bus.CTRL.Set((uart.Bus.CTRL.Get() & ^uint32(nxp.LPUART_CTRL_TIE)) | nxp.LPUART_CTRL_TCIE)
}
}
if ((inte & nxp.LPUART_CTRL_TCIE) != 0) && ((stat & nxp.LPUART_STAT_TC) != 0) {
uart.stopTransmitting()
uart.Bus.CTRL.ClearBits(nxp.LPUART_CTRL_TCIE)
}
}

2
src/machine/uart.go

@ -1,4 +1,4 @@
// +build avr esp nrf sam sifive stm32 k210 nxpmk66f18
// +build avr esp nrf sam sifive stm32 k210 nxp
package machine

10
src/runtime/runtime_mimxrt1062.go

@ -5,6 +5,7 @@ package runtime
import (
"device/arm"
"device/nxp"
"machine"
"math/bits"
"unsafe"
)
@ -114,6 +115,7 @@ func initPeripherals() {
initPins() // configure GPIO
enablePeripheralClocks() // activate peripheral clock gates
initUART() // configure UART (initialized first for debugging)
}
func initPins() {
@ -124,7 +126,13 @@ func initPins() {
nxp.IOMUXC_GPR.GPR29.Set(0xFFFFFFFF)
}
func putchar(c byte) {}
func initUART() {
machine.UART1.Configure(machine.UARTConfig{})
}
func putchar(c byte) {
machine.UART1.WriteByte(c)
}
func abort() {
for {

Loading…
Cancel
Save