325 строки
10 КиБ
Go
325 строки
10 КиБ
Go
// +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
|
|
}
|
|
|
|
// Disable disables the UART interface.
|
|
//
|
|
// If any buffered data has not yet been transmitted, Disable waits until
|
|
// transmission completes before disabling the interface. The receiver UART's
|
|
// interrupt is also disabled, and the RX/TX pins are reconfigured for GPIO
|
|
// input (pull-up).
|
|
func (uart *UART) Disable() {
|
|
|
|
// first ensure the device is enabled
|
|
if uart.configured {
|
|
|
|
// wait for any buffered data to send
|
|
uart.Sync()
|
|
|
|
// 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
|
|
}
|
|
|
|
// Sync blocks the calling goroutine until all data in the output buffer has
|
|
// been transmitted.
|
|
func (uart *UART) Sync() error {
|
|
for uart.isTransmitting() {
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// WriteByte writes a single byte of data to the UART interface.
|
|
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)
|
|
}
|
|
}
|