github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_mimxrt1062_uart.go (about)

     1  //go:build mimxrt1062
     2  
     3  package machine
     4  
     5  import (
     6  	"device/nxp"
     7  	"runtime/interrupt"
     8  	"runtime/volatile"
     9  )
    10  
    11  // UART peripheral abstraction layer for the MIMXRT1062
    12  
    13  type UART struct {
    14  	Bus       *nxp.LPUART_Type
    15  	Buffer    *RingBuffer
    16  	Interrupt interrupt.Interrupt
    17  
    18  	// txBuffer should be allocated globally (such as when UART is created) to
    19  	// prevent it being reclaimed or cleaned up prematurely.
    20  	txBuffer *RingBuffer
    21  
    22  	// these hold the input selector ("daisy chain") values that select which pins
    23  	// are connected to the LPUART device, and should be defined where the UART
    24  	// instance is declared. see the godoc comments on type muxSelect for more
    25  	// details.
    26  	muxRX, muxTX muxSelect
    27  
    28  	// these are copied from UARTConfig, during (*UART).Configure(UARTConfig), and
    29  	// should be considered read-only for internal reference (i.e., modifying them
    30  	// will have no desirable effect).
    31  	rx, tx Pin
    32  	baud   uint32
    33  
    34  	// auxiliary state data used internally
    35  	configured   bool
    36  	transmitting volatile.Register32
    37  }
    38  
    39  func (uart *UART) isTransmitting() bool { return uart.transmitting.Get() != 0 }
    40  func (uart *UART) startTransmitting()   { uart.transmitting.Set(1) }
    41  func (uart *UART) stopTransmitting()    { uart.transmitting.Set(0) }
    42  func (uart *UART) resetTransmitting() {
    43  	uart.stopTransmitting()
    44  	uart.Bus.GLOBAL.SetBits(nxp.LPUART_GLOBAL_RST)
    45  	uart.Bus.GLOBAL.ClearBits(nxp.LPUART_GLOBAL_RST)
    46  }
    47  
    48  // Configure initializes a UART with the given UARTConfig and other default
    49  // settings.
    50  func (uart *UART) Configure(config UARTConfig) {
    51  
    52  	const defaultUartFreq = 115200
    53  
    54  	// use default baud rate if not specified
    55  	if config.BaudRate == 0 {
    56  		config.BaudRate = defaultUartFreq
    57  	}
    58  
    59  	// use default UART pins if not specified
    60  	if config.RX == 0 && config.TX == 0 {
    61  		config.RX = UART_RX_PIN
    62  		config.TX = UART_TX_PIN
    63  	}
    64  
    65  	uart.baud = config.BaudRate
    66  	uart.rx = config.RX
    67  	uart.tx = config.TX
    68  
    69  	// configure the mux and pad control registers
    70  	uart.rx.Configure(PinConfig{Mode: PinModeUARTRX})
    71  	uart.tx.Configure(PinConfig{Mode: PinModeUARTTX})
    72  
    73  	// configure the mux input selector
    74  	uart.muxRX.connect()
    75  	uart.muxTX.connect()
    76  
    77  	// reset all internal logic and registers
    78  	uart.resetTransmitting()
    79  
    80  	// disable until we have finished configuring registers
    81  	uart.Bus.CTRL.Set(0)
    82  
    83  	// determine the baud rate and over-sample divisors
    84  	sbr, osr := uart.getBaudRateDivisor(uart.baud)
    85  
    86  	// set the baud rate, over-sample configuration, stop bits
    87  	baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
    88  		((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk)
    89  	if osr <= 8 {
    90  		// if OSR less than or equal to 8, we must enable sampling on both edges
    91  		baudBits |= nxp.LPUART_BAUD_BOTHEDGE
    92  	}
    93  	uart.Bus.BAUD.Set(baudBits)
    94  	uart.Bus.PINCFG.Set(0) // disable triggers
    95  
    96  	// configure watermarks, flush and enable TX/RX FIFOs
    97  	rxSize, txSize := uart.getFIFOSize()
    98  	rxWater := rxSize >> 1
    99  	if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
   100  		rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
   101  	}
   102  	txWater := txSize >> 1
   103  	if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
   104  		txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
   105  	}
   106  	uart.Bus.WATER.Set(
   107  		((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
   108  			((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
   109  	uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE |
   110  		nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
   111  
   112  	// for now we assume some configuration. in particular:
   113  	//  Data bits         -> 8-bit
   114  	//  Parity bit        -> None (parity bit generation disabled)
   115  	//  Stop bits         -> 1 stop bit
   116  	//  MSB first         -> false
   117  	//  RX idle type      -> idle count starts after start bit
   118  	//  RX idle config    -> 1 idle character
   119  	//  RX RTS enabled    -> false
   120  	//  TX CTS enabled    -> false
   121  
   122  	// enable transmitter, receiver functions
   123  	uart.Bus.CTRL.Set(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE |
   124  		// enable receiver, idle line interrupts
   125  		nxp.LPUART_CTRL_RIE | nxp.LPUART_CTRL_ILIE)
   126  
   127  	// clear all status flags
   128  	uart.Bus.STAT.Set(uart.Bus.STAT.Get())
   129  
   130  	// enable RX interrupt
   131  	uart.Interrupt.SetPriority(0xC0)
   132  	uart.Interrupt.Enable()
   133  
   134  	uart.configured = true
   135  }
   136  
   137  // Disable disables the UART interface.
   138  //
   139  // If any buffered data has not yet been transmitted, Disable waits until
   140  // transmission completes before disabling the interface. The receiver UART's
   141  // interrupt is also disabled, and the RX/TX pins are reconfigured for GPIO
   142  // input (pull-up).
   143  func (uart *UART) Disable() {
   144  
   145  	// first ensure the device is enabled
   146  	if uart.configured {
   147  
   148  		// wait for any buffered data to send
   149  		uart.Sync()
   150  
   151  		// stop trapping RX interrupts
   152  		uart.Interrupt.Disable()
   153  
   154  		// reset all internal registers
   155  		uart.resetTransmitting()
   156  
   157  		// disable RX/TX functions
   158  		uart.Bus.CTRL.ClearBits(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE)
   159  
   160  		// put pins back into GPIO mode
   161  		uart.rx.Configure(PinConfig{Mode: PinInputPullup})
   162  		uart.tx.Configure(PinConfig{Mode: PinInputPullup})
   163  	}
   164  	uart.configured = false
   165  }
   166  
   167  // Sync blocks the calling goroutine until all data in the output buffer has
   168  // been transmitted.
   169  func (uart *UART) Sync() error {
   170  	for uart.isTransmitting() {
   171  	}
   172  	return nil
   173  }
   174  
   175  // WriteByte writes a single byte of data to the UART interface.
   176  func (uart *UART) writeByte(c byte) error {
   177  	uart.startTransmitting()
   178  	for !uart.txBuffer.Put(c) {
   179  	}
   180  	uart.Bus.CTRL.SetBits(nxp.LPUART_CTRL_TIE)
   181  	return nil
   182  }
   183  
   184  func (uart *UART) flush() {}
   185  
   186  // getBaudRateDivisor finds the greatest over-sampling factor (4..32) and
   187  // corresponding baud rate divisor (1..8191) that best partition a given baud
   188  // rate into equal intervals.
   189  //
   190  // This is an integral (non-floating point) translation of the logic at the
   191  // beginning of:
   192  //
   193  //	void HardwareSerial::begin(uint32_t baud, uint16_t format)
   194  //
   195  // (from Teensyduino: cores/teensy4/HardwareSerial.cpp)
   196  //
   197  // We don't want to use floating point here in case it gets called from an ISR
   198  // or very early during system init.
   199  func (uart *UART) getBaudRateDivisor(baudRate uint32) (sbr uint32, osr uint32) {
   200  	const clock = 24000000 // UART is muxed to 24 MHz OSC
   201  	err := uint32(0xFFFFFFFF)
   202  	sbr, osr = 0, 0
   203  	for o := uint32(4); o <= 32; o++ {
   204  		s := ((clock*10)/(baudRate*o) + 5) / 10
   205  		if s == 0 {
   206  			s = 1
   207  		}
   208  		b := clock / (s * o)
   209  		var e uint32
   210  		if b > baudRate {
   211  			e = b - baudRate
   212  		} else {
   213  			e = baudRate - b
   214  		}
   215  		if e <= err {
   216  			err = e
   217  			osr = o
   218  			sbr = s
   219  		}
   220  	}
   221  	return sbr, osr
   222  }
   223  
   224  func (uart *UART) getFIFOSize() (rx, tx uint32) {
   225  	fifo := uart.Bus.FIFO.Get()
   226  	rx = uint32(1) << ((fifo & nxp.LPUART_FIFO_RXFIFOSIZE_Msk) >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
   227  	if rx > 1 {
   228  		rx <<= 1
   229  	}
   230  	tx = uint32(1) << ((fifo & nxp.LPUART_FIFO_TXFIFOSIZE_Msk) >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
   231  	if tx > 1 {
   232  		tx <<= 1
   233  	}
   234  	return rx, tx
   235  }
   236  
   237  func (uart *UART) getStatus() uint32 {
   238  	return uart.Bus.STAT.Get() |
   239  		((uart.Bus.FIFO.Get() & uint32(nxp.LPUART_FIFO_TXEMPT_Msk|nxp.LPUART_FIFO_RXEMPT_Msk|
   240  			nxp.LPUART_FIFO_TXOF_Msk|nxp.LPUART_FIFO_RXUF_Msk)) >> 16)
   241  }
   242  
   243  func (uart *UART) getEnabledInterrupts() uint32 {
   244  	return ((uart.Bus.BAUD.Get() & uint32(nxp.LPUART_BAUD_LBKDIE_Msk|nxp.LPUART_BAUD_RXEDGIE_Msk)) >> 8) |
   245  		((uart.Bus.FIFO.Get() & uint32(nxp.LPUART_FIFO_TXOFE_Msk|nxp.LPUART_FIFO_RXUFE_Msk)) >> 8) |
   246  		(uart.Bus.CTRL.Get() & uint32(0xFF0C000))
   247  }
   248  
   249  func (uart *UART) disableInterrupts(mask uint32) {
   250  	uart.Bus.BAUD.ClearBits((mask << 8) & uint32(nxp.LPUART_BAUD_LBKDIE_Msk|nxp.LPUART_BAUD_RXEDGIE_Msk))
   251  	uart.Bus.FIFO.Set((uart.Bus.FIFO.Get() & ^uint32(nxp.LPUART_FIFO_TXOF_Msk|nxp.LPUART_FIFO_RXUF_Msk)) &
   252  		^uint32((mask<<8)&(nxp.LPUART_FIFO_TXOFE_Msk|nxp.LPUART_FIFO_RXUFE_Msk)))
   253  	mask &= uint32(0xFFFFFF00)
   254  	uart.Bus.CTRL.ClearBits(mask)
   255  }
   256  
   257  func (uart *UART) handleInterrupt(interrupt.Interrupt) {
   258  
   259  	stat := uart.getStatus()
   260  	inte := uart.getEnabledInterrupts()
   261  
   262  	_, txSize := uart.getFIFOSize()
   263  
   264  	// check for and clear overrun, otherwise RX will not work
   265  	if (stat & uint32(nxp.LPUART_STAT_OR)) != 0 {
   266  		uart.Bus.STAT.Set((uart.Bus.STAT.Get() & uint32(0x3FE00000)) | nxp.LPUART_STAT_OR)
   267  	}
   268  
   269  	// idle or receive data register is full
   270  	if (stat & uint32(nxp.LPUART_STAT_RDRF|nxp.LPUART_STAT_IDLE)) != 0 {
   271  		count := (uart.Bus.WATER.Get() & uint32(nxp.LPUART_WATER_RXCOUNT_Msk)) >> nxp.LPUART_WATER_RXCOUNT_Pos
   272  		for ; count > 0; count-- {
   273  			// read up to 8 bits of data at a time
   274  			// TODO: 7, 9, and 10-bit support?
   275  			uart.Buffer.Put(uint8(uart.Bus.DATA.Get() & uint32(0xFF)))
   276  		}
   277  		// if it was an IDLE status, clear the flag
   278  		if (stat & uint32(nxp.LPUART_STAT_IDLE)) != 0 {
   279  			uart.Bus.STAT.SetBits(nxp.LPUART_STAT_IDLE)
   280  		}
   281  		// disable idle line interrupts
   282  		uart.disableInterrupts(nxp.LPUART_CTRL_RIE | nxp.LPUART_CTRL_ORIE)
   283  	}
   284  
   285  	// check if we have data to write
   286  	if ((inte & nxp.LPUART_CTRL_TIE) != 0) && ((stat & nxp.LPUART_STAT_TDRE) != 0) {
   287  		for ((uart.Bus.WATER.Get() & uint32(nxp.LPUART_WATER_TXCOUNT_Msk)) >> nxp.LPUART_WATER_TXCOUNT_Pos) < txSize {
   288  			if b, ok := uart.txBuffer.Get(); ok {
   289  				uart.Bus.DATA.Set(uint32(b))
   290  			} else {
   291  				break
   292  			}
   293  		}
   294  		if uart.Bus.STAT.HasBits(nxp.LPUART_STAT_TDRE) {
   295  			uart.Bus.CTRL.Set((uart.Bus.CTRL.Get() & ^uint32(nxp.LPUART_CTRL_TIE)) | nxp.LPUART_CTRL_TCIE)
   296  		}
   297  	}
   298  
   299  	if ((inte & nxp.LPUART_CTRL_TCIE) != 0) && ((stat & nxp.LPUART_STAT_TC) != 0) {
   300  		uart.stopTransmitting()
   301  		uart.Bus.CTRL.ClearBits(nxp.LPUART_CTRL_TCIE)
   302  	}
   303  }