/* The following program is a pic-based PSK31 sender, Version 2.0x. *** NOTE: Look below for a conditional flag defining the use of a PIC16C84 or PIC16F84. The former has enough RAM for a 16 byte RAM buffer, whereas the PIC16F84 can provide for 48 bytes. The 0.05 base version had PWM analog output. This is accomplished by sampling the output at 32 times the bitrate (i.e. 1 KHz) with a PWM signal. The PWM generator in the ISR has a total of 100 "counts." The input variable "PWM" varies from 1 to 99 - one portion of routine outputs a 1 for "PWM" counts while the other portion outputs a zero for "100-PWM" counts. For the rest of the time this output is in hi-Z state. The R/C filter on the output smooths the output and allows for a linear 0-5 volt output for a 1-99 input (0 and 100 are not used.) The sine table (SINTBL) goes from 28 to 78 to keep away from the supply rails and within the working range of the output buffer/amplifier. The "zero" output (for the balanced modulator) occurs at approximately mid-supply. The 0.09 base version has an input select (PB4) that, when low (grounded) selects the mode whereby PA3 outputs a PWM version as described above. When PB4 is open (high) PA3 outputs a modulation envelope for an outboard PA. In this case, when a 0 is to be transmitted (i.e. a phase shift) the PA3 output goes low, PB3 changes state (i.e. phase) and then PA3 goes high again. This allows the phase shift to occur when the output power is at (or very near) zero. The phase information is taken from PB2. It should be noted that pin PB3 carries the varicode data output. This is simply a logic level that correlates with the current phase state. Originally a debug tool, this is also useful for FSK31 modulation. This pin outputs the data whether in the "AM" *or* the "balanced modulator" mode. It is worth noting that in the "AM" mode, the same data is output on pin PB2 (but slightly delayed to account for delay in the R/C lowpass filter) for phase modulation. Note that ALL input pins on port-B (i.e. RB4, RB5 and RB6) have weak pull-up resistors that are internal to the PIC. These input pins are also diode-protected, so voltages above Vdd and below Vss (ground) are clamped, as long as the input currents are limited to under 20 milliamps with a series resistance. For "high" level states, the appropriate pin may be left floating. THE SERIAL INPUT MODE: Version 2.00 introduces a serial port. This is selected by grounding pin RB5. This operates at 1200 baud, 8 bits, no parity, 1 stop bit. The serial data expected at this pin is to be "inverted" - that is, the output of an RS-232 port may be put directly into this pin through a series resistor. Recommended values are 2.7k for a unipolar output (i.e. voltage that goes down to zero, but not negative) or up to 10k if the output is bipolar (i.e. like most RS-232 ports.) NOTICE: WHEN TRANSMITTING, THE SERIAL INPUT IS DISABLED! If this is used "interactively", the host device should monitor the "PTT" line (RB0) to determine when data is being transmitted. NOTICE: The MSB (the eighth bit) of all incoming data is ignored and internally set to zero! This version allows beacon text to be sent. To maintain compatibility with previous versions, the PB5 pin is used as a "mode select" pin: When this pin is left open, it defaults to the "PLAY from EEPROM" mode. When this pin is grounded, the "serial input mode" is active. It is important to note that in the SERIAL INPUT mode, the MSB of the first byte in the EEPROM (byte address 0) is used to indicate if, in RAM mode, EEPROM data is to be sent repeatedly. There are several "commands" available in the serial input mode. These are as follows: STX (ASCII code 02h, control-B) - Clear buffer ETX (ASCII code 03h, control-C) - Transmit contents of buffer, followed immediately by EEPROM contents SO (ASCII code 0Eh, control-N) - "Replay" RAM (*NOT* followed by EEPROM data) SI (ASCII code 0Fh, control-O) - Turn on oscillator (it gets turned off when PTT is unkeyed) DC1 (ASCII code 11h, control-Q) - Copy contents of RAM buffer into EEPROM. No other action is taken. DC2 (ASCII code 12h, control-R) - Append contents of RAM buffer into EEPROM. No other actiion is taken. DC3 (ASCII code 13h, control-S) - Enable EEPROM-send mode when in RAM mode DC4 (ASCII code 14h, control-T) - Disable EEPROM-send mode when in RAM mode ETB (ASCII code 17h, control-W) - Enable "heartbeat" flash CAN (ASCII code 18h, control-X) - Disable "heartbeat" flash ESC (ASCII code 1Bh, "escape") - Transmit contents of buffer only. (No EEPROM data) It is recommended that, prior to ANY RAM buffer loading, the "STX" (clear buffer) command is sent to assure that the RAM buffer doesn't have any "garbage" in it. Sending a message: First, it should be noted that this software supports ONLY ASCII codes 0-127: The 8th bit is NOT supported. Also, the above control characters may NOT be sent as they will be intercepted and interpreted as their control character equivalents BEFORE they are sent. There are TWO modes whereby a message in RAM may be sent: The first of these is uses the ETX (control-C, 03h) character. This transmits the contents of the RAM buffer (up to 47 bytes) and follows it IMMEDIATELY with the EEPROM contents. This is useful if, for example, you have a beacon that is sending telemetry and you are always ending the message with unchanging information, such as a callsign. The second mode uses the ESCape (1Bh) character. This sends the current RAM test WITHOUT following it with the EEPROM contents. There is one more character that will cause a transmission: This is the SO (control-N, 0Eh) character. This will cause whatever is in RAM to be played back and is NOT followed by the EEPROM contents. This may be useful if you want to replay the RAM contents but do NOT want re-load them. Note that this command will play back ANYTHING that is in RAM - even garbage! It should be noted that this command works ***EVEN*** if you sent the character to clear the RAM! (The command only resets the RAM pointer, and doesn't erase anything.) A note: To force a playback of EEPROM contents ONLY, the following sequence is recommended: The character clears the RAM buffer and the character plays back the RAM buffer (which contains NOTHING at this point) followed by the EEPROM contents. Finally, it should be noted that the and characters are saved in RAM - this is necessary both to mark the end of the text and to indicate the desired sending mode. For this reason one RAM location is required for storing this information. The "Transfer to EEPROM" command does not need this, so 48 bytes are available for *THAT* commands... (In case you were wondering...) Transferring data into EEPROM: The "DC1" character (11h, control-Q) takes the current RAM buffer data and copies it into the EEPROM, terminating the EEPROM data automatically with the FFh character needed to signify the end of the text. Note that only as many bytes may be entered as there is room in the RAM buffer (48 bytes) so the EEPROM may NEVER be filled to capacity with just this command. Once this command is given, wait at least 250 milliseconds before issuing ANY other command. A "companion" command uses the "DC2" character (12h, control-R) appends the current RAM buffer data to what is currently in the EEPROM. The use of this command in conjunction with the "DC1" character command allows all 64 bytes of the EEPROM to be filled. The DC3 and DC4 control characters simply set and clear (respectively) the MSB in EEPROM location 0. This indicates whether or not the "beacon" mode is to be enabled. When in "beacon" mode, the EEPROM text will be repeatedly sent, pausing for a second or so between cycles to allow a "DC4" character to be received to disable the "beacon" mode. OPERATION OF THE SERIALLY-INITIATED "BEACON MODE" (NOT to be confused with the mode that is operated when pin RB5 is left ungrounded...): Sending a control-S (13h) will put the controller in the beacon mode. This is done by setting the MSB of the first EEPROM character (byte 0). In this mode, a preamble will be sent (about 2.5 seconds of "zeros") to allow syncronization of the receiver followed by the contents of the EEPROM, followed by about 250 milliseconds of "zeroes". Following this is a pause of approximately 1 second where only the PTT_OUT (RB0) line is are dropped. It is ONLY during this "window" that the "stop EEPROM Beacon Mode" command (control-T, 14h) will be recognized. If you are using a host computer, it is recommended that the PTT_OUT line be monitored to determine when you have "seized" control of the processor. It should be further noted that upon going into and out of beacon mode, the processor will experience a forced watchdog reset, causing all I/O lines to momentarily enter a Hi-Z state. A final note: While in the serially-initiated EEPROM "BEACON" mode the SERIAL INPUT line may be used to control transmission. If this line is held LOW (as is the case on an RS-232 output) then the beacon will operate normally. If this line is HIGH (or disconnected) then the beacon mode will halt after the current message cycle is completed. This may be used to control a timed beacon PTT and OSCILLATOR control lines: There are two lines used for controlling power to various pieces of an attached transmitter. RB0 is the PTT_OUT line and it goes high when a message is being sent in any mode. (I.E. in the strappable EEPROM-Beacon mode, it is ALWAYS high.) The other line is the KEY_OSC line (RB1) and it is ALWAYS high when PTT_OUT is high, and it is ALWAYS set to 0 when the PTT_OUT line is "unkeyed" - EXCEPT in the serially-controlled EEPROM PLAYBACK mode. It does, however, allow for a special command, the SI control character (control-O, 0Fh) to set this to a HIGH state. This allows a host processor to turn on the oscillator BEFORE the beginning of a transmission so that its frequency may stabilize, but not pull full current of the transmitter's output stages. The HEARTBEAT indicator. An LED may be connected between pin RB7 and ground (with a series resistor) for an indication of PIC activity. It is recommended that when used as an unattended beacon, this LED be disconnected (by using a push-on jumper, for example) to reduce power consumption. This pin will be pulsed high under the following conditions: - During the first bit of a varicode character being transmitted (for 32 milliseconds) - While the PIC is awaiting an input character. It will flash approximately once per second for about 4 milliseconds. - While an ASCII character is being received on the serial line (approximately 8 milliseconds.) These flashes may be used to determine what the PIC is doing and may be a useful troubleshooting tool. In order to save power, the "heartbeat" LED may be disabled via software. This is done by sending a the CAN control character (control-X, 18h.) It may be re-enabled with the ETB character (control-W, 17h.) The HEARTBEAT is enabled by default upon powerup and it is enabled in both the pin-strapped EEPROM-SEND mode and the serially-initiated BEACON mode. Copyright 1999, 2000 by Clint Turner, KA7OEI Revision History: 0.01 - 19990905 - First working version 0.05 - 19990906 - Version with PWM analog output for sine-shaped output 0.07 - 19990910 - Version with another PWM output mode for modulation of an output power amp (see above) 0.09 - 19991025 - PB4 (mode select) inverted (leaving PB4 open for power-saving AM mode). Also transmits EEPROM contents 1.00 - 19991103 - First "released" version - still using R/C LPF. 2.00 - 20001008 - Work begun on version with "serial port" - semi operational. 2.01 - 20001013 - First fully operational, mostly debugged version 2 code. 2.02 - 20001022 - Minor bug fixed which prevented some non "beacon-control" control characters from being transmitted 2.03 - 20001025 - Added 0.75 seconds of "dead carrier" tail to transmissions to facilitate squelch operation 2.04 - 20001107 - Changed "sine" lookup tables to greatly reduce IMD - especially in "AM" mode */ // *********** THE FOLLOWING STATEMENT DEFININES WHETHER TO USE PIC16C84 or PIC16F84 ************ // Operational differences between the 16C84 and 16F84 are that the former has 16 bytes of RAM and the latter has // 48 bytes of RAM available: This correlates with 15 and 47 bytes of message text available, respectively. //#define 16C84 TRUE // this is DEFINEd if a 16C84 is to be used, or comment it out if 16F84 is to be used #OPT 9 #ifdef 16C84 // are we using a 16C84 or 16F84? #include <16c84.h> // we use a 16C84 #else // we do not use 16C84 #include <16f84.h> // define use of 16F84 #endif #include "varicode.h" // include varicode table #define PORT_A_ADDR 0x05 // port A address #define PORT_A_TRIS 0x00 // port A I/O mask - all output #define PORT_A_TRIS_A3I 0x08 // port A I/O mask with A3 as input (for PWM output) #define PORT_B_ADDR 0x06 // port B address #define PORT_B_TRIS 0x70 // port B I/O mask - Outputs except B4, B5, B6 #define CALL_LEN 16 // length of "callsign" in bytes #define RTCC_CNT 39 // timer setting of RTCC loop for 1 KHz interrupt (32x bitrate) // - do not adjust this value! (error: approx +0.3 Hz) // #ifdef 16C84 // set buffer size appropriate for 16C84 #define BUFSIZE 16 // size of RAM buffer in bytes #else #define BUFSIZE 48 // size of RAM buffer in bytes #endif // #define STX 0x02 // (^B) character indicating that buffer should be cleared. #define ETX 0x03 // (^C) character indicating end of RAM text - to be followed by EEPROM text #define SO 0x0E // (^N) character indicating that contents of RAM should be "played" #define SI 0x0F // (^O) character indicating that oscillator control pin (RB1) should be active (=1) #define DC1 0x11 // (^Q) character indicating that RAM data be copied to EEPROM #define DC2 0x12 // (^R) character indicating that RAM data is to be appended to EEPROM data #define DC3 0x13 // (^S) character indicating that EEPROM Beacon mode should be initiated #define DC4 0x14 // (^T) character indicating that EEPROM Beacon mode should be stopped #define ESC 0x1b // (ESC) character indicating end of RAM text - NOT followed by EEPROM text #define ETB 0x17 // (^W) character to enable "heartbeat" (default mode) #define CAN 0x18 // (^X) character to disable "heartbeat" // #byte PORT_A = 5 #byte PORT_B = 6 #fuses XT, WDT, NOPROTECT, PUT // standard xtal, watchdog, no code protect, Power Up Timer, no brownout protect #use delay(clock=3579545, RESTART_WDT) // calibrate delays for apparent clock speed #use rs232(baud=1200, rcv=PIN_B6, RESTART_WDT, PARITY=N, INVERT) // #ROM 0x2100 = { 10, 13,'Y','o','u','r',' ','m','e','s','s','a','g','e',' ','g','o','e','s', ' ','h','e','r','e','.',' ','I','t',' ','m','a','y',' ','c','o','n','t','a','i','n',' ','u','p', ' ','t','o',' ','6','4',' ','c','h','a','r','a','c','t','e','r','s','.',10,13,0xff } // byte const sintbl[32] = { // for cosine modulation of balanced modulator 24, 24, 25, 27, 29, 30, 33, 35, 37, 40, 42, 44, 47, 49, 52, 54, 56, 59, 61, 64, 66, 68, 70, 72, 73, 75, 76, 77, 78, 79, 79, 79 }; // original waveform 28, 28, 29, 30, 31, 32, 34, 35, 37, 39, 41, 43, 46, 48, 51, 53, 55, 58, 60, 63, //65, 67, 69, 71, 72, 74, 75, 76, 77, 78, 78, 78 }; byte const pw_sine[32] = { // for power modulation cosinewave (when RB4 is left open (high)) 92, 92, 89, 88, 86, 82, 78, 73, 67, 61, 53, 46, 38, 32, 22, 13, 5, 23, 32, 40, 49, 57, 62, 68, 73, 77, 82, 84, 88, 90, 91, 92}; // original waveform 98, 96, 91, 86, 79, 71, 63, 54, 45, 37, 29, 22, 17, 12, 10, 9, 10, 12, 17, 22, //29, 37, 45, 54, 63, 71, 79, 86, 91, 96, 98, 99 }; #use fast_io(a) // set port A for fixed-mode of I/O direction #use fast_io(b) // set port B for fixed-mode of I/O direction #byte PORT_A = PORT_A_ADDR // define Port A as a memory location #byte PORT_B = PORT_B_ADDR // define Port B as a memory location // #bit TOGGLE = PORT_A_ADDR.1 // debug/test bit #bit PWM_OUT = PORT_A_ADDR.3 // PWM output // #bit PTT_OUT = PORT_B_ADDR.0 // push-to-talk output. 1=keyed #bit KEY_OSC = PORT_B_ADDR.1 // oscillator keying #bit OUT_PHASE = PORT_B_ADDR.2 // phase shift output - used when P.A. is modulated #bit OUT_BIT = PORT_B_ADDR.3 // data bit output. May be used for modulating a frequency-shift network // if FSK31 operation is desired. #bit OUT_MODE = PORT_B_ADDR.4 // mode-select input. When high (open) it is in the // amplitude modulator mode #bit SERIAL_MODE = PORT_B_ADDR.5 // serial port input enable. When high (open) the // EEPROM data is repeatedly sent. When low (connected to // ground) the serial input mode is enabled. #bit SER_IN = PORT_B_ADDR.6 // Serial data input #bit HEARTBEAT = PORT_B_ADDR.7 // this may be used to flash an LED (connected to ground) to // indicate that the PIC is operating. // byte c; // general-purpose character holder and counter byte cnt; // another general-purpose byte counter byte v_byte; // varicode word bit holder byte idx; // index of varicode byte v_len; // varicode word length byte pwm; // pwm value byte pwm_n; // pwm work variable byte pwm_i; // inverse of pwm work variable byte sinptr; // pointer within sine table byte baud_cnt; // baud rate counter byte buffer[BUFSIZE]; // RAM buffer for storing serial inputted data // short send_ok; // set to TRUE by the ISR if it is time to send another bit short doing_char; // flag to indicate that a character is being sent short bit_wait; // flag to indicate that a bit is waiting to be sent short bit_send; // bit that contains bit that is currently being sent (1 or 0) short mbit_send; // bit that contains a copy of bit_send for the PA modulation routine short mbit_flag; // bit that flags when mbit_send is used short last_bit; // bit that contains the previous bit that was sent short msg_complete; // this indicates that the RAM message being input is complete. short ram_complete; // this indicates that the RAM message being *sent* is complete. short no_eepromdat; // this indicates that no EEPROM data is to be sent following RAM text short heartbeat_enb; // this is true if heartbeat is to be enabled. short eeprom_playback; // this is true if in "eeprom" playback mode // Code begins #int_rtcc // interrupt function for the timeout of the RTCC timer_isr() { #asm nop // just a leeetle bit more delay to make it as close to 1KHz as possible nop #endasm set_rtcc(RTCC_CNT); // refresh RTCC counter pwm_n = pwm; // load current PWM value pwm_i = 100 - pwm_n; // calculate inverse of pwm variable set_tris_a(PORT_A_TRIS); // take out of HI-Z mode to output mode PWM_OUT = 1; // set PWM output to high state charge cap #asm // do the PWM part in assembly norloop: decfsz pwm_n,f // spin wheels here until pwm count is zero and charge cap goto norloop #endasm PWM_OUT = 0; // set PWM output low to discharge cap #asm invloop: decfsz pwm_i,f // spin wheels here, too - to discharge cap goto invloop #endasm set_tris_a(PORT_A_TRIS_A3I); // go to hi-z state between ISR cycles baud_cnt++; // increment baud counter if(baud_cnt > 31) { // every 32 interrupts, a new data bit is ready send_ok = 1; // it is now ok to send a character baud_cnt = 0; // reset bit counter } if(!OUT_MODE) { // if in the "feed the balanced modulator" mode, do this... if((OUT_BIT) && (sinptr < 31)) { // is output bit 1 AND pointer in table NOT at top? sinptr++; // increment position in sine table pwm = sintbl[sinptr]; // look up sine value (only if index was changed...) } else if(sinptr > 0) { // if output bit is 0 AND pointer in table is NOT at bottom sinptr--; // decrement position in sine table pwm = sintbl[sinptr]; // look up sine value } } else { // We are in the "AM" mode if(!mbit_send) { // is there supposed to be a phase shift? mbit_send = 1; // yes - clear it... sinptr = 0; // reset sine pointer } pwm = pw_sine[sinptr]; // get current data point in sine table if(sinptr < 31) { sinptr++; if(sinptr == 19) { // are we in the middle of the phase shift? OUT_PHASE = !OUT_PHASE; // yes - flip the phase when the sine is at zero } } } } // the following function interfaces with the ISR and the send_varichar() function and // actually diddles the bit sending data void bitsend(void) { if(send_ok) { // is there a bit ready to be sent? if(!bit_send) { // yes - is it a zero? OUT_BIT = !OUT_BIT; // toggle send bit (This represents a zero) } send_ok = 0; // clear the send-bit flag bit_wait = 1; // let it be known that the current bit has been sent... } } // This function sends a "preamble" of "c+1" "zeros" (bit transitions) to allow synchronization of the receiver. void send_preamble(void) { restart_wdt(); for(cnt = 0; cnt < c; cnt++) { // send some zeroes before each EEPROM playback cycle bit_send = 0; mbit_send = 0; bit_wait = 0; while(!bit_wait) { restart_wdt(); bitsend(); } } } // the following function sends the the character in the global variable "c" as the varicode bitstream at // 31.25 baud. It uses interrupt-based timers for pacing. Note: This is a consolidation of several // discrete functions from the previous version of the code. void send_character(void) { byte b_count; c &= 0x7f; // make sure the MSB is clear idx = c; idx += c; // double the index - each varicode entry takes two bytes v_byte = varicode[idx]; // get bits of varicode doing_char = 1; // indicate that a character is ready to be sent b_count = 0; // init bit counter bit_send = 1; // init holder for current bit last_bit = 1; // init holder for the last bit sent if(heartbeat_enb) { // is the "heartbeat" enabled? HEARTBEAT = 1; // yes - turn on "heartbeat" LED at beginning of each character } while(doing_char) { // hang around here until the character is done restart_wdt(); bit_wait = 0; // reset bit indicator last_bit = bit_send; // get previous bit bit_send = bit_test(v_byte, 7); // get the current bit to send if(!mbit_flag) { // copy current bit if it hasn't been done before... mbit_flag = 1; mbit_send = bit_send; // actually copy the bit... } b_count++; // bump bit count v_byte <<= 1; // shift the current varicode byte left 1 bit if((!last_bit) && (!bit_send)) { // are the past two recent bits both zero? doing_char = 0; // signal that this is the last bit of this character } if(b_count > 7) { // if this is the last bit of this word, get the next one... idx++; // look at the next bit of the v_byte = varicode[idx]; // get the remaining bits of the varicode b_count = 0; // reset the bit counter } mbit_flag = 0; // clear flag in preparation for next bit while(!bit_wait) { restart_wdt(); bitsend(); // hang around until the current bit is sent } HEARTBEAT = 0; // turn off "heartbeat" LED after first bit is sent... } } // the following function copies the contents of the RAM buffer to EEPROM starting at address zero. Note // that since the size of the available RAM is less than the EEPROM size, one may NEVER fill the EEPROM // using this method. // the global variable "c" should indicate where copy should *start* and the global variable "cnt" should // indicate where copy should *end*. // since 'pwm_n' and 'pwm_i' are used ONLY in the interrupt routine - and since the interrupt is disabled, we can // use them here. void copy_to_eeprom(void) { c = 0; while(c < cnt) { restart_wdt(); pwm_n = c + pwm_i; // calculated offset address (if any, offset will be in 'pwm_i') write_eeprom(pwm_n, buffer[c]); // write EEPROM data *plus* offset address c++; } restart_wdt(); pwm_n++; // a 0xff goes one character past where we ended if(pwm_n < 64) { // unless we go past end of EEPROM write_eeprom(pwm_n, 0xff); restart_wdt(); } } // the following function finds the end of the current EEPROM data and appends to it what is in the RAM. void append_to_eeprom(void) { pwm_i = 0; // find the ending of the EEPROM data while((pwm_i < 64) && (read_eeprom(pwm_i) != 0xff)) { // look for a 0xff, marking end of EEPROM restart_wdt(); pwm_i++; // set pointer to it } if(pwm_i + cnt > 63) { cnt = 64; // set to indicate "end" of EEPROM (plus one...) } // pwm_i contains position where current EEPROM data ends and where appending is to begin copy_to_eeprom(); // copy to EEPROM beginning at this location. } // the following function sends the contents of the EEPROM - until it hits the last character in the // EEPROM *OR* the end-of-EEPROM character (i.e. 0xff) void do_eeprom_send(void) { cnt = 0; // initialize EEPROM send count c = read_eeprom(cnt); // get first character to send... while((c != 0xff) && (cnt < 64)) { // was this the last character? c = read_eeprom(cnt); send_character(); cnt++; c = read_eeprom(cnt); // get new character... } } // the following function reads the EEPROM data at address zero and puts it in c void read_eeprom_zero(void) { c = read_eeprom(0); } // the following function puts what is in 'c' into EEPROM address zero. // A few bytes of ROM are saved if we define a function to do this task... void write_eeprom_zero(void) { write_eeprom(0, c); } // this function simply puts the character in 'c' into the RAM buffer // at location [cnt] and increments the [cnt] pointer by one. void put_in_buffer(void) { buffer[cnt] = c; cnt++; } // the following function keys transmit lines void key_tx(void) { PTT_OUT = 1; KEY_OSC = 1; } // the following function turns off the interrupts, PTT lines as appropriate for the mode, and appends // a "dead carrier" tail. void tx_end(void) { disable_interrupts(GLOBAL); // turn off interrupts again delay_ms(250); // give 3/4 second of dead carrier after transmission delay_ms(250); delay_ms(250); PTT_OUT = 0; // turn off transmitter... if(!eeprom_playback) { KEY_OSC = 0; // we never shut off oscillator when in serial EEPROM Beacon mode } } void main(void) { set_rtcc(RTCC_CNT); // initialize the rtcc setup_counters(RTCC_INTERNAL, RTCC_DIV_4); // set up to use internal clock enable_interrupts(RTCC_ZERO); // setup for interrupt on an RTCC count hitting zero // enable_interrupts(GLOBAL); // we don't turn the interrupt on during serial mode operations PORT_B_PULLUPS(TRUE); // PORT_A = 0; // init output bits // PORT_B = 0; SET_TRIS_A(PORT_A_TRIS); // set I/O direction for ports SET_TRIS_B(PORT_B_TRIS); // send_ok = 0; // initialize various flags and counters msg_complete = 0; ram_complete = 0; heartbeat_enb = 1; // "heartbeat" is enabled by default doing_char = 0; // bit_wait = 0; // bit_send = 0; sinptr = 15; // place the sine table pointer in the middle last_bit = 0; pwm = 50; // init the PWM algorithm // while(TRUE) { if(!SERIAL_MODE) { // is "mode select" input high or low? read_eeprom_zero(); if(c & 0x80) { // is MSB of first byte in EEPROM set? eeprom_playback = 1; // yes - it is in the "eeprom" playback mode } else { eeprom_playback = 0; // no, it is "normal" serial mode. } restart_wdt(); // it is LOW, therefore it is in serial mode. tx_end(); cnt = 0; // reset receive character count msg_complete = 0; // reset "message complete" flag ram_complete = 0; no_eepromdat = 0; while(!msg_complete) { // we wait here for a message to be received via the serial port pwm_i = 1; // use pwm_i and pwm_n as timers, as interupt isn't using them now... pwm_n = 0; while(!kbhit()) { restart_wdt(); pwm_i++; if(pwm_i == 0) { pwm_n++; pwm_i++; } if(pwm_n == 254) { if(heartbeat_enb) { HEARTBEAT = 1; // turn on Heartbeat LED, and then... } if(eeprom_playback) { // if this times out AND we are in EEPROM playback mode, play EEPROM message HEARTBEAT = 0; // make sure heartbeat LED is off... key_tx(); // key transmitter enable_interrupts(GLOBAL); // now, we actually turn on the interrupts... c = 80; send_preamble(); // warble a preamble cnt = 0; // start at beginning of message do_eeprom_send(); tx_end(); // end transmission - note that KEY_OSC isn't affected in EEPROM_BEACON mode pwm_i = 1; // reinitialize timer variables pwm_n = 0; } } else if(pwm_n == 255) { HEARTBEAT = 0; // turn off after 1/255th of loop delay duration } } if(heartbeat_enb) { HEARTBEAT = 1; // if there *is* a character, flash the LED } c = getch(); // get current character on serial port HEARTBEAT = 0; // (turn off LED after receiving character) c &= 0x7f; // mask off the MSB if((c < ' ') && (c != ETX) && (c != ESC)) { // is what follows a control character? if(c == STX) { // is this the "Clear RAM buffer" character? cnt = 0; // yes - reset the counter } else if(c == DC1) { // is this the "copy to EEPROM" command? pwm_i = 0; // preset "offset" for zero copy_to_eeprom(); // yes - do the copy... cnt = 0; } else if(c == DC2) { // is this the "append to EEPROM" command? append_to_eeprom(); // yes - do the append... cnt = 0; } else if(c == DC3) { // is this the "turn on beacon mode" character? read_eeprom_zero(); // yes - get the first EEPROM byte c |= 0x80; // set the MSB of this byte write_eeprom_zero(); // put it back into the EEPROM while(TRUE); // stall here, forcing a WDT reset... } else if(c == DC4) { // is this the "turn off beacon mode" character? read_eeprom_zero(); // yes - get the first EEPROM byte c &= 0x7f; // clear the MSB write_eeprom_zero(); // put it back... while(TRUE); // stall here, forcing a WDT reset... } else if(c == SO) { // is this the "play RAM again" character? msg_complete = 1; // yes - make it play the RAM again - w/out EEPROM no_eepromdat = 1; cnt = 0; } else if(c == SI) { // turn on oscillator? KEY_OSC = 1; // yes - turn on the oscillator } else if(c == ETB) { // is this the "enable heartbeat" command? heartbeat_enb = 1; // yes - enable heartbeat } else if(c == CAN) { // is this the "disable heartbeat" command? heartbeat_enb = 0; // yes - disable heartbeat } else { // if it was ***NOT*** one of the above control characters, we simply pass it through put_in_buffer(); // put this in the RAM buffer at location [cnt] } } else { // not a control character... put_in_buffer(); // put this in the RAM buffer at location [cnt] } if(cnt > BUFSIZE) { // are we at the end of the buffer space? cnt--; // yes - move the buffer back. Allow overwriting of the last character } if(c == ETX) { // Control character to send w/EEPROM text appended? msg_complete = 1; // yes - indicate that we are now ready to send a message } else if(c == ESC) { // Control character to send w/out EEPROM text? msg_complete = 1; no_eepromdat = 1; } } if(msg_complete) { key_tx(); // key transmitter enable_interrupts(GLOBAL); // now, we actually turn on the interrupts... c = 160; // set for 5 seconds of "zeroes" send_preamble(); // send preamble to allow receiver sync cnt = 0; // initialize buffer index c = 32; // load *something* (like a space...) in the send register to begin with... while(!ram_complete) { // was this the last character or end of RAM buffer? c = buffer[cnt]; cnt++; if((c == ETX) || (c == ESC) || (cnt > BUFSIZE)) { // are we at the end of the RAM buffer? ram_complete = 1; // yes - set the flag } else { // no, we aren't at the end. Continue... send_character(); } } if(ram_complete && !no_eepromdat) { // send at end of message *if* eeprom data is to be sent. do_eeprom_send(); // send contents of EEPROM } if(ram_complete) { // once the message(s) are done... msg_complete = 0; // we are done - clear flag } } } else { // do the EEPROM send if we are in the hardware "EEPROM Send" mode enable_interrupts(GLOBAL); // actually turn on interrupts key_tx(); // make sure we are keying tx c = 80; // set for 2.5 seconds of preamble (zeroes) send_preamble(); do_eeprom_send(); // send contents of EEPROM } } } !no_eepromdat) { // send at end of message *if* eeprom data is to be sent. do_eeprom_send(); // send contents of EEPROM } if(ram_complete) { // once the message(s) are done... msg_complete = 0; // we are done - clear flag } } } else { // do the EEPROM send if we are in the hardware "EEPROM Send" mode enable_interrupts(GLOBAL); // actually turn on interrupts key_tx(); // make sure we are keying tx c = 80; // set for 2.5 seconds of preamble (zeroes) send_preamble(); do_eeprom_send(); // send contents of EEPROM } } }