LOGO

無線電收發機與個人電腦連線
(ICOM 系列、上)

No.17   1994 May   p86~93,   by 林茂榮 / BV5OC 彰化市郵政575號信箱



    ICOM 收發機所採用的遙控介面比較特殊,已經融入有網路的概念,此處分為上、下兩部份,依序討論 ICOM CI-V 介面原理、收發機電腦介面控制指令、列表機並聯介面用程式、收發機用硬體介面、及現成的 ICOM 收發機遙控軟 體商品。


ICOM CI-V介面原理

    ICOM 收發機電腦連線介面的最大特點是,它已經採取網路的觀念,所以它的介面就稱為遙控區域網路 (Remote Control Local Area Network) 系統。

    而 ICOM 則自行稱此介面為「 CI-V 通訊介面系統」 (Communication Interface System),它引用了所謂的「載 波式感知避免相碰的多頻道並存標準」 (CSMA/CD; Carrier Sense Multiple Access with Collision Detection)。

Fig 1
圖 1:ICOM CI-V 區域遙控介面網路系統。

Fig 2
圖 2:IC-735 CI-V的遙控介面的線路。
    經由 ICOM 的 CI-V 系統,可以遙控任何 ICOM 收發機的頻率、模式、VFO A/B、記憶頻道等。所有遙控指令與資 料,都由介面採 1200 鮑傳速的串列訊號來做溝通。

    整個系統只要電腦有可供使用的 RS-232 介面及一些硬體介面,就可以設立起來,當然,適當的軟體是不可少的。

    所有 ICOM 廠牌的收發機或接收機,都可以安排如圖 1 所示的並聯雙向連線,這樣的安排,可以很方便地遙控任 何並聯在遙控網路上的收發機或接收機。

介面如何工作

    下面就拿 ICOM IC-735 收發機為例,說明此種遙控介面是如何使用網路的概念,控制並列在一起的收、發機。

Fig 4
圖 4:典型的不回零資料樣子。
    參看圖 2,是 IC-735 所謂 CI-V 遙控介面的線路部份。它很明顯的特點是:同樣一個端點,身兼三個功能:發射 資料、接收資料、及偵測線上是否忙碌。

    系統上的資料型態是串列的不回零 (NRZ;Non-Return to Zero) 格式,這種格式的一位元資料例子,如圖 3 所示。

    參看圖 4,如果爻與爻接連著的都是 "1",那麼就不回到 "0" 狀態,並保持著,直到碰到是 "0" 的爻資料再回到 "0"。

Fig 3
圖 3:不回零 (NRZ;Non-Return to Zero)資料型態格式。

Fig 5
圖 5:ICOM 收發機與外界電腦溝通時資料的交換所採用的包封方式。

包封(PACKET)

    參看圖 5,包封中的每個小方塊,都含有一位元的資料。當 ICOM 收發機與外界電腦溝通時,資料的交換都採用這 種包封方式。

CSMA/CD

    載波式感知避免資料相碰撞的多頻道並存系統,可以拿來供做區域性的網路用途,系統上的每個發言者 (發射資料 ),在發送資料的同時,會偵測網路上的交通狀況,凡可能發生資料互相衝撞時,發射資料的動作馬上停止,這可 以簡化硬體的接線。

    一旦偵測到資料互撞時,發射資料的動作會暫停一段時間。雖然這樣,網路的使用效率也可以達到百分之九十以上。

如何知道資料互撞

    因為 CI-V 介面是個小型區域網路系統,因此,線上可能同時存在許多收發機,當然,更有可能含在同一時間,有 兩處甚至多處終端點發射資料,而造成資料互撞的情事,參看圖 1 的介面線路。此線路的結構會比較發射出去的 資料與接收到的資料,也就是發射資料的同時,也一併接收資料,一旦接收到的資料與自己發射出去的資料不相同 ,就表示線上發生了資料互撞。

Fig 6
圖 6:發生資料互撞時發出的擁擠碼 (Jammer code)。
    發生此種情形時,發射動作會暫時閒置,並送出如圖 6 的擁擠碼 (Jammer Code),等閒置時間一過 (此時間的長 短由收發機自行控制,因機種不同而稍異),馬上又開始繼續先前的動作,如果又發生資料互撞,則再次閒置,若 重複五次都沒能成功,則會放棄該筆資料。

    如果尚有資料待傳,它會拿下一筆資料做傳送。當接收到如圖 6 的擁擠碼時,會把該筆資料丟掉不用。

硬體設定

收發機形式 預設位址值
IC-725 40
IC-735 04
IC-751 28 (須外加UX-14)
IC-765 44
IC-275 16
IC-475 20
IC-R72 50
IC-R71 26
IC-R7000 08 (須外加UX-14)
表1:ICOM 收發機 CI-V 介面預設位址值。
    圖 7 是 IC-735 的典型硬體設定,利用一矩陣開關來設定各種參數。DB0 到 DB2 這三爻是用來設定該收發機的位 址,因為在 CI-V 系統內,線上的每部收發機都必須像每間屋子的門牌一樣,有不同的號碼。但因位址 "0" 保留 給其他用途,所以不能拿來做位址的設定,因此只能有從 1 到 7 的七種選擇。

    IC-735 出廠的預設值是 4,其他機型的位址設定,請參看表 1。DB3 則只要收發機打開電源,它就是呈 "ON" 狀態。

    串列訊號的傳送速度有三種選擇,IC-735 的出廠預設值是 1200 鮑,可以利用 DB4 及 DB5 來選擇其他兩種不同 的傳速,9600 或 300 鮑,參看圖 7。


電腦介面控制指令

00:設定頻率用。

Fig 7
圖 7:IC-735收發機與遙控介面有關的硬體設定。
    先送出收發機編號,再送 "00" 碼,之後再發出頻率資料。只有指定編號的收發機可以收到頻率資料。

    利用此碼送頻率資料給收發機後,收到資料的收發機不會發出任何反應資料出來。

01:設定模式用。

    使用方式與00碼相同。

02:要求告知選台範圍。

    收發機收到 02 碼後的反應是:[02,(頻率下限),$2D (頻率上限)],但有些機種的反應沒有 02,變為: [(頻 率下限),$2D (頻率上限)]。

03:要求告知現在頻率。

    收發機收到 03 碼後的反應是:[03,現在頻率],例如 21.145.000MHz 傳出的情形:

BYTE1 BYTE2 BYTE3 BYTE4
  00    50    14    21
  (x Hz)         (x MHz)
  ←---------資料流向

04:要求告知模式。

    收發機會回答出現在使用的模式,如果該收發機有不同的頻寬,除了送出使用的模式外,還會送出目前選用的頻寬。

05:設定頻率及要求告知頻率。

    此功能可以說集合了一些其他控制碼於一身。例如原來頻率是 21.145.000MHz,要改為 21.150.000MHz,則送出 "05" 碼之後,頻率資料是:

BYTE1 BYTE2 BYTE3
  00    00    15
           (x MHz)
  ←------資料流向

    收到上面資料之後,如果一切正常,收發機會發出 OK 碼 "$FB",表示資料一切收妥。如果有了意外,像是頻率資 料超出收發機的工作頻率範圍,則收發機會送出一個 NO GOOD 碼 "$FA"。

    00 碼與 05 碼的主要差別是,前者收發機不會同送反應訊息,而 05 碼則有 OK 或 NO GOOD 的反應。

06:設定模式,然後把模式資料送給接收機。也可設定頻寬。

    跟在 06 碼之後,如果是 1 位元資料,則是設定模式,如果跟著的是 2 位元資料,則是設定頻寬。

    如果接收機完成了設定工作,會發出 OK 碼 "$FB"。

    如果接收機沒有完成設定工作,會發出 NO GOOD "$FA"。

    01 碼與 06 碼最主要的差別是,前者沒有回應,而後者下過指令之後會有回應。

07:設定 VFO,然後把 VFO 資料送給接收機。

    跟在 07 碼之後,如果是沒有任何資料,則是從記憶模式轉到 VFO。當然如果收發機已經處在 VFO,自然不會有任 何反應。

    跟在 07 碼之後,如果 1 位元的資料是 "00",則是設定 VFO A,如果 1 位元資料是 "01",則是設定 VFO B。

    如果接收機完成了設定工作,會發出 OK 碼 "$FB"。

    如果接收機沒有完成設定工作,會發出 NO GOOD 碼 "$FA"。

    如果沒有指定 VFO,接收機就處在最後使用的狀態。

08:設定記憶頻道,然後把頻道資料送給接收機。

    跟在 08 碼之後,如果是沒有任何資料,則是從 VFO 轉到記憶模式,當然如果收發機已經處在記憶模式,自然不 會有任何反應。

    跟在 08 碼之後如果是頻道資料,則是跳到記憶頻道,記憶頻道資料採十進位。例如:記憶頻道 5:08,05,記憶頻 道 120:08,01,20,

    如果接收機完成了設定工作,會發出 OK 碼 "$FB"。

    如果接收機沒有完成設定工作,會發出 NO GOOD 碼 "$FA"。

09:儲存顯示頻率、記憶頻道、模式等。相當於按一下收發機的記憶寫入鈕。

    如果接收機完成了設定工作,會發出 OK 碼 "$FB"。如果接收機沒有完成設定工作,會發出 NO GOOD 碼 "$FA"。

0A:把儲存在記憶頻道的顯示頻率、記憶頻道、模式等轉入 VFO。

    如果接收機完成了設定工作,會發出 OK 碼 "$FB"。

    如果接收機沒有完成設定工作,會發出 NO GOOD 碼 "$FA"。

FA:NO GOOD 碼。

FB:OK 碼。


列表機並聯介面程式

列表機介面接腳

Printer Pin Printer Function Icom DB25 pin Icom Function
2 Data Bit 0 1 Data bit 0
3 Data Bit 1 2 Data bit 1
4 Data Bit 2 3 Data bit 2
5 Data Bit 3 4 Data bit 3
6 Data Bit 4 5 Data bit 4
7 Data Bit 5 6 Data bit 5
8 Data Bit 6 7 Data bit 6
9 Data Bit 7 8 Data bit 7
17 SLCTIN 9 RP
1 STROBE 10 SRQ/
14 AUTO FDXT 22(21 at radio) WP
10 ACK 23(22 at radio) DAV/
18-25(any) GND 25(24 at radio) GND


程式相關位址資料

3BCH
(read/write)
The 8-bit data bus. The direction of data flow is controlled by bit 5 in port 3BEH.
3BDH
(read only)
Bit 6 (i.e., mask 40h) is the DAV bit, active low (a zero bit corresponds to the ICOM saying that Data is Available).
3BEH
(write only)
Bit 0 (01H) controlls the SRQ line to the radio, active high (setting this bit to a one tells the radio that Service is ReQuested).
Bit 1 (o2H) is the WP (Write Pulse), active low. (Note that this bit should be kept high when the interface is idle, although it will not have any effect as long as SRQ is inactive.
Bit 3 (08H) is the RP (Read Pulse), active low. This bit should also be kept high the interface is idle.
Bit 4 (10H) is the IRQ (Interrupt ReQuest enable) bit that allows the ACK input pin to cause an interrupt. Since DAV is connected to the ACK input, this makes it possible to make the computer-to-radio protocol interrupt drived. This isn't usually worth it, though.
Bit 5 (20H) controls the direction (input/output) of the 8-bit data port. (This was the bit we had to wire up on the interface card). Clearing this bit puts the data port in output (computer => radio) mode; setting allows the computer to read data from the radio.

下面就是 ICOM 遙控介面的一個程式,利用個人電腦的列表機介面埠作為遙控。


 /* icom.c
  *利用個人電腦的列表機介面埠來遙控 ICOM 收發機
  */

 #include "icom.h"

 char *modes[] = {
 "LSB",  /* 0 */
 "USB",  /* 1 */
 "AM",   /* 2 */
 "CW",   /* 3 */
 "RTTY", /* 4 */
 "FM",   /* 5 */
 "CW Narrow",    /* 6 */
 "RTTY Narrow",  /* 7 */
 "lsb",  /* 8 */
 "usb",  /* 9 */
 "am",   /* a */
 "cw narrow",    /* b */
 "rtty narrow",  /* c */
 "fm",   /* d */
 "0xe",  /* e */
 "0xf",  /* f */
 };

 /* Read Band*/
 int
 read_band(freq,lower,upper)
 long freq;                 /* Used just to select the radio */
 long *lower,*upper;        /* Band limits returned through here */
 {
        register int i;

        start_cmd();
        if (send_byte(Band | band(freq)) < 0)
                 {
                 end_cmd();
                 return -1;
                 }
        *upper = o0;
        read_byte();         /* Toss opening delim */
        for(i=0;i<6;i++)
                *upper = *upper * 10 + (read_byte() & 0xf);
        read_byte();         /* Toss closing delim */
        *upper *= 10000;     /* Convert to hertz */

        *lower = 0;
        read_byte();         /* Toss second opening delim */
        for(i=0;i<6;i++)
                *lower = *lower * 10 + (read_byte() & 0xf);
        read_byte();         /* Toss second closing delim */
        *lower *= 10000;     /* Convert to hertz */
        end_cmd();
        return 0;
 }
 /* 設定頻率;自動選擇適當的收發機 */
 int
 set_freq(freq)
 long freq;     /* Frequency, hertz */
 {
         register int i;
         char fstr[15];

         start_cmd();
         if(send_byte(FREQ | band(freq)) < 0)
                 {
                 end_cmd();
                 return -1;
                 }
                 send_byte(FREQ | 0xd);
                 sprintf(fstr,"%091d",freq/10);
                 for(i=0;i<9;i++)
                         send_byte(FREQ | (fstr[i] - '0'));
                 send_byte(FREQ | 0xe);
                 end_cmd();
                 return 0;
 }

 /* 讀出頻率 */
 long
 read_freq(freq)
 long freq;      /* For band selection only */
 {
         register int i;
         start_cmd();
         if(send_byte(FREQ | band(freq)) <0)
         {
                 end_cmd();
                 return -1;
         }
         if(read_byte() < 0)     /* Discard opening delimiter */
                 return -1;
         freq=0;
         for(i=0;i<9;i++) {
                 freq = freq * 10 + (read_byte() & 0xf);
         }
         read_byte();            /* Discard closing delimiter */
         freq *=10;
         end_cmd();
         return freq;
 }
 /* 設定模式 */
 int
 set mode(freq,mode)
 long freq;              /* For radio selection */
 int mode;               /* Desired operating mode */
 {
         start_cmd();
         if(send_byte(MODE | band(freq)) <0)
         {
                 end_cmd();
                 return -1;
         }
         send_byte(MODE | 0xd);
         send_byte(MODE | mode);
         send_byte(MODE | 0xe);
         end_cmd();
         return 0;
 }
 /* 回到現行模式 */
 int
 read_mode(freq)
 long freq;              /* For radio selection */
 {
         int c;

         start_cmd();
         if(send_byte(MODE | band(freq)) <0)
         {
                 end_cmd();
                 return -1;
         }
         read_byte();
         c = read_byte();
         read_byte();
         end_cmd();
         return c & 0xf;
 }
 /* Set offset */
 int
 set_offset(freq,offset)
 long freq;              /* For radio selection */
 long offset;            /* Offset, hertz */
 {
         register int i;
         char fstr[15];

         start_cmd();
         if(send_byte(OFFSET | band(freq)) < 0)
                 {
                 end_cmd();
                 return -1;
                 }
                 send_byte(OFFSET | 0xd);
                 sprintf(fstr,"%091d",freq/1000);
                 for(i=0;i<9;i++)
                         send_byte(OFFSET | (fstr[i] - '0'));
                 send_byte(OFFSET | 0xe);
                 end_cmd();
                 return 0;
 }

 /* Read offset */
 long
 read_offset(freq)
 long freq;      /* For band selection only */
 {
         register int i;
         long offset;

         start_cmd();
         if(send_byte(OFFSET | band(freq)) <0)
         {
                 end_cmd();
                 return -1;
         }
         read_byte();            /* Discard opening delimiter */
         offset = 0;
         for(i=0;i<9;i++) {
                 offset = offset * 10 + (read_byte() & 0xf);
         }
         read_byte();            /* Discard closing delimiter */
         offset *=1000;
         end_cmd();
         return offset;
 }
 /* 選擇記憶頻道或vfo */
 int
 set_mem(freq,val)
 long freq;              /* For radio selection */
 int val;                /* Desired VFO/channel number */
 {
         start_cmd();
         if(send_byte(MEMVFO | band(freq)) <0){
                 end_cmd();
                 return -1;
         }
         send_byte(MEMVFO | 0xd);
         send_byte(MEMVFO | ((val >> 4) & 0xf)); /* tens digit */
         send_byte(MEMVFO | (val & 0xf));        /* units digit */
         send_byte(MEMVFO | 0xe);
         end_cmd();
         return 0;
 }
 /* 在VFO與記憶頻道之間相互轉換 */
 int
 transfer(freq,dir)
 long freq;              /* For radio selection */
 int dir;                /* Desired direction of transfer */
 {
         start_cmd();
         if(send_byte(MEMRW | band(freq)) < 0){
                 end_cmd();
                 return -1;
         }
         send_byte(MEMRW | 0xd);
         send_byte(MEMRW | dir);
         send_byte(MEMRW | 0xe);
         end_cmd();
 return 0;
 }
 /* Set band */

 int
 set_band(freq,b)
 long freq;              /* For radio selection */
 int b;                  /* Desired band */
 {
         long funny;

         set_mem(freq_38);       /* Select channel 38 */
         funny = (freq/1000000) * 1000000;       /* Truncate to MHz */
         funny += 100000 * b;    /* Desired band goes in 100khz digit */
         set_freq(funny);
         transfer(freq,WRITE);   /* Store in memory */
         set_mem(freq,0);        /* Go back to VFO */
         transfer(freq,READ);    /* Get the funny value */
         set_freq(freq);         /* Put in the one we really want */
         return 0;
 }

 /* The following are internal subroutines that perform the low-level
  * parts of the host/radio protocol. these can be "static" if you wish.
  */

 /* Send individual byte of a message */
 int
 send byte(c)
 char c;
 {
         register int i;

         outportb(I_DATA,C);

         /* Turn on WP and output mode in addition to SRQ */
 outportb(I_CTL,CTL_POL^(SRQ_CMD|WP_CMD|OUTPUT_MODE));

         /* Wait for DAV to go active low */
         for(i=TIMEOUT;i !=0;i--){
                 if((inportb(I_DAV) & DAV_STAT) == 0)
                         break;
         }
         if(i == 0){
                 outportb(I_CTL,CTL_POL);
                 printf("sendbyte fail\n");
                 return -1;
         }
         /* Drop WP and output mode, keeping SRQ active */
         outportb(I_CTL,CTL_POL^SRQ_CMD);

         /* Wait for DAV to go inactive high */
         for(i=TIMEOUT;i !=0;i--){
                 if((inportb(I_DAV) & DAV_STAT) != 0)
                         break;
         }
         if(i == 0){
                 outportb(I_CTL,CTL_POL);
                 printf("sendbyte fail 2\n");
                 return -2;
         }
         ur 0;
 }

 /* Read individual byte within a message */

 int
 read_byte()
 {
         register int i;
         register int c;

         /* Configure for input */
 outportb(I_CTL,CTL_POL^(RP_CMD|SRQ_CMD));

         /* Wait for DAV to go active low */
         for(i=TIMEOUT;i !=0;i--){
                 if((inportb(I_DAV) & DAV_STAT) == 0)
                         break;
         }
         if(i == 0){
                 outportb(I_CTL,CTL_POL);
                 printf("read fail\n");
                 return -1;
         }

         /* Read data byte from bue */
         c = inportb(I_DATA);
         /* Drop RP, Keeping SRQ active */
         outportb(I_CTL,CTL_POL^SRQ_CMD);

         /* Wait for DAV to go inactive high */
         for(i=TIMEOUT;i !=0;i--){
                 if((inportb(I_DAV) & DAV_STAT) != 0)
                         break;
         }
         if(i == 0){
                 outportb(I_CTL,CTL_POL);
                 printf("read fail 2\n");
                 return -2;
         }
         return c& 0xff;
 }
 /* Derive band number from frequency */
 int
 band(freq)
 register long freq;
 {
         if freq >= 1200000000){
                 return MHZ_1200;
         } else if(freq >= 4200000000){
                 return MHZ_430;
         } else if(freq >= 2200000000){
                 return MHZ_220;
         } else if(freq >= 1400000000){
                 return MHZ_144;
         } else if(freq >= 500000000){
                 return MHZ_50;
         } else
                 return HF;
 }
 /* Begin a message */
 start_cmd()
 {
         /* Assert SRQ */
         outportb(I_CTL,CTL_POL^SRQ_CMD);
 }
 /* End a message */
 end_cmd()
 {
         register int i;

         /* Wait a little bit */
         for (i=WAIT;i !=0;i--)

         /* Deactivate SRQ */
         outportb(I_CTL,CTL_POL);
 }

 /* icom.h
  * 定義 ICOM 的 library 功能
  */

 /* 系統參數;請自行斟酌更改 */

 /* I/O 埠的位址 */
 #define I_DATA          0x3bc   /* Data I/O port */
 #define I_CTL           0x3be   /* Control port (output) */
 #define I_DAV           0x3bd   /* Data available port (input) */

 /* Bits within I_DAV */
 #define DAV_STAT        0x40
 #define DAV_POL         0x40    /* DAV is negative polarity */

 /* Bits within I_CTL */
 #define OUTPUT_MODE     0x20
 #define RP_CMD          0x8
 #define WP_CMD          0x2
 #define SRQ_CMD         0x1
 /* Specify any bits in I_CTL which are negative logic.
  * Output mode, RP and WP are negative logic; SRQ is positive
  */
 #define CTL_POL (OUTPUT_MODE|RP_CMD|WP_CMD)

 /* 下面兩數值是適用在 8-MHz 的個人電腦
  * 如果常發生 timeouts 或 protocol lockups 請增加 WAIT 值
  */
 #define TIMEOUT         65535   /* Timeout on a read/write operation */
 #define WAIT            1100    /* Delay at end of sequence */

 /* 下面是ICOM收發機的特有設計,你不用去更動它
  */

 /* Commands */
 #define BAND            0x10
 #define FREQ            0x20
 #define MODE            0x30
 #define OFFSET          0x40
 #define MWMVFO          0x50
 #define MEMRW           0x60

 /* Addresses */
 #define HF              0x1     /* IC-735 OR IC-751 OR IC-781 */
 #define MHZ_50          0x2     /* IC-726 */
 #define MHZ_144         0x3     /* IC-275 */
 #define MHZ_220         0x4     /* IC- */
 #define MHZ_430         0x5     /* IC-475 */
 #define MHZ_1200        0x6     /* IC-1271 */

 /* Modes */
 #define LSB     0
 #define USB     1
 #define AM      2
 #define CW      3
 #define RTTY    4
 #define FM      5
 #define CWN     6
 #define RTTYN   7

 #define WRITE   1       /* VFO to memory */
 #define READ    2       /* Memory to VFO */

 long read_freq();

END


雜誌目錄 依順序 雜誌目錄 依主題分類