github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_nxpmk66f18_uart.go (about) 1 // Derivative work of Teensyduino Core Library 2 // http://www.pjrc.com/teensy/ 3 // Copyright (c) 2017 PJRC.COM, LLC. 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining 6 // a copy of this software and associated documentation files (the 7 // "Software"), to deal in the Software without restriction, including 8 // without limitation the rights to use, copy, modify, merge, publish, 9 // distribute, sublicense, and/or sell copies of the Software, and to 10 // permit persons to whom the Software is furnished to do so, subject to 11 // the following conditions: 12 // 13 // 1. The above copyright notice and this permission notice shall be 14 // included in all copies or substantial portions of the Software. 15 // 16 // 2. If the Software is incorporated into a build system that allows 17 // selection among a list of target devices, then similar target 18 // devices manufactured by PJRC.COM must be included in the list of 19 // target devices and selectable in the same manner. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 25 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 26 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 27 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 // SOFTWARE. 29 30 //go:build nxp && mk66f18 31 32 package machine 33 34 import ( 35 "device/arm" 36 "device/nxp" 37 "errors" 38 "runtime/interrupt" 39 "runtime/volatile" 40 41 _ "unsafe" // for go:linkname 42 ) 43 44 const ( 45 uartC2Enable = nxp.UART_C2_TE | nxp.UART_C2_RE | nxp.UART_C2_RIE | nxp.UART_C2_ILIE 46 uartC2TXActive = uartC2Enable | nxp.UART_C2_TIE 47 uartC2TXCompleting = uartC2Enable | nxp.UART_C2_TCIE 48 uartC2TXInactive = uartC2Enable 49 50 uartIRQPriority = 64 51 52 // determined from UARTx_PFIFO 53 uartRXFIFODepth = 8 54 uartTXFIFODepth = 8 55 ) 56 57 var ( 58 ErrNotImplemented = errors.New("device has not been implemented") 59 ErrNotConfigured = errors.New("device has not been configured") 60 ) 61 62 // PutcharUART writes a byte to the UART synchronously, without using interrupts 63 // or calling the scheduler 64 func PutcharUART(u *UART, c byte) { 65 // ensure the UART has been configured 66 if !u.SCGC.HasBits(u.SCGCMask) { 67 u.configure(UARTConfig{}, false) 68 } 69 70 for u.TCFIFO.Get() > 0 { 71 // busy wait 72 } 73 u.D.Set(c) 74 u.C2.Set(uartC2TXActive) 75 } 76 77 // PollUART manually checks a UART status and calls the ISR. This should only be 78 // called by runtime.abort. 79 func PollUART(u *UART) { 80 if u.SCGC.HasBits(u.SCGCMask) { 81 u.handleStatusInterrupt(u.Interrupt) 82 } 83 } 84 85 type UART struct { 86 *nxp.UART_Type 87 SCGC *volatile.Register32 88 SCGCMask uint32 89 90 DefaultRX Pin 91 DefaultTX Pin 92 93 // state 94 Buffer RingBuffer // RX Buffer 95 TXBuffer RingBuffer 96 Configured bool 97 Transmitting volatile.Register8 98 Interrupt interrupt.Interrupt 99 } 100 101 var ( 102 UART0 = &_UART0 103 UART1 = &_UART1 104 UART2 = &_UART2 105 UART3 = &_UART3 106 UART4 = &_UART4 107 _UART0 = UART{UART_Type: nxp.UART0, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART0, DefaultRX: defaultUART0RX, DefaultTX: defaultUART0TX} 108 _UART1 = UART{UART_Type: nxp.UART1, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART1, DefaultRX: defaultUART1RX, DefaultTX: defaultUART1TX} 109 _UART2 = UART{UART_Type: nxp.UART2, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART2, DefaultRX: defaultUART2RX, DefaultTX: defaultUART2TX} 110 _UART3 = UART{UART_Type: nxp.UART3, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART3, DefaultRX: defaultUART3RX, DefaultTX: defaultUART3TX} 111 _UART4 = UART{UART_Type: nxp.UART4, SCGC: &nxp.SIM.SCGC1, SCGCMask: nxp.SIM_SCGC1_UART4, DefaultRX: defaultUART4RX, DefaultTX: defaultUART4TX} 112 ) 113 114 func init() { 115 UART0.Interrupt = interrupt.New(nxp.IRQ_UART0_RX_TX, _UART0.handleStatusInterrupt) 116 UART1.Interrupt = interrupt.New(nxp.IRQ_UART1_RX_TX, _UART1.handleStatusInterrupt) 117 UART2.Interrupt = interrupt.New(nxp.IRQ_UART2_RX_TX, _UART2.handleStatusInterrupt) 118 UART3.Interrupt = interrupt.New(nxp.IRQ_UART3_RX_TX, _UART3.handleStatusInterrupt) 119 UART4.Interrupt = interrupt.New(nxp.IRQ_UART4_RX_TX, _UART4.handleStatusInterrupt) 120 } 121 122 // Configure the UART. 123 func (u *UART) Configure(config UARTConfig) { 124 u.configure(config, true) 125 } 126 127 func (u *UART) configure(config UARTConfig, canSched bool) { 128 // from: serial_begin 129 130 if !u.Configured { 131 u.Transmitting.Set(0) 132 133 // turn on the clock 134 u.SCGC.Set(u.SCGCMask) 135 136 // configure pins 137 u.DefaultRX.Control().Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_PFE | (3 << nxp.PORT_PCR0_MUX_Pos)) 138 u.DefaultTX.Control().Set(nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_SRE | (3 << nxp.PORT_PCR0_MUX_Pos)) 139 u.C1.Set(nxp.UART_C1_ILT) 140 } 141 142 // default to 115200 baud 143 if config.BaudRate == 0 { 144 config.BaudRate = 115200 145 } 146 147 // copied from teensy core's BAUD2DIV macro 148 divisor := ((CPUFrequency() * 2) + (config.BaudRate >> 1)) / config.BaudRate 149 if divisor < 32 { 150 divisor = 32 151 } 152 153 if u.Configured { 154 // don't change baud rate mid transmit 155 if canSched { 156 u.Flush() 157 } else { 158 for u.Transmitting.Get() != 0 { 159 // busy wait flush 160 } 161 } 162 } 163 164 // set the divisor 165 u.BDH.Set(uint8((divisor >> 13) & 0x1F)) 166 u.BDL.Set(uint8((divisor >> 5) & 0xFF)) 167 u.C4.Set(uint8(divisor & 0x1F)) 168 169 if !u.Configured { 170 u.Configured = true 171 172 u.C1.Set(nxp.UART_C1_ILT) 173 174 // configure TX and RX watermark 175 u.TWFIFO.Set(2) // causes bit TDRE of S1 to set 176 u.RWFIFO.Set(4) // causes bit RDRF of S1 to set 177 178 // enable FIFOs 179 u.PFIFO.Set(nxp.UART_PFIFO_TXFE | nxp.UART_PFIFO_RXFE) 180 181 // setup interrupts 182 u.C2.Set(uartC2TXInactive) 183 u.Interrupt.SetPriority(uartIRQPriority) 184 u.Interrupt.Enable() 185 } 186 } 187 188 func (u *UART) Disable() { 189 // from: serial_end 190 191 // check if the device has been enabled already 192 if !u.SCGC.HasBits(u.SCGCMask) { 193 return 194 } 195 196 u.Flush() 197 198 u.Interrupt.Disable() 199 u.C2.Set(0) 200 201 // reconfigure pin 202 u.DefaultRX.Configure(PinConfig{Mode: PinInputPullup}) 203 u.DefaultTX.Configure(PinConfig{Mode: PinInputPullup}) 204 205 // clear flags 206 u.S1.Get() 207 u.D.Get() 208 u.Buffer.Clear() 209 } 210 211 func (u *UART) Flush() { 212 for u.Transmitting.Get() != 0 { 213 gosched() 214 } 215 } 216 217 func (u *UART) handleStatusInterrupt(interrupt.Interrupt) { 218 // from: uart0_status_isr 219 220 // receive 221 if u.S1.HasBits(nxp.UART_S1_RDRF | nxp.UART_S1_IDLE) { 222 intrs := arm.DisableInterrupts() 223 avail := u.RCFIFO.Get() 224 if avail == 0 { 225 // The only way to clear the IDLE interrupt flag is 226 // to read the data register. But reading with no 227 // data causes a FIFO underrun, which causes the 228 // FIFO to return corrupted data. If anyone from 229 // Freescale reads this, what a poor design! There 230 // write should be a write-1-to-clear for IDLE. 231 u.D.Get() 232 // flushing the fifo recovers from the underrun, 233 // but there's a possible race condition where a 234 // new character could be received between reading 235 // RCFIFO == 0 and flushing the FIFO. To minimize 236 // the chance, interrupts are disabled so a higher 237 // priority interrupt (hopefully) doesn't delay. 238 // TODO: change this to disabling the IDLE interrupt 239 // which won't be simple, since we already manage 240 // which transmit interrupts are enabled. 241 u.CFIFO.Set(nxp.UART_CFIFO_RXFLUSH) 242 arm.EnableInterrupts(intrs) 243 244 } else { 245 arm.EnableInterrupts(intrs) 246 247 for { 248 u.Buffer.Put(u.D.Get()) 249 avail-- 250 if avail <= 0 { 251 break 252 } 253 } 254 } 255 } 256 257 // transmit 258 if u.C2.HasBits(nxp.UART_C2_TIE) && u.S1.HasBits(nxp.UART_S1_TDRE) { 259 data := make([]byte, 0, uartTXFIFODepth) 260 avail := uartTXFIFODepth - u.TCFIFO.Get() 261 262 // get avail bytes from ring buffer 263 for len(data) < int(avail) { 264 if b, ok := u.TXBuffer.Get(); ok { 265 data = append(data, b) 266 } else { 267 break 268 } 269 } 270 271 // write data to FIFO 272 l := len(data) 273 for i, b := range data { 274 if i == l-1 { 275 // only clear TDRE on last write, per the manual 276 u.S1.Get() 277 } 278 u.D.Set(b) 279 } 280 281 // if FIFO still has room, disable TIE, enable TCIE 282 if u.S1.HasBits(nxp.UART_S1_TDRE) { 283 u.C2.Set(uartC2TXCompleting) 284 } 285 } 286 287 // transmit complete 288 if u.C2.HasBits(nxp.UART_C2_TCIE) && u.S1.HasBits(nxp.UART_S1_TC) { 289 u.Transmitting.Set(0) 290 u.C2.Set(uartC2TXInactive) 291 } 292 } 293 294 // WriteByte writes a byte of data to the UART. 295 func (u *UART) writeByte(c byte) error { 296 if !u.Configured { 297 return ErrNotConfigured 298 } 299 300 for !u.TXBuffer.Put(c) { 301 gosched() 302 } 303 304 u.Transmitting.Set(1) 305 u.C2.Set(uartC2TXActive) 306 return nil 307 } 308 309 func (uart *UART) flush() {}