📄 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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -