From 0dd521e1d13da8ee5ed4982ce6ea12ad2a3f7918 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 3 Feb 2022 11:00:50 -0500 Subject: [PATCH] add support for esp32c3 to receive UART data --- src/machine/machine_esp32c3.go | 261 ++++++++++++++++++++++++++++++++- 1 file changed, 257 insertions(+), 4 deletions(-) diff --git a/src/machine/machine_esp32c3.go b/src/machine/machine_esp32c3.go index e1892f47..cb9f2d6f 100644 --- a/src/machine/machine_esp32c3.go +++ b/src/machine/machine_esp32c3.go @@ -5,6 +5,8 @@ package machine import ( "device/esp" + "device/riscv" + "errors" "runtime/interrupt" "runtime/volatile" "sync" @@ -214,18 +216,269 @@ func setupPinInterrupt() error { }).Enable() } -var DefaultUART = UART0 - var ( + DefaultUART = UART0 + UART0 = &_UART0 _UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()} UART1 = &_UART1 _UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()} + + onceUart = sync.Once{} + errSamePins = errors.New("UART: invalid pin combination") + errWrongUART = errors.New("UART: unsupported UARTn") + errWrongBitSize = errors.New("UART: invalid data size") + errWrongStopBitSize = errors.New("UART: invalid bit size") ) type UART struct { - Bus *esp.UART_Type - Buffer *RingBuffer + Bus *esp.UART_Type + Buffer *RingBuffer + ParityErrorDetected bool // set when parity error detected + DataErrorDetected bool // set when data corruption detected + DataOverflowDetected bool // set when data overflow detected in UART FIFO buffer or RingBuffer +} + +type UARTStopBits int + +const ( + UARTStopBits_Default UARTStopBits = iota + UARTStopBits_1 + UARTStopBits_1_5 + UARTStopBits_2 +) + +const ( + defaultDataBits = 8 + defaultStopBit = 1 + defaultParity = ParityNone + + uartInterrupts = esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA | + esp.UART_INT_ENA_PARITY_ERR_INT_ENA | + esp.UART_INT_ENA_FRM_ERR_INT_ENA | + esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA | + esp.UART_INT_ENA_GLITCH_DET_INT_ENA + + pplClockFreq = 80e6 +) + +type registerSet struct { + interruptMapReg *volatile.Register32 + uartClockBitMask uint32 + gpioMatrixSignal uint32 +} + +func (uart *UART) Configure(config UARTConfig) error { + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + if config.TX == config.RX { + return errSamePins + } + switch { + case uart.Bus == esp.UART0: + return uart.configure(config, registerSet{ + interruptMapReg: &esp.INTERRUPT_CORE0.UART_INTR_MAP, + uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART_CLK_EN, + gpioMatrixSignal: 6, + }) + case uart.Bus == esp.UART1: + return uart.configure(config, registerSet{ + interruptMapReg: &esp.INTERRUPT_CORE0.UART1_INTR_MAP, + uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART1_CLK_EN, + gpioMatrixSignal: 9, + }) + } + return errWrongUART +} + +func (uart *UART) configure(config UARTConfig, regs registerSet) error { + + initUARTClock(uart.Bus, regs) + + // - disbale TX/RX clock to make sure the UART transmitter or receiver is not at work during configuration + uart.Bus.SetCLK_CONF_TX_SCLK_EN(0) + uart.Bus.SetCLK_CONF_RX_SCLK_EN(0) + + // Configure static registers (Ref: Configuring URATn Communication) + + // - default clock source: 1=APB_CLK, 2=FOSC_CLK, 3=XTAL_CLK + uart.Bus.SetCLK_CONF_SCLK_SEL(1) + // reset divisor of the divider via UART_SCLK_DIV_NUM, UART_SCLK_DIV_A, and UART_SCLK_DIV_B + uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(0) + uart.Bus.SetCLK_CONF_SCLK_DIV_A(0) + uart.Bus.SetCLK_CONF_SCLK_DIV_B(0) + + // - the baud rate + uart.SetBaudRate(config.BaudRate) + // - the data format + uart.SetFormat(defaultDataBits, defaultStopBit, defaultParity) + // - set UART mode + uart.Bus.SetRS485_CONF_RS485_EN(0) + uart.Bus.SetRS485_CONF_RS485TX_RX_EN(0) + uart.Bus.SetRS485_CONF_RS485RXBY_TX_EN(0) + uart.Bus.SetCONF0_IRDA_EN(0) + // - disable hw-flow control + uart.Bus.SetCONF0_TX_FLOW_EN(0) + uart.Bus.SetCONF1_RX_FLOW_EN(0) + + // synchronize values into Core Clock + uart.Bus.SetID_REG_UPDATE(1) + + uart.setupPins(config, regs) + uart.configureInterrupt(regs.interruptMapReg) + uart.enableTransmitter() + uart.enableReceiver() + + // Start TX/RX + uart.Bus.SetCLK_CONF_TX_SCLK_EN(1) + uart.Bus.SetCLK_CONF_RX_SCLK_EN(1) + return nil +} + +func (uart *UART) SetFormat(dataBits, stopBits int, parity UARTParity) error { + if dataBits < 5 { + return errWrongBitSize + } + if stopBits > 1 { + return errWrongStopBitSize + } + // - data length + uart.Bus.SetCONF0_BIT_NUM(uint32(dataBits - 5)) + // - stop bit + uart.Bus.SetCONF0_STOP_BIT_NUM(uint32(stopBits)) + // - parity check + switch parity { + case ParityNone: + uart.Bus.SetCONF0_PARITY_EN(0) + case ParityEven: + uart.Bus.SetCONF0_PARITY_EN(1) + uart.Bus.SetCONF0_PARITY(0) + case ParityOdd: + uart.Bus.SetCONF0_PARITY_EN(1) + uart.Bus.SetCONF0_PARITY(1) + } + return nil +} + +func initUARTClock(bus *esp.UART_Type, regs registerSet) { + uartClock := &esp.SYSTEM.PERIP_CLK_EN0 + uartClockReset := &esp.SYSTEM.PERIP_RST_EN0 + + // Initialize/reset URATn (Ref: Initializing URATn) + // - enable the clock for UART RAM + uartClock.SetBits(esp.SYSTEM_PERIP_CLK_EN0_UART_MEM_CLK_EN) + // - enable APB_CLK for UARTn + uartClock.SetBits(regs.uartClockBitMask) + // - reset sequence + uartClockReset.ClearBits(regs.uartClockBitMask) + bus.SetCLK_CONF_RST_CORE(1) + uartClockReset.SetBits(regs.uartClockBitMask) + uartClockReset.ClearBits(regs.uartClockBitMask) + bus.SetCLK_CONF_RST_CORE(0) + // synchronize core register + bus.SetID_REG_UPDATE(0) + // enable RTC clock + esp.RTC_CNTL.SetRTC_CLK_CONF_DIG_CLK8M_EN(1) + // wait for Core Clock to ready for configuration + for bus.GetID_REG_UPDATE() > 0 { + riscv.Asm("nop") + } +} + +func (uart *UART) SetBaudRate(baudRate uint32) { + // based on esp-idf + max_div := uint32((1 << 12) - 1) + sclk_div := (pplClockFreq + (max_div * baudRate) - 1) / (max_div * baudRate) + clk_div := (pplClockFreq << 4) / (baudRate * sclk_div) + uart.Bus.SetCLKDIV(clk_div >> 4) + uart.Bus.SetCLKDIV_FRAG(clk_div & 0xf) + uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(sclk_div - 1) +} + +func (uart *UART) setupPins(config UARTConfig, regs registerSet) { + config.RX.Configure(PinConfig{Mode: PinInputPullup}) + config.TX.Configure(PinConfig{Mode: PinInputPullup}) + + // link TX with GPIO signal X (technical reference manual 5.10) (this is not interrupt signal!) + config.TX.outFunc().Set(regs.gpioMatrixSignal) + // link RX with GPIO signal X and route signals via GPIO matrix (GPIO_SIGn_IN_SEL 0x40) + inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | uint32(config.RX)) +} + +func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { // Disable all UART interrupts + // Disable all UART interrupts + uart.Bus.INT_ENA.ClearBits(0x0ffff) + + intrMapReg.Set(7) + onceUart.Do(func() { + _ = interrupt.New(7, func(i interrupt.Interrupt) { + UART0.serveInterrupt(0) + UART1.serveInterrupt(1) + }).Enable() + }) +} + +func (uart *UART) serveInterrupt(num int) { + // get interrupt status + interrutFlag := uart.Bus.INT_ST.Get() + if (interrutFlag & uartInterrupts) == 0 { + return + } + + // block UART interrupts while processing + uart.Bus.INT_ENA.ClearBits(uartInterrupts) + + if interrutFlag&esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA > 0 { + for uart.Bus.GetSTATUS_RXFIFO_CNT() > 0 { + b := uart.Bus.GetFIFO_RXFIFO_RD_BYTE() + if !uart.Buffer.Put(byte(b & 0xff)) { + uart.DataOverflowDetected = true + } + } + } + if interrutFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 { + uart.ParityErrorDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA { + uart.DataErrorDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA { + uart.DataOverflowDetected = true + } + if 0 != interrutFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA { + uart.DataErrorDetected = true + } + + // Clear the UART interrupt status + uart.Bus.INT_CLR.SetBits(interrutFlag) + uart.Bus.INT_CLR.ClearBits(interrutFlag) + // Enable interrupts + uart.Bus.INT_ENA.Set(uartInterrupts) +} + +const uart_empty_thresh_default = 10 + +func (uart *UART) enableTransmitter() { + uart.Bus.SetCONF0_TXFIFO_RST(1) + uart.Bus.SetCONF0_TXFIFO_RST(0) + // TXINFO empty threshold is when txfifo_empty_int interrupt produced after the amount of data in Tx-FIFO is less than this register value. + uart.Bus.SetCONF1_TXFIFO_EMPTY_THRHD(uart_empty_thresh_default) + // we are not using interrut on TX since write we are waiting for FIFO to have space. + // uart.Bus.INT_ENA.SetBits(esp.UART_INT_ENA_TXFIFO_EMPTY_INT_ENA) +} + +func (uart *UART) enableReceiver() { + uart.Bus.SetCONF0_RXFIFO_RST(1) + uart.Bus.SetCONF0_RXFIFO_RST(0) + // using value 1 so that we can start populate ring buffer with data as we get it + uart.Bus.SetCONF1_RXFIFO_FULL_THRHD(1) + // enable interrupts for: + uart.Bus.SetINT_ENA_RXFIFO_FULL_INT_ENA(1) + uart.Bus.SetINT_ENA_FRM_ERR_INT_ENA(1) + uart.Bus.SetINT_ENA_PARITY_ERR_INT_ENA(1) + uart.Bus.SetINT_ENA_GLITCH_DET_INT_ENA(1) + uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1) } func (uart *UART) WriteByte(b byte) error {