#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>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 /** *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: *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