15-Nov-87 22:41:42-MST,9407;000000000000 Date: Thursday, 21 August 1986 01:45-MDT From: Phil Karn To: info-hams Re: Controlling ICOM radios from the IBM PC - Part 2 (source code) Here's the source code I wrote for controlling the ICOM radios through my PC clone's printer port. Note that all frequencies are represented in hertz, using longs; this makes things more consistent since different radio functions require different precision. ----------- /* icom.c * Library functions for driving the ICOM computer interface * Copyright 1986 by Phil Karn, KA9Q * Permission granted for free noncommercial copying and use by * licensed radio amateurs. */ #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 = 0; 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; } /* Set frequency; the proper radio is automatically selected */ 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,"%09ld",freq/10); for(i=0;i<9;i++) send_byte(FREQ | (fstr[i] - '0')); send_byte(FREQ | 0xe); end_cmd(); return 0; } /* Read frequency */ 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; } /* set mode */ 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; } /* Return current mode */ 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,"%09ld",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 radio selection */ { 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; } /* Select memory channel or 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; } /* Transfer between VFO and memory */ 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 * Uses the hack by NG6Q in April 1986 Ham Radio * Warning: untested (I don't have a 751). */ 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; } return 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 bus */ 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 >= 420000000){ return MHZ_430; } else if(freq >= 220000000){ return MHZ_220; } else if(freq >= 140000000){ return MHZ_144; } else if(freq >= 50000000){ 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 * Definitions for the ICOM library functions * 21 Aug 1986 * Phil Karn, KA9Q */ /* System-dependent constants; edit to taste */ /* Port addresses */ #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) /* These two values were found experimentally to work on an 8-MHz 8088 * Increase WAIT if you get frequent timeouts or protocol lockups */ #define TIMEOUT 65535 /* Timeout on a read/write operation */ #define WAIT 1100 /* Delay at end of sequence */ /* The following definitions are fixed by the ICOM design; they should not * have to be changed. */ /* Commands */ #define BAND 0x10 #define FREQ 0x20 #define MODE 0x30 #define OFFSET 0x40 #define MEMVFO 0x50 #define MEMRW 0x60 /* Addresses */ #define HF 0x1 /* IC-71 or IC-751 */ #define MHZ_50 0x2 #define MHZ_144 0x3 /* IC-271 */ #define MHZ_220 0x4 #define MHZ_430 0x5 /* IC-471 */ #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 of code