/***********************************************************************/ /* */ /* XModem CRC Implementation for PIC16F8xx */ /* */ /* By: J. Winpenny */ /* */ /* Date: 28/11/1999 */ /* */ /* Fuction: */ /* */ /* Sends a series of blocks of a file */ /* to a Terminal Program running an XModem CRC Protocol. */ /* */ /* This is a test version, it 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, 128 byte blocks */ /* */ /* 9600 Baud, 8 bits, No parity, 1 stop */ /* */ /* Notes: Tested on Windows 98 HyperTerminal */ /* Compiled with C2C Version 3.3.1 beta */ /* Updated 02/01/2000 */ /***********************************************************************/ // Comms control characters #define SOH 0x01 #define STX 0x02 #define ETX 0x03 #define EOT 0x04 #define ENQ 0x05 #define ACK 0x06 #define NAK 0x15 #define ETB 0x17 #define CAN 0x18 #define NUL 0x00 // Xmodem state machine, states #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 // 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 char ElapsedTime; // Elapsed time in tenths of seconds // Bits of XModemFlags #define RxCharReady 7 // Set when a character is ready. #define RxCharReadyMask 0x80 #define BufferReady 0 #define BufferReadyMask 1 #define SendMode 2 // Set when Xmodem in send mode. #define EndOfTx 3 // Set when no more blocks to send. #define EndOfRx 4 // Not yet used. #define CRC_MODE 5 // Set when in CRC mode clear for checksum. // Checksum is not yet implemented - // as it's not needed for most terminals. // Bit masks for XModemFlags #define EndOfTxMask 0x08 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 *ptr); // Send a string void XmodemSend(void); // The XModem Send State Machine char StartXModemTx(void); // Start a new transfer void AssemblePacket(void); // Assemble a packet void SendPacket(void); // Send the packet void SendChar(char ch); // Send a character // 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; // 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 // LED Output bits( Application specific ) #define LED_1 0 #define RADIO_ENABLE 0 #define LED_2 1 #define RS232_ENABLE 1 // Messages const char *Msg1 = "XModem Test"; const char *OkMsg = "Transfer Ok"; const char *TimeoutMsg = "Timeout"; const char *AbortMsg = "Aborted"; // Rename a function. #define lcd LCD_Write_4_Bit(char); #include "lcd.c" // Include the LCD interface code void main(void) { char c; 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 // Send sign on message to terminal and LCD SendChar('\r'); // Send carage return SendChar('\n'); // and line feed SendString(Msg1); // Send the sign on string SendChar('\r'); SendChar('\n'); LCD_Line_1(); // Top line of LCD WriteLCDString(Msg1); LCD_Line_2(); // Fill the Xmodem block buffer with dummy data. // If sending a large amount of data from perhaps // an EEPROM, then this will be implemented in // the AssemblePacket function c = '0'; for ( i=0;i<48;i++) // Fill the first buffer { buffer[i] = c; if ( c++ == '9' ) c = '0'; } c = 'a'; for ( i=0;i<80;i++) // Fill the second buffer { buffer2[i] = c; if ( c++ == 'z' ) c = 'a'; } // Start the Xmodem Protocol // Note: *** The Sending device should be started first *** c = StartXModemTx(); switch(c) { case IDLE: SendChar('\r'); // Good transfer. SendChar('\n'); SendString(OkMsg); LCD_Line_2(); WriteLCDString(OkMsg); break; case TIMEOUT: // Transfer timed out. SendChar('\r'); SendChar('\n'); SendString(TimeoutMsg); LCD_Line_2(); WriteLCDString(TimeoutMsg); break; case ABORTED: // Transfer aborted. SendChar('\r'); SendChar('\n'); SendString(AbortMsg); LCD_Line_2(); WriteLCDString(AbortMsg); break; } while(1) { // Wait here a while ( Until Reset ) } } /**********************************/ /* Interrupt Service Routine */ /**********************************/ void interrupt(void) { if ( ( PIR1 & RCIF_MASK ) != 0 ) // If USART RX Interrupt { RxChar(); // Process the received character 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 ) set_bit( TXSTA, BRGH ); // RRGH = 1 ( High 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 *ptr) { char s = 0; // Check for end of string while( ptr[s] != 0 ) { SendChar( ptr[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 set_bit( XModemFlags, BufferReady ); set_bit( RCSTA, CREN ); // Enable receiver. // Enter the XModem state machine set_bit( XModemFlags, RxCharReady ); // Save status } 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 second part of the buffer for( i=0; i