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 }