/**************************************************************************//** * @file wb_uart.c * @brief The UART related function of Nuvoton ARM9 MCU * * SPDX-License-Identifier: Apache-2.0 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include <string.h> #include <stdio.h> #include "wblib.h" #ifdef __HW_SIM__ #undef REG_UART_THR #define REG_UART_THR (0xFFF04400) // TUBE ON #endif #define vaStart(list, param) list = (INT8*)((INT)¶m + sizeof(param)) #define vaArg(list, type) ((type *)(list += sizeof(type)))[-1] /* Global variables */ BOOL volatile _sys_bIsUARTInitial = FALSE; BOOL volatile _sys_bIsUseUARTInt = TRUE; UINT32 _sys_uUARTClockRate = EXTERNAL_CRYSTAL_CLOCK; //UINT32 UART_BA = UART0_BA; #define sysTxBufReadNextOne() (((_sys_uUartTxHead+1)==UART_BUFFSIZE)? (UINT32)NULL: _sys_uUartTxHead+1) #define sysTxBufWriteNextOne() (((_sys_uUartTxTail+1)==UART_BUFFSIZE)? (UINT32)NULL: _sys_uUartTxTail+1) #define UART_BUFFSIZE 256 UINT8 _sys_ucUartTxBuf[UART_BUFFSIZE]; UINT32 volatile _sys_uUartTxHead, _sys_uUartTxTail; PVOID _sys_pvOldUartVect; UINT32 u32UartPort =0x100; void sysUartPort(UINT32 u32Port) { u32UartPort = (u32Port & 0x1)*0x100; if(u32Port==0) {//High Speed UART outp32(REG_GPDFUN, inp32(REG_GPDFUN) | (MF_GPD2 | MF_GPD1)); } else if(u32Port==1) {//Nornal Speed UART outp32(REG_GPAFUN, inp32(REG_GPAFUN) | (MF_GPA11 | MF_GPA10)); } } VOID sysUartISR() { UINT32 volatile regIIR, i; regIIR = inpb(REG_UART_ISR+u32UartPort*0x100); if (regIIR & THRE_IF) {// buffer empty if (_sys_uUartTxHead == _sys_uUartTxTail) {//Disable interrupt if no any request! outpb((REG_UART_IER+u32UartPort), inp32(REG_UART_IER+u32UartPort) & (~THRE_IEN)); } else {//Transmit data for (i=0; i<8; i++) { #ifndef __HW_SIM__ outpb(REG_UART_THR+u32UartPort, _sys_ucUartTxBuf[_sys_uUartTxHead]); #endif _sys_uUartTxHead = sysTxBufReadNextOne(); if (_sys_uUartTxHead == _sys_uUartTxTail) // buffer empty break; } } } } static VOID sysSetBaudRate(UINT32 uBaudRate) { UINT32 _mBaudValue; /* First, compute the baudrate divisor. */ #if 0 // mode 0 _mBaudValue = (_sys_uUARTClockRate / (uBaudRate * 16)); if ((_sys_uUARTClockRate % (uBaudRate * 16)) > ((uBaudRate * 16) / 2)) _mBaudValue++; _mBaudValue -= 2; outpw(REG_UART_BAUD+u32UartPort, _mBaudValue); #else // mode 3 _mBaudValue = (_sys_uUARTClockRate / uBaudRate)-2; outpw(REG_UART_BAUD+u32UartPort, ((0x30<<24)| _mBaudValue)); #endif } INT32 sysInitializeUART(WB_UART_T *uart) { /* Enable UART multi-function pins*/ //outpw(REG_PINFUN, inpw(REG_PINFUN) | 0x80); outpw(REG_GPAFUN, inpw(REG_GPAFUN) | 0x00F00000); //Normal UART pin function /* Check the supplied parity */ if ((uart->uiParity != WB_PARITY_NONE) && (uart->uiParity != WB_PARITY_EVEN) && (uart->uiParity != WB_PARITY_ODD)) /* The supplied parity is not valid */ return WB_INVALID_PARITY; /* Check the supplied number of data bits */ else if ((uart->uiDataBits != WB_DATA_BITS_5) && (uart->uiDataBits != WB_DATA_BITS_6) && (uart->uiDataBits != WB_DATA_BITS_7) && (uart->uiDataBits != WB_DATA_BITS_8)) /* The supplied data bits value is not valid */ return WB_INVALID_DATA_BITS; /* Check the supplied number of stop bits */ else if ((uart->uiStopBits != WB_STOP_BITS_1) && (uart->uiStopBits != WB_STOP_BITS_2)) /* The supplied stop bits value is not valid */ return WB_INVALID_STOP_BITS; /* Verify the baud rate is within acceptable range */ else if (uart->uiBaudrate < 1200) /* The baud rate is out of range */ return WB_INVALID_BAUD; /* Reset the TX/RX FIFOs */ outpw(REG_UART_FCR+u32UartPort, 0x07); /* Setup reference clock */ _sys_uUARTClockRate = uart->uiFreq; /* Setup baud rate */ sysSetBaudRate(uart->uiBaudrate); /* Set the modem control register. Set DTR, RTS to output to LOW, and set INT output pin to normal operating mode */ //outpb(UART_MCR, (WB_DTR_Low | WB_RTS_Low | WB_MODEM_En)); /* Setup parity, data bits, and stop bits */ outpw(REG_UART_LCR+u32UartPort,(uart->uiParity | uart->uiDataBits | uart->uiStopBits)); /* Timeout if more than ??? bits xfer time */ outpw(REG_UART_TOR+u32UartPort, 0x80+0x20); /* Setup Fifo trigger level and enable FIFO */ outpw(REG_UART_FCR+u32UartPort, uart->uiRxTriggerLevel|0x01); // hook UART interrupt service routine #if 0 if (uart->uart_no == WB_UART_0) { _sys_uUartTxHead = _sys_uUartTxTail = NULL; _sys_pvOldUartVect = sysInstallISR(IRQ_LEVEL_1, IRQ_UART, (PVOID)sysUartISR); sysEnableInterrupt(IRQ_UART); sysSetLocalInterrupt(ENABLE_IRQ); } #endif _sys_bIsUARTInitial = TRUE; return Successful; } VOID _PutChar_f(UINT8 ucCh) { if (_sys_bIsUseUARTInt == TRUE) { while(sysTxBufWriteNextOne() == _sys_uUartTxHead) ; // buffer full _sys_ucUartTxBuf[_sys_uUartTxTail] = ucCh; _sys_uUartTxTail = sysTxBufWriteNextOne(); if (ucCh == '\n') { while(sysTxBufWriteNextOne() == _sys_uUartTxHead) ; // buffer full _sys_ucUartTxBuf[_sys_uUartTxTail] = '\r'; _sys_uUartTxTail = sysTxBufWriteNextOne(); } if (!(inpw(REG_UART_IER+u32UartPort) & 0x02)) outpw(REG_UART_IER+u32UartPort, 0x02); } else { /* Wait until the transmitter buffer is empty */ while (!(inpw(REG_UART_FSR+u32UartPort) & 0x400000)); /* Transmit the character */ outpb(REG_UART_THR+u32UartPort, ucCh); if (ucCh == '\n') { /* Wait until the transmitter buffer is empty */ while (!(inpw(REG_UART_FSR+u32UartPort) & 0x400000)); outpb(REG_UART_THR+u32UartPort, '\r'); } } } VOID sysPutString(INT8 *string) { while (*string != '\0') { _PutChar_f(*string); string++; } } static VOID sysPutRepChar(INT8 c, INT count) { while (count--) _PutChar_f(c); } static VOID sysPutStringReverse(INT8 *s, INT index) { while ((index--) > 0) _PutChar_f(s[index]); } static VOID sysPutNumber(INT value, INT radix, INT width, INT8 fill) { INT8 buffer[40]; INT bi = 0; UINT32 uvalue; UINT16 digit; UINT16 left = FALSE; UINT16 negative = FALSE; if (fill == 0) fill = ' '; if (width < 0) { width = -width; left = TRUE; } if (width < 0 || width > 80) width = 0; if (radix < 0) { radix = -radix; if (value < 0) { negative = TRUE; value = -value; } } uvalue = value; do { if (radix != 16) { digit = uvalue % radix; uvalue = uvalue / radix; } else { digit = uvalue & 0xf; uvalue = uvalue >> 4; } buffer[bi] = digit + ((digit <= 9) ? '0' : ('A' - 10)); bi++; if (uvalue != 0) { if ((radix == 10) && ((bi == 3) || (bi == 7) || (bi == 11) || (bi == 15))) { buffer[bi++] = ','; } } } while (uvalue != 0); if (negative) { buffer[bi] = '-'; bi += 1; } if (width <= bi) sysPutStringReverse(buffer, bi); else { width -= bi; if (!left) sysPutRepChar(fill, width); sysPutStringReverse(buffer, bi); if (left) sysPutRepChar(fill, width); } } static INT8 *FormatItem(INT8 *f, INT a) { INT8 c; INT fieldwidth = 0; INT leftjust = FALSE; INT radix = 0; INT8 fill = ' '; if (*f == '0') fill = '0'; while ((c = *f++) != 0) { if (c >= '0' && c <= '9') { fieldwidth = (fieldwidth * 10) + (c - '0'); } else switch (c) { case '\000': return (--f); case '%': _PutChar_f('%'); return (f); case '-': leftjust = TRUE; break; case 'c': { if (leftjust) _PutChar_f(a & 0x7f); if (fieldwidth > 0) sysPutRepChar(fill, fieldwidth - 1); if (!leftjust) _PutChar_f(a & 0x7f); return (f); } case 's': { if (leftjust) sysPutString((PINT8)a); if (fieldwidth > strlen((PINT8)a)) sysPutRepChar(fill, fieldwidth - strlen((PINT8)a)); if (!leftjust) sysPutString((PINT8)a); return (f); } case 'd': case 'i': radix = -10; break; case 'u': radix = 10; break; case 'x': radix = 16; break; case 'X': radix = 16; break; case 'o': radix = 8; break; default: radix = 3; break; /* unknown switch! */ } if (radix) break; } if (leftjust) fieldwidth = -fieldwidth; sysPutNumber(a, radix, fieldwidth, fill); return (f); } /*================================================================== Default check chip version G Version ND2 = 0: Enable Debug Message default. ND2 = 1: Disable Debug Message default. ==================================================================*/ #define VERSION_ADDR 0xFFFF3EB4 static UINT32 u32DbgMessage = 0xFFFFFFFF; CHAR sysGetChipVersion(void) { if(inp32(0xFFFF3EB4) == 0x50423238) return 'G'; else return 'A'; } VOID sysUartEnableDebugMessage(BOOL bIsDebugMessage) { if( bIsDebugMessage == TRUE ) u32DbgMessage = 1; else u32DbgMessage = 0; sysprintf("u32DbgMessage = 0x%x\n", u32DbgMessage); } VOID sysPrintf(PINT8 pcStr,...) { WB_UART_T uart; INT8 *argP; if(u32DbgMessage == 0xFFFFFFFF) /* Default */ { if( sysGetChipVersion() == 'G' ) { if((inp32(REG_CHIPCFG) & 0x4) == 0x4) return; } }else if(u32DbgMessage == 0) /* Disable UART message from UART1 */ return; _sys_bIsUseUARTInt = TRUE; if (!_sys_bIsUARTInitial) { uart.uart_no = WB_UART_0; uart.uiFreq = EXTERNAL_CRYSTAL_CLOCK; uart.uiBaudrate = 115200; uart.uiDataBits = WB_DATA_BITS_8; uart.uiStopBits = WB_STOP_BITS_1; uart.uiParity = WB_PARITY_NONE; uart.uiRxTriggerLevel = LEVEL_1_BYTE; sysInitializeUART(&uart); } vaStart(argP, pcStr); /* point at the end of the format string */ while (*pcStr) { /* this works because args are all ints */ if (*pcStr == '%') pcStr = FormatItem(pcStr + 1, vaArg(argP, INT)); else _PutChar_f(*pcStr++); } } VOID sysprintf(PINT8 pcStr,...) { WB_UART_T uart; INT8 *argP; if(u32DbgMessage == 0xFFFFFFFF) /* Default */ { if( sysGetChipVersion() == 'G' ) { if((inp32(REG_CHIPCFG) & 0x4) == 0x4) return; } }else if(u32DbgMessage == 0) /* Disable UART message from UART1 */ return; _sys_bIsUseUARTInt = FALSE; if (!_sys_bIsUARTInitial) {//Default use external clock 12MHz as source clock. uart.uart_no = WB_UART_0; uart.uiFreq = EXTERNAL_CRYSTAL_CLOCK; uart.uiBaudrate = 115200; uart.uiDataBits = WB_DATA_BITS_8; uart.uiStopBits = WB_STOP_BITS_1; uart.uiParity = WB_PARITY_NONE; uart.uiRxTriggerLevel = LEVEL_1_BYTE; sysInitializeUART(&uart); } vaStart(argP, pcStr); /* point at the end of the format string */ while (*pcStr) { /* this works because args are all ints */ if (*pcStr == '%') pcStr = FormatItem(pcStr + 1, vaArg(argP, INT)); else _PutChar_f(*pcStr++); } } INT8 sysGetChar() { while (1) { if (inpw(REG_UART_ISR+u32UartPort) & 0x01) return (inpb(REG_UART_RBR+u32UartPort)); } } VOID sysPutChar(UINT8 ucCh) { if(u32DbgMessage == 0xFFFFFFFF) /* Default */ { if( sysGetChipVersion() == 'G' ) { if((inp32(REG_CHIPCFG) & 0x4) == 0x4) return; } }else if(u32DbgMessage == 0) /* Disable UART message from UART1 */ return; /* Wait until the transmitter buffer is empty */ while (!(inpw(REG_UART_FSR+u32UartPort) & 0x400000)); /* Transmit the character */ outpb(REG_UART_THR+u32UartPort, ucCh); }