|
|
@ -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 { |
|
|
|