www.pudn.com > ucosii_block.rar > COMM_PC.C


/* 
********************************************************************************************************* 
*                                     Embedded Systems Building Blocks 
*                                  Complete and Ready-to-Use Modules in C 
* 
*                                    Asynchronous Serial Communications 
*                                    IBM-PC Serial I/O Low Level Driver 
* 
*                            (c) Copyright 1999, Jean J. Labrosse, Weston, FL 
*                                           All Rights Reserved 
* 
* Filename   : COMM_PC.C 
* Programmer : Jean J. Labrosse 
* 
* Notes      : 1) The code in this file assumes that you are using a National Semiconductor NS16450 (most 
*                 PCs do or, an Intel i82C50) serial communications controller. 
* 
*              2) The functions (actually macros) OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() are used to 
*                 disable and enable interrupts, respectively.  If using the Borland C++ compiler V3.1, 
*                 all you need to do is to define these macros as follows: 
* 
*                     #define OS_ENTER_CRITICAL()  disable() 
*                     #define OS_EXIT_CRITICAL()   enable() 
* 
*              3) You will need to define the following constants: 
*                     COMM1_BASE    is the base address of COM1 on your PC (typically 0x03F8) 
*                     COMM2_BASE    is the base address of COM2 on your PC (typically 0x02F8) 
*                     COMM_MAX_RX   is the number of characters buffered by the UART 
*                                        2 for the NS16450 
*                                       16 for the NS16550 
* 
*              4) COMM_BAD_CH, COMM_NO_ERR and COMM_TX_EMPTY, 
*                 COMM_NO_PARITY, COMM_ODD_PARITY and COMM_EVEN_PARITY 
*                     are all defined in other modules (i.e. COMM1.H, COMM2.H or COMM3.H) 
********************************************************************************************************* 
*/ 
 
/* 
********************************************************************************************************* 
*                                               INCLUDES 
********************************************************************************************************* 
*/ 
 
#include "includes.h" 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                               CONSTANTS 
********************************************************************************************************* 
*/ 
 
#define  BIT0                       0x01 
#define  BIT1                       0x02 
#define  BIT2                       0x04 
#define  BIT3                       0x08 
#define  BIT4                       0x10 
#define  BIT5                       0x20 
#define  BIT6                       0x40 
#define  BIT7                       0x80 
 
#define  PIC_INT_REG_PORT         0x0020 
#define  PIC_MSK_REG_PORT         0x0021 
 
#define  COMM_UART_RBR                 0 
#define  COMM_UART_THR                 0 
#define  COMM_UART_DIV_LO              0 
#define  COMM_UART_DIV_HI              1 
#define  COMM_UART_IER                 1 
#define  COMM_UART_IIR                 2 
#define  COMM_UART_LCR                 3 
#define  COMM_UART_MCR                 4 
#define  COMM_UART_LSR                 5 
#define  COMM_UART_MSR                 6 
#define  COMM_UART_SCR                 7 
 
/* 
********************************************************************************************************* 
*                                         LOCAL GLOBAL VARIABLES 
********************************************************************************************************* 
*/ 
 
static  void   (*Comm1ISROld)(void); 
static  void   (*Comm2ISROld)(void); 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                            CONFIGURE PORT 
* 
* Description : This function is used to configure a serial I/O port.  This code is for IBM-PCs and 
*               compatibles and assumes a National Semiconductor NS16450. 
* 
* Arguments   : 'ch'          is the COMM port channel number and can either be: 
*                                 COMM1 
*                                 COMM2 
*               'baud'        is the desired baud rate (anything, standard rates or not) 
*               'bits'        defines the number of bits used and can be either 5, 6, 7 or 8. 
*               'parity'      specifies the 'parity' to use: 
*                                 COMM_PARITY_NONE 
*                                 COMM_PARITY_ODD 
*                                 COMM_PARITY_EVEN 
*               'stops'       defines the number of stop bits used and can be either 1 or 2. 
* 
* Returns     : COMM_NO_ERR   if the channel has been configured. 
*               COMM_BAD_CH   if you have specified an incorrect channel. 
* 
* Notes       : 1) Refer to the NS16450 Data sheet 
*               2) The constant 115200 is based on a 1.8432 MHz crystal oscillator and a 16 x Clock. 
*               3) 'lcr' is the Line Control Register and is define as: 
* 
*                        B7  B6  B5  B4  B3  B2  B1  B0 
*                                                ------ #Bits  (00 = 5, 01 = 6, 10 = 7 and 11 = 8) 
*                                            --         #Stops (0 = 1 stop, 1 = 2 stops) 
*                                        --             Parity enable (1 = parity is enabled) 
*                                    --                 Even parity when set to 1. 
*                                --                     Stick parity (see 16450 data sheet) 
*                            --                         Break control (force break when 1) 
*                        --                             Divisor access bit (set to 1 to access divisor) 
*               4) This function enables Rx interrupts but not Tx interrupts. 
********************************************************************************************************* 
*/ 
INT8U  CommCfgPort (INT8U ch, INT16U baud, INT8U bits, INT8U parity, INT8U stops) 
{ 
    INT16U  div;                                      /* Baud rate divisor                             */ 
    INT8U   divlo; 
    INT8U   divhi; 
    INT8U   lcr;                                      /* Line Control Register                         */ 
    INT16U  base;                                     /* COMM port base address                        */ 
 
 
    switch (ch) {                                     /* Obtain base address of COMM port              */ 
        case COMM1: 
             base = COMM1_BASE; 
             break; 
 
        case COMM2: 
             base = COMM2_BASE; 
             break; 
 
        default: 
             return (COMM_BAD_CH); 
    } 
    div   = (INT16U)(115200L / (INT32U)baud);         /* Compute divisor for desired baud rate         */ 
    divlo =  div & 0x00FF;                            /* Split divisor into LOW and HIGH bytes         */ 
    divhi = (div >> 8) & 0x00FF; 
    lcr   = ((stops - 1) << 2) + (bits - 5); 
    switch (parity) { 
        case COMM_PARITY_ODD:  
             lcr |= 0x08;                             /* Odd  parity                                   */ 
             break; 
 
        case COMM_PARITY_EVEN:  
             lcr |= 0x18;                             /* Even parity                                   */ 
             break; 
    } 
    OS_ENTER_CRITICAL(); 
    outp(base + COMM_UART_LCR, BIT7);                 /* Set divisor access bit                        */ 
    outp(base + COMM_UART_DIV_LO, divlo);             /* Load divisor                                  */ 
    outp(base + COMM_UART_DIV_HI, divhi); 
    outp(base + COMM_UART_LCR, lcr);                  /* Set line control register (Bit 8 is 0)        */ 
    outp(base + COMM_UART_MCR, BIT3 | BIT1 | BIT0);   /* Assert DTR and RTS and, allow interrupts      */ 
    outp(base + COMM_UART_IER, 0x00);                 /* Disable both Rx and Tx interrupts             */ 
    OS_EXIT_CRITICAL(); 
    CommRxFlush(ch);                                  /* Flush the Rx input                            */ 
    return (COMM_NO_ERR); 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                           COMM ISR HANDLER 
* 
* Description : This function processes an interrupt from a COMM port.  The function verifies whether the 
*               interrupt comes from a received character, the completion of a transmitted character or 
*               both. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
* Notes       : 'switch' statements are used for expansion. 
********************************************************************************************************* 
*/ 
void  CommISRHandler (INT8U ch) 
{ 
    INT8U   c; 
    INT8U   iir;                                          /* Interrupt Identification Register (IIR)   */ 
    INT8U   stat; 
    INT16U  base;                                         /* COMM port base address                    */ 
    INT8U   err; 
    INT8U   max;                                          /* Max. number of interrupts serviced        */ 
 
 
    switch (ch) {                                         /* Obtain pointer to communications channel  */ 
        case COMM1: 
             base = COMM1_BASE; 
             break; 
 
        case COMM2: 
             base = COMM2_BASE; 
             break; 
 
        default: 
             base = COMM1_BASE; 
             break; 
    } 
    max = COMM_MAX_RX; 
    iir = (INT8U)inp(base + COMM_UART_IIR) & 0x07;        /* Get contents of IIR                       */ 
    while (iir != 1 && max > 0) {                         /* Process ALL interrupts                    */ 
        switch (iir) { 
            case 0:                                       /* See if we have a Modem Status interrupt   */ 
                 c = (INT8U)inp(base + COMM_UART_MSR);    /* Clear interrupt (do nothing about it!)    */ 
                 break; 
 
            case 2:                                       /* See if we have a Tx interrupt             */ 
                 c = CommGetTxChar(ch, &err);             /* Get next character to send.               */ 
                 if (err == COMM_TX_EMPTY) {              /* Do we have anymore characters to send ?   */ 
                                                          /* No,  Disable Tx interrupts                */ 
                     stat = (INT8U)inp(base + COMM_UART_IER) & ~BIT1;  
                     outp(base + COMM_UART_IER, stat); 
                 } else { 
                     outp(base + COMM_UART_THR, c);       /* Yes, Send character                       */ 
                 } 
                 break; 
 
            case 4:                                       /* See if we have an Rx interrupt            */ 
                 c  = (INT8U)inp(base + COMM_UART_RBR);   /* Process received character                */ 
                 CommPutRxChar(ch, c);                    /* Insert received character into buffer     */ 
                 break; 
 
            case 6:                                       /* See if we have a Line Status interrupt    */ 
                 c   = (INT8U)inp(base + COMM_UART_LSR);  /* Clear interrupt (do nothing about it!)    */ 
                 break; 
        } 
        iir = (INT8U)inp(base + COMM_UART_IIR) & 0x07;    /* Get contents of IIR                       */ 
        max--; 
    } 
    switch (ch) { 
        case COMM1: 
        case COMM2: 
             outp(PIC_INT_REG_PORT, 0x20);                /* Reset interrupt controller                */ 
             break; 
 
        default: 
             outp(PIC_INT_REG_PORT, 0x20); 
             break; 
    } 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                    RESTORE OLD INTERRUPT VECTOR 
* 
* Description : This function restores the old interrupt vector for the desired communications channel. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
* Note(s)     : This function assumes that the 80x86 is running in REAL mode. 
********************************************************************************************************* 
*/ 
 
void  CommRclIntVect (INT8U ch) 
{ 
    CommRxIntDis(ch); 
    CommRxFlush(ch); 
    CommTxIntDis(ch); 
    switch (ch) { 
        case COMM1: 
             PC_VectSet(0x0C, Comm1ISROld); 
             break; 
 
        case COMM2: 
             PC_VectSet(0x0B, Comm1ISROld); 
             break; 
    } 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                            FLUSH RX PORT 
* 
* Description : This function is called to flush any input characters still in the receiver.  This 
*               function is useful when you replace the NS16450 with the more powerful NS16550. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
********************************************************************************************************* 
*/ 
 
void  CommRxFlush (INT8U ch) 
{ 
    INT8U  ctr; 
    INT16U base; 
 
 
    switch (ch) { 
        case COMM1: 
             base = COMM1_BASE; 
             break; 
 
        case COMM2: 
             base = COMM2_BASE; 
             break; 
    } 
    ctr = COMM_MAX_RX;                                /* Flush Rx input                                */ 
    OS_ENTER_CRITICAL(); 
    while (ctr-- > 0) { 
        inp(base + COMM_UART_RBR); 
    } 
    OS_EXIT_CRITICAL(); 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                         DISABLE RX INTERRUPTS 
* 
* Description : This function disables the Rx interrupt. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
********************************************************************************************************* 
*/ 
 
void  CommRxIntDis (INT8U ch) 
{ 
    INT8U stat; 
 
 
    switch (ch) { 
        case COMM1: 
             OS_ENTER_CRITICAL(); 
                                                           /* Disable Rx interrupts                    */ 
             stat = (INT8U)inp(COMM1_BASE + COMM_UART_IER) & ~BIT0;     
             outp(COMM1_BASE + COMM_UART_IER, stat); 
             if (stat == 0x00) {                           /* Both Tx & Rx interrupts are disabled ?   */ 
                                                           /* Yes, disable IRQ4 on the PC              */ 
                 outp(PIC_MSK_REG_PORT, (INT8U)inp(PIC_MSK_REG_PORT) | BIT4);   
             } 
             OS_EXIT_CRITICAL(); 
             break; 
 
        case COMM2: 
             OS_ENTER_CRITICAL(); 
                                                           /* Disable Rx interrupts                    */ 
             stat = (INT8U)inp(COMM2_BASE + COMM_UART_IIR) & ~BIT0;     
             outp(COMM2_BASE + COMM_UART_IER, stat); 
             if (stat == 0x00) {                           /* Both Tx & Rx interrupts are disabled ?   */ 
                                                           /* Yes, disable IRQ3 on the PC              */ 
                 outp(PIC_MSK_REG_PORT, (INT8U)inp(PIC_MSK_REG_PORT) | BIT3);   
             } 
             OS_EXIT_CRITICAL(); 
             break; 
    } 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                          ENABLE RX INTERRUPTS 
* 
* Description : This function enables the Rx interrupt. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
********************************************************************************************************* 
*/ 
 
void  CommRxIntEn (INT8U ch) 
{ 
    INT8U stat; 
 
 
    switch (ch) { 
        case COMM1: 
             OS_ENTER_CRITICAL(); 
                                                           /* Enable Rx interrupts                     */ 
             stat = (INT8U)inp(COMM1_BASE + COMM_UART_IER) | BIT0;      
             outp(COMM1_BASE + COMM_UART_IER, stat); 
                                                           /* Enable IRQ4 on the PC                    */ 
             outp(PIC_MSK_REG_PORT, (INT8U)inp(PIC_MSK_REG_PORT) & ~BIT4);      
             OS_EXIT_CRITICAL(); 
             break; 
 
        case COMM2: 
             OS_ENTER_CRITICAL(); 
                                                           /* Enable Rx interrupts                     */ 
             stat = (INT8U)inp(COMM2_BASE + COMM_UART_IER) | BIT0;      
             outp(COMM2_BASE + COMM_UART_IER, stat); 
                                                           /* Enable IRQ3 on the PC                    */ 
             outp(PIC_MSK_REG_PORT, (INT8U)inp(PIC_MSK_REG_PORT) & ~BIT3);      
             OS_EXIT_CRITICAL(); 
             break; 
    } 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                          SET INTERRUPT VECTOR 
* 
* Description : This function installs the interrupt vector for the desired communications channel. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
* Note(s)     : This function assumes that the 80x86 is running in REAL mode. 
********************************************************************************************************* 
*/ 
 
void  CommSetIntVect (INT8U ch) 
{ 
    switch (ch) { 
        case COMM1: 
             Comm1ISROld = PC_VectGet(0x0C); 
             PC_VectSet(0x0C, Comm1ISR); 
             break; 
 
        case COMM2: 
             Comm2ISROld = PC_VectGet(0x0B); 
             PC_VectSet(0x0B, Comm2ISR); 
             break; 
    } 
} 
 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                         DISABLE TX INTERRUPTS 
* 
* Description : This function disables the character transmission. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
********************************************************************************************************* 
*/ 
 
void  CommTxIntDis (INT8U ch) 
{ 
    INT8U  stat; 
    INT8U  cmd; 
 
 
    switch (ch) { 
        case COMM1: 
             OS_ENTER_CRITICAL(); 
                                                           /* Disable Tx interrupts                    */ 
             stat = (INT8U)inp(COMM1_BASE + COMM_UART_IER) & ~BIT1;     
             outp(COMM1_BASE + COMM_UART_IER, stat); 
             if (stat == 0x00) {                           /* Both Tx & Rx interrupts are disabled ?   */ 
                 cmd = (INT8U)inp(PIC_MSK_REG_PORT) | BIT4; 
                 outp(PIC_MSK_REG_PORT, cmd);              /* Yes, disable IRQ4 on the PC              */ 
             } 
             OS_EXIT_CRITICAL(); 
             break; 
 
        case COMM2: 
             OS_ENTER_CRITICAL(); 
                                                           /* Disable Tx interrupts                    */ 
             stat = (INT8U)inp(COMM2_BASE + COMM_UART_IER) & ~BIT1;     
             outp(COMM2_BASE + COMM_UART_IER, stat); 
             if (stat == 0x00) {                           /* Both Tx & Rx interrupts are disabled ?   */ 
                 cmd = (INT8U)inp(PIC_MSK_REG_PORT) | BIT3; 
                 outp(PIC_MSK_REG_PORT, cmd);              /* Yes, disable IRQ3 on the PC              */ 
             } 
             OS_EXIT_CRITICAL(); 
             break; 
    } 
} 
/*$PAGE*/ 
/* 
********************************************************************************************************* 
*                                          ENABLE TX INTERRUPTS 
* 
* Description : This function enables transmission of characters.  Transmission of characters is 
*               interrupt driven.  If you are using a multi-drop driver, the code must enable the driver 
*               for transmission. 
* Arguments   : 'ch'    is the COMM port channel number and can either be: 
*                           COMM1 
*                           COMM2 
********************************************************************************************************* 
*/ 
 
void  CommTxIntEn (INT8U ch) 
{ 
    INT8U  stat; 
    INT8U  cmd; 
 
 
    switch (ch) { 
        case COMM1: 
             OS_ENTER_CRITICAL(); 
             stat = (INT8U)inp(COMM1_BASE + COMM_UART_IER) | BIT1;     /* Enable Tx interrupts                     */ 
             outp(COMM1_BASE + COMM_UART_IER, stat); 
             cmd  = (INT8U)inp(PIC_MSK_REG_PORT) & ~BIT4; 
             outp(PIC_MSK_REG_PORT, cmd);                              /* Enable IRQ4 on the PC                    */ 
             OS_EXIT_CRITICAL(); 
             break; 
 
        case COMM2: 
             OS_ENTER_CRITICAL(); 
             stat = (INT8U)inp(COMM2_BASE + COMM_UART_IER) | BIT1;     /* Enable Tx interrupts                     */ 
             outp(COMM2_BASE + COMM_UART_IER, stat); 
             cmd  = (INT8U)inp(PIC_MSK_REG_PORT) & ~BIT3; 
             outp(PIC_MSK_REG_PORT, cmd);                              /* Enable IRQ3 on the PC                    */ 
             OS_EXIT_CRITICAL(); 
             break; 
    } 
}