การ Interface กับ Serial Port (Part VI)
Modem Control Register (MCR)
Table 11. แสดงรายละเอียดของ Modem Control Register.
Table 11. Modem Control Register.
Bit
|
Notes
|
7
|
Reserved
|
6
|
Reserved
|
5
|
Autoflow Control Enabled (16750 only)
|
4
|
Loopback Mode
|
3
|
Aux. Output 2
|
2
|
Aux. Output 1
|
1
|
Force Request To Send
|
0
|
Force Data Terminal Ready
|
MCR เป็น register ชนิด read/write สำหรับ bit 5, 6 และ 7 ถูก
reserve ไว้
Line Status Register (LSR)
Table 12. แสดงรายละเอียดของ Line Status Register
Table 12. Line Status Register.
Bit
|
Notes
|
7
|
Error in Received FIFO
|
6
|
Empty Data Holding Registers
|
5
|
Empty Transmitter Holding Register
|
4
|
Break Interrupt
|
3
|
Framing Error
|
2
|
Parity Error
|
1
|
Overrun Error
|
0
|
Data Ready
|
Line Status Register เป็น register ชนิด read only
Bit 7 จะบ่งบอกถึงความผิดปรกติของสิ่งที่รับเข้ามาใน FIFO เช่น มี bit ที่เสียหายหรือ
parity ไม่ถูกต้องหรือ frame ของข้อมูลไม่ถูกต้องเป็นต้น
Bit 6 ถ้ามีค่าเป็น “1” จะแสดงว่าทั้งใน Tx holding register และ shift register
ว่าง ซึ่ง Tx holding register จะเป็นตัวเก็บข้อมูลที่จะส่งในรูปแบบ parallel ส่วน
shift register จะทำหน้าที่แปลงข้อมูลจาก parallel ให้เป็น serial ส่งออกไป
Bit 5 บ่งบอกว่าเฉพาะ Tx holding register ที่ว่างลง แต่ยังมีข้อมูลใน shift register
อยู่ ความแตกต่างระหว่าง bit5 และ bit 6 คือ bit 5 บอกว่าสามารถส่งข้อมูลเข้าไปยัง
Tx holding register และ Tx ก็ยังส่งข้อมูลอีก 1 byte ที่ค้างอยู่ใน shift register
อยู่ ส่วน bit 6 บอกว่าไม่มีข้อมูลเหลือค้างในระบบการส่งแล้ว
Bit 4 เป็นตัวบอกว่าทางด้าน Rx อยู่ในสถานะ “space” หรือ “0” นานกว่าระยะเวลาของ
1 full word แล้ว ซึ่งระยะเวลาของ 1 full word หมายถึงเวลาของ (data bits)+(parity
bit)+(start bit)+(stop bits)
Bit 3 จะเกิดขึ้นเมื่อปรากฏว่า bit สุดท้ายไม่ใช่ stop bit ซึ่งอาจจะเกิดจาก timing
error เช่นการปรับ speed ของตัวรับกับตัวส่งไม่เท่ากัน
Bit 2 เป็นตัวบอกว่ามี parity error เกิดขึ้น แสดงว่าข้อมูลที่รับได้มี error
หรือไม่ก็ตั้งเงื่อนไขของ parity ระหว่างตัวรับกับตัวส่งไม่ตรงกัน
Bit 1 บอกว่าเกิด overrun ขึ้น แสดงว่า software อ่านข้อมูลไม่ทัน
ทำให้ข้อมูลที่รับเข้ามาใหม่ไปทับซ้อนแทนที่ข้อมูลเก่าที่ยังไม่ได้รับการ
อ่านจาก
software
Bit 0 บอกว่าข้อมูลรับเข้ามารออยู่ใน Rx buffer แล้ว พร้อมรอให้อ่านอยู่
Modem Status Register (MSR)
Table 13. แสดงรายละเอียดของ Modem Status Register
Table 13. Modem Status Register.
Bit
|
Notes
|
7
|
Carrier Detect
|
6
|
Ring Indicator
|
5
|
Data Set Ready
|
4
|
Clear To Send
|
3
|
Delta Data Carrier Detect
|
2
|
Trailing Edge Ring Indicator
|
1
|
Delta Data Set Ready
|
0
|
Delta Clear To Send
|
Modem Status Register เป็น register ชนิด read only
Bit 0 บอกว่า delta clear to send คำว่า delta หมายถึงมีการเปลี่ยนแปลงเกิดขึ้น
เช่นในกรณีนี้แสดงว่ามีการเปลี่ยนแปลงของสัญญาณ clear to send เกิดขึ้นเมื่อเทียบกับการอ่าน
register นี้ในครั้งที่แล้ว bit 1 และ 3 ก็ทำหน้าที่ในทำนองเดียวกัน
Bit 2 บอกว่ามีการเปลี่ยนสถานะจาก “low เป็น “high” ที่สัญญาณ ring indicator
เกิดขึ้น
Bit 4 ถึง 7 บอกถึงสถานะของสัญญาณนั้น ๆ
Scratch Register
Scratch register ไม่ได้นำมาใช้ในการสื่อสารของ PC โดยตรง แต่ใช้เป็นที่พักข้อมูลในการอื่น
ซึ่งประโยชน์ที่พอจะพบใน PC/AT คือใช้ตรวจว่า UART เป็นเบอร์ 8250/8250B ซึ่งไม่ได้ออกแบบมาให้ใช้กับ
PC/AT หรือเบอร์ 8250A/16450
Programming
Polling or Interrupt Driven?
การเขียน Program ทางสื่อสารจะมี 2 วิธีหลัก ๆ คือ
- Polling คือไปคอยอ่าน UART ว่ามีข้อมูลมาหรือไม่เป็นระยะ ๆ ซึ่งเป็นวิธีที่ต้องใช้เวลาของ
CPU อย่างมาก และทำให้สื่อสารด้วย speed ที่ไม่สูงเพราะจะทำให้ poll ไม่ทันเช่นอาจจะใช้ได้เพียง
สิบ ๆ Kbps ขึ้นอยู่กับความเร็วของ CPU
- สร้าง Interrupt handler เพื่ออ่านข้อมูลจาก UART เมื่อมี interrupt เกิดขึ้น
วิธีนี้จะสามารถสื่อสารได้เร็วขึ้นมากเช่น 115.2 Kbps หรือมากกว่านี้ขึ้นอยู่กับความเร็วของ
UART และ CPU ที่ใช้
Termpoll.c - A Simple Comms Program using Polling
/* Name : Sample Comm's Program - Polled Version - termpoll.c */
/* Written By : Craig Peacock (cpeacock@senet.com.au) */
/* Date : Saturday 22nd February 1997 */
/* Copyright 1997 CRAIG PEACOCK (cpeacock@senet.com.au)*/
/* See http://www.senet.com.au/~cpeacock/serial.htm */
/* For More Information */
#include
#include
#include
#define PORT1 0x3F8
/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */
void main(void)
{
int c;
int ch;
outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */
/* PORT 1 - Communication Settings */
outportb(PORT1 + 3 , 0x80); /* SET DLAB ON */
outportb(PORT1 + 0 , 0x03); /* Set Baud rate - Divisor Latch Low Byte */
/* Default 0x03 = 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 56,700 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
outportb(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outportb(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
outportb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outportb(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */
printf("\nSample Comm's Program. Press ESC to quit \n");
do { c = inportb(PORT1 + 5); /* Check to see if char has been */
/* received. */
if (c & 1) {ch = inportb(PORT1); /* If so, then get Char */
printf("%c",ch);} /* Print Char to Screen */
if (kbhit()){ch = getch(); /* If key pressed, get Char */
outportb(PORT1, ch);} /* Send Char to Serial Port */
} while (ch !=27); /* Quit when ESC (ASC 27) is pressed */
}
การ polling ไม่ใช่ว่าจะไม่ควรใช้เสียทีเดียว มันยังมีประโยชน์อยู่เช่น ใช้ตรวจสอบว่า
UART อยู่ที่ address ไหนและใช้ IRQ ใดโดยการ enable ทีละ IRQ ผ่าน PIC ซึ่งสามารถใช้ตรวจหาอุปกรณ์อื่น
ๆ ด้วยเช่น Modem เป็นต้น
Buff1024.c - An Interrupt Driven Comms Program
/* Name : Sample Comm's Program - 1024 Byte Buffer - buff1024.c */
/* Written By : Craig Peacock (cpeacock@senet.com.au) */
/* Copyright 1997 CRAIG PEACOCK (cpeacock@senet.com.au) */
/* See http://www.senet.com.au/~cpeacock/serial.htm */
/* For More Information */
#include
#include
#include
#define PORT1 0x2E8 /* Port Address Goes Here */
/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */
#define INTVECT 0x0B /* Com Port's IRQ here */
/* (Must also change PIC setting) */
int bufferin = 0;
int bufferout = 0;
char ch;
char buffer[1025];
void interrupt (*oldport1isr)();
void interrupt PORT1INT() /* Interrupt Service Routine (ISR) for PORT1 */
{
int c;
do { c = inportb(PORT1 + 5);
if (c & 1) {buffer[bufferin] = inportb(PORT1);bufferin++;
if (bufferin == 1024) bufferin = 0;}
}while (c & 1);
outportb(0x20,0x20);
}
void main(void)
{
int c;
outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */
oldport1isr = getvect(INTVECT); /* Save old Interrupt Vector for */
/* later recovery */
setvect(INTVECT, PORT1INT); /* Set Interrupt Vector Entry */
/* COM1 - 0x0C */
/* COM2 - 0x0B */
/* COM3 - 0x0C */
/* COM4 - 0x0B */
/* PORT 1 - Communication Settings */
outportb(PORT1 + 3 , 0x80); /* SET DLAB ON */
outportb(PORT1 + 0 , 0x03); /* Set Baud rate - Divisor Latch Low Byte */
/* Default 0x03 = 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 56,700 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
outportb(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outportb(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
outportb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outportb(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */
outportb(0x21,(inportb(0x21) & 0xF7)); /* Set Programmable Interrupt */
/* Controller */
/* COM1 (IRQ4) - 0xEF */
/* COM2 (IRQ3) - 0xF7 */
/* COM3 (IRQ4) - 0xEF */
/* COM4 (IRQ3) - 0xF7 */
outportb(PORT1 + 1 , 0x01); /* Interrupt when data received */
printf("\nSample Comm's Program. Press ESC to quit \n");
do {
if (bufferin != bufferout){ch = buffer[bufferout];
bufferout++;
if (bufferout == 1024) bufferout = 0;
printf("%c",ch);}
if (kbhit()){c = getch();
outportb(PORT1, c);}
} while (c !=27);
outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */
outportb(0x21,(inportb(0x21) | 0x08)); /* MASK IRQ using PIC */
/* COM1 (IRQ4) - 0x10 */
/* COM2 (IRQ3) - 0x08 */
/* COM3 (IRQ4) - 0x10 */
/* COM4 (IRQ3) - 0x08 */
setvect(INTVECT, oldport1isr); /* Restore old interrupt vector */
}
หมายเหตุ Source code ข้างบนนี้เป็นการย่นย่อเพื่อให้ง่ายต่อการเข้าใจ
ในทางปฏิบัติจริง เมื่อเข้าสู่ communication program ควรมีการเก็บค่า status ของ
registers ต่าง ๆ ใน UART ไว้ และก่อนที่จะออกจาก communication program ก็ต้องทำการ
restore มันกลับสภาพเดิม เพื่อ program อื่นจะได้ใช้ต่อไป