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

📄 pc8250.cpp

📁 DOS下采用中断接收数据的串口通讯的例子,很难找到的好东西!
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//
//  PC8250.CPP
//
//  Source code from:
//
//  Serial Communications: A C++ Developer's Guide, 2nd Edition
//  by Mark Nelson, IDG Books, 1999
//
//  Please see the book for information on usage.
//
//  This file contains most of the code used in the PC8250
//  class.  The remainder of the code can be found in
//  ISR_8250.CPP, which has the ISR and its support code.
//

#include <stdio.h>
#include <dos.h>
#include <ctype.h>
#include <conio.h>

#include "rs232.h"
#include "pc8250.h"
#include "_pc8250.h"
#include "ascii.h"

// Data used to initialize UART addresses and IRQ lines.

static int Uarts[]            = { 0x3f8,  0x2f8,  0x3e8,  0x2e8 };
static enum irq_name IRQs[]   = { IRQ4,   IRQ3,   IRQ10,  IRQ11 };//IRQ4,   IRQ3

// This is the one and only constructor for an object of class
// PC8250.  A quick look at PC8250.H will show you that all of
// the parameters in the list except the port have default
// values, so the list isn't as overwhelming as it might look.

PC8250::PC8250( RS232PortName port,
                long baud_rate,
                char parity,
                int word_length,
                int stop_bits,
                int dtr,
                int rts,
                int xon_xoff,
                int rts_cts,
                int dtr_dsr,
                Handler *handler,
                int uart_address,
                irq_name irq_line )
{
    int mcr;

    interrupt_handler = handler;
    port_name = port;
    error_status = RS232_SUCCESS;

// This section of code initializes most of the items in the
// isr_data structure, which contains all of the items used in
// the ISR.

    isr_data = new isr_data_block;
    if ( isr_data == 0 ) {
        error_status = RS232_MEMORY_ALLOCATION_ERROR;
        return;
    }
    set_uart_address_and_irq( handler, uart_address, irq_line );
    if ( error_status < RS232_SUCCESS )
        return;
    isr_data->overflow = 0;
    isr_data->tx_running = 0;
    isr_data->tx_int_count = 0;
    isr_data->rx_int_count = 0;
    isr_data->ls_int_count = 0;
    isr_data->ms_int_count = 0;
    isr_data->line_status = 0;
    isr_data->handshaking = 0;
    isr_data->blocking = 0;
    isr_data->blocked = 0;
    isr_data->send_handshake_char = -1;

// PC8250 has to share the debug output with the parent
// class.  To determine where our first line starts, we call the
// FormatDebugOutput() function from our parent class.

    first_debug_output_line = RS232::FormatDebugOutput();
    debug_line_count = FormatDebugOutput();

// Determine whether the UART is there and what type it is.

    check_uart();
    if ( error_status < RS232_SUCCESS )
        return;

//  Save all of the old UART settings, and then set it to the
//  new ones passed to the constructor.

    read_settings();
    saved_settings = settings;
    settings.Adjust( baud_rate,
                     parity,
                     word_length,
                     stop_bits,
                     dtr,
                     rts,
                     xon_xoff,
                     rts_cts,
                     dtr_dsr );
    write_settings();

// Here we set up the interrupt handler, then turn on
// interrupts.  After this code is done the UART will be
// running.

    outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
    mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
    mcr |= MCR_OUT2;
    mcr &= ~MCR_LOOPBACK;
    outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
    if ( interrupt_handler == 0 ) {
        error_status = ConnectToIrq( irq,
                                     isr_data, (void (*)(void *))
                                     isr_8250 );
        if ( error_status < RS232_SUCCESS ) {
            outp( isr_data->uart + MODEM_CONTROL_REGISTER, 0 );
            outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
            return;
        }
    } else {
        error_status = interrupt_handler->AddPort( port_name,
                                                   isr_data );
        if ( error_status < RS232_SUCCESS )
            return;
    }
    inp( isr_data->uart );  // Clear any pending interrupts
    inp( isr_data->uart + INTERRUPT_ID_REGISTER );
    _disable();
    isr_data->modem_status =
        (unsigned int)
            inp( isr_data->uart + MODEM_STATUS_REGISTER );
    outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER,
            IER_RX_DATA_READY + IER_TX_HOLDING_REGISTER_EMPTY +
            IER_MODEM_STATUS + IER_LINE_STATUS );
    outp( 0x20, 0xc0 + IRQ3 - 1 );
    _enable();

// Finally, set up the last few parameters and exit.

    Dtr( settings.Dtr );
    Rts( settings.Rts );
    XonXoffHandshaking( settings.XonXoff );
    RtsCtsHandshaking( settings.RtsCts );
    DtrDsrHandshaking( settings.DtrDsr );
}

void PC8250::set_uart_address_and_irq( Handler *handler,
                                      int uart_address,
                                      irq_name irq_line )
{

// If I have a handler or have a defined irq_line, I won't
// use the default IRQs.  If I have a uart_address, I won't
// use the default UART address.

    if ( handler == 0 && irq_line == ILLEGAL_IRQ ) {
        if ( port_name > COM4 )
            error_status = RS232_PORT_NOT_FOUND;
        else
            irq = IRQs[ port_name ];
    } else
        irq = irq_line;

    if ( uart_address == 0 ) {
        if ( port_name > COM4 )
            error_status = RS232_PORT_NOT_FOUND;
        else
            isr_data->uart = Uarts[ port_name ];
    } else
        isr_data->uart = uart_address;
}

// The destructor has a much easier time of it than the
// constructor.  It disables interrupts, then restores the line
// settings of the UART.

PC8250::~PC8250( void )
{
    if ( error_status == RS232_SUCCESS ) {
        outp( isr_data->uart + INTERRUPT_ENABLE_REGISTER, 0 );
        outp( isr_data->uart + MODEM_CONTROL_REGISTER, 0 );
        if ( interrupt_handler == 0 )
            DisconnectFromIRQ( irq );
        else
            interrupt_handler->DeletePort( port_name );
        settings = saved_settings;
        write_settings();
        Dtr( settings.Dtr );
        Rts( settings.Rts );
    }
    if ( isr_data != 0 )
        delete isr_data;
}

// This routine determines if a UART is present, and if so,
// whether or not it is a 16550.  If it is a 16550, the FIFO is
// enabled with a trigger at 14 bytes.

void PC8250::check_uart( void )
{
    int temp;

    outp( isr_data->uart + FIFO_CONTROL_REGISTER, 0 );
    temp = inp( isr_data->uart + INTERRUPT_ID_REGISTER );
    if ( ( temp & 0xf8 ) != 0 ) {
        isr_data->uart_type = UART_UNKNOWN;
        error_status = RS232_PORT_NOT_FOUND;
        return;
    }
    outp( isr_data->uart + FIFO_CONTROL_REGISTER,
            FCR_FIFO_ENABLE + FCR_TRIGGER_14 );
    temp = inp( isr_data->uart + INTERRUPT_ID_REGISTER );
    if ( ( temp & 0xf8 ) == 0xc0 ) {
        isr_data->uart_type = UART_16550;
        fifo_setting = 14;
    } else {
        isr_data->uart_type = UART_8250;
        fifo_setting = 0;
        outp( isr_data->uart + FIFO_CONTROL_REGISTER, 0 );
    }
}

// After any function that reads data from the ISR buffers,
// this routine is called.  If the read operation dropped us
// below a handshaking trigger point, this routine will figure
// out what action to take.

void PC8250::check_rx_handshaking()
{
    int mcr;

// Take a quick exit if we aren't handshaking, blocking, or if
// the RX Queue is not below the low-water mark.

    if ( !isr_data->handshaking || !isr_data->blocking )
        return;
    if ( isr_data->RXQueue.InUseCount() > LowWaterMark )
        return;

// If RTS/CTS handshaking is in effect, I raise RTS.

    if ( ( isr_data->handshaking & rts_cts ) &&
         ( isr_data->blocking & rts_cts ) ) {
        _disable();
        mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
        mcr |= MCR_RTS;
        outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
        isr_data->blocking &= ~rts_cts;
        _enable();
    }

// If DTR/DSR handshaking is in effect, I raise DTR.

    if ( ( isr_data->handshaking & dtr_dsr ) &&
         ( isr_data->blocking & dtr_dsr ) ) {
        _disable();
        mcr = inp( isr_data->uart + MODEM_CONTROL_REGISTER );
        mcr |= MCR_DTR;
        outp( isr_data->uart + MODEM_CONTROL_REGISTER, mcr );
        isr_data->blocking &= ~dtr_dsr;
        _enable();
    }

// If XON/XOFF is in effect, I send an XON.  Note that if
// there is a pending XOFF that never made it out, I cancel it
// and don't send anything else.

    if ( ( isr_data->handshaking & xon_xoff ) &&
         ( isr_data->blocking & xon_xoff ) ) {
         _disable();
         isr_data->blocking &= ~xon_xoff;
         if ( isr_data->send_handshake_char == XOFF )
             isr_data->send_handshake_char = -1;
         else {
             isr_data->send_handshake_char = XON;
             jump_start( isr_data );
         }
         _enable();
    }
}

// This routine just pulls out a byte and checks for
// handshaking activity.

int PC8250::read_byte( void )
{
    int c;

    if ( error_status < 0 )
        return error_status;
    c = isr_data->RXQueue.Remove();
    if ( c < 0 )
        return RS232_TIMEOUT;
    check_rx_handshaking();
    return c;
}

// When sending a byte to the output buffer, I have to check
// to see if the TX interrupt system needs to be restarted.

int PC8250::write_byte( int c )
{
    if ( error_status < 0 )
        return error_status;
    if ( !isr_data->TXQueue.Insert( (unsigned char) c ) )
        return RS232_TIMEOUT;
    if ( !isr_data->tx_running && !isr_data->blocked )
        jump_start( isr_data );
    return RS232_SUCCESS;
}

// read_buffer() pulls in only as many bytes as are
// immediately available.  Any high-level functions such as
// timing out or looking for a terminator are handled by one of
// the higher level Read() routines from class RS232.

int PC8250::read_buffer( char *buffer, unsigned int count )
{
    ByteCount = 0;
    if ( error_status < 0 )
        return error_status;
    while ( isr_data->RXQueue.InUseCount() ) {
        if ( count <= 0 )
            break;
        *buffer++ = (char) isr_data->RXQueue.Remove();
        count--;
        ByteCount++;
    }
    *buffer = '\0';
    if ( ByteCount > 0 )
        check_rx_handshaking();
    if ( count > 0 )
        return RS232_TIMEOUT;
    else
        return RS232_SUCCESS;
}

// write_buffer() sends as many characters as the buffer can
// immediately manage.  Like read_buffer(), it relies on higher
// level routines from class RS232 to perform the nicer functions
// such as adding termination, timing, etc.

⌨️ 快捷键说明

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