/* ****************************************************************************** SAMPLE2.C INTERRUPT DRIVEN RS485 COMMUNICATIONS This sample provides for simple communications between two or more computers over an RS485 line. Each terminal is assigned its own identification code. When one sends data out onto the line, it sends the destination terminal's code first, followed by the data. The destination terminal will read its code, and then receive the string. If more than two computers are used, then any other terminal will ignore the incoming data. Interrupts will be used for data reception. Transmission will occur through polling. ****************************************************************************** */ #include #include #include #include #include unsigned int Base = 0x2A8; /* Base Address */ #define IRQLEVEL 5 /* IRQ Line */ /* The following are address offsets for the various registers. */ #define RECEIVE 0 /* Receiver buffer register */ #define TRANSMIT 0 /* Transmitter holding register */ #define L_DIVISOR 0 /* Divisor latch, least significant byte */ #define U_DIVISOR 1 /* Divisor latch, most significant byte */ #define INT_ENB 1 /* Interrupt enable register */ #define INT_ID 2 /* Interupt identification register */ #define LINE_CTRL 3 /* Line control register */ #define MODEM_CTRL 4 /* Modem control register */ #define LINE_STAT 5 /* Line status register */ #define MODEM_STAT 6 /* Modem status register */ void (interrupt far *Old_Interrupt)(); /* Old IRQ5 interrupt vector */ void interrupt far (*oldisr[16])(__CPPARGS); //global to store old handler pointer unsigned ID_CODE = 0; /* Set at transmission time. */ int flag = 0; /* Controls data storage. The value of flag determines what action is taken by the interrupt handler. See handler code for more details. */ int sender = 0; /* ID code of the message origin terminal. */ char *store; /* Used to store the message. */ int index = 0; /* Index to store. */ /* ****************************************************************************** FUNCTION initialize(int) This function performs the initialization of the 16450 UART. ****************************************************************************** */ void initialize(int br_code) { /* First, set up the divisor latches for the baud rate. The program assumes that a 1.8432 clock is used. The upper divisor is set to 0 and the lower is variable, depending on the selected baud rate. Note: The 56000 baud rate may cause lockup in XT computers. */ outportb(Base + LINE_CTRL,0x80); /* Set DLAB high for Divisor latch enable */ switch (br_code) { case 1: outportb(Base + L_DIVISOR, 96); /* 1200 baud */ break; case 2: outportb(Base + L_DIVISOR, 48); /* 2400 baud */ break; case 3: outportb(Base + L_DIVISOR, 24); /* 4800 baud */ break; case 4: outportb(Base + L_DIVISOR, 12); /* 9600 baud */ break; case 5: outportb(Base + L_DIVISOR, 6); /* 19200 baud */ break; case 6: outportb(Base + L_DIVISOR, 3); /* 38400 baud */ break; case 7: outportb(Base + L_DIVISOR, 2); /* 56000 baud */ break; } outportb(Base + U_DIVISOR,0); /* Load upper latch with 0 */ /* Now set the line control register for the following communications protocol bit0, bit1 = 1 -- character is eight bits bit2 = 0 -- one stop bit bit3 = 0 -- no parity is enabled bit4 = 0 -- doesn't matter with parity disabled bit5 = 0 -- doesn't matter with parity disabled bit6 = 0 -- disable break control bit7 = 0 -- DLAB bit, 0 disables divisor latches */ outportb(Base + LINE_CTRL,0x03); /* Set the bits of the Modem Control Register. Setting Bit 3 is necessary to enable interrupts. Note that the RTS line, or bit 1, is low. For RS485 communication to work, only one trasnitter can be active at any one time. The RTS line is used to disable transmitters that are not in use. */ outportb(Base + MODEM_CTRL,0x0D); } /* End initialize() */ /* ****************************************************************************** FUNCTION Our_Interrupt() PURPOSE This is the actual interrupt vector source code. This code is used to replace the current IRQ4 interrupt code. ****************************************************************************** */ void interrupt far Our_Interrupt(void) { int in_data, readback; enable(); /* Enable further interrupts */ while (1) /* Endless loop */ { in_data = 0; readback = inp(Base + INT_ID); /* Read interrupt ident. register */ if (readback & 1) /* If no interrupt is pending, exit. */ { outp(0x20,0x20); /* Send EOI signal to 8259 */ return; } if (readback & 4) /* Received Data Available Interrupt. This is the only interrupt type that this routine services. */ { in_data = inp(Base + RECEIVE); switch (flag) { case 0 : /* STARTING STATE - If the data is for this terminal, start the read cycle. */ if (in_data == ID_CODE) flag++; break; case 1 : /* FIRST READ STATE - The first byte following the ID code is the origin code. After reading this, move to next state. */ sender = in_data; index = 0; flag++; break; case 2 : /* MAIN READ STATE - Read data until end of transmission character is read. Then move to final state. */ if (in_data == (255 - ID_CODE)) { flag++; *(store + index) = 0; } else { *(store + index) = (char)in_data; index++; if (index == 79) { flag++; *(store + index) = 0; } } break; } } } } /* ****************************************************************************** FUNCTION initirq() RETURNS PARAMETERS PURPOSE ****************************************************************************** */ unsigned char initirq(char IRQnumber,void interrupt (*ISR)()) { unsigned char intmask, oldmask; if (IRQnumber <=7) { oldmask = inportb(0x21); oldisr[IRQnumber] = getvect(IRQnumber + 8); setvect(IRQnumber + 8,ISR); intmask = oldmask & (~(1 << IRQnumber)); outportb(0x21,intmask); } else { oldmask = inportb(0xA1); oldisr[IRQnumber] = getvect(IRQnumber + 0x70 - 8); setvect(IRQnumber - 8 + 0x70, ISR); intmask = oldmask & (~(1 << (IRQnumber - 8))); outportb(0xA1, intmask); } return(oldmask); } // end initirq /* ****************************************************************************** FUNCTION restoreirq() RETURNS PARAMETERS PURPOSE ****************************************************************************** */ void restoreirq(char IRQnumber) { unsigned char intmask; if (IRQnumber <=7 ){ intmask = inportb(0x21); intmask |= (1 << IRQnumber); setvect(IRQnumber + 8,oldisr[IRQnumber]); outportb(0x21, intmask); } else { intmask = inportb(0xA1); intmask |= (1 << (IRQnumber - 8)); setvect(IRQnumber - 8 + 0x70, oldisr[IRQnumber]); outportb(0xA1, intmask); } } // end restoreirq /* ****************************************************************************** FUNCTION Setup(char*,int*,int*) PURPOSE This function accepts user input defining the characteristics of the setup, including transmission message, number of terminals, terminal number, and baud rate. ****************************************************************************** */ void Setup(unsigned int Base,char *message,int *br_code,int *term_num) { clrscr(); puts(" SAMPLE 2.C : RS485 SAMPLE PROGRAM\n\n"); puts("This program demonstrates RS485 communications using multiple"); puts("terminals, at a baud rate selected by the user."); puts("\n\nBoard Configuration:\n"); printf(" -- Base Address is %X hex\n",Base); puts(" -- All cards must be connected with an RS485 cable."); puts(" -- The IRQ5 jumper should be installed (required)"); puts(" -- Full Duplex mode (required)"); puts(" -- The RTS jumper should be installed (required)\n"); cputs("Enter number of terminals on this line: "); gotoxy(13,40); scanf("%d",term_num); cputs("\nEnter number for this terminal (starting with zero): "); scanf("%d",&ID_CODE); ID_CODE = 255 - ID_CODE; puts("\nBaud Rates:\n1.. 1200\n2.. 2400\n3.. 4800\n4.. 9600\n5.. 19200\ \n6.. 38400\n7.. 56000"); cputs("\nEnter baud rate code:"); scanf("%d",br_code); flushall(); /* Empties input buffer. */ puts("\nEnter message to send:"); gets(message); clrscr(); puts("Press space to transmit, 'X' to exit.\n"); } /* ****************************************************************************** FUNCTION transmit(char*,int) PURPOSE This function takes the user message and transmits it to every terminal on the network. Interrupts are disable so that transmission is not halted due to data read on the line. ****************************************************************************** */ void transmit(char *str, int term_num) { int where, loop; gotoxy(1,10); printf("Sending....\n"); outportb(Base + MODEM_CTRL, 0x0F); /* Turn on RTS to enable transmitter */ outportb(Base + INT_ENB, 0x00); /* Disable Received Data Available Interrupt */ for(loop = 0;loop < term_num;loop++) { where = 0; outportb(Base + TRANSMIT, 255 - loop); while (!(inportb(Base + LINE_STAT) & 0x20)); /* Pause until transmitter is ready for the next character. */ outportb(Base + TRANSMIT, ID_CODE); while (!(inportb(Base + LINE_STAT) & 0x20)); /* Pause until transmitter is ready for the next character. */ outportb(Base + TRANSMIT, *(str+ where)); do { if (inportb(Base + LINE_STAT) & 0x20) { where++; outportb(Base + TRANSMIT, *(str+where)); } } while (*(str+where)); while (!(inportb(Base + LINE_STAT) & 0x20)); /* Pause until transmitter is ready for the next character. */ outportb(Base + TRANSMIT, loop); delay(50); /* Pause between transmissions. */ } outportb(Base + MODEM_CTRL, 0x0D); /* Disable transmitter through RTS */ gotoxy(1,22); puts("Data sent."); inp(Base); /* Clear received data buffer */ delay(50); inp(Base); outportb(Base + INT_ENB, 0x01); /* Enable Received Data Available Interrupt */ } unsigned AskForBaseAddress(unsigned int OldOne) { char msg[7]; int NewOne = 0, Success = 0, Dummy; int AddrInputPosX, AddrInputPosY; puts("\nPlease enter the Base Address for your card (in hex)"); printf("or press ENTER for %X.\n>", OldOne); AddrInputPosX = wherex(); AddrInputPosY = wherey(); do { gotoxy(AddrInputPosX, AddrInputPosY); clreol(); msg[0] = 5; msg[1] = 0; cgets(msg); sscanf(msg + 2, "%x", &NewOne); Success = 1; Dummy = NewOne; if (msg[1] == 0) { gotoxy(AddrInputPosX, AddrInputPosY); printf("%X", OldOne); Success = 1; Dummy = OldOne; } } while(!Success); return (Dummy); } /* end of AskForBaseAddress */ /* ****************************************************************************** FUNCTION main() PURPOSE This is the main program loop. ****************************************************************************** */ void main() { int key; int term_num = 0; int br_code; int Dummy; char *message; clrscr(); puts(" SAMPLE2.C: INTERRUPT DRIVEN RS485 COMMUNICATIONS\n"); puts("This sample provides for simple communications between two or more"); puts("computers over an RS485 line. Each terminal is assigned its own"); puts("identification code. When one sends data out onto the line, it sends"); puts("the destination terminal's code first, followed by the data. The"); puts("destination terminal will read its code, and then receive the string."); puts("If more than two computers are used, then any other terminal will"); puts("ignore the incoming data. Interrupts will be used for data reception."); puts("Transmission will occur through polling."); gotoxy(1,12); Base = AskForBaseAddress(0x2A8); index = 0; message = (char *)malloc(sizeof(char) * 255); /* Define storage area */ Setup(Base,message,&br_code,&term_num); store = (char *)malloc(sizeof(char) * 255); /* Define storage area */ initialize(br_code); initirq(IRQLEVEL, Our_Interrupt); do { if (kbhit()) key = getch(); else key = '\0'; if (key != 'x' && key != 'X' && key !='\0') transmit(message,term_num); if(flag==3) { printf("Message from terminal %d\n",255-sender); printf("Message: %s\n",store); flag = 0; } } while (key != 'x' && key != 'X'); /* Stay in loop until a key is pressed. */ restoreirq(IRQLEVEL); }