DOS的串口編程2

來源:互聯網
上載者:User
 

串口編程之DOS的串口編程22008-11-26 11:46
在DOS平台下,操作串口主要有下列方式:通過BIOS調用、通過串口的硬體中斷或通過對串口硬體進行輪詢,本章將對以上三種方式進行具體的介紹並給出例子。

  1.BIOS中斷

  在DOS作業系統下,IBM PC及其相容機提供了一種靈活的串口I/O存取方法,即通過INT 14H調用ROM BIOS串列通訊例行程式。當設定AH為不同的值時,產生不同的功能:

  AH 0 初始化連接埠
  AH 1 向串口寫字元
  AH 2 從串口讀字元
  AH 3 取通訊口狀態

  初始化連接埠時(即當AH=0時),需要在AL寄存器中賦一位元組初始化參數,其各項意義1;


圖1 調用INT 14H時AL寄存器設定

  當向串口寫字元時(即當AH=1時),AL寄存器中的字元是需要寫入的字元;

  當向串口寫字元時(即當AH=2時),AL寄存器中的字元是需要讀取的字元。

  看看下面的常式:

#include <stdio.h>
#include <dos.h>
#include <bios.h>
#define STR "author:sbh"
union REGS inregs,outregs;

main()
{
 //設定串口參數
 init_rs232();
 //寫串口的例子
 write_rs232(STR,strlen(STR));
 //讀串口的例子
 read_rs232();

 return(0);
}

init_rs232()
{
 do{
  inregs.h.ah=0; //AH=0表示初始化連接埠
  inregs.h.al=0xe7;
  inregs.x.dx=0; //COM1
  int86(0x14, &inregs, &outregs);
 }while(outregs.h.ah>=0x80);

 return(0);
}

write_rs232(char *string, int len)
{
 int i;
 do{
  inregs.h.ah=1;//發送AL寄存器的字元
  inregs.h.al= *string;
  inregs.x.dx=0;
  int86(0x14, &inregs, &outregs);
 }while(outregs.h.al>=0x80);

 for(i=1;i<len;i++)
 {
  inregs.h.ah=1;
  inregs.h.al=*(string+i);
  inregs.x.dx=0;
  int86(0x14, &inregs, &outregs);
 }
}

read_rs232()
{
 do{
  inregs.h.ah=2; //讀取AL寄存器中的字元
  inregs.x.dx=0;
  int86(0x14, &inregs, &outregs);
 }while(outregs.h.al!=3||outregs.h.ah>=0x80);

 return(0);
}

  其中使用的int86函數的原型為:

int _Cdecl int86(int intno, union REGS *inregs, union REGS *outregs);

  int86()函數可以調用BIOS功能,現在的程式員們已經很少接觸這個函數,80%的程式員甚至都未曾見過這個函數。其實,在茹毛飲血的DOS時代,int86()函數幾乎是最常用和最核心的函數之一。幾乎可以說,在那個時代,不會int86()就等於不會編程。而與int86配合使用的,就是REGS這樣一個聯合體,定義為:

union REGS {
 struct WORDREGS x;
 struct BYTEREGS h;
};

  其中的WORDREGS定義為:

struct WORDREGS {
 unsigned int ax, bx, cx, dx, si, di,
 cflag /*進位標誌*/,
 flags /*標誌寄存器*/;
};

  而BYTEREGS則定義為:

struct BYTEREGS {
 unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};

  原來WORDREGS和BYTEREGS是16位的8086處理器內部的寄存器啊!因此,當CPU發展到286、386以後,再安裝DOS也是建立在利用CPU實模式的基礎上的!

  另外一個函數與int86()的功能是類似的:

Int _Cdecl int86x(int intno, union REGS inregs, union REGS outregs, struct SREGS segregs);

  其中的SREGS為段寄存器結構體,定義為:

struct SREGS
{
 unsigned int es;
 unsigned int cs;
 unsigned int ss;
 unsigned int ds;
};

  int86和int86x這兩個函數的功能都是執行一個由參數intno指定的8086非強制中斷。在執行非強制中斷之前,兩個函數都把inregs中的內容放置到各寄存器中(int86x還把segregs.x.es和segregs.x.ds的值存到相應的段寄存器中),非強制中斷返回後,這兩個函數都把當前寄存器的值存到outregs,並把系統進位標誌拷貝到outregs.s.cflag中,把8086標誌寄存器值存到outregs.x.flag中(int86x還恢複DS,並設定Segregs.es和Segregs.ds的值為對應段寄存器的值)。

  查閱BIOS中斷調用手冊,發現絕大多數調用都未用到ES和DS段寄存器,故在程式設計中經常只利用了int86函數。
 2.硬體中斷

  為了給讀者一個直觀的印象,我們通過在Windows作業系統中查看COM的資源屬性獲得某COM對應的中斷號,2(該對話方塊中裝置管理員中開啟)。


圖2 COM中斷號

  實際上COM的確直接對應於一個中斷,而系統也按照一定的規律為各類硬體分配了一個較固定的中斷號,如表1。

  表1 中斷向量表

INT (Hex) IRQ Common Uses
08 0 System Timer
09 1 Keyboard
0A 2 Redirected
0B 3 Serial Comms. COM2/COM4
0C 4 Serial Comms. COM1/COM3
0D 5 Reserved/Sound Card
0E 6 Floppy Disk Controller
0F 7 Parallel Comms.
70 8 Real Time Clock
71 9 Reserved
72 10 Reserved
73 11 Reserved
74 12 PS/2 Mouse
75 13 Maths Co-Processor
76 14 Hard Disk Drive
77 15 Reserved

  通過編寫COM對應的中斷服務程式,我們也可以操作串口,涉及到的相關函數有:

  (1)設定中斷向量表

/*dos.h*/
void _Cdecl setvect (int interruptno, void interrupt (*isr) ());

  例如,COM3對應的中斷號是4,那麼對應中斷向量表中的地址是0x0C,設定0x0C對應中斷程式的函數為:

setvect(0x0C, PORT1INT);

  其中的中斷服務程式PORT1INT為:

void interrupt PORT1INT()
{
 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);
}

  上述中斷服務程式檢查是否有字元可接收,其後將其通過inportb(PORT1)語句將其從UART中讀出並放入輸入buffer。持續的檢查UART,以便能在一次中斷裡讀取所有可獲得的資料。

  最後的"outportb(0x20,0x20);"語句告訴可程式化插斷控制器(Programmable Interrupt Controller,PIC)中斷已經完成。

  (2)讀取中斷向量表

/*dos.h*/
void interrupt (* _Cdecl getvect(int interruptno)) ();

  例如:

oldport1isr = getvect(INTVECT);

  其中的oldport1isr定義為:

void interrupt (*oldport1isr)();

  我們融合setvect()函數、中斷服務程式和getvect()函數,給出一個由Craig Peacock編寫的完備常式:

/* Name : Sample Comm's Program - 1024 Byte Buffer - buff1024.c */
/* Written By : Craig Peacock <cpeacock@senet.com.au> */
#include <dos.h>
#include <stdio.h>
#include <conio.h>

#define PORT1 0x3F8 /* Port Address Goes Here */
#define INTVECT 0x0C /* Com Port's IRQ here (Must also change PIC setting) */

/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */

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 of 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, 0x0C); /* Set Baud rate - Divisor Latch Low Byte */
 /* Default 0x03 = 38,400 BPS */
 /* 0x01 = 115,200 BPS */
 /* 0x02 = 57,600 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) &0xEF)); /* 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) | 0x10)); /* MASK IRQ using PIC */
/* COM1 (IRQ4) - 0x10 */
/* COM2 (IRQ3) - 0x08 */
/* COM3 (IRQ4) - 0x10 */
/* COM4 (IRQ3) - 0x08 */
setvect(INTVECT, oldport1isr); /* Restore old interrupt vector */
}

 3.硬體查詢

  通過讀取和寫入串口UART對應的硬體連接埠,我們可以控制串口的收發。請看下面的例子:

/* Name : Sample Comm's Program - Polled Version - termpoll.c */
/* Written By : Craig Peacock <cpeacock@senet.com.au> */
#include <dos.h>
#include <stdio.h>
#include <conio.h>

00000000000000000000#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 = 57,600 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 */
}

  程式中的

c = inportb(PORT1 + 5); /* Check to see if char has been */
/* received. */
if (c &1)

  檢查PORT1 + 5連接埠地址,通過c&1可以判斷是否有資料被UART接收到。關於UART對應的連接埠範圍,從圖2中也可以直觀地看出。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.