#include #include #include #include "inc/hw_types.h" #include "inc/hw_uart.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/gpio.h" #include "driverlib/uart.h" #include ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// static HFIFO g_hFifoTX = NULL, g_hFifoRX = NULL; static volatile uint32_t g_nTxEchoCount = 0; static volatile uint64_t g_nTimerTick = 0; static GFA_UART_STATUS_COUNTERS g_statCnt = {0}; static GFA_UART_CONFIG g_uartCfg = {0}; static GFA_MODBUS_PROTOCOL_TIMEOUTS g_mpt = {0}; ///////////////////////////////////////////////////////////////////////////// #define GFA_UART_BASE g_uartCfg.P_UART_BASE #define GFA_UART_BASE_SYSCTL g_uartCfg.P_UART_BASE_SYSCTL #define GFA_UART_PORT g_uartCfg.P_UART_PORT #define GFA_UART_PORT_SYSCTL g_uartCfg.P_UART_PORT_SYSCTL #define GFA_UART_RX_PIN g_uartCfg.P_UART_RX_PIN #define GFA_UART_RX_PIN_MUX g_uartCfg.P_UART_RX_PIN_MUX #define GFA_UART_TX_PIN g_uartCfg.P_UART_TX_PIN #define GFA_UART_TX_PIN_MUX g_uartCfg.P_UART_TX_PIN_MUX #define GFA_UART_INT g_uartCfg.P_UART_INT #define GFA_EN_485_PORT_SYSCTL g_uartCfg.P_EN_485_PORT_SYSCTL #define GFA_EN_485_PORT g_uartCfg.P_EN_485_PORT #define GFA_EN_485_PIN g_uartCfg.P_EN_485_PIN #define GFA_UART_INT_PRIORITY g_uartCfg.P_UART_INT_PRIORITY #define GFA_FRAME_TIMEOUT_US g_mpt.nFrameTimeoutUs #define GFA_CHAR_TIMEOUT_US g_mpt.nCharTimeoutUs #define IS_INT_ENABLED(f) !!(HWREG(GFA_UART_BASE + UART_O_IM) & (f)) #define INT_MASK_REGISTER HWREG(GFA_UART_BASE + UART_O_IM) ///////////////////////////////////////////////////////////////////////////// static bool _TimerElapsed(void) { struct timeval tv; return g_nTimerTick < GfaSystickTimeval2Us(GfaSystickGetUsClock(&tv)); } ///////////////////////////////////////////////////////////////////////////// static void _FrameTimerStart(void) { struct timeval tv; g_nTimerTick = GfaSystickTimeval2Us(GfaSystickGetUsClock(&tv)) + GFA_FRAME_TIMEOUT_US; } ///////////////////////////////////////////////////////////////////////////// static void _CharTimerStart(void) { struct timeval tv; g_nTimerTick = GfaSystickTimeval2Us(GfaSystickGetUsClock(&tv)) + GFA_CHAR_TIMEOUT_US; } ///////////////////////////////////////////////////////////////////////////// static bool _OnRxFinalize(void) { _FrameTimerStart(); return true; } ///////////////////////////////////////////////////////////////////////////// static bool _OnTxPrepare(void) { return _TimerElapsed(); } ///////////////////////////////////////////////////////////////////////////// static void _OnTxStart(void) { uint8_t b; if(!UARTBusy(GFA_UART_BASE) && GfaMbFifoPop(g_hFifoTX, &b, false)) { g_nTxEchoCount++; GPIOPinWrite(GFA_EN_485_PORT, GFA_EN_485_PIN, GFA_EN_485_PIN); // enable the bus UARTIntEnable(GFA_UART_BASE, UART_INT_TX); ++g_statCnt.nCharsTransmitted; UARTCharPutNonBlocking(GFA_UART_BASE, b); } } ///////////////////////////////////////////////////////////////////////////// static bool _OnTxFinalize(void) { return !UARTBusy(GFA_UART_BASE) && _TimerElapsed(); } ///////////////////////////////////////////////////////////////////////////// static void _UART_RX_ISR(void) { uint8_t b; if(g_nTxEchoCount) { UARTCharGetNonBlocking(GFA_UART_BASE); // discard transmit echo chars --g_nTxEchoCount; _FrameTimerStart(); return; } ++g_statCnt.nCharsReceived; if(_TimerElapsed()) // check, if the time since the last character was received, is greater than 3.5/1.5 characters { GfaMbFifoReset(g_hFifoRX, false); GfaMbFifoSetFlags(g_hFifoRX, MB_RTU_FLAG_FRAME_GAP_DETECT, false); if(GfaMbFifoMatchFlags(g_hFifoRX, MB_RTU_FLAG_IGNORE_FRAME, false)) { GfaMbFifoClearFlags(g_hFifoRX, MB_RTU_FLAG_IGNORE_FRAME, false); } GfaMbFifoClearFlags(g_hFifoTX, MB_RTU_FLAG_TRANSMIT_END, false); } else if(GfaMbFifoMatchFlags(g_hFifoRX, MB_RTU_FLAG_IGNORE_FRAME, false)) { UARTCharGetNonBlocking(GFA_UART_BASE); // discard chars _FrameTimerStart(); return; } else if(GfaMbFifoMatchFlags(g_hFifoTX, MB_RTU_FLAG_TRANSMIT_END, false)) { UARTCharGetNonBlocking(GFA_UART_BASE); // discard chars GfaMbFifoReset(g_hFifoRX, false); return; } b = (uint8_t)UARTCharGetNonBlocking(GFA_UART_BASE); GfaMbFifoPush(g_hFifoRX, b, false); _CharTimerStart(); } ///////////////////////////////////////////////////////////////////////////// static void _UART_TX_ISR(void) { uint8_t b; if(GfaMbFifoPop(g_hFifoTX, &b, false)) { ++g_nTxEchoCount; UARTCharPutNonBlocking(GFA_UART_BASE, b); ++g_statCnt.nCharsTransmitted; } else { UARTIntDisable(GFA_UART_BASE, UART_INT_TX); GPIOPinWrite(GFA_EN_485_PORT, GFA_EN_485_PIN, 0); // disable the bus GfaMbFifoClearFlags(g_hFifoTX, MB_RTU_FLAG_TRANSMIT_IN_PROGRESS, false); GfaMbFifoSetFlags(g_hFifoTX, MB_RTU_FLAG_TRANSMIT_END, false); _FrameTimerStart(); } } ///////////////////////////////////////////////////////////////////////////// static void _UART_OE_ISR(void) { ++g_statCnt.nOverrunErrors; GfaMbFifoSetFlags(g_hFifoRX, MB_RTU_FLAG_OVERRUN_ERROR, false); } static void _UART_BE_ISR(void) { ++g_statCnt.nBreakErrors; GfaMbFifoSetFlags(g_hFifoRX, MB_RTU_FLAG_BREAK_ERROR, false); } static void _UART_PE_ISR(void) { ++g_statCnt.nParityErrors; GfaMbFifoSetFlags(g_hFifoRX, MB_RTU_FLAG_PARITY_ERROR, false); } static void _UART_FE_ISR(void) { ++g_statCnt.nFramingErrors; GfaMbFifoSetFlags(g_hFifoRX, MB_RTU_FLAG_FRAMING_ERROR, false); } ///////////////////////////////////////////////////////////////////////////// static void _UART_ISR(void) { uint32_t nStatus = UARTIntStatus(GFA_UART_BASE, true); // Get the interrrupt status. UARTIntClear(GFA_UART_BASE, nStatus); // Clear the asserted interrupts. if(nStatus & UART_INT_TX) _UART_TX_ISR(); if(nStatus & UART_INT_RX) _UART_RX_ISR(); if(nStatus & UART_INT_OE) // UART Overrun Error _UART_OE_ISR(); if(nStatus & UART_INT_BE) // UART Break Error _UART_BE_ISR(); if(nStatus & UART_INT_PE) // UART Parity Error _UART_PE_ISR(); if(nStatus & UART_INT_FE) // UART Framing Error _UART_FE_ISR(); } ///////////////////////////////////////////////////////////////////////////// static bool _EnableRxInt(bool bEnable) { bool bIsEnabled = IS_INT_ENABLED(UART_INT_RX); if(bEnable && !bIsEnabled) UARTIntEnable(GFA_UART_BASE, UART_INT_RX); else if(!bEnable && bIsEnabled) UARTIntDisable(GFA_UART_BASE, UART_INT_RX); return bIsEnabled; } ///////////////////////////////////////////////////////////////////////////// static bool _EnableTxInt(bool bEnable) { bool bIsEnabled = IS_INT_ENABLED(UART_INT_TX); if(bEnable && !bIsEnabled) UARTIntEnable(GFA_UART_BASE, UART_INT_TX); else if(!bEnable && bIsEnabled) UARTIntDisable(GFA_UART_BASE, UART_INT_TX); return bIsEnabled; } ///////////////////////////////////////////////////////////////////////////// static uint32_t _GetDatabits(int nDatabits) { switch(nDatabits) { case 5: return UART_CONFIG_WLEN_5; case 6: return UART_CONFIG_WLEN_6; case 7: return UART_CONFIG_WLEN_7; case 8: return UART_CONFIG_WLEN_8; default: return (uint32_t)-1; } } ///////////////////////////////////////////////////////////////////////////// static uint32_t _GetStopbits(int nStopbits) { switch(nStopbits) { case 1: return UART_CONFIG_STOP_ONE; case 2: return UART_CONFIG_STOP_TWO; default: return (uint32_t)-1; } } ///////////////////////////////////////////////////////////////////////////// static uint32_t _GetParity(GfA_UART_Parity parity) { switch(parity) { case P_None: return UART_CONFIG_PAR_NONE; case P_Even: return UART_CONFIG_PAR_EVEN; case P_Odd: return UART_CONFIG_PAR_ODD; case P_Zero: return UART_CONFIG_PAR_ZERO; case P_One: return UART_CONFIG_PAR_ONE; default: return (uint32_t)-1; } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// bool GfaMbUartInit(LPCGFA_UART_CONFIG pCfg) { uint32_t nData, nStop, nPar; GFA_FIFO_BACKEND backend; ///////////////////////////////////////////////////////////////////////// // validate parameters if(!pCfg) return false; if((nData = _GetDatabits(pCfg->nDatabits)) == (uint32_t)-1) return false; if((nStop = _GetStopbits(pCfg->nStopbits)) == (uint32_t)-1) return false; if((nPar = _GetParity(pCfg->parity)) == (uint32_t)-1) return false; if(pCfg->nFifoIndexTx == pCfg->nFifoIndexRx) return false; memcpy(&g_uartCfg, pCfg, sizeof(g_uartCfg)); ///////////////////////////////////////////////////////////////////////// // Fifos memset(&backend, 0, sizeof(backend)); backend.pfnLockBackend = _EnableTxInt; backend.pfnTxPrepare = _OnTxPrepare; backend.pfnTxStart = _OnTxStart; backend.pfnTxFinalize = _OnTxFinalize; if(!(g_hFifoTX = GfaMbFifoCreate(pCfg->nFifoIndexTx, &backend))) return false; memset(&backend, 0, sizeof(backend)); backend.pfnLockBackend = _EnableRxInt; backend.pfnRxFinalize = _OnRxFinalize; if(!(g_hFifoRX = GfaMbFifoCreate(pCfg->nFifoIndexRx, &backend))) { GfaMbFifoRelease(g_hFifoTX); g_hFifoTX = NULL; return false; } ///////////////////////////////////////////////////////////////////////// // Timer tick g_nTimerTick = 0; ///////////////////////////////////////////////////////////////////////// // UART if(!GfaMbCalcProtocolTimeouts(pCfg->nBaud, &g_mpt)) return false; // Enable and setup the PORT that is used for the UART SysCtlPeripheralEnable(GFA_UART_PORT_SYSCTL); while(!SysCtlPeripheralReady(GFA_UART_PORT_SYSCTL)) ; // Enable and setup the PORT that is used for EN_485 SysCtlPeripheralEnable(GFA_EN_485_PORT_SYSCTL); while(!SysCtlPeripheralReady(GFA_EN_485_PORT_SYSCTL)) ; GPIOPinTypeGPIOOutput(GFA_EN_485_PORT, GFA_EN_485_PIN); // Enable the uart used for RS485 interface SysCtlPeripheralEnable(GFA_UART_BASE_SYSCTL); while(!SysCtlPeripheralReady(GFA_UART_BASE_SYSCTL)) ; // Configure the UART pins GPIOPinConfigure(GFA_UART_RX_PIN_MUX); GPIOPinConfigure(GFA_UART_TX_PIN_MUX); GPIOPinTypeUART(GFA_UART_PORT, GFA_UART_RX_PIN | GFA_UART_TX_PIN); // Initialize the UART. Set the baud rate, number of data bits, turn off // parity, number of stop bits, and stick mode. UARTConfigSetExpClk(GFA_UART_BASE, SysCtlClockGet(), pCfg->nBaud, nData | nStop | nPar); // transmit interrupt is asserted when the transmitter is completely idle UARTTxIntModeSet(GFA_UART_BASE, UART_TXINT_MODE_EOT); // Enable the UART interrupt. IntPrioritySet(GFA_UART_INT, GFA_UART_INT_PRIORITY); // priority must be lower than system tick UARTIntRegister(GFA_UART_BASE, _UART_ISR); UARTIntEnable(GFA_UART_BASE, (UART_INT_RX | UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE)); // Enable the UART. UARTEnable(GFA_UART_BASE); UARTFIFODisable(GFA_UART_BASE); return true; } ///////////////////////////////////////////////////////////////////////////// void GfaMbUartRelease(void) { UARTIntDisable(GFA_UART_BASE, UART_INT_RX | UART_INT_TX | UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE); UARTIntUnregister(GFA_UART_BASE); UARTDisable(GFA_UART_BASE); SysCtlPeripheralDisable(GFA_UART_BASE_SYSCTL); SysCtlPeripheralDisable(GFA_EN_485_PORT_SYSCTL); SysCtlPeripheralDisable(GFA_UART_PORT_SYSCTL); if(g_hFifoRX) { GfaMbFifoRelease(g_hFifoRX); g_hFifoRX = NULL; } if(g_hFifoTX) { GfaMbFifoRelease(g_hFifoTX); g_hFifoTX = NULL; } } ///////////////////////////////////////////////////////////////////////////// HFIFO GfaMbUartGetRxFifo(void) { return g_hFifoRX; } ///////////////////////////////////////////////////////////////////////////// HFIFO GfaMbUartGetTxFifo(void) { return g_hFifoTX; } ///////////////////////////////////////////////////////////////////////////// void GfaMbUartGetProtocolTimeouts(LPGFA_MODBUS_PROTOCOL_TIMEOUTS pmpt) { if(pmpt) memcpy(pmpt, &g_mpt, sizeof(g_mpt)); } ///////////////////////////////////////////////////////////////////////////// void GfaMbUartGetStatusCounters(LPGFA_UART_STATUS_COUNTERS pSc) { if(pSc) { uint32_t nIrMask = INT_MASK_REGISTER; UARTIntDisable(GFA_UART_BASE, nIrMask); memcpy(pSc, &g_statCnt, sizeof(g_statCnt)); UARTIntEnable(GFA_UART_BASE, nIrMask); } } ///////////////////////////////////////////////////////////////////////////// void GfaMbUartResetStatusCounters(void) { uint32_t nIrMask = INT_MASK_REGISTER; UARTIntDisable(GFA_UART_BASE, nIrMask); memset(&g_statCnt, 0, sizeof(g_statCnt)); UARTIntEnable(GFA_UART_BASE, nIrMask); }