/***********************************************************************/ /* */ /* XModem CRC Implementation for PIC16F8xx */ /* */ /* By J. Winpenny */ /* */ /* Date: 02/01/2000 */ /* */ /* Fuction: */ /* */ /* Sends or Receives a series of blocks of a file */ /* to a Terminal Program running an XModem CRC Protocol. */ /* */ /* This is a test version only and repeats the same block */ /* stored in the buffer. */ /* Later I shall add a routine to load the buffer */ /* one block at a time from an EEPROM device. */ /*---------------------------------------------------------------------*/ /* */ /* Clock Frequency = 3.579545Mhz */ /* */ /* CRC ( 16 bit ) mode, Checksum ( 8 bit ) mode, 128 byte blocks */ /* */ /* 9600 Baud, 8 bits, No parity, 1 stop */ /* */ /* Notes: Tested on Windows 98 HyperTerminal */ /* Compiled with C2C Version 3.3.4 beta */ /* */ /***********************************************************************/ #pragma CLOCK_FREQ 3579545 // Processor clock frequency in Hz. // Default value for PIC is 4000000 (4MHz) // and for SX 50000000 (50MHz). // Comms control characters #define SOH 0x01 #define STX 0x02 #define ETX 0x03 #define EOT 0x04 #define ENQ 0x05 #define EOF 0x1A #define ACK 0x06 #define NAK 0x15 #define ETB 0x17 #define CAN 0x18 #define NUL 0x00 // Xmodem state machine, states Send Mode #define IDLE 0 #define WAIT_START 1 #define START 2 #define NEXT_PACKET 3 #define RESEND 4 #define WAIT_ACK 5 #define ABORTED 6 #define TIMEOUT 7 // Xmodem state machine, states Receive Mode #define TRY_CRC 12 #define GET_SOH 13 #define GET_BLKNUM 14 #define GET_NOTBLKNUM 15 #define GET_PACKET 16 #define RE_GET_PACKET 17 #define GET_HIGHCRC 18 #define GET_LOWCRC 19 #define TRY_CHECKSUM 20 // USART Register bits #define CSCR 7 #define TX9 6 #define TXEN 5 #define SYNC 4 #define BRGH 2 #define TRMT 1 #define TX9D 0 #define SPEN 7 #define RX9 6 #define SREN 5 #define CREN 4 #define ADDEN 3 #define FERR 2 #define OERR 1 #define RX9D 0 // SendChar defines #define TRMT_MASK 2 // Masks for PIR1 #define PSPIF_MASK 0x80 #define ADIF_MASK 0x40 #define RCIF_MASK 0x20 #define TXIF_MASK 0x10 // Registers for I2C char SSPSTAT@0x94; // Bank 1 char SSPCON@0x14; // Bank 0 char SSPCON2@0x91; // Bank 1 char SSPBUF@0x13; // I2C Buffer char SSPADD@0x93; // I2C Slave Address register // Bits of SSPSTAT #define SMP 7 #define CKE 6 #define D_A 5 #define P 4 #define S 3 #define R_W 2 #define R_W_MASK 0x04 #define UA 1 #define BF 0 // Bits of SSPCON2 #define GCEN 7 #define ACKSTAT 6 #define ACKDT 5 #define ACKEN 4 #define RCEN 3 #define PEN 2 #define RSEN 1 #define SEN 0 // Bits of PIR1 #define PSPIF 7 #define ADIF 6 #define RCIF 5 #define TXIF 4 #define SSPIF 3 #define SSPIF_MASK 0x08 #define CCP1IF 2 #define TMR2IF 1 #define TMR1IF 0 // Bits of SSPCON #define WCOL 7 #define SSPOV 6 #define SSPEN 5 #define CKP 4 #define SSPM3 3 #define SSPM2 2 #define SSPM1 1 #define SSPM0 0 // Port addresses char PORTC@0x07; char PORTD@0x08; char PORTE@0x09; // USART Registers char TXREG@0x19; char RCREG@0x1a; char TXSTA@0x98; char RCSTA@0x18; char SPBRG@0x99; // Extra Ports on PIC16F877 char TRISC@0x87; char TRISD@0x88; char TRISE@0x89; // Other PIC registers char PIE1@0x8c; char PIE2@0x8d; char PIR1@0x0c; char PIR2@0x0d; char PCON@0x8e; // ADC bits char ADCON0@0x1f; char ADCON1@0x9f; // Registers char Temp; // All temps used in CRC calculation char Temp2; char Temp3; char Temp4; char Temp5; // .................................. char state; // State of the Xmodem state machine char Retrys; // Retry counter char XModemFlags; // Flags for state machine int ElapsedTime; // Elapsed time in tenths of seconds char BufPtr; // Bits of XModemFlags #define BufferReady 0 #define SendMode 2 // Set when Xmodem in send mode. #define EndOfTx 3 // Set when no more blocks to send. #define buf2 4 // Set after 48 bytes of block received. // so received data goes into second buffer #define CrcMode 5 // Set when in CRC mode clear for checksum. // Checksum is not yet implemented - // as it's not needed for most terminals. #define RxStarted 6 #define RxCharReady 7 // Set when a character is ready. // Bit masks for XModemFlags #define RxCharReadyMask 0x80 #define RxStartedMask 0x40 #define CrcModeMask 0x20 #define buf2Mask 0x10 #define EndOfTxMask 0x08 #define BufferReadyMask 1 void ClearCrc(void); // Clear CRC void UpdateCrc(char); // Update crc with data void FinishCrc(void); // Finish the CRC calculation char RxChar(void); // Receive a character when RX Interrupt occurs void Setup(void); // Setup the PIC void ConfigureComms(void); // Configure the USART void SendString(const char *); // Send a string void XmodemSend(void); // The XModemSend Send state - machine char StartXModemTx(void); // Start a new transfer ( Sending ) void AssemblePacket(void); // Assemble a packet void SendPacket(void); // Send the packet void SendChar(char); // Send a character char StartXModemRx(void); // Start a new transfer ( Receiving ) void XmodemReceive(void); // The XModemSend Receive state - machine char CheckPacket(void); // Checks received packets // Return types of CheckPacket() #define OK 0 #define BAD_BLKNUM 1 #define BAD_CRC 2 // Have to split the buffer into two parts // because there isn't enough contigous ram // for 128 bytes in a single memory page. // May implement this as a structure. char sohP; // The start of header character char blknum; // Storage for block number char notblknum; // Complement of block number char buffer[48]; // First half of the 128 byte packet buffer char buffer2[80]; // Second half of the 128 byte packet buffer char CrcLowByte; // Storage for CRC char CrcHighByte; // - sent or calculated during receive char RxCrcLowByte; // Received CRC char RxCrcHighByte;// """""""""""" // Size of buffers ( Totals 128 bytes ) #define buffer_size 48 #define buffer2_size 80 // General purpose counter etc. char i; // PORT Configuration #define PortAConfig 0x00 #define PortBConfig 0xf0 #define PortCConfig 0x98 /* SCL & SDA as Inputs */ #define PortDConfig 0x00 #define PortEConfig 0x00 // EEPROM device E2 addresses ( Application specific ) #define E2Write 0xA0 // M24128 memory write address #define E2Read 0xA1 // M24128 memory read address // LED Output bits ( Application specific ) #define LED_1 0 #define RADIO_ENABLE 0 #define LED_2 1 #define RS232_ENABLE 1 // DIP Switches ( Application specific ) #define DIP_4 0x80 #define DIP_3 0x40 #define DIP_2 0x20 #define DIP_1 0x10 #include "lcd.c" // Include the LCD interface code const char *SignOn = "Start receiver\r\n"; const char *MsgTx = "XModem Tx Test"; const char *MsgRx = "XModem Rx Test"; const char *MsgBadCrc = "Bad CRC"; const char *MsgBadBlockNumber = "Bad Block Number"; const char *MsgAck = "Ack"; void main(void) { char c; XModemFlags = 0; Setup(); // Setup the PIC Hardware etc. /**********************************/ /* My board has dual RS232 inputs */ /* that are selected here */ /* */ output_high_port_a( RS232_ENABLE ); /* Enable RS232 Input to USART */ output_low_port_a( RADIO_ENABLE ); /* Disable RADIO Input to USART */ /**********************************/ state = IDLE; // Reset the state machine LCD_Clear(); // Clear LCD // Note: The Sending device should be started first // Start in Transmit or receive mode depending on the state of a DIP Switch. if ( PORTB & DIP_1 ) { WriteLCDString(MsgRx); delay_s(5); c = StartXModemRx(); // XModem Receive } else { WriteLCDString(MsgTx); delay_s(5); c = StartXModemTx(); // XModem Transmit } switch(c) { case IDLE: LCDClearLine2(); // Transfer Ok. LCD_Line_2(); WriteLCDString("Transfer Ok"); break; case TIMEOUT: LCDClearLine2(); // Transfer timed out. LCD_Line_2(); WriteLCDString("Timeout"); break; case ABORTED: LCDClearLine2(); // Transfer aborted. LCD_Line_2(); WriteLCDString("Aborted"); break; } while(1); // Wait for PIC to be RESET........... } /**********************************/ /* Interrupt Service Routine */ /**********************************/ void interrupt(void) { if ( PIR1 & RCIF_MASK ) // If USART RX Interrupt { RxChar(); // Process the received character clear_wdt(); clear_bit( PIR1, RCIF ); // Clear flag } } /*****************************************************/ /* setup PIC16F877 options,ports,interrupts */ /*****************************************************/ void Setup(void) { INTCON = 0x00; set_bit( INTCON, GIE ); // Enable Global Interrupts set_bit( INTCON, PEIE ); // Enable all Peripheral Interrupts OPTION_REG = 0x0E; // Set Option register // Prescaler = WDT // 0x0C = WDT rate := 1:16 // 0x0E = WDT rate := 1:64 TRISD = PortDConfig; ADCON1 = 0x7f; // Disable ADC //PCON = 0x03; // Reset Power up status flags TRISA = PortAConfig; TRISB = PortBConfig; TRISC = PortCConfig; TRISE = PortEConfig; PIR1 = 0; LCD_Setup(); ConfigureComms(); // Configure USART for Asyncronous Comms } /*******************************************************/ /* Configure USART for communications */ /* */ /* Asynchronous mode */ /* 19,200 Baud ( With 3.579545 Mhz Clock ) */ /* 8 data bits ( For other rates see PIC16F8XX Data ) */ /* 1 stop bit */ /* No Parity */ /* */ /*******************************************************/ void ConfigureComms(void) { set_bit( RCSTA, SPEN ); // Enable Serial port clear_bit( RCSTA, RX9 ); // 8 bit receive mode clear_bit( TXSTA, TX9 ); // 8 bit transmit mode // Values for a clock frequency of 3.579545Mhz // for other clock frequency values see the microchip data. // SPBRG = 0; // SPBRG = 1 ( Set Baud rate 115,200 ) // SPBRG = 5; // SPBRG = 5 ( Set Baud rate 38,400 ) // SPBRG = 22; // SPBRG = 22 ( Set Baud rate 9,600 ) // SPBRG = 11; // SPBRG = 11 ( Set Baud rate 19,200 ) SPBRG = 22; // SPBRG = 22 ( Set Baud rate 9,600 ) // SPBRG = 185; // SPBRG = 185 with BRGH = 0 = 300 Baud // SPBRG = 46; // SPBRG = 46 with BRGH = 0 = 1200 Baud set_bit( TXSTA, BRGH ); // BRGH = 1 ( High speed mode ) //clear_bit( TXSTA, BRGH ); // BRGH = 0 ( Low speed mode ) clear_bit( TXSTA, SYNC ); // Asyncronous mode; set_bit( TXSTA, TXEN ); // Enable Transmitter set_bit( PIE1, RCIE ); // Enable Receive Interrupt set_bit( RCSTA, CREN ); // Enable continuous receive clear_bit( PIR1, RCIF ); // Clear Receive Interrupt flag set_bit( INTCON, PEIE ); // Enable all Peripheral Interrupts set_bit( INTCON, GIE ); // Enable Global Interrupts } /****************************************/ /* Send s const string */ /****************************************/ void SendString(const char *p) { char s = 0; // Check for end of string while( p[s] != 0 ) { SendChar( p[s++] ); } } /*****************************************************/ /* Receive a character over the RS232 Port */ /* */ /* Called from Interrupt service routine */ /*****************************************************/ char RxChar(void) { if ( ( RCSTA & 6 ) == 0 ) // Then if no errors { // Process received character // Enter the XModem state machine set_bit( XModemFlags, RxCharReady ); // Save status set_bit( RCSTA, CREN ); // Enable receiver. } else { clear_bit( RCSTA, CREN ); // Clear any errors set_bit( RCSTA, CREN ); // Enable receiver. } return RCREG; } /**************************************************************************/ /* Update CRC */ /* */ /* */ /* Cyclic Redundancy Code Subroutines. Version 1.3 */ /* ( Recoded for PIC ) */ /* */ /* These subroutines will compute and check a true 16-bit */ /* Cyclic Redundancy Code for a message of arbitary length. */ /* The use of this scheme will guarantee detection off all */ /* single and double bit errors, all errors with an odd */ /* number of error bits, all burst errors of length 16 or */ /* less, 99.9969% of all 17-bit error bursts, and 99.9984% */ /* of all posible longer error bursts. */ /*( Ref: Computer Networks, Andrew S. Tanenbaum, Prentiss-Hall, 1981) */ /* */ /* The generator is X^16 + X^12 + X^5 + 1 */ /* ( as recommended by CCITT ) */ /* */ /**************************************************************************/ void UpdateCrc( char data ) { asm { movf param00_UpdateCrc, W;// Get the value to add movwf _Temp; // Save update value movlw 8; movwf _Temp5; // Set counter movf _CrcLowByte, W; movwf _Temp3; movf _CrcHighByte, W; movwf _Temp4; } asm: updloop asm { rlf _Temp, F; movf STATUS, W; // get carry flag movwf _Temp2; // save carry flag andlw 0x01; // Mask others iorwf _Temp, F; // Carry > _spare <0> movf _Temp2, W; // Restore carry flag movwf STATUS; // - in status reg rlf _Temp3, F; rlf _Temp4, F; btfss STATUS, C; goto skipit movlw 0x10; xorwf _Temp4, F; movlw 0x21; xorwf _Temp3, F; } asm: skipit asm { decfsz _Temp5, F; goto updloop movf _Temp3, W; // Updata Low CRC movwf _CrcLowByte; movf _Temp4, W; // Updata High CRC movwf _CrcHighByte; } } /***********************************************/ /* Clear the CRC for the next calculation */ /***********************************************/ void ClearCrc(void) { CrcHighByte = 0; CrcLowByte = 0; } /***********************************************/ /* Complete the CRC calculation */ /***********************************************/ void FinishCrc(void) { UpdateCrc(0); UpdateCrc(0); } /*************************************************/ /* Assemble the Packet */ /* from the 2 buffers */ /* 2 Buffers are needed because memory paging */ /* */ /* Should pad last block if less than 128 bytes */ /* Add code here to load buffer from EPROM etc */ /*************************************************/ void AssemblePacket(void) { ClearCrc(); // Clear the CRC for the next packet sohP = SOH; // Setup the start of header character notblknum = ~blknum; // Calc the complement of the // current block number. // Calculate the CRC or Checksum // for the first part of the buffer for( i=0; i 0 ) { ElapsedTime = 0; XmodemReceive(); // Sends the 'C' or NAK // If the sender responds then all systems go ! while ( ++ElapsedTime < 8000 ) { if ( XModemFlags & RxCharReadyMask ) { clear_bit( XModemFlags, RxCharReady ); state = GET_SOH; // Try to get the packet Retrys = 16; // 16 retrys at each packet XmodemReceive(); while ( !( (state==IDLE)||(state==ABORTED)||(state==TIMEOUT) ) ) { if ( XModemFlags & RxCharReadyMask ) { clear_bit( XModemFlags, RxCharReady ); XmodemReceive(); } if ( Retrys == 0 ) { state = TIMEOUT; } } return state; } delay_ms(1); } // Switching to checksum if( --Retrys == 3 ) { LCDClearLine2(); LCD_Line_2(); WriteLCDString("Trying Checksum"); clear_bit(XModemFlags, CrcMode ); state = TRY_CHECKSUM; } } state = TIMEOUT; return state; } /*************************************************/ char CheckPacket(void) { // Check complement of block number if ( notblknum != ~blknum ) { return BAD_BLKNUM; } ClearCrc(); // Clear the CRC for the packet calculation // Calculate the CRC or Checksum // for the first part of the buffer. for( i=0; i