rt-thread-official/bsp/lpc5410x/Libraries/lpc_chip/chip_5410x/hw_uart.c

596 lines
15 KiB
C

/*
* @brief UART ROM API declarations and functions
*
* @note
* Copyright(C) NXP Semiconductors, 2014
* All rights reserved.
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licensor disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*/
#include "error.h"
#include "hw_uart_rom_api.h"
#define UART_IDLE_FIX /* Remove once IDLE problem is fixed */
/* UART Driver internal data structure */
typedef struct {
void *pUserData; /* Pointer to user data */
UART_REGS_T *pREGS; /* Pointer to Registers */
UART_DATA_T xfer[2]; /* TX/RX transfer data */
#ifdef UART_IDLE_FIX
uint32_t dly; /* Delay to count 1 bit time; REMOVE: when H/W is fixed */
#endif
void(*cbTable[UART_CB_RESERVED]) (UART_HANDLE_T, UART_EVENT_T, void *); /* Call-back index table */
} UART_DRIVER_T;
/* PRIVATE: Division logic to divide without integer overflow */
static uint32_t _UART_DivClk(uint32_t pclk, uint32_t m)
{
uint32_t q, r, u = pclk >> 24, l = pclk << 8;
m = m + 256;
q = (1 << 24) / m;
r = (1 << 24) - (q * m);
return ((q * u) << 8) + (((r * u) << 8) + l) / m;
}
/* PRIVATE: Get highest Over sampling value */
static uint32_t _UART_GetHighDiv(uint32_t val, uint8_t strict)
{
int32_t i, max = strict ? 16 : 5;
for (i = 16; i >= max; i--) {
if (!(val % i)) {
return i;
}
}
return 0;
}
/* PRIVATE: Queue a transfer in UART */
static ErrorCode_t _UART_Xfer(UART_DRIVER_T *pUART, void *buff, uint16_t len, uint8_t op)
{
UART_DATA_T *xfr = &pUART->xfer[op];
/* Xfer of 0 bytes in a UART should always be successful */
if (!len) {
return LPC_OK;
}
/* Check if a Xfer is alredy in progress */
if (xfr->count > xfr->offset) {
return ERR_BUSY;
}
xfr->buf = (void *) buff;
xfr->count = len;
xfr->offset = 0;
xfr->state = UART_ST_BUSY;
if (!op) {
pUART->pREGS->INTENSET = UART_INT_TXRDY;
}
else {
pUART->pREGS->INTENSET = UART_INT_RXRDY | UART_INT_FRMERR | UART_INT_RXNOISE | UART_INT_START | UART_INT_OVR;
}
return LPC_OK;
}
/* Calculate error difference */
static int32_t _CalcErr(uint32_t n, uint32_t d, uint32_t *prev)
{
uint32_t err = n - (n / d) * d;
uint32_t herr = ((n / d) + 1) * d - n;
if (herr < err) {
err = herr;
}
if (*prev <= err) {
return 0;
}
*prev = err;
return (herr == err) + 1;
}
/* Calculate the base DIV value */
static ErrorCode_t _UART_CalcDiv(UART_BAUD_T *ub)
{
int32_t i = 0;
uint32_t perr = ~0UL;
if (!ub->div) {
i = ub->ovr ? ub->ovr : 16;
}
for (; i > 4; i--) {
int32_t tmp = _CalcErr(ub->clk, ub->baud * i, &perr);
/* Continue when no improvement seen in err value */
if (!tmp) {
continue;
}
ub->div = tmp - 1;
if (ub->ovr == i) {
break;
}
ub->ovr = i;
}
if (!ub->ovr) {
return ERR_UART_BAUDRATE;
}
ub->div += ub->clk / (ub->baud * ub->ovr);
if (!ub->div) {
return ERR_UART_BAUDRATE;
}
ub->baud = ub->clk / (ub->div * ub->ovr);
return LPC_OK;
}
/* Calculate the best MUL value */
static void _UART_CalcMul(UART_BAUD_T *ub)
{
uint32_t m, perr = ~0UL, pclk = ub->clk, ovr = ub->ovr;
/* If clock is UART's base clock calculate only the divider */
for (m = 0; m < 256; m++) {
uint32_t ov = ovr, x, v, tmp;
/* Get clock and calculate error */
x = _UART_DivClk(pclk, m);
tmp = _CalcErr(x, ub->baud, &perr);
v = (x / ub->baud) + tmp - 1;
/* Update if new error is better than previous best */
if (!tmp || (ovr && (v % ovr)) ||
(!ovr && ((ov = _UART_GetHighDiv(v, ovr)) == 0))) {
continue;
}
ub->ovr = ov;
ub->mul = m;
ub->clk = x;
ub->div = tmp - 1;
}
}
/* PRIVATE: Invoke UART Call back functions */
static void _UART_InvokeCB(UART_DRIVER_T *pUART, UART_EVENT_T event, void *arg)
{
void (*cbfn)(UART_HANDLE_T, UART_EVENT_T, void *);
cbfn = pUART->cbTable[(uint32_t) event >> 1];
if (cbfn != NULL) {
cbfn((UART_HANDLE_T) pUART, event, arg);
}
}
/* PRIVATE: Handler for data transfers */
static void _UART_HandleTxRx(UART_HANDLE_T hUART, UART_EVENT_T event, void *arg)
{
UART_DATA_T *dat = (UART_DATA_T *) arg;
UART_DRIVER_T *pUART = (UART_DRIVER_T *) hUART;
uint16_t *buf16 = dat->buf;
uint8_t *buf8 = dat->buf;
/* Transmit data */
if (event == UART_TX_DATA) {
while (dat->count && (pUART->pREGS->INTSTAT & UART_INT_TXRDY)) {
if (dat->dwidth) {
pUART->pREGS->TXDAT = *buf16++;
}
else {
pUART->pREGS->TXDAT = *buf8++;
}
dat->count--;
}
return;
}
/* Receive data */
while (dat->count && (pUART->pREGS->INTSTAT & UART_INT_RXRDY)) {
if (dat->dwidth) {
*buf16++ = pUART->pREGS->RXDAT & 0x1FF;
}
else {
*buf8++ = pUART->pREGS->RXDAT & 0xFF;
}
dat->count--;
}
}
/* Handle UART Receive event */
static int32_t _UART_HandleXfer(UART_DRIVER_T *pUART, uint8_t op)
{
UART_DATA_T dat;
UART_DATA_T *xfr = &pUART->xfer[op];
/* See if the transfer is already complete */
if (xfr->offset >= xfr->count) {
return 2;
}
/* Fill the buffer data structure */
dat.count = xfr->count - xfr->offset;
dat.dwidth = ((pUART->pREGS->CFG >> 2) & 3) > 1;
if (dat.dwidth) {
dat.buf = &((uint16_t *) xfr->buf)[xfr->offset];
}
else {
dat.buf = &((uint8_t *) xfr->buf)[xfr->offset];
}
if (!xfr->offset && xfr->count) {
_UART_InvokeCB(pUART, UART_TX_START, xfr);
}
pUART->cbTable[UART_CB_DATA]((UART_HANDLE_T) pUART, (UART_EVENT_T) (UART_TX_DATA + op), &dat);
xfr->offset = (xfr->count - dat.count);
if (xfr->offset >= xfr->count) {
if (!op) {
pUART->pREGS->INTENCLR = UART_INT_TXRDY;
}
else {
pUART->pREGS->INTENCLR = UART_INT_RXRDY;
}
_UART_InvokeCB(pUART, (UART_EVENT_T) (UART_TX_DONE + op), xfr);
if (xfr->state == UART_ST_BUSY) {
xfr->state = UART_ST_DONE;
}
return 1;
}
return 0;
}
/* STOP Receive under progress */
static void _UART_StopRx(UART_HANDLE_T hUART)
{
UART_DRIVER_T *pUART = (UART_DRIVER_T *) hUART;
UART_DATA_T *rx = &pUART->xfer[1];
volatile uint16_t *idx = (volatile uint16_t *) &rx->offset;
if (*idx >= rx->count) {
return;
}
/* Disable further receive interrupts */
pUART->pREGS->INTENCLR = UART_INT_RXRDY;
rx->count = *idx;
_UART_InvokeCB(pUART, UART_RX_DONE, rx);
}
/* EXPROTED API: Returns memory required for UART ROM driver */
uint32_t UART_GetMemSize(void)
{
return sizeof(UART_DRIVER_T);
}
/* EXPORTED API: Calculate UART Baudrate divisors */
ErrorCode_t UART_CalculateBaud(UART_BAUD_T *ub)
{
if (!ub->mul) {
_UART_CalcMul(ub);
}
return _UART_CalcDiv(ub);
}
/* EXPORTED API: UART Initialization function */
UART_HANDLE_T UART_Init(void *mem, uint32_t base_addr, void *args)
{
UART_DRIVER_T *pUART;
/* Check if the memory is word aligned */
if ((uint32_t) mem & 0x3) {
return NULL;
}
/* Assign memory provided by application */
pUART = (UART_DRIVER_T *) mem;
memset(pUART, 0, sizeof(UART_DRIVER_T));
/* Assign the base address */
pUART->pREGS = (UART_REGS_T *) base_addr;
pUART->pUserData = args;
/* Set default handler for TX and RX */
pUART->cbTable[UART_CB_DATA] = _UART_HandleTxRx;
return (UART_HANDLE_T) pUART;
}
/* EXPORTED API: Configure UART parameters */
ErrorCode_t UART_Configure(UART_HANDLE_T hUART, const UART_CFG_T *cfg)
{
UART_DRIVER_T *pUART = (UART_DRIVER_T *) hUART;
UART_REGS_T *pREGS = pUART->pREGS;
if (((cfg->cfg & UART_PAR_MASK) == (1 << 4)) ||
( (cfg->cfg & UART_DATA_MASK) == (3 << 2)) ) {
return ERR_UART_PARAM;
}
/* Enable parity error when parity is enabled */
if ((cfg->cfg & UART_PAR_MASK) >> 4) {
pREGS->INTENSET = UART_INT_PARERR;
}
if (((int32_t) cfg->div <= 0) || ((int32_t) cfg->ovr <= 0)) {
return ERR_UART_PARAM;
}
pREGS->OSR = (cfg->ovr - 1) & 0x0F;
pREGS->BRG = (cfg->div - 1) & 0xFFFF;
pREGS->CFG = UART_CFG_ENABLE | (cfg->cfg & ~UART_CFG_RES);
/* Enabled RX of BREAK event */
if (cfg->cfg & UART_CFG_BRKRX) {
pREGS->INTENSET = UART_INT_BREAK;
}
/* Enable CTS interrupt if requested */
if (cfg->cfg & UART_CFG_CTSEV) {
pREGS->INTENSET = UART_INT_CTS;
}
#ifdef UART_IDLE_FIX
/* REMOVE: if/else block after H/W idle is fixed */
if (cfg->res > 224) {
pUART->dly = 3072 * (cfg->res - 224);
}
else {
pUART->dly = cfg->res << 2;
}
#endif
return LPC_OK;
}
/* EXPORTED API: UART setup special operation like BREAK etc. */
void UART_SetControl(UART_HANDLE_T hUART, uint32_t cfg)
{
uint32_t en, dis;
UART_REGS_T *pREGS = ((UART_DRIVER_T *) hUART)->pREGS;
/* Get list of enabled and disabled options */
en = ((cfg >> 16) & (cfg & 0xFFFF)) << 1;
dis = ((cfg >> 16) & ~(cfg & 0xFFFF)) << 1;
/* See if it is RX Stop request */
if (cfg & UART_RX_STOP) {
_UART_StopRx(hUART);
}
/* See if any IDLEs are enabled */
if (cfg & (UART_IDLE_MASK << 16)) {
pREGS->INTENSET = (en >> 1) & UART_IDLE_MASK;
pREGS->INTENCLR = (dis >> 1) & UART_IDLE_MASK;
}
/* See if it is a request BREAK after TX */
if (en & UART_CTL_TXDIS) {
if (en & UART_CTL_TXBRKEN) {
pREGS->CTL = (pREGS->CTL & ~UART_CTL_RES) | UART_CTL_TXDIS;
while (!(pREGS->STAT & UART_INT_TXDIS)) {}
#ifdef UART_IDLE_FIX
if (1) {
volatile uint32_t dly = ((UART_DRIVER_T *) hUART)->dly;
while (dly--) {}/* Provide some idling time H/W does not do this */
}
#endif
}
else {
pREGS->INTENSET = UART_INT_TXDIS;
}
}
/* See if we are releasing break and resume TX operation */
if ((dis & UART_CTL_TXDIS) && (dis & UART_CTL_TXBRKEN)) {
pREGS->CTL = pREGS->CTL & ~(UART_CTL_RES | UART_CTL_TXBRKEN);
#ifdef UART_IDLE_FIX
if (1) {
volatile uint32_t dly = ((UART_DRIVER_T *) hUART)->dly;
while (dly--) {} /* Provide some idling time H/W does not do this */
}
#endif
}
/* Check for autobaud and enable autobaud err interrupt */
if (en & UART_CTL_AUTOBAUD) {
pREGS->INTENSET = UART_INT_ABAUDERR;
}
pREGS->CTL = ((pREGS->CTL | en) & ~dis) & ~UART_CTL_RES;
}
/* EXPORTED API: Register a call-back function */
ErrorCode_t UART_RegisterCB(UART_HANDLE_T hUART,
UART_CBINDEX_T idx,
void (*cb_func)(UART_HANDLE_T, UART_EVENT_T, void *))
{
if (idx < UART_CB_RESERVED) {
((UART_DRIVER_T *) hUART)->cbTable[idx] = cb_func;
}
else {
return ERR_UART_PARAM;
}
/* Restore internal data handlers when external ones are un-registered */
if ((idx == UART_CB_DATA) && (cb_func == NULL)) {
((UART_DRIVER_T *) hUART)->cbTable[idx] = _UART_HandleTxRx;
}
return LPC_OK;
}
/* EXPORTED API: UART Event handler */
void UART_Handler(UART_HANDLE_T hUART)
{
UART_DRIVER_T *pUART = (UART_DRIVER_T *) hUART;
uint32_t flags = pUART->pREGS->INTENSET & pUART->pREGS->INTSTAT;
if (flags & UART_INT_TXRDY) {
_UART_HandleXfer(pUART, 0);
}
if (flags & UART_INT_FRMERR) {
pUART->pREGS->STAT = UART_INT_FRMERR;
if (pUART->xfer[1].state == UART_ST_BUSY) {
pUART->xfer[1].state = UART_ST_ERRFRM;
}
_UART_InvokeCB(pUART, UART_EV_ERROR, (void *) UART_ERROR_FRAME);
}
if (flags & UART_INT_PARERR) {
pUART->pREGS->STAT = UART_INT_PARERR;
if (pUART->xfer[1].state == UART_ST_BUSY) {
pUART->xfer[1].state = UART_ST_ERRPAR;
}
_UART_InvokeCB(pUART, UART_EV_ERROR, (void *) UART_ERROR_PARITY);
}
if (flags & UART_INT_ABAUDERR) {
pUART->pREGS->STAT = UART_INT_ABAUDERR;
if (pUART->xfer[1].state == UART_ST_BUSY) {
pUART->xfer[1].state = UART_ST_ERR;
}
_UART_InvokeCB(pUART, UART_EV_ERROR, (void *) UART_ERROR_AUTOBAUD);
}
if (flags & UART_INT_RXNOISE) {
pUART->pREGS->STAT = UART_INT_RXNOISE;
if (pUART->xfer[1].state == UART_ST_BUSY) {
pUART->xfer[1].state = UART_ST_ERRNOISE;
}
_UART_InvokeCB(pUART, UART_EV_ERROR, (void *) UART_ERROR_RXNOISE);
}
if (flags & UART_INT_OVR) {
pUART->pREGS->STAT = UART_INT_OVR;
if (pUART->xfer[1].state == UART_ST_BUSY) {
pUART->xfer[1].state = UART_ST_ERROVR;
}
_UART_InvokeCB(pUART, UART_EV_ERROR, (void *) UART_ERROR_OVERRUN);
}
if (flags & UART_INT_RXRDY) {
_UART_HandleXfer(pUART, 1);
#ifdef UART_IDLE_FIX
if (1) {
volatile uint32_t dly = ((UART_DRIVER_T *) hUART)->dly;
while ((pUART->pREGS->STAT & UART_STAT_RXIDLE) && dly--) {}
}
#else
while (pUART->pREGS->STAT & UART_STAT_RXIDLE) {}
#endif
_UART_InvokeCB(pUART, (UART_EVENT_T) (UART_RX_INPROG + ((pUART->pREGS->STAT >> 1) & 1)), &pUART->xfer[1]);
}
if (flags & UART_INT_TXIDLE) {
_UART_InvokeCB(pUART, UART_EV_EVENT, (void *) UART_EVENT_TXIDLE);
}
if (flags & UART_INT_TXDIS) {
pUART->pREGS->INTENCLR = UART_INT_TXDIS;/* Disable interrupt */
_UART_InvokeCB(pUART, UART_EV_EVENT, (void *) UART_EVENT_TXPAUSED);
}
if (flags & UART_INT_CTS) {
pUART->pREGS->STAT = UART_INT_CTS;
_UART_InvokeCB(pUART, UART_EV_EVENT,
(void *) ((pUART->pREGS->STAT & UART_STAT_CTS) ? UART_EVENT_CTSHI : UART_EVENT_CTSLO));
}
if (flags & UART_INT_BREAK) {
pUART->pREGS->STAT = UART_INT_BREAK | UART_INT_FRMERR;
_UART_InvokeCB(pUART, UART_EV_EVENT,
(void *) ((pUART->pREGS->STAT & UART_STAT_BREAK) ? UART_EVENT_BREAK : UART_EVENT_NOBREAK));
}
if (flags & UART_INT_START) {
pUART->pREGS->STAT = UART_INT_START;
_UART_InvokeCB(pUART, UART_RX_START, &pUART->xfer[1]);
}
}
/* EXPORTED API: UART Transmit API */
ErrorCode_t UART_Tx(UART_HANDLE_T hUART, const void *buff, uint16_t len)
{
return _UART_Xfer((UART_DRIVER_T *) hUART, (void *) buff, len, 0);
}
/* EXPORTED API: UART Receive API */
ErrorCode_t UART_Rx(UART_HANDLE_T hUART, void *buff, uint16_t len)
{
return _UART_Xfer((UART_DRIVER_T *) hUART, buff, len, 1);
}
/* EXPORTED API: Flush the TX buffer */
void UART_WaitTX(UART_HANDLE_T hUART)
{
while (!_UART_HandleXfer(hUART, 0)) {}
}
/* EXPORTED API: Fetch the data from UART into RX buffer */
void UART_WaitRX(UART_HANDLE_T hUART)
{
UART_REGS_T *pREGS = ((UART_DRIVER_T *) hUART)->pREGS;
/* See if the data needs to be discarded */
if (_UART_HandleXfer(hUART, 1) == 2) {
volatile uint32_t dummy;
while ((pREGS->STAT & UART_INT_RXRDY) || !(pREGS->STAT & UART_STAT_RXIDLE)) {
dummy = pREGS->RXDAT;
}
}
while (!_UART_HandleXfer(hUART, 1)) {}
}
/* EXPORTED API: Function to Get the firmware Version */
uint32_t UART_GetDriverVersion(void)
{
return UART_DRIVER_VERSION;
}
/**
* @brief Table of the addresses of all the UART ROM APIs
* @note This table of function pointers is the API interface.
*/
const ROM_UART_API_T uartrom_api = {
UART_GetMemSize,
UART_CalculateBaud,
UART_Init,
UART_Configure,
UART_SetControl,
UART_RegisterCB,
UART_Handler,
UART_Tx,
UART_Rx,
UART_WaitTX,
UART_WaitRX,
UART_GetDriverVersion,
};