⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl011api.c

📁 ARM9基于WINDOWSCE的BSP源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Release Status:OS005-SW-70002-r0p0-00REL0
// $Copyright: 
// ----------------------------------------------------------------
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
//   (C) COPYRIGHT 2004 ARM Limited
//       ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
// by a licensing agreement from ARM Limited.
// ----------------------------------------------------------------
// File:     PL011api.c,v
// Revision: 1.2
// ----------------------------------------------------------------
// $
//
#include <windows.h>    // Main include file for Windows development

#include "PL011.h"      // PL011 UART register and bit definitions
#include "PL011api.h"   // PL011 API


// Types

// PL011 register
//
typedef ULONG volatile  REG32;      // PL011 register type (volatile, 32 bit registers)

// PL011 context
//
typedef struct tagPL011_CONTEXT
{
    // Pointers to each register
    //
    REG32               *pDR;       // Data transmit/receive [r:12,w:8]
    REG32 const         *pRSR;      // Receive status [r:4]
    REG32               *pECR;      // Error clear [w:4]
    REG32 const         *pFR;       // Flags [r:9]
    REG32               *pILPR;     // IrDA low-power counter [rw:8]
    REG32               *pIBRD;     // Integer baud rate divisor [rw:16]
    REG32               *pFBRD;     // Fractional baud rate divisor [rw:6]
    REG32               *pLCR_H;    // Line control (high byte) [rw:8]
    REG32               *pCR;       // Control [rw:16]
    REG32               *pIFLS;     // Interrupt FIFO level select [rw:6]
    REG32               *pIMSC;     // Interrupt mask set/clear [rw:11]
    REG32 const         *pRIS;      // Raw interrupt status [r:11]
    REG32 const         *pMIS;      // Masked interrupt status [r:11]
    REG32               *pICR;      // Interrupt clear [w:11]
    REG32               *pDMACR;    // DMA control [rw:3]

    // Shadow registers (for power on/off)
    //
    ULONG               ILPR;       // IrDA low-power counter
    ULONG               IBRD;       // Integer baud rate divisor
    ULONG               FBRD;       // Fractional baud rate divisor
    ULONG               LCR_H;      // Line control
    ULONG               CR;         // Control
    ULONG               IFLS;       // Interrupt FIFO level select
    ULONG               IMSC;       // Interrupt mask set/clear
    ULONG               DMACR;      // DMA control

                                    // Critical sections for serialising access to:
    CRITICAL_SECTION    csXmit;     //  data register (for transmission)
    CRITICAL_SECTION    csRegs;     //  general registers (for non-atomic usage)
    CRITICAL_SECTION    csFlow;     //  control register (for free flow control)

    // NOTE: To prevent deadlock, critical sections should be entered in the order 'csXmit, csRegs,
    //        csFlow' and left in the reverse order 'csFlow, csRegs, csXmit'.

} PL011_CONTEXT;


// Inline I/O helpers
//
static __inline ULONG ReadReg32(REG32 const *pReg32)
{
    return *pReg32;
}

static __inline VOID WriteReg32(REG32 *pReg32, ULONG ulValue)
{ 
    *pReg32 = ulValue;
}


// Inline helpers
//
static __inline BOOL PL011IsBusy(PL011_CONTEXT *pPL011)
{
    return ((ReadReg32(pPL011->pFR) & PL011_FR_BUSY) != 0);
}


///////////////////////////////////////////////////////////////////////////////////////////////////
// PL011 device API
//
PL011_CONTEXT* PL011Create(ULONG ulBaseAddress)
{
    PL011_CONTEXT *pPL011;

    // Must have a valid base address pointer for the UART, with write access to its register space
    //
    if ((ulBaseAddress == 0) || (IsBadWritePtr((VOID*)ulBaseAddress, 0x1000) != 0))
        return NULL;

    // Allocate space for the PL011 context (no need to use LPTR or LMEM_ZEROINIT to initialise the
    //  memory to zero because we will be explicitly initialising each member of the context next).
    //
    pPL011 = (PL011_CONTEXT*)LocalAlloc(LMEM_FIXED , sizeof(PL011_CONTEXT));
    if (pPL011 == NULL)
        return NULL;

    // Pointers to each UART register
    //
    pPL011->pDR    = (REG32*)(ulBaseAddress + PL011_DR);    // Data transmit/receive
    pPL011->pRSR   = (REG32*)(ulBaseAddress + PL011_RSR);   // Receive status (r)
    pPL011->pECR   = (REG32*)(ulBaseAddress + PL011_ECR);   // Error clear (w)
    pPL011->pFR    = (REG32*)(ulBaseAddress + PL011_FR);    // Flags (r)
    pPL011->pILPR  = (REG32*)(ulBaseAddress + PL011_ILPR);  // IrDA low-power counter
    pPL011->pIBRD  = (REG32*)(ulBaseAddress + PL011_IBRD);  // Integer baud rate divisor
    pPL011->pFBRD  = (REG32*)(ulBaseAddress + PL011_FBRD);  // Fractional baud rate divisor
    pPL011->pLCR_H = (REG32*)(ulBaseAddress + PL011_LCR_H); // Line control (high byte)
    pPL011->pCR    = (REG32*)(ulBaseAddress + PL011_CR);    // Control
    pPL011->pIFLS  = (REG32*)(ulBaseAddress + PL011_IFLS);  // Interrupt FIFO level select
    pPL011->pIMSC  = (REG32*)(ulBaseAddress + PL011_IMSC);  // Interrupt mask set/clear
    pPL011->pRIS   = (REG32*)(ulBaseAddress + PL011_RIS);   // Raw interrupt status (r)
    pPL011->pMIS   = (REG32*)(ulBaseAddress + PL011_MIS);   // Masked interrupt status (r)
    pPL011->pICR   = (REG32*)(ulBaseAddress + PL011_ICR);   // Interrupt clear (w)
    pPL011->pDMACR = (REG32*)(ulBaseAddress + PL011_DMACR); // DMA control (rw)

    // Shadow UART registers (for power on/off)
    //
    pPL011->IMSC  = 0;  // Interrupt mask set/clear
    pPL011->ILPR  = 0;  // IrDA low-power counter
    pPL011->IBRD  = 0;  // Integer baud rate divisor
    pPL011->FBRD  = 0;  // Fractional baud rate divisor
    pPL011->LCR_H = 0;  // Line control
    pPL011->CR    = 0;  // Control
    pPL011->IFLS  = 0;  // Interrupt FIFO level select
    pPL011->IMSC  = 0;  // Interrupt mask set/clear
    pPL011->DMACR = 0;  // DMA control
                                                // Critical sections for serialising access to:
    InitializeCriticalSection(&pPL011->csXmit); //  data register (for transmission)
    InitializeCriticalSection(&pPL011->csRegs); //  general registers (for non-atomic usage)
    InitializeCriticalSection(&pPL011->csFlow); //  control register (for responsive flow control)

    // The PL011 only has one type of transmit interrupt, which is generated as the FIFO trigger
    //  level is crossed. The serial device driver MDD implementation requires a physical interrupt
    //  to signal the end of transmission; this would never occur if insufficient data was written
    //  from an application (less than the FIFO trigger level). All this means that we need to be
    //  a bit creative and use the transmit interrupt mask to generate an interrupt when we need
    //  one, which means that we must ensure the actual transmit interrupt flag is always set. The
    //  following block of code should set the transmit interrupt flag and enable us to cause a
    //  physical interrupt on demand by unmasking that interrupt. We acheive all this transparently
    //  and very quickly by disabling the FIFO and sending just one character with all bits set, no
    //  parity, only 5 data bits and at the fastest posssible baud rate using IR mode to avoid
    //  activity on the RS232 transmit line. The only udesirable artefact of this scheme is a
    //  harmles, sub microsecond pulse of IR, if equiped, during cold-start.
    //
    WriteReg32(pPL011->pIMSC, 0);               // Disable all interrupts
    WriteReg32(pPL011->pIBRD, PL011_IBRD_MIN);  // Fastest possible baud rate (write LCR_H next)
    WriteReg32(pPL011->pLCR_H, 0);              // 5 data bits, no parity, FIFO disabled
    WriteReg32(pPL011->pCR, PL011_CR_TXE |      // Enable transmitter
                            PL011_CR_SIREN |    // IR mode (to avoid activity on RS232 line)
                            PL011_CR_UARTEN);   // Enable transmitter, enable UART
    WriteReg32(pPL011->pDR, 0xFF);              // Transmit a character (all bits set)

    // Wait up to 10ms for the transmitter to empty, then check that the TX interrupt flag is set
    //
    VERIFY(PL011WaitTxEmpty(pPL011, 10));
    DEBUGCHK((ReadReg32(pPL011->pRIS) & PL011_RIS_TXRIS) != 0);

    // Now the TX interrupt flag is set, de-activate the UART by restoring default settings
    //
    WriteReg32(pPL011->pIBRD, 0);               // Reset integer baud rate divisor
    WriteReg32(pPL011->pCR, 0);                 // Disable UART

    return pPL011;
}


PL011_CONTEXT* PL011Delete(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return NULL;

    DeleteCriticalSection(&pPL011->csFlow);
    DeleteCriticalSection(&pPL011->csRegs);
    DeleteCriticalSection(&pPL011->csXmit);

#ifdef DEBUG
    memset(pPL011, 0, sizeof(PL011_CONTEXT));
#endif

    return LocalFree(pPL011);
}


BOOL PL011WaitTxEmpty(PL011_CONTEXT *pPL011, ULONG ulmsTimeout)
{
    ULONG ulmsStart;

    if (pPL011 == NULL)
        return FALSE;

    ulmsStart = GetTickCount();
    while (((GetTickCount() - ulmsStart) < ulmsTimeout) && PL011IsBusy(pPL011))
    {
        Sleep(1);
    }

    return !PL011IsBusy(pPL011);
}


BOOL PL011ClearDTR(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csFlow);

    try
    {
        WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) & ~PL011_CR_DTR);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        LeaveCriticalSection(&pPL011->csFlow);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csFlow);
    return TRUE;
}


BOOL PL011SetDTR(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csFlow);
    try
    {
        WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) | PL011_CR_DTR);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csFlow);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csFlow);

    return TRUE;
}


BOOL PL011ClearRTS(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csFlow);

    try
    {
        WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) & ~PL011_CR_RTS);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csFlow);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csFlow);

    return TRUE;
}


BOOL PL011SetRTS(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csFlow);

    try
    {
        WriteReg32(pPL011->pCR, ReadReg32(pPL011->pCR) | PL011_CR_RTS);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csFlow);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csFlow);

    return TRUE;
}


BOOL PL011ClearBreak(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;
        
    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pLCR_H, ReadReg32(pPL011->pLCR_H) & ~PL011_LCR_H_BRK);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

    return TRUE;
}


BOOL PL011SetBreak(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pLCR_H, ReadReg32(pPL011->pLCR_H) | PL011_LCR_H_BRK);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

    return TRUE;
}


BOOL PL011ClearPendingInterrupts(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pICR, PL011_ICR_ALLINTERRUPTS & ~PL011_ICR_TXIC);
        WriteReg32(pPL011->pECR, 0x1); // Need to clear any error status separately, can be any value

    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

    return TRUE;
}


BOOL PL011DisableAllInterrupts(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pIMSC, 0);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

    return TRUE;
}


BOOL PL011EnableAllInterrupts(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pIMSC, PL011_IMSC_ALLINTERRUPTS & ~PL011_IMSC_TXIM);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

    return TRUE;
}


BOOL PL011DisableTxInterrupts(PL011_CONTEXT *pPL011)
{
    if (pPL011 == NULL)
        return FALSE;

    EnterCriticalSection(&pPL011->csRegs);

    try
    {
        WriteReg32(pPL011->pIMSC, ReadReg32(pPL011->pIMSC) & ~PL011_IMSC_TXIM);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        // Just get out of here.
        LeaveCriticalSection(&pPL011->csRegs);
        return FALSE;
    }

    LeaveCriticalSection(&pPL011->csRegs);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -