RS232.C
上传用户:gofo119
上传日期:2007-01-03
资源大小:5k
文件大小:11k
源码类别:

串口编程

开发平台:

DOS

  1. *****Listing 3*****
  2. /*
  3.  *  COM.C
  4.  *
  5.  *  Copyright (C) Mark R. Nelson 1990
  6.  *
  7.  * This file contains all the code to implement a complete
  8.  * interrupt driven interface to one of the RS-232 COM
  9.  * ports on an IBM compatible PC.
  10.  */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <dos.h>
  14. #include <conio.h>
  15. #include "com.h"
  16. /*
  17.  * This group of defines creates all the definitions used
  18.  * to access registers and bit fields in the 8250 UART.
  19.  * While the names may not be the best, they are the ones
  20.  * used in the 8250 data sheets, so they are generally not
  21.  * changed.  Since the definitions are only used in COM.C,
  22.  * they are not included in the COM.H header file where
  23.  * they might normally be expected.
  24.  */
  25. #define RBR              0    /* Receive buffer register */
  26. #define THR              0    /* Transmit holding reg.   */
  27. #define IER              1    /* Interrupt Enable reg.   */
  28. #define IER_RX_DATA      1    /* Enable RX interrupt bit */
  29. #define IER_THRE         2    /* Enable TX interrupt bit */
  30. #define IIR              2    /* Interrupt ID register   */
  31. #define IIR_MODEM_STATUS 0    /* Modem stat. interrupt ID*/
  32. #define IIR_TRANSMIT     2    /* Transmit interrupt ID   */
  33. #define IIR_RECEIVE      4    /* Receive interrupt ID    */
  34. #define IIR_LINE_STATUS  6    /* Line stat. interrupt ID */
  35. #define LCR              3    /* Line control register   */
  36. #define LCR_DLAB         0x80 /* Divisor access bit      */
  37. #define LCR_EVEN_PARITY  0x8  /* Set parity 'E' bits     */
  38. #define LCR_ODD_PARITY   0x18 /* Set parity 'O' bits     */
  39. #define LCR_NO_PARITY    0    /* Set parity 'N' bits     */
  40. #define LCR_1_STOP_BIT   0    /* Bits to set 1 stop bit  */
  41. #define LCR_2_STOP_BITS  4    /* Bits to set 2 stop bits */
  42. #define LCR_5_DATA_BITS  0    /* Bits to set 5 data bits */
  43. #define LCR_6_DATA_BITS  1    /* Bits to set 6 data bits */
  44. #define LCR_7_DATA_BITS  2    /* Bits to set 7 data bits */
  45. #define LCR_8_DATA_BITS  3    /* Bits to set 8 data bits */
  46. #define MCR              4    /* Modem control register  */
  47. #define MCR_DTR          1    /* Bit to turn on DTR      */
  48. #define MCR_RTS          2    /* Bit to turn on RTS      */
  49. #define MCR_OUT1         4    /* Bit to turn on OUT1     */
  50. #define MCR_OUT2         8    /* Bit to turn on OUT2     */
  51. #define LSR              5    /* Line Status register    */
  52. #define MSR              6    /* Modem Status register   */
  53. #define DLL              0    /* Divisor latch LSB       */
  54. #define DLM              1    /* Divisor latch MSB       */
  55. /*
  56.  * Various constants used only in this program.
  57.  */
  58. #define INT_CONTROLLER   0x20  /* The address of the 8259*/
  59. #define EOI              0x20  /* The end of int command */
  60. #define BREAK_VECTOR     0x23  /* The CTRL-BREAK vector  */
  61. /*
  62.  * These are two static variables used in COM.C.  com is
  63.  * the pointer to the port that will be serviced by the
  64.  * ISR.  The old break handler points to the CTRL-BREAK
  65.  * handler that was in place before the port was opened.
  66.  * It will be restored when the port is closed.
  67.  */
  68. static PORT *com = NULL;
  69. static void ( interrupt far * old_break_handler )();
  70. /*
  71.  * This routine intercepts the CTRL-BREAK vector.  This
  72.  * prevents the program from terminating before having a
  73.  * chance to turn off COM interrupts.  This handler does
  74.  * nothing, but it could be used to set a flag indicating
  75.  * it is time to abort.
  76.  */
  77. void interrupt far break_handler()
  78. {
  79. }
  80. /*
  81.  * This is the interrupt service routine for the COM port.
  82.  * It sits in a loop reading the interrrupt ID register, then
  83.  * servicing one of the four different types of interrupts.
  84.  * Note that we shouldn't even get Modem Status and Line
  85.  * interrupts in this implementation, but they are left
  86.  * in for later enhancements.
  87.  */
  88. static void interrupt far interrupt_service_routine()
  89. {
  90.   unsigned char c;
  91.   enable();
  92.   for ( ; ; ) {
  93.     switch ( inportb( com->uart_base + IIR ) ) {
  94. /*
  95.  * If the present interrupt is due to a modem status line
  96.  * change, the MSR is read to clear the interrupt, but
  97.  * nothing else is done.
  98.  */
  99.       case IIR_MODEM_STATUS :
  100.         inportb( com->uart_base + MSR );
  101.         break;
  102. /*
  103.  * If the interrupt is due to the transmit holding register
  104.  * being ready for another character, I first check to see
  105.  * if any characters are left in the output buffer.  If
  106.  * not, Transmit interrupts are disabled.  Otherwise, the
  107.  * next character is extracted from the buffer and written
  108.  * to the UART.
  109.  */
  110.       case IIR_TRANSMIT :
  111.         if ( com->out.read_index == com->out.write_index )
  112.           outportb( com->uart_base + IER, IER_RX_DATA );
  113.         else {
  114.           c = com->out.buffer[ com->out.read_index++ ];
  115.           outportb( com->uart_base + THR, c );
  116.         }
  117.         break;
  118. /*
  119.  * When a new character comes in and generates an
  120.  * interrupt, it is read in.  If there is room in the input
  121.  * buffer, it is stored, otherwise it is discarded.
  122.  */
  123.       case IIR_RECEIVE :
  124.         c = (unsigned char) inportb( com->uart_base+RBR );
  125.         if ((com->in.write_index+1 ) != com->in.read_index)
  126.             com->in.buffer[ com->in.write_index++ ] =  c ;
  127.         break;
  128. /*
  129.  * All this code does is read the Line Status register, to
  130.  * clear the source of the interrupt.
  131.  */
  132.       case IIR_LINE_STATUS :
  133.         inportb( com->uart_base + LSR );
  134.         break;
  135. /*
  136.  * If there are no valid interrupts left to service, an EOI
  137.  * is written to the 8259 interrupt controller, and the
  138.  * routine exits.
  139.  */
  140.       default :
  141.         outportb( INT_CONTROLLER, EOI );
  142.         return;
  143.     }
  144.   }
  145. }
  146. /*
  147.  * This routine opens an RS-232 port up.  This means it
  148.  * allocates space for a PORT strcture, initializes the
  149.  * input and output buffers, stores the uart address and
  150.  * the interrupt number.  It then gets and stored the
  151.  * interrupt vector presently set up for the UART, then
  152.  * installs its own.  It also sets up a handler to
  153.  * intercept the CTRL-BREAK handler.  Finally, it tells the
  154.  * 8259 interrupt controller to begin accepting interrupts
  155.  * on the IRQ line used by this COM port.
  156.  */
  157. PORT *port_open( int address, int int_number )
  158. {
  159.   unsigned char temp;
  160.   PORT *port;
  161.   if ((port = malloc( sizeof( PORT ))) == NULL)
  162.     return( NULL );
  163.   com = port;
  164.   port->in.write_index = port->in.read_index = 0;
  165.   port->out.write_index = port->out.read_index = 0;
  166.   port->uart_base = address;
  167.   port->irq_mask = (char) 1 << (int_number % 8 );
  168.   port->interrupt_number = int_number;
  169.   port->old_vector = getvect( int_number );
  170.   setvect( int_number, interrupt_service_routine );
  171.   old_break_handler = getvect( BREAK_VECTOR );
  172.   setvect( BREAK_VECTOR, break_handler );
  173.   temp = (char) inportb( INT_CONTROLLER + 1 );
  174.   outportb( INT_CONTROLLER + 1, ~port->irq_mask & temp );
  175.   return( port );
  176. }
  177. /*
  178.  * This routine establishes the operating parameters for a
  179.  * port after it has been opened.  This means it sets the
  180.  * baud rate, parity, number of data bits, and number of
  181.  * stop bits.  Interrupts are disabled before the routine
  182.  * starts changing registers, and are then reenabled after
  183.  * the changes are complete.
  184.  */
  185. void port_set( PORT *port,
  186.                long speed,
  187.                char parity,
  188.                int data,
  189.                int stopbits )
  190. {
  191.   unsigned char lcr_out;
  192.   unsigned char mcr_out;
  193.   unsigned char low_divisor;
  194.   unsigned char high_divisor;
  195. /*
  196.  * First disable all interrupts from the port.  I also read
  197.  * RBR just in case their is a char sitting there ready to
  198.  * generate an interupt.
  199.  */
  200.   outportb( port->uart_base + IER, 0 );
  201.   inportb( port->uart_base );
  202. /*
  203.  * Writing the baud rate means first enabling the divisor
  204.  * latch registers, then writing the 16 bit divisor int
  205.  * two steps, then disabling the divisor latch so the other
  206.  * registers can be accessed normally.
  207.  */
  208.   low_divisor = (char) (115200L / speed ) & 0xff;
  209.   high_divisor = (char) ((115200L /  speed ) >> 8);
  210.   outportb( port->uart_base + LCR, LCR_DLAB );
  211.   outportb( port->uart_base + DLL, low_divisor );
  212.   outportb( port->uart_base + DLM, high_divisor );
  213.   outportb( port->uart_base + LCR, 0 );
  214. /*
  215.  * Setting up the line control register establishes the
  216.  * parity, number of bits, and number of stop bits.
  217.  */
  218.   if ( parity== 'E' )
  219.     lcr_out = LCR_EVEN_PARITY;
  220.   else if ( parity == 'O' )
  221.     lcr_out = LCR_ODD_PARITY;
  222.   else
  223.     lcr_out = LCR_NO_PARITY;
  224.   if ( stopbits == 2 )
  225.     lcr_out |= LCR_2_STOP_BITS;
  226.   if ( data == 6 )
  227.     lcr_out |= LCR_6_DATA_BITS;
  228.   else if ( data == 7 )
  229.     lcr_out |= LCR_7_DATA_BITS;
  230.   else if ( data == 8 )
  231.     lcr_out |= LCR_8_DATA_BITS;
  232.   outportb( port->uart_base + LCR, lcr_out );
  233. /*
  234.  * I turn on RTS and DTR, as well as OUT2.  OUT2 is needed
  235.  * to allow interrupts on PC compatible cards.
  236.  */
  237.   mcr_out = MCR_RTS | MCR_DTR | MCR_OUT2 ;
  238.   outportb( port->uart_base + MCR, mcr_out );
  239. /*
  240.  * Finally, restart receiver interrupts, and exit.
  241.  */
  242.   outportb( port->uart_base + IER, IER_RX_DATA );
  243. }
  244. /*
  245.  * In order to close the port, I first disable interrupts
  246.  * at the UART, then disable interrupts from the UART's IRQ
  247.  * line at the 8259 interrupt controller.  DTR, RTS, and
  248.  * OUT2 are all dropped. The UART's previous interrupt
  249.  * handler is restored, and the old break handler
  250.  * is restored.  Finally, the port data structure is freed,
  251.  * and things should be completely back to normal.
  252.  */
  253. void port_close( PORT *port )
  254. {
  255.   unsigned char temp;
  256.   outportb( port->uart_base + IER, 0 );
  257.   temp = (unsigned char) inportb( INT_CONTROLLER + 1 );
  258.   outportb( INT_CONTROLLER + 1, port->irq_mask | temp );
  259.   setvect( port->interrupt_number, port->old_vector );
  260.   setvect( BREAK_VECTOR, old_break_handler );
  261.   outportb( port->uart_base + MCR, 0 );
  262.   free( port );
  263. }
  264. /*
  265.  * This routine is used to send a single character out to
  266.  * the UART.  If there is room in the output buffer, the
  267.  * character is inserted in the buffer.  Then the routine
  268.  * checks to see if Transmit interrupts are presently
  269.  * enabled for this UART.  If they aren't, they are turned
  270.  * on so the ISR will see this new character.
  271.  */
  272. int port_putc( unsigned char c, PORT *port )
  273. {
  274.   if (( port->out.write_index+1 ) == port->out.read_index)
  275.     return( -1 );
  276.   port->out.buffer[ port->out.write_index ] = c;
  277.   port->out.write_index += 1;
  278.   if (( inportb( port->uart_base + IER ) & IER_THRE) == 0 )
  279.     outportb( port->uart_base+IER,IER_THRE | IER_RX_DATA);
  280.   return( c );
  281. }
  282. /*
  283.  * This routine checks to see if there is a character
  284.  * available in the input buffer for the specified port.
  285.  * If there is, it is pulled out and returned to the
  286.  * caller.
  287.  */
  288. int port_getc(PORT *port)
  289. {
  290.   if ( port->in.write_index == port->in.read_index )
  291.     return( -1 );
  292.   else
  293.     return( port->in.buffer[ port->in.read_index++ ] );
  294. }