#define DEBUG //#define TESTTX #define TESTRX /** * @mainpage CAPE1 Communications Firmware * * @section overview_sec Overview * *

The CAPE1 Communications Firmware is responsible for interfacing the communications subsystem of the CAPE1 picosatellite with * the onboard computer (OBC). This includes configuring the Chipcon CC1020 for operation at 9600 baud FSK compatible with * James Miller's, G3RUH, 9600 baud modem. This firmware also handles AX.25 packet creation and CW Morse encoding.

* * @section history_sec Revision History * * @subsection v099a V0.99b * 9 June 2006, Pre-flight release. Transmit is fully working. Receive mostly works. Need to finalize main functionality and handshaking. * * * @section copyright_sec Copyright * * Copyright (c) 2004-2006 Jonathan Wagner, KE5FSG <jonathann@jonathanwagner.net>

* * * @section gpl_sec GNU General Public License * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *

* * * @section design Design Details * * Provides design details on a variety of the components that make up the CAPE1 Communications Subsystem. * * @subpage cc1020overview * @subpage cc1020registers */ /** * @page cc1020overview Chipcon CC1020 * * The Chipcon CC1020 is a FSK/GFSK/ASK/OOK RF transciever geared toward ISM use in the 435 and 900 MHz bands. CAPE1 is using * this transciever at its coordinated frequency of 435.245 MHz but a few register changes could easily change this to any other * frequency in the 420-450 MHz amateur band. * */ // Processor specific library for C18 #include // C18 Libraries to make things easier #include #include #include #include #include #include // Definitions, etc. These would normally go in a header file except for C18's perverse handling of multiple files. // CC1020 Configuration Register Settings /** * @page cc1020overview Chipcon CC1020 * * The Chipcon CC1020 is a FSK/GFSK/ASK/OOK RF transciever geared toward ISM use in the 435 and 900 MHz bands. CAPE1 is using * this transciever at its coordinated frequency of 435.245 MHz but a few register changes could easily change this to any other * frequency in the 420-450 MHz amateur band. * */ /** * @page cc1020registers Register Settings * *

The CC1020 is configured to be compatible with the G3RUH modems readily available. Since the CC1020 was not intended to be * used with such a narrow bandwidth to data rate spacing we had to tell the CC1020 to transmit with a frequency separation of * 3kHz to get it to transmit with a 6kHz separation. The following is the output from Chipcon's SmartRF Studio software's * "Print Settings" menu option. The actual register settings were further "tweaked" for better receive response.

* * *

The following configuration is good for these System parameters:

*
 * X-tal frequency: 14.745600 MHz	Internal
 * X-tal accuracy: +/-  5 ppm
 * RF frequency A: 435.245000 MHz	Inactive	Rx
 * RF frequency B: 435.245000 MHz	Active  	Tx
 * Frequency separation: 5.900 kHz
 * Data rate: 9.600 kBaud
 * Data Format: NRZ		Accurate
 * RF output power: 10 dBm
 * Channel spacing: 25 kHz
 * Modulation: GFSK		Dithering enabled
 * Lock: Continuous
 * Carrier sense offset:   0 dBm	DCLK squelch disabled
 * Operator Mode: Tx
 * 
*/ /** Set MAIN Register Value to 0xC1 for transmit mode.*/ #define MAIN 0xC1 /** Set MAIN Register Value to 0x01 for receive mode.*/ #define RXMAIN 0x01 /** Set INTERFACE Register to 0x0F for all modes.*/ #define INTERFACE 0x0F /** Set RESET Register Value to 0xFF for all modes.*/ #define RESET 0xFF /** Set SEQUENCING Register Value to 0x8F for all modes.*/ #define SEQUENCING 0x8F /** Set FREQ_2A Register Value to 0x3A for 435.245 MHz.*/ #define FREQ_2A 0x3A /** Set FREQ_1A Register Value to 0x3E for 435.245 MHz.*/ #define FREQ_1A 0x3E /** Set FREQ_0A Register Value to 0x01 for 435.245 MHz.*/ #define FREQ_0A 0x01 /** Set CLOCK_A Register Value to 0x39 for 435.245 MHz and 9600 baud.*/ #define CLOCK_A 0x39 /** Set FREQ_2B Register Value to 0x3A for 435.245 MHz.*/ #define FREQ_2B 0x3A /** Set FREQ_1B Register Value to 0x48 for 435.245 MHz.*/ #define FREQ_1B 0x48 /** Set FREQ_0B Register Value to 0xAD for 435.245 MHz.*/ #define FREQ_0B 0xAD /** Set CLOCK_B Register Value to 0x39 for 435.245 MHz and 9600 baud.*/ #define CLOCK_B 0x39 /** Set VCO Register Value to 0x44 for all modes.*/ #define VCO 0x44 /** Set MODEM Register Value for 9600 baud.*/ #define MODEM 0x50 /** Set DEVIATION Register Value to 0x8D for 2.950 kHz frequency separation.*/ #define DEVIATION 0x9D /** Set TXDEVIATION Register Value to 0x8D for 2.950 kHz frequency separation.*/ #define TXDEVIATION 0x9D /** Set RXDEVIATION Register Value to 0x9D for 2.950 kHz frequency separation.*/ #define RXDEVIATION 0x9D /** Set AFC_CONTROL Register Value to 0xC1 for proper reception. RFStudio says this should be 0xC3, 0xC1 seems to work better.*/ #define AFC_CONTROL 0xC3 /** Set FILTER Register to 0x2F for a 25 kHz wide channel spacing. 0x2B was tried for a wider spacing but 0x2F seems better */ #define FILTER 0x2F /** Set VGA1 Register Value to 0x61 for all modes.*/ #define VGA1 0x61 /** Set VGA2 Register Value to 0x55 for all modes.*/ #define VGA2 0x55 /** Set VGA3 Register Value to 0x2F for all modes.*/ #define VGA3 0x2F // 2E for RX /** Set VGA4 Register Value to 0x2D for all modes.*/ #define VGA4 0x2D // 29 for RX /** Set LOCK Register Value to 0x20 for all modes.*/ #define LOCK 0x20 /** Set FRONTEND Register Value to 0x78 for all modes.*/ #define FRONTEND 0x78 /** Set ANALOG Register Value to 0x47 for all modes.*/ #define ANALOG 0x47 /** Set RXANALOG Register Value to 0x47 for all modes.*/ #define RXANALOG 0x47 /** Set TXANALOG Register Value to 0x47 for all modes.*/ #define TXANALOG 0x47 /** Set BUFF_SWING Register Value to 0x14 for all modes.*/ #define BUFF_SWING 0x14 /** Set BUFF_CURRENT Register Value to 0x22 for all modes.*/ #define BUFF_CURRENT 0x22 /** Set PLL_BW Register Value to 0xAE for all modes.*/ #define PLL_BW 0xAE /** Set CALIBRATE Register Value to 0x34 for all modes.*/ #define CALIBRATE 0x34 /** Set PA_POWER Register Value to 0x0F for all modes.*/ #define PA_POWER 0x0F /** Set PA_POWER Register Value to 0x60 for 0 dBm.*/ #define PA_POWER_0_DBM 0x60 /** Set PA_POWER Register Value to 0x80 for 5 dBm.*/ #define PA_POWER_5_DBM 0x80 /** Set PA_POWER Register Value to 0x05 for -10 dBm.*/ #define PA_POWER_n10_DBM 0x05 /** Set MATCH Register Value to 0x00 for all modes.*/ #define MATCH 0x00 /** Set PHASE_COMP Register Value to 0x00 for all modes.*/ #define PHASE_COMP 0x00 /** Set GAIN_COMP Register Value to 0x00 for all modes.*/ #define GAIN_COMP 0x00 /** Set POWERDOWN Register Value to 0x00 for all modes.*/ #define POWERDOWN 0x00 // CC1020 Register Definitions /** Define the CC1020's MAIN register as address 0x00 */ #define CC1020_MAIN 0x00 /** Define the CC1020's INTERFACE register as address 0x01 */ #define CC1020_INTERFACE 0x01 /** Define the CC1020's RESET register as address 0x02 */ #define CC1020_RESET 0x02 /** Define the CC1020's SEQUENCING register as address 0x03 */ #define CC1020_SEQUENCING 0x03 /** Define the CC1020's FREQ_2A register as address 0x04 */ #define CC1020_FREQ_2A 0x04 /** Define the CC1020's FREQ_1A register as address 0x05 */ #define CC1020_FREQ_1A 0x05 /** Define the CC1020's FREQ_0A register as address 0x06 */ #define CC1020_FREQ_0A 0x06 /** Define the CC1020's CLOCK_A register as address 0x07 */ #define CC1020_CLOCK_A 0x07 /** Define the CC1020's FREQ_2B register as address 0x08 */ #define CC1020_FREQ_2B 0x08 /** Define the CC1020's FREQ_1B register as address 0x09 */ #define CC1020_FREQ_1B 0x09 /** Define the CC1020's FREQ_0B register as address 0x0A */ #define CC1020_FREQ_0B 0x0A /** Define the CC1020's CLOCK_B register as address 0x0B */ #define CC1020_CLOCK_B 0x0B /** Define the CC1020's VCO register as address 0x0C */ #define CC1020_VCO 0x0C /** Define the CC1020's MODEM register as address 0x0D */ #define CC1020_MODEM 0x0D /** Define the CC1020's DEVIATION register as address 0x0E */ #define CC1020_DEVIATION 0x0E /** Define the CC1020's AFC_CONTROL register as address 0x0F */ #define CC1020_AFC_CONTROL 0x0F /** Define the CC1020's FILTER register as address 0x10 */ #define CC1020_FILTER 0x10 /** Define the CC1020's VGA1 register as address 0x11 */ #define CC1020_VGA1 0x11 /** Define the CC1020's VGA2 register as address 0x12 */ #define CC1020_VGA2 0x12 /** Define the CC1020's VGA3 register as address 0x13 */ #define CC1020_VGA3 0x13 /** Define the CC1020's VGA4 register as address 0x14 */ #define CC1020_VGA4 0x14 /** Define the CC1020's LOCK register as address 0x15 */ #define CC1020_LOCK 0x15 /** Define the CC1020's FRONTEND register as address 0x16 */ #define CC1020_FRONTEND 0x16 /** Define the CC1020's ANALOG register as address 0x17 */ #define CC1020_ANALOG 0x17 /** Define the CC1020's BUFF_SWING register as address 0x18 */ #define CC1020_BUFF_SWING 0x18 /** Define the CC1020's BUFF_CURRENT register as address 0x19 */ #define CC1020_BUFF_CURRENT 0x19 /** Define the CC1020's PLL_BW register as address 0x1A */ #define CC1020_PLL_BW 0x1A /** Define the CC1020's CALIBRATE register as address 0x1B */ #define CC1020_CALIBRATE 0x1B /** Define the CC1020's PA_POWER register as address 0x1C */ #define CC1020_PA_POWER 0x1C /** Define the CC1020's MATCH register as address 0x1D */ #define CC1020_MATCH 0x1D /** Define the CC1020's PHASE_COMP register as address 0x1E */ #define CC1020_PHASE_COMP 0x1E /** Define the CC1020's GAIN_COMP register as address 0x1F */ #define CC1020_GAIN_COMP 0x1F /** Define the CC1020's POWERDOWN register as address 0x20 */ #define CC1020_POWERDOWN 0x20 /** Define the CC1020's TEST1 register as address 0x21 */ #define CC1020_TEST1 0x21 /** Define the CC1020's TEST2 register as address 0x22 */ #define CC1020_TEST2 0x22 /** Define the CC1020's TEST3 register as address 0x23 */ #define CC1020_TEST3 0x23 /** Define the CC1020's TEST4 register as address 0x24 */ #define CC1020_TEST4 0x24 /** Define the CC1020's TEST5 register as address 0x25 */ #define CC1020_TEST5 0x25 /** Define the CC1020's TEST6 register as address 0x26 */ #define CC1020_TEST6 0x26 /** Define the CC1020's TEST7 register as address 0x27 */ #define CC1020_TEST7 0x27 /** Define the CC1020's STATUS register as address 0x40 */ #define CC1020_STATUS 0x40 /** Define the CC1020's RESET_DONE register as address 0x41 */ #define CC1020_RESET_DONE 0x41 /** Define the CC1020's RSS register as address 0x42 */ #define CC1020_RSS 0x42 /** Define the CC1020's AFC register as address 0x43 */ #define CC1020_AFC 0x43 /** Define the CC1020's GAUSS_FILTER register as address 0x44 */ #define CC1020_GAUSS_FILTER 0x44 /** Define the CC1020's STATUS1 register as address 0x45 */ #define CC1020_STATUS1 0x45 /** Define the CC1020's STATUS2 register as address 0x46 */ #define CC1020_STATUS2 0x46 /** Define the CC1020's STATUS3 register as address 0x47 */ #define CC1020_STATUS3 0x47 /** Define the CC1020's STATUS4 register as address 0x48 */ #define CC1020_STATUS4 0x48 /** Define the CC1020's STATUS5 register as address 0x49 */ #define CC1020_STATUS5 0x49 /** Define the CC1020's STATUS6 register as address 0x4A */ #define CC1020_STATUS6 0x4A /** Define the CC1020's STATUS7 register as address 0x4B */ #define CC1020_STATUS7 0x4B // Set up some boolean conditions /** A FALSE boolean condition. */ #define FALSE 0x00; /** A TRUE boolean condition. */ #define TRUE 0x01; // PIC Pin Assignment /** Define PSEL as port B, bit 2 on the processor. */ #define PSEL PORTBbits.RB2 #define DCLK PORTBbits.RB0 #define DIO PORTBbits.RB1 /** * Define the Power Amplifier On/Off control as port D, bit 0 on the processor. * * @todo Verify that this is the proper I/O pinn for this purpose. * */ #define TX_ON PORTBbits.RB5 #define PTT PORTBbits.RB6 #define PWRCTRL PORTBbits.RB4 #define RXCTRL PORTDbits.RD4 #define TXCTRL PORTDbits.RD5 #define OBC_HS PORTDbits.RD2 #define TNC_HS PORTDbits.RD1 #define HILOPWR PORTDbits.RD0 // Timout Definitions /** Timeout value to wait for Calibration to complete. */ #define CAL_TIMEOUT 0x7FFE /** Timeout value while waiting for frequency lock. */ #define LOCK_TIMEOUT 0x7FFE /** Timeout value to wait for Reset to complete. */ #define RESET_TIMEOUT 0x7FFE // Miscellaneous Definitions #define flagbyte 0x7E #define WPM 10 /** * Lock Status of the CC1020. * Defines three states which the CC1020 may exhibit during calibration or receive. */ enum lockStatus { LOCK_OK, /**< Frequency Lock is ok */ LOCK_RECAL_OK, /**< Frequency Lock after recalibration is ok */ LOCK_NOK /**< Frequency Lock is not ok */ }; /** * Communications Processor state machine definitions */ enum StateType { RX_STATE, /**< Communications subsystem is in receive mode. */ TX_STATE, /**< Communications subsystem is in transmit mode. */ IDLE_STATE /**< Communications subsystem is idle. */ }; // Prototypes for CC1020 Configuration void ConfigureCC1020(void); void InitializeSPI(void); void WriteToCC1020Register(char addr, char data); char ReadFromCC1020Register(char addr); void ResetCC1020(void); char CalibrateCC1020(void); char SetupCC1020RX(void); char SetupCC1020TX(void); void SetupCC1020PD(void); void WakeUpCC1020ToRX(void); void WakeUpCC1020ToTX(void); // Initialization prototypes void InitializePorts(void); void InitializeINT(void); void InitializeTimer(void); void InitializeUSART(void); void InitializeDIT(void); void InitializeCC1020(void); // TNC Modes void RxMode(void); void TxMode(void); // Receive Functions void RxPacket(void); char RxByte(void); // Transmit Functions void TxPacket(void); void TxByte(char byte); // Error Detection Function void CalculateCRC(void); // CW Function void TxCW(void); // External Communications Functions void GetStoreData(void); void OutputUSART(void); void ChangeState(void); void TxHighPower(void); void TxLowPower(void); void TxHighPower30sec(void); void TxLowPower30sec(void); // Global variables which need cleaning up. // Variables to keep bitstuffing and NRZI sane /** Keep track of ones for bitstuffing */ char ONEScount = 0; /** Used to remember bit for NRZI encoding */ char lastBit = 0; /** * Used to keep from overwriting lastBit before comparison checks * * @todo This shouldn't be global. */ char nextBit; /** * Are we transmitting a flag character? If so, don't bitstuff. This really * shouldn't need to be global but...no buts. * * @todo

Remove this and all references.

*

This wasn't needed, never should have been used and certainly shouldn't have been global.

*

On second thought, maybe this should stay

*/ char isFlag; // Variables for use with the CRC functions /** This is the high byte of the 16-bit crc generated by CalculateCRC(). */ char crcHIGH; /** This is the low byte of the 16-bit crc generated by CalculateCRC(). */ char crcLOW; // Variables for scrambler /** LFSR is the Linear Feedback Shift Register */ short long LFSR; /** The length of a single dit—the basis of time for all Morse code.(i.e. dah is 3 dits, word space is 7 dits) */ int DIT; // Header information /** * The header of an AX.25 packet consists of the destination address (usually a callsign), the * destination SSID (Secondary Station ID), the source address (also * usually a callsign), a source SSID, the AX.25 control byte (0x03 for UI frames; * see http://www.tapr.org/pdf/AX25.2.2.pdf for reference), and the PID (Protocol ID) * which is 0xF0 for no layer 3 protocol such as IP. */ const char header[17] = { 'K' << 1, // dest address '5' << 1, 'U' << 1, 'S' << 1, 'L' << 1, ' ' << 1, 0x60, // SSID for destination - earth station 'K' << 1, // source address 'E' << 1, '5' << 1, 'F' << 1, 'S' << 1, 'G' << 1, 0x67, // SSID for source - satellite 0x03, // control byte - for UI or unnumbered frame 0xF0 }; // PID - for no layer 3 protocol implementation /** * The data buffer. * */ char data[150] = "UL Lafayette C.A.P.E. AX.25 Protocol UI Frame 2006"; /** * The total bytes to either transmit or which have been received. * */ int totalbytes = 50; /** * This table translates ASCII codes to morse code and is based on Ward Cunningham * and Jim Wilson's cw.py script included with Morse, an excellent CW Trainer for * Windows and Linux released under the GNU GPL V2 license. * * @see http://c2.com/morse for more information on Morse * * The hex codes are little endian in that they are read from right to left. Each * zero represents a silent space and each one represents a mark. Each bit * represents a dit (Iknow it rhymes :P ) or at least the length of a dit. For * example a 010 would be a space, a dit and another space, whereas a 01110 would be * a space, a dah and another space. In morse every time unit is based on that of a * dit. A dah or letter space requires 3 dits and a word space requires 7. * * There is also a built in letter space at the end of each code as well as a bit * to mark the end of the character. * */ rom far unsigned long Code[] = { 0x00000080, 0x00000000, 0x00045D5D, 0x00000000, /* [' ', '!', '"', '#'] */ 0x000475D5, 0x00000000, 0x00000000, 0x0045DDDD, /* ['$', '%', '&', "'"] */ 0x00045DD7, 0x00475DD7, 0x00000000, 0x0001175D, /* ['(', ')', '*', '+'] */ 0x00477577, 0x00047557, 0x0011D75D, 0x00011757, /* [',', '-', '.', '/'] */ 0x00477777, 0x0011DDDD, 0x00047775, 0x00011DD5, /* ['0', '1', '2', '3'] */ 0x00004755, 0x00001155, 0x00004557, 0x00011577, /* ['4', '5', '6', '7'] */ 0x00045777, 0x00117777, 0x00115777, 0x001175D7, /* ['8', '9', ':', ';'] */ 0x00000000, 0x00011D57, 0x00000000, 0x00045775, /* ['<', '=', '>', '?'] */ 0x001175DD, 0x0000011D, 0x00001157, 0x000045D7, /* ['@', 'A', 'B', 'C'] */ 0x00000457, 0x00000011, 0x00001175, 0x00001177, /* ['D', 'E', 'F', 'G'] */ 0x00000455, 0x00000045, 0x00011DDD, 0x000011D7, /* ['H', 'I', 'J', 'K'] */ 0x0000115D, 0x00000477, 0x00000117, 0x00004777, /* ['L', 'M', 'N', 'O'] */ 0x000045DD, 0x00011D77, 0x0000045D, 0x00000115, /* ['P', 'Q', 'R', 'S'] */ 0x00000047, 0x00000475, 0x000011D5, 0x000011DD, /* ['T', 'U', 'V', 'W'] */ 0x00004757, 0x00011DD7, 0x00004577, /* ['X', 'Y', 'Z'] */ }; /** State variable stores the current state of the state machine */ volatile enum StateType State; /** * NextState stores the state to switch to. * It is updated by the interrupt routine, while the main program does the actual state switch */ volatile enum StateType NextState; /** * @defgroup Main Main Driver * * This is the brains of the code. * * @{ */ void main(void) { int RSSI = 0; char* RSSIstr; char Value, i; char addr = CC1020_RSS; long t; char highLowBuffer = 0; InitializePorts(); InitializeUSART(); InitializeSPI(); InitializeDIT(); // InitializeINT(); PSEL = 1; #ifdef DEBUG PORTDbits.RD6 = 1; Delay10KTCYx(10); PORTDbits.RD7 = 1; Delay10KTCYx(10); PORTEbits.RE0 = 1; Delay10KTCYx(10); PORTEbits.RE1 = 1; Delay10KTCYx(10); PORTEbits.RE2 = 1; Delay10KTCYx(10); PORTDbits.RD6 = 0; Delay10KTCYx(10); PORTDbits.RD7 = 0; Delay10KTCYx(10); PORTEbits.RE0 = 0; Delay10KTCYx(10); PORTEbits.RE1 = 0; Delay10KTCYx(10); PORTEbits.RE2 = 0; Delay10KTCYx(10); // OutputUSART(); #endif InitializeCC1020(); // Now the CC1020 is calibrated for both RX and TX, we do not need to recalibrate // unless the frequency is changed, the temperature changes by 40C // or if the supply voltage changes by more than 0.5V NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,RXMAIN); #ifdef DEBUG NextState = TX_STATE; ChangeState(); if ((ReadFromCC1020Register(CC1020_STATUS) & 0x10) == 0x10){ PORTEbits.RE0 = PORTEbits.RE1 = PORTEbits.RE2 = 1; } Delay10KTCYx(250); PORTEbits.RE0 = PORTEbits.RE1 = PORTEbits.RE2 = 0; NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,RXMAIN); #endif /** REMOVE ME **/ #ifdef TESTRX TRISD = 0x00; TRISB = 0x03; while(1) { PORTDbits.RD0 = 1; PORTDbits.RD1 = 0; RxPacket(); OutputUSART(); PORTDbits.RD0 = 0; PORTDbits.RD1 = 1; Delay10KTCYx(100); } #endif #ifdef TESTTX while(1) { NextState = TX_STATE; ChangeState(); WriteToCC1020Register(CC1020_PA_POWER, PA_POWER_0_DBM); // Transmit Data if ( totalbytes > 3 ) { CalculateCRC(); // Calculate CRC from data TxPacket(); // Transmit AX25 packet with bitstuffing and NRZI } } #endif /** END REMOVE ME **/ while(1){ if (OBC_HS == 1){ highLowBuffer = HILOPWR; TNC_HS = 1; Delay100TCYx(250); // 10ms pulse TNC_HS = 0; if (highLowBuffer) TxHighPower(); else TxLowPower(); } //while(PORTDbits.RD6 == 1); // Testing for lockup. Remove this in final version } // // Sample RSSI code // while (1) // { // for (i=0; i < 4; i++) // { // RSSI = 0 + ReadFromCC1020Register(CC1020_RSS) & 0x00FF; // RSSI = (1.5 * RSSI) - 48 - 60; // itoa(RSSI,RSSIstr); // putsUSART( RSSIstr ); // while( BusyUSART()); // putrsUSART( (char*)"dBm" ); // while( BusyUSART()); // WriteUSART( 0x0D ); // Since the OBC is waiting for a CR before processing, then send the CR // while( BusyUSART()); // WriteUSART( 0x0A ); // // Delay10KTCYx(100); // wait for databit to stable // } // RxMode(); // } } /** @} */ /** * @defgroup Init General Hardware Initialization * * Initialize the PIC's onboard peripherals including data busses, interrupts, timers, etc. * * @todo Determine the usefulness of these functions. * * @{ */ void InitializePorts(void) { PORTA = 0x00; TRISA = 0x00; PORTB = 0x00; TRISB = 0x01; PORTC = 0x00; TRISC = 0x92; PORTD = 0x00; TRISD = 0xC5; PORTE = 0x00; TRISE = 0x00; ADCON0 = 0x89; ADCON1 = 0x84; } /** * Initialise peripheral interrupts which includes those on the USART bus. * * @see InitializeUSART() */ void InitializeINT(void) { // Interrupt Settings INTCONbits.GIE = 1; // enable interrupts INTCONbits.PEIE = 1; // enable peripheral interupts RCONbits.IPEN = 0; // disable priorities } /** * Initialise the PIC's builtin hardware timer 0. Nothing currently calls this function, but the initialization code is here if * it is needed. */ void InitializeTimer(void) { OpenTimer0( TIMER_INT_OFF & T0_16BIT & T0_SOURCE_INT & T0_PS_1_1); } /** * Initialises the PIC's builtin asynchronous UART bus at 1200 baud. * * This is kind of silly, but it makes the main() function a little easier to follow, I think. It will stay for now. */ void InitializeUSART(void) { OpenUSART( USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_CONT_RX & USART_BRGH_LOW, 129); } /** * Initialise the PIC's builtin SPI (Serial Peripheral Interface) bus for communication with the CC1020's * configuration bus. Must be called before SPI is used. */ void InitializeSPI(void) { OpenSPI( SPI_FOSC_16, MODE_00, SMPMID); } /** * From the comments in Cw.cxx of Morse by Ward Cunningham and Jim Wilson and found at * http://c2.com/morse/ *

* Somewhere in the ARRL Handbook is a formula that gives the WPM rate * in terms of the dots/second or dots/minute, or somesuch. However * the Handbook has grown so large and cluttered, and the indexing so * inadequate, that I cannot lay my eyes on it. We'll have to rely on * Google instead... * * In, "The Art & Skill of Radio-Telegraphy", 2nd Ed, William Pierpont, * (http://www.geocities.com/gm0rse/n0hff/c23.htm), N0HFF suggests there * are "50 elements used as today's standard word". * * Elsewhere, "PARIS" is frequently mentioned as the "standard word": * * P = .--. = 1+1 + 3+1 + 3+1 + 1+3 = 14 elements * A = .- = 1+1 + 3+3 = 8 elements * R = .-. = 1+1 + 3+1 + 1+3 = 10 elements Total: 50 elements * I = .. = 1+1 + 1+3 = 6 elements * S = ... = 1+1 + 1+1 + 1+7 = 12 elements * * Thus, if unanimity is to be believed, the standard word has 50 elements, * and the formula that I can't find in the Handbook is: * * WPM = (DPM*2)/50 = dpm/25, where DPM is dots per minute * * (I vaguely remember "25" in the ARRL formula, so maybe this is right.) * * For a code speed, WPM, we'll need an element time, E: * * E = 60/(50*WPM) *

* *

If this is the case, then E = the length of one dit in seconds and our function should be rather simple.

* *

We will use Delay10KTCYx(DIT) so we need to know the length of our dit with respect to PIC instruction cycles (TCY) * One TCY is equal to 4 / Fosc. In our case Fosc is 10 MHz so one TCY = 400ns or 4E-7 seconds and 10,000 TCY = 4ms.

* * @todo Fix DIT equation for our purposes */ void InitializeDIT(void) { DIT = (60 / (50 * WPM))/0.004; /** 1KTCY */ } /** *

InitializeCC1020 is just a cozy place to call all of the functions needed to set up * the CC1020 on power up. All of these only need to be called at startup and having them * here helps tidy up main().

* */ void InitializeCC1020(void) { SetupCC1020PD(); ResetCC1020(); ConfigureCC1020(); WakeUpCC1020ToTX(); TRISC&=~(0x02); // Set DIO as output CalibrateCC1020(); WakeUpCC1020ToRX(); TRISC|=0x02; CalibrateCC1020(); } /** @} */ /** * @defgroup CC1020 Chipcon CC1020 Configuration * * Configuration of the Chipcon CC1020. This includes routines for reading and writing to configuration. * * @todo Determine the usefulness of these functions. * * @{ */ /** * Send configuration data to the CC1020. */ void ConfigureCC1020(void) { short val; char i; char Configuration[33]={MAIN, INTERFACE, RESET, SEQUENCING, FREQ_2A, FREQ_1A, FREQ_0A, CLOCK_A, FREQ_2B, FREQ_1B, FREQ_0B, CLOCK_B, VCO, MODEM, DEVIATION, AFC_CONTROL, FILTER, VGA1, VGA2, VGA3, VGA4, LOCK, FRONTEND, ANALOG, BUFF_SWING, BUFF_CURRENT, PLL_BW, CALIBRATE, PA_POWER, MATCH, PHASE_COMP, GAIN_COMP, POWERDOWN}; for (i=0;i<33;i++) { val=Configuration[i]; WriteToCC1020Register(i,val); } } /** * Writes configuration data given by 'char data' to a register in the CC1020 configuration registers * given by 'char addr.' * * @param addr The register address to write to. * @param data Register contents to write. */ void WriteToCC1020Register(char addr, char data) { char dummy; PSEL=0; dummy=SSPBUF; SSPBUF=(addr<<1)|0x01; // Write address to CC1020, write bit is always 1 // Wait until data is written while (SSPSTATbits.BF==0); dummy=SSPBUF; SSPBUF=data; while (SSPSTATbits.BF==0); PSEL=1; } /** * Reads a byte from one of the CC1020 registers. * * @param addr The address to read from * @return The value contained in the CC1020 register specified by address. */ char ReadFromCC1020Register(char addr) { char Value; PSEL=0; Value=SSPBUF; SSPBUF=(addr<<1)&0xFE; // Write address to CC1020, write bit is always 0 // Wait until data is written while (SSPSTATbits.BF==0); SSPCON1bits.SSPOV=0; // Switch direction TRISC|=0x20; // Set up PDATAOUT as an input SSPBUF=0xFF; // Dummy write while (SSPSTATbits.BF==0); Value=SSPBUF; TRISC&=~0x20; // Set PDATAOUT as an output PSEL=1; return Value; } /** * Reset the CC1020, thus clearing all registers. */ void ResetCC1020(void) { // Reset CC1020 WriteToCC1020Register(CC1020_MAIN, 0x0F&~0x01); // Bring CC1020 out of reset WriteToCC1020Register(CC1020_MAIN, 0x1F); } /** * This routine calibrates the CC1020. * * @return 0 if calibration fails, non-zero otherwise. Checks the LOCK to check for success */ char CalibrateCC1020(void) { volatile int TimeOutCounter; volatile int nCalAttempt; // Turn off PA to avoid spurs during calibration in TX mode WriteToCC1020Register(CC1020_PA_POWER,0x00); // Calibrate, and re-calibrate if necessary: for (nCalAttempt = 2 /*CAL_ATTEMPT_MAX*/; (nCalAttempt>0); nCalAttempt--) { // Start calibration WriteToCC1020Register(CC1020_CALIBRATE,0xB4); // Monitor actual calibration start (ref. Errata Note 04 - CC1020) for(TimeOutCounter=CAL_TIMEOUT; ((ReadFromCC1020Register(CC1020_STATUS)&0x80)==0x80)&&(TimeOutCounter>0); TimeOutCounter--); // Important note: // In active mode the CC1020 should theoretically initiate an internal action/process more or less // instantly upon receiving any command from e.g. an MCU. However, upon sending a [calibration start] // command to CC1020, tests shows that the [STATUS.CAL_COMPLETE]-signal sometimes remains asserted // (or idle) close to 100 usec after the command has been originally issued. Consequently this process // must be carefully monitored to avoid premature PLL LOCK monitoring; do not proceed with subsequent // PLL LOCK monitoring before the calibration has actually completed inside the CC1020! Errata Note 04 // suggests that [calibration start] should be monitored by a fixed timeout > 100 usec. However, the // above method implements an adaptive monitoring of [calibration start], which basically waits for the // [STATUS.CAL_COMPLETE]-signal to initialise/deassert (indicating calibration has actually started) // before proceeding with monitoring calibration complete and PLL LOCK. Chipcon considers both methods // safe, and thus leaves it up to the user, which one to use. // Monitor calibration complete for(TimeOutCounter=CAL_TIMEOUT; ((ReadFromCC1020Register(CC1020_STATUS)&0x80)==0x00)&&(TimeOutCounter>0); TimeOutCounter--); // Monitor lock for(TimeOutCounter=LOCK_TIMEOUT; ((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0x00)&&(TimeOutCounter>0); TimeOutCounter--); // Abort further recalibration attempts if successful LOCK if((ReadFromCC1020Register(CC1020_STATUS)&0x10) == 0x10) { break; } } // Restore PA setting WriteToCC1020Register(CC1020_PA_POWER, PA_POWER); // Return state of LOCK_CONTINUOUS bit return ((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0x10); } /** * This routine puts the CC1020 into RX mode (from TX). When switching to RX from PD, use WakeupC1020ToRX first * * @return Lock status. */ char SetupCC1020RX(void) { volatile int TimeOutCounter; char lock_status; char dummy, addr; // Switch into RX, switch to freq. reg A WriteToCC1020Register(CC1020_MAIN,0x11); // Setup bias current adjustment WriteToCC1020Register(CC1020_ANALOG,RXANALOG); WriteToCC1020Register(CC1020_DEVIATION,RXDEVIATION); WriteToCC1020Register(CC1020_AFC_CONTROL,0xC3); WriteToCC1020Register(CC1020_VGA3,0x2E); WriteToCC1020Register(CC1020_VGA4,0x29); // Monitor LOCK for(TimeOutCounter=LOCK_TIMEOUT; ((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0)&&(TimeOutCounter>0); TimeOutCounter--); // If PLL in lock if((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0x10){ // Indicate PLL in LOCK lock_status = LOCK_OK; // Else (PLL out of LOCK) }else{ // If recalibration ok if(CalibrateCC1020()){ // Indicate PLL in LOCK lock_status = LOCK_RECAL_OK; // Else (recalibration failed) }else{ // Indicate PLL out of LOCK lock_status = LOCK_NOK; } } // Switch RX part of CC1020 on //WriteToCC1020Register(CC1020_MAIN,0x01); PSEL=0; dummy=SSPBUF; SSPBUF=(CC1020_MAIN<<1)|0x01; // Write address to CC1020, write bit is always 1 // Wait until data is written while (SSPSTATbits.BF==0); dummy=SSPBUF; SSPBUF=0x01; while (SSPSTATbits.BF==0); PSEL=1; // Return LOCK status to application return (lock_status); } /** * This routine puts the CC1020 into TX mode (from RX). When switching to TX from PD, use WakeupC1020ToTX first * * @return Lock status. */ char SetupCC1020TX(void) { volatile int TimeOutCounter; char lock_status; // Turn off PA to avoid frequency splatter WriteToCC1020Register(CC1020_PA_POWER,0x00); // Setup bias current adjustment WriteToCC1020Register(CC1020_ANALOG,TXANALOG); WriteToCC1020Register(CC1020_DEVIATION,TXDEVIATION); WriteToCC1020Register(CC1020_AFC_CONTROL,0xC1); WriteToCC1020Register(CC1020_VGA3,0x2F); WriteToCC1020Register(CC1020_VGA4,0x2D); // Switch into TX, switch to freq. reg B WriteToCC1020Register(CC1020_MAIN,0xC1); // Monitor LOCK for(TimeOutCounter=LOCK_TIMEOUT; ((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0)&&(TimeOutCounter>0); TimeOutCounter--); // If PLL in lock if((ReadFromCC1020Register(CC1020_STATUS)&0x10)==0x10){ // Indicate PLL in LOCK lock_status = LOCK_OK; // Else (PLL out of LOCK) }else{ // If recalibration ok if(CalibrateCC1020()){ // Indicate PLL in LOCK lock_status = LOCK_RECAL_OK; // Else (recalibration failed) }else{ // Indicate PLL out of LOCK lock_status = LOCK_NOK; } } // Restore PA setting WriteToCC1020Register(CC1020_PA_POWER,PA_POWER); // Turn OFF DCLK squelch in TX WriteToCC1020Register(CC1020_INTERFACE,ReadFromCC1020Register(CC1020_INTERFACE)&~0x10); // Return LOCK status to application return (lock_status); } /** * This routine puts the CC1020 into power down mode. Use WakeUpCC1020ToRX followed by SetupCC1020RX or WakeupCC1020ToTX * followed by SetupCC1020TX to wake up from power down. */ void SetupCC1020PD(void) { // Put CC1020 into power-down WriteToCC1020Register(CC1020_MAIN,0x1F); // Turn off PA to minimise current draw WriteToCC1020Register(CC1020_PA_POWER,0x00); } /** * This routine wakes the CC1020 up from PD mode to RX mode */ void WakeUpCC1020ToRX(void) { volatile int i; // Turn on xtal oscillator core WriteToCC1020Register(CC1020_MAIN,0x1B); // Setup bias current adjustment WriteToCC1020Register(CC1020_ANALOG,RXANALOG); WriteToCC1020Register(CC1020_DEVIATION,RXDEVIATION); WriteToCC1020Register(CC1020_AFC_CONTROL,0xC3); WriteToCC1020Register(CC1020_VGA3,0x2E); WriteToCC1020Register(CC1020_VGA4,0x29); // Insert wait routine here, must wait for xtal oscillator to stabilise, // typically takes 2-5ms. Delay100TCYx(125); // for (i=0x0260; i > 0; i--); // Turn on bias generator WriteToCC1020Register(CC1020_MAIN,0x19); // Wait for 150 usec Delay10TCYx(28); // for (i=0x0010; i > 0; i--); // Turn on frequency synthesiser WriteToCC1020Register(CC1020_MAIN,0x11); } /** * This routine wakes the CC1020 up from PD mode to TX mode */ void WakeUpCC1020ToTX(void) { volatile int i; // Turn on xtal oscillator core WriteToCC1020Register(CC1020_MAIN,0xDB); // Setup bias current adjustment WriteToCC1020Register(CC1020_ANALOG,TXANALOG); WriteToCC1020Register(CC1020_DEVIATION,TXDEVIATION); WriteToCC1020Register(CC1020_AFC_CONTROL,0xC1); WriteToCC1020Register(CC1020_VGA3,0x2F); WriteToCC1020Register(CC1020_VGA4,0x2D); // Insert wait routine here, must wait for xtal oscillator to stabilise, // typically takes 2-5ms. Delay100TCYx(125); // for (i=0x0260; i > 0; i--); // Turn on bias generator WriteToCC1020Register(CC1020_MAIN,0xD9); // Wait for 150 usec Delay10TCYx(28); // for (i=0x0010; i > 0; i--); // Turn on frequency synthesiser WriteToCC1020Register(CC1020_MAIN,0xD1); } /** @} */ /** * @defgroup TNC Terminal Node Controller * * The Terminal Node Controller (TNC) is responsible for creating AX.25 protocol compatible UI packets. Among the * duties of the TNC is header creation, bitstuffing, and NRZI encoding. The TNC also performs scrambling compatible * with G3RUH though this is usually a bit more lower level than a TNC is responsible for. * * @{ */ /** * Handles the flow of our State Machine * * @todo This function should handle some of the stuff inside RxMode() and TXMode(). * It probably wouldn't hurt to call RxMode and TxMode from within here either. * @todo Update this to be more thorough such as including CW States as well. */ void ChangeState(void) { switch(NextState) { case RX_STATE: if (State == TX_STATE) { TRISC |= 0x02; // Set DIO as input SetupCC1020RX(); } State = RX_STATE; break; case TX_STATE: if (State!=TX_STATE) { TRISC&=~(0x02); // Set DIO as output SetupCC1020TX(); } State = TX_STATE; break; case IDLE_STATE: if (State == TX_STATE) { TRISC |= 0x02; // Set DIO as input SetupCC1020RX(); } State = IDLE_STATE; break; } } /** * ISR Vector * The following code is run only once an interrupt is given by a byte received in the USART port of the TNC. * * @todo Clean this up. This shouldn't call TxMode function directly, but should call an ISR which calls ChangeState */ /** Vector Definition */ #pragma code low_vector=0x18 #pragma code high_vector=0x08 void low_interrupt( void ) { _asm goto TxMode _endasm } #pragma code // Return to normal code space /** *

Interrupt Service Routine

*

TxMode is currently deprecated in the current handshaking methods. If receive becomes functional on CAPE1 * this method should be replaced with the handshaking routine and handle the distinction between requests for * low power CW and high power G3RUH 9600 baud AX.25 transmissions the handshaking could also take place here.

* *

As it stands, this code is ueful in an AX.25 transmit/receive communications system without handshaking using * UART based interrupts for transmit. All in all this is a much better solution than the currently hob-glob * implemented for handling lowspeed/highspeed transmission in the main function.

* * @todo Update the ISR to handle: *
    *
  • Handshaking
  • *
  • CW versus AX.25 requests
  • *
  • Clean Interrupt-based State Changes
  • */ #pragma interrupt TxMode // save=PROD void TxMode(void) { volatile char dummy; PIR1bits.RCIF = 0; /// Reset so no more interrupts TRISB = 0x01; /// Configure PORTB for Transmit Mode GetStoreData(); /// Get data from external USART source NextState = TX_STATE; /// Update State Machine ChangeState(); Delay100TCYx(250); /// Wait a few hundred ms before transmitting so the amplifier and transmitter /// can stabilize if ( totalbytes > 3 ) { PORTBbits.RB5 = 1; /// Turn on Transmit LED CalculateCRC(); /// Calculate CRC from data PORTBbits.RB5 = 0; /// Turn off Transmit LED TxPacket(); /// Transmit AX25 packet with bitstuffing and NRZI PORTBbits.RB5 = 0; /// Turn off Transmit LED } NextState = RX_STATE; /// Return State Machine to Receive Mode ChangeState(); WriteToCC1020Register(CC1020_MAIN,0x01); /// Make sure that the CC1020 is in receive mode per some earlier bugs TRISB = 0x03; /// Configure PORTB for Receive Mode PIR1bits.RCIF = 0; /// Reset so no more interrupts dummy = RCREG; /// This read allows the RCIF flag to stay 0 so no more interrupts occur. } // TNC Modes /** *

    RxMode is another deprecated method as the Current State Machine method from this firmware's original intention has * been made null by requests for handshaking by those who did not understand the interrupt methods used which worked flawlessly.

    * *

    The current handshaking method is a hodge-podge and should be integrated into State Machine Form. There is no reason so far * that would cause to be changed in the handshaking scenario, it is just simply not called in the current form of this firmware.

    * * @todo Restore this method to it's former glory and perhaps rename to a more appropriate naming convention. */ void RxMode(void) { #ifdef DEBUG PORTBbits.RB7 = 1; Delay10KTCYx(100); PORTBbits.RB7 = 0; Delay10KTCYx(100); #endif TRISB = 0x03; RxPacket(); if ( (totalbytes > 22) || (totalbytes < 150) ) // if packet byte length not over 22, then ignore { // if larger than 150, then ignore PORTBbits.RB7 = 1; CalculateCRC(); OutputUSART(); PORTBbits.RB7 = 0; } } /** *

    The receive function. Listens to the CC1020 data bus until a flag is received. After a flag is received all following * bytes of data are stored until another flag is received.

    * *

    While it is currently commented out, it may be beneficial to also look for a preamble.

    * * @todo
    1. Possibly uncomment or make preamble check preprocessor checked for inclusion
    2. *
    3. Handle multiple consecutive flags (you know, like a real TNC)
    4. *
    5. Add a timeout so we don't sit here for ages
    6. *
    7. Figure out how to fit this in with handshaking
    8. */ void RxPacket(void) { int i; char byte = 0x00; char byteA; volatile char DIN, A, B, Y; ONEScount = 0; lastBit = 0; //#ifdef PREAMBLE_CHK //take NRZI decoding out first // // Check for preamble ------------------------------------------------------------------ // while ( (byte != 0x55) && (byte != 0xAA) ) // Check for flag // { // byte = byte >> 1; // shift byte 1 bit to the right inserting a 0 as MSB // // while( DCLK == 0 ) // wait for CC1020 clock to rise and then // if( DIO == 0 ) // sample nextBit from CC1020 DIO // { // byte &= 0x7F; // } // else // { // byte |= 0x80; // } // // End preamble check -------------------------------------------------------------- //#endif // Check for flag ------------------------------------------------------------------ while ( byte != 0x7e ) // Check for flag { A = LFSR & 0x01; B = (LFSR >> 5) & 0x01; Y = A ^ B; byte = byte >> 1; // shift byte 1 bit to the right inserting a 0 as MSB while( DCLK == 0 ); // wait for CC1020 clock to rise and then Delay10TCYx(2); // wait for databit to stable DIN = DIO; // sample nextBit from CC1020 DIO nextBit = DIN ^ Y; if ( nextBit == lastBit ) // if no change, then bit is a 1 using NRZI { byte = byte | 0x80; // insert a 1 as the MSB by ORing 'byte' with 0x80 } else // otherwise, bit is a 0 using NRZI { byte &= 0x7f; // insert a 0 as the MSB by ANDing 'byte' with 0x7F } lastBit = nextBit; // change the value of lastBit for next NRZI comparison LFSR >>=1; if (DIN) LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle } // End flag check ------------------------------------------------------------------ PORTBbits.RB6 = 1; /** NEW WAY **/ // After we find a flag we are synched up byte-wise and can start ignoring subsequent flags until a non-flag byte is reached // Once a non-flag byte is reached the bytes are then stored in the data[] array. while (byte == 0x7e) { byte = RxByte(); } data[0] = byte; totalbytes = 1; while ( byte != 0x7e ) // Start reading bytes until flag is reached { byte = RxByte(); if ( byte != 0x7e) { data[totalbytes] = byte; //totalbytes++; // This should be used, but need to modify functions that look at CRC } totalbytes++; if ( totalbytes > (128 + 17 + 2) ) { break; } } /** END NEW WAY **/ /** OLD WAY **/ /* // Receive bytes until the end flag is reached ------------------------------------- // Note: This breaks if more than one consecutive flag is being sent. This can be // fixed later if need be. i = 0; byte = 0x00; // reset byte so it is no longer 0x7e totalbytes = 0; while ( byte != 0x7e ) // Start reading bytes until flag is reached { byte = RxByte(); if ( byte != 0x7e) { data[i] = byte; } i = i + 1; totalbytes = i; }*/ /** END OLD WAY **/ } // End void RxPacket(void) /** * Receives a byte from the CC1020, descrambling, decoding NRZI and undoing bit-stuffing done on the fly. Returns the byte to calling function, * typically RxPacket(); * * @see RxPacket() * * Theory of operation. *
        *
      1. Shift byte right one bit
      2. *
      3. Read incoming bit
      4. *
      5. Unscramble bit
      6. *
      7. Decode NRZI Encoding
      8. *
      9. Place decoded bit in MSB of byte
      10. *
      11. If five(5) successive ones *
        1. *
        2. Check next bit (i.e., unscramble & NRZI decode)
        3. *
        4. If zero, then ignore since it was bitstuffed
        5. *
        6. If one, then flag has been reached so return flag
        7. *
      12. *
      13. If byte is full, return byte
      14. *
      15. Else goto step one
      16. * * @return Received byte decoded appropriately */ char RxByte(void) { int i; char byte = 0x00; volatile char DIN, A, B, Y; for ( i = 0; i < 8; i++ ) // for all 8 bits { A = LFSR & 0x01; B = (LFSR >> 5) & 0x01; Y = A ^ B; byte = byte >> 1; // shift the byte over to the right inserting a 0 for MSB while( DCLK == 0 ); // wait for CC1020 clock to rise and then DIN = DIO; // sample nextBit from CC1020 DIO nextBit = DIN ^ Y; if ( nextBit != lastBit ) { byte &= 0x7f; // zero out MSB ONEScount = 0; // reset ONEScount } else if ( nextBit == lastBit ) // then no change so one in NRZI { byte = byte | 0x80; // MSB when shifting right ONEScount = ONEScount + 1; } lastBit = nextBit; LFSR >>=1; /// Added this because I think it was a stupid mistake not to include it if (DIN) LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle //---------------------------------------------------------------------------------------- // if there have been 5 ones and the next bit is a zero (change in bit stream) // then remove the bitstuffed zero. if ( ONEScount >= 5 ) { A = LFSR & 0x01; B = (LFSR >> 5) & 0x01; Y = A ^ B; // Check the next bit to see if it is a bitstuffed zero; if it is not then it // is probably the flag while( DCLK == 0 ); // wait for CC1020 clock to rise and then DIN = DIO; // sample nextBit from CC1020 DIO nextBit = DIN ^ Y; if (nextBit != lastBit) // then zero needs to be skipped { /// Then the next bit was a bitstuffed zero and we can ignore lastBit = nextBit; // fix lastBit for NRZI ONEScount = 0; // reset ONEScount LFSR >>=1; /// Added this because I think it was a stupid mistake not to include it if (DIN) /// perhaps this should be nextBit LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle } else // otherwise, flag byte has been encountered { ONEScount = 0; // Reset ones since we're assuming a flag has been received return 0x7e; // If we have 6 ones then it is either the flag // or an error. Either way we trick TxPacket into // thinking it is an end flag by setting it as 0x7e & } } } return byte; } /** * The main AX.25 packet encoding function. Takes data field from the data[] array, header information and adds frame delimiters to compose AX.25 UI frames. * * @see TxByte() * */ void TxPacket(void) { int i; lastBit = 0; // since we force the last bit of a packet to be 0, then we need to declare the last bit 0 for NRZI to work // Transmit a preamble to synch the transceiver for ( i = 0; i < 8; i++ ) { TxByte(0x00); } // Transmit the flag byte isFlag = 1; TxByte(flagbyte); // transmit START flag isFlag = 0; // Transmit the header bytes for (i=0; i<17; i++) // for all header bytes in an array { TxByte(header[i]); // transmit header data } // Transmit the data (information) bytes for(i=0; i < totalbytes; i++) // for all information bytes in an array { TxByte(data[i]); // transmit information data } // Transmit CRC bytes TxByte(crcHIGH); // transmit CRC (FCS) TxByte(crcLOW); // Transmit the flag byte isFlag = 1; TxByte(flagbyte); // transmit STOP flag // TxByte(flagbyte); // transmit START flag isFlag = 0; DIO = 0; // force the last bit to be 0 } /** * Transmit one byte to the CC1020 including bitstuffing, NRZI encoding and scrambling. We can do this byte by byte because ONEScount is global, * otherwise it would have to be returned to the calling function Note: during the transmission of the flag we ignore bitstuffing and do not reset * ONEScount after 5 ones. This means that ONEScount will reach a value of 6 during the flags. This is okay though because the flag is 01111110, * so the ONEScount will be reset on the last 0 and therefore not affect the rest of the packet's bitstuffing. * * @see TxPacket() */ void TxByte(char byte) { volatile char DIN, DOUT, A, B, Y; volatile char mask; volatile int i; mask = 0x01; for(i=0; i < 8; i++) // for all databtes in an array { A = (LFSR & 0x000001); // A = bit 17 of the LFSR B = ((LFSR >> 5) & 0x01); // B = bit 12 of the LFSR Y = A ^ B; // Just typical Boolean XORing here //Transmit bit -------------------------------------------------------------------------- if( (byte & mask ) != mask ) // if bit is a zero, then { while( DCLK == 0 ); // wait for CC1020 clock to rise and then if (lastBit == 0) { DIO = 1 ^ Y; // transmit a zero Y ^= 1; lastBit = 1; // update lastBit } else if (lastBit == 1) { DIO = 0 ^ Y; // transmit a one Y ^= 0; lastBit = 0; // update lastBit } ONEScount = 0; // reset ONES count LFSR >>= 1; if (Y) LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle } else //( (temp & mask) == mask ) // if bit is a one, then { while( DCLK == 0 ); // wait for CC1020 clock to rise and then // Transmit a one. In NRZI a one alternates high and low for a "one" and is low // for a zero if (lastBit == 0) // if the last bit transmitted was a zero { DIO = 0 ^ Y; // then signal is high for a one Y ^= 0; lastBit = 0; // update lastBit } else if (lastBit == 1) // if the last bit transmitted was a one { DIO = 1 ^ Y; // then signal is low for a one Y ^= 1; lastBit = 1; // update lastBit } ONEScount++; // increment ONES count LFSR >>= 1; if (Y) LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle } // End of Transmit bit ------------------------------------------------------------------- mask = mask << 1; // move bit mask to the next significant bit if( (ONEScount >= 5) && (isFlag != 1) ) // if more than 5 ones, then { A = (LFSR & 0x000001); // A = bit 17 of the LFSR B = ((LFSR >> 5) & 0x01); // B = bit 12 of the LFSR Y = A ^ B; // Just typical Boolean XORing here while( DCLK == 0 ); // wait for CC1020 clock to rise and then if (lastBit == 0) { DIO = 1 ^ Y; // transmit a zero Y ^= 1; lastBit = 1; // update lastBit } else if (lastBit == 1) { DIO = 0 ^ Y; // transmit a one Y ^= 0; lastBit = 0; // update lastBit } ONEScount = 0; // reset the ONES count LFSR >>= 1; if (Y) LFSR |= 0x010000; while( DCLK == 1 ); // wait for next clock cycle } } } // Error Detection Function /** * Calculates the 16 bit CRC checksum for error detection. The polynomial used is x^16 + x^12 + x^5 + 1. This function calculates the * CRC kind of backlwards, but doing so creates the CRC with the proper endianess, since the CRC is transmitted MSB first whereas the rest * of the AX.25 packet is transmitted LSB first. Calculating the CRC this way allows us to pass the calculated values directly to TxByte. */ void CalculateCRC(void) { // Create the CRC 16 bit (2 byte) number. 8408 represents the CRC-16 CCITT polynomial used. // The polynomial used is x^16 + x^12 + x^5 + 1. volatile char temp; // temporary place to store data volatile char mask; volatile int crc, i, j, k; crc = 0xFFFF; // CRC-16 must start with all 1's if (State == TX_STATE) { // Create CRC from header array for(i=0; i < 17; i++) // for all header bytes { mask = 0x01; // create mask to determine the bit value (0000 0001) temp = header[i]; // temporary store data for ( j = 0; j < 8; j++) // For every bit in a byte { crc = crc ^ (temp & 0x01); // XOR the LSB of 'temp' with CRC if (crc & 0x01) // if bit is 1, then { crc = (crc >> 1) ^ 0x8408; // shift CRC to the right once, and XOR with 0x8408 } else // otherwise, bit is 0 { crc = crc >> 1; // then shift CRC to the right once } temp = temp >> 1; // when done, shift 'temp' to the right once } } k = totalbytes; } else k = totalbytes - 3; // Create CRC from data array for(i=0; i < k; i++) // Don't CRC the CRC or end flag of the incoming packet { mask = 0x01; // create mask to determine the bit value (0000 0001) temp = data[i]; // temporary store data for ( j = 0; j < 8; j++) // For every bit in a byte { crc = crc ^ (temp & 0x01); // XOR the LSB of 'temp' with CRC if (crc & 0x01) // if bit is 1, then { crc = (crc >> 1) ^ 0x8408; // shift CRC to the right once, and XOR with 0x8408 } else // otherwise, bit is 0 { crc = crc >> 1; // then shift CRC o the right once } temp = temp >> 1; // when done, shift 'temp' to the right once } } crc = crc ^ 0xffff; // final step is to invert the CRC by XOR with FFFF (1111 1111) // Break the 16 bit CRC into HIGH and LOW 8 bit bytes for transmission. These bytes are flipped. crcHIGH = crc & 0xFF; // move low bits to crcHIGH crcLOW = (crc >> 8) & 0xFF; // move high bits to crcLOW } /** @} */ /** * @defgroup UART Serial Data Bus Communications * * Manages communications over the UART bus between the OBC and Communications processors. * * @todo Determine the usefulness of these functions. * * @{ */ /** * Get data from the OBC until a carriage return is received and store the data in the data[] array. * Also updates the totalbytes counter. */ void GetStoreData(void) { volatile char temp; // create temp location to store incoming databyte char count; // create count location to store number of databytes count = 0; temp = 0; while(temp != 0x0D) // repeat until carriage return appears { PORTEbits.RE2 = 1; while( !DataRdyUSART() ); // while there is no data received in the buffer, wait temp = ReadUSART(); // get databyte from buffer and store in temp data[count] = temp; // store data in array count = count + 1; // increment the 'count' for the next byte if(count >= 120) // if more than 128 charachters are inputed, then automatically temp = 0x0D; // return, package, and transmit #ifdef DEBUG if (temp == 0x06) { TxHighPower30sec(); count = 1; break; } if (temp == 0x07) { TxLowPower30sec(); count =1; break; } #endif } // once carriage return appears, no more data is accepted // and the packet building and transmission can begin totalbytes = count - 1; // the total number of bytes are transferred from 'count' // minus 1 is so the carriage return is not transmitted PORTEbits.RE2 = 0; } /** * Check to see if CRC matches and transmit data over USART if possible to the OBC or PC terminal */ void OutputUSART(void) { int i; if( totalbytes < 2) // If the number of bytes to transmit is less than 2 return; // then return // The total number of bytes in a command must be at least 3 CalculateCRC(); for(i=17; i < (totalbytes-3) ; i++) // For all the bytes of information, excluding address and CRC data bytes { while( BusyUSART() ); WriteUSART( data[i] ); // Output all the information data to the OBC or PC } printf( (char*)" \n\r Transmitted CRC value is : %02X%02X \n\r", data[totalbytes-3], data[totalbytes-2]); printf( (char*)" CRC value is : %02X%02X \n\n\r", (char)crcHIGH, (char)crcLOW); // WriteUSART( 0x0D ); // Since the OBC is waiting for a CR before processing, then send the CR // while( BusyUSART()); // WriteUSART( 0x0A ); } /** @} */ void TxCW(void) { int x; unsigned long codeword; char character; char callsign[5] = {'K','5','U','S','L'}; for (x=0;x<5;x++) { character = callsign[x]; if ('a' <= character && character <= 'z') // Tableless "toupper()" character -= ('a'-'A'); if (' ' <= character && character <= 'Z') // If in Code[] table, codeword = Code[character-' ']; // convert it to Morse. else codeword = 0; // If not, it's not Morse codeword = Code[callsign[x] - ' ']; while (codeword > 1) { if (codeword & 1) { PTT = 1; PORTEbits.RE2 = 1; } else { PTT = 0; PORTEbits.RE2 = 0; } Delay10KTCYx(DIT); codeword >>= 1; } PTT = 0; PORTEbits.RE2 = 0; Delay10KTCYx(DIT); } for (x=0;x 1) { if (codeword & 1) { PTT = 1; PORTEbits.RE2 = 1; } else { PTT = 0; PORTEbits.RE2 = 0; } Delay10KTCYx(DIT); codeword >>= 1; } PTT = 0; PORTEbits.RE2 = 0; Delay10KTCYx(DIT); } } void TxLowPower(void) { GetStoreData(); // Get data from external USART source TX_ON = 1; // Turn on PA Delay100TCYx(250); // 10ms delay TXCTRL = 1; RXCTRL = 0; NextState = TX_STATE; ChangeState(); WriteToCC1020Register(CC1020_PA_POWER, PA_POWER_n10_DBM); PWRCTRL = 0; // Low power Delay100TCYx(250); // 10ms delay PORTEbits.RE0 = 1; PORTEbits.RE1 = 0; TxCW(); // Turn off transmitter PTT = 0; TX_ON = 0; TXCTRL = 0; RXCTRL = 1; PORTEbits.RE0 = 0; PORTEbits.RE1 = 0; NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,0x01); TRISB = 0x03; // Configure PORTB for Receive Mode return; } void TxHighPower(void) { int i; TRISB = 0x01; // Configure PORTB for Transmit Mode GetStoreData(); // Get data from external USART source PORTEbits.RE0 = 0; PORTEbits.RE1 = 1; TX_ON = 1; // Turn on PA Delay100TCYx(250); // 10ms delay TXCTRL = 1; RXCTRL = 0; NextState = TX_STATE; ChangeState(); WriteToCC1020Register(CC1020_PA_POWER, PA_POWER_0_DBM); PWRCTRL = 1; // High power Delay100TCYx(250); // 10ms delay PTT = 1; Delay100TCYx(250); // 10ms delay // Transmit Data if ( totalbytes > 3 ) { CalculateCRC(); // Calculate CRC from data // for (i=0;i<16;i++){ // while (DCLK == 0); // DIO = 1; // while (DCLK == 1); // while (DCLK == 0); // DIO = 0; // while (DCLK == 1); //} TxPacket(); // Transmit AX25 packet with bitstuffing and NRZI } // Turn off transmitter PTT = 0; TX_ON = 0; PWRCTRL = 0; // Low power TXCTRL = 0; RXCTRL = 1; PORTEbits.RE0 = 0; PORTEbits.RE1 = 0; NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,0x01); TRISB = 0x03; // Configure PORTB for Receive Mode return; } /** @} */ void TxHighPower30sec(void) { int i, j; TRISB = 0x01; // Configure PORTB for Transmit Mode GetStoreData(); // Get data from external USART source PORTEbits.RE0 = 0; PORTEbits.RE1 = 1; TX_ON = 1; // Turn on PA Delay100TCYx(250); // 10ms delay TXCTRL = 1; RXCTRL = 0; NextState = TX_STATE; ChangeState(); WriteToCC1020Register(CC1020_PA_POWER, PA_POWER_0_DBM); PWRCTRL = 1; // High power Delay100TCYx(250); // 10ms delay PTT = 1; Delay10KTCYx(250); // 100ms delay // Transmit Data data[0] = (unsigned char)"Welcome to the CAPE1 CubeSat."; totalbytes = 30; CalculateCRC(); // Calculate CRC from data for (i=0;i<100;i++) { // for (i=0;i<16;i++) // { // while (DCLK == 0); // DIO = 1; // while (DCLK == 1); // while (DCLK == 0); // DIO = 0; // while (DCLK == 1); // } TxPacket(); // Transmit AX25 packet with bitstuffing and NRZI } // Turn off transmitter PTT = 0; TX_ON = 0; PWRCTRL = 0; // Low power TXCTRL = 0; RXCTRL = 1; PORTEbits.RE0 = 0; PORTEbits.RE1 = 0; NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,0x01); TRISB = 0x03; // Configure PORTB for Receive Mode return; } void TxLowPower30sec(void) { TX_ON = 1; // Turn on PA Delay100TCYx(250); // 10ms delay TXCTRL = 1; RXCTRL = 0; NextState = TX_STATE; ChangeState(); WriteToCC1020Register(CC1020_PA_POWER, PA_POWER_n10_DBM); PWRCTRL = 0; // Low power Delay100TCYx(250); // 10ms delay PORTEbits.RE0 = 1; PORTEbits.RE1 = 0; data[0]=(unsigned char)"Hello CAPE1"; totalbytes = 11; TxCW(); // Turn off transmitter PTT = 0; TX_ON = 0; TXCTRL = 0; RXCTRL = 1; PORTEbits.RE0 = 0; PORTEbits.RE1 = 0; NextState = RX_STATE; ChangeState(); WriteToCC1020Register(CC1020_MAIN,0x01); TRISB = 0x03; // Configure PORTB for Receive Mode return; }