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() {}