//***************************************************************************************** // // Code for logging data directly to a USB Fash disk using a PIC16F688 and the VNC1L. // Commands received through UART. // The PIC only handles Tx and Rx automatically for the UART, so handshaking must // be implemented in code. // // Author: FTDI // // Code written for PIC16F688 using SourceBoost C compiler (www.sourceboost.com) // //***************************************************************************************** //***************************************************************************************** // // Pin definitions for PIC16F688 on VPIC board // // PIC16F688 Pin Number Designation Function // 1 VDD Power // 2 RA5 OSC_IN // 3 RA4 OSC_OUT // 4 MCLR# MCLR# // 5 RC5 Rx (UART) // 6 RC4 Tx (UART) // 7 RC3 CTS# (connects to RTS# on TTL cable) // 8 RC2 RTS# (connects to CTS# on TTL cable) // 9 RC1 Analogue/Digital Input // 10 RC0 Analogue/Digital Input // 11 RA2 Analogue/Digital Input (Stop button) // 12 RA1 Analogue/Digital Input (Start button) // 13 RA0 Analogue/Digital Input (Use this one for analog input) // 14 GND Ground // //***************************************************************************************** #include #include #pragma DATA 0x2007, _HS_OSC & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _IESO_OFF & _FCMEN_OFF; #pragma CLOCK_FREQ 20000000; //***************************************************************************************** // // Constants and variables for serial port // //***************************************************************************************** // Define serial port flags char _RTS = 0x04; // RTS# is RC2 (Port C bit 2) char _CTS = 0x08; // CTS# is RC3 (Port C bit 3) char FLOW_NONE = 0x00; char FLOW_RTS_CTS = 0x01; char FLOW_CONTROL_ENABLED = 0x00; // Initialise FLOW_CONTROL_ENABLED to off // Define serial port Rx buffer variables char SERIAL_RX_FIFO[0x20]; // Allocate buffer for RX FIFO USE MULTIPLE OF 0x04! char RX_FIFO_SIZE = sizeof(SERIAL_RX_FIFO); // Initialise FIFO size variable char RX_FIFO_COUNT = 0x00; // Reset RX FIFO count char * RX_FIFO_HEAD_PTR = &SERIAL_RX_FIFO[0x00]; // Initialise head pointer char * RX_FIFO_TAIL_PTR = &SERIAL_RX_FIFO[0x00]; // Initialise tail pointer char * RX_FIFO_START = RX_FIFO_TAIL_PTR; // Set lowest address of RX_FIFO char * RX_FIFO_END = RX_FIFO_START + RX_FIFO_SIZE; // Set highest address of RX_FIFO char RX_STATUS = 0x00; // Reset RX status char RX_FIFO_OVERRUN = 0x01; char RX_FIFO_UPPER_LIMIT = 0x03 *(RX_FIFO_SIZE / 0x04); // Buffer limit for handshaking char RX_FIFO_LOWER_LIMIT = (RX_FIFO_SIZE / 0x04); // Buffer limit for handshaking //***************************************************************************************** // // Define states for state machine // //***************************************************************************************** enum { // Enum states VNC1L_Idle, // Idle state VNC1L_DiskAvailable, VNC1L_CreateFile, VNC1L_WriteFile, VNC1L_EndFile, VNC1L_WaitForRemove }; char VNC1L_State = VNC1L_Idle; // Initialise state to idle char Synchronised = 0x01; char GotDisk = 0x01; /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // // Routines for serial port // /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// //***************************************************************************************** // // Reset Rx FIFO // //***************************************************************************************** void serial_resetrxfifo() { pie1.RCIE = 0x00; // Disable interrupts intcon.GIE = 0x00; intcon.PEIE = 0x00; RX_FIFO_COUNT = 0x00; // Reset FIFO count to 0 RX_STATUS = 0x00; // Reset FIFO status RX_FIFO_HEAD_PTR = (&SERIAL_RX_FIFO[0x00]); // Set FIFO head pointer to address of first byte of SERIAL_RX_FIFO RX_FIFO_TAIL_PTR = (&SERIAL_RX_FIFO[0x00]); // Set FIFO tail pointer to address of first byte of SERIAL_RX_FIFO pie1.RCIE = 0x01; // Enable interrupts for Rx byte intcon.GIE = 0x01; intcon.PEIE = 0x01; if (FLOW_CONTROL_ENABLED == 0x01) // If using flow control { portc &= ~_RTS; // Set RTS# active (low) } } //***************************************************************************************** // // Initialise serial port (Baud rate, handshaking, interrupt enable, enable receiver) // //***************************************************************************************** void serial_init(long BaudRate, char Handshaking) // Set up serial port { pie1.RCIE = 0x00; // Disable interrupts intcon.GIE = 0x00; intcon.PEIE = 0x00; baudctl = 0x00; ansel &= 0x3F; // Digital IO on RC2, RC3 trisc = 0xDB; // Set RC4 (Tx) & RC2 (RTS#) as output,RC5 (Rx) & RC3 (CTS#) as input // TRISC bits 1 for input, 0 for output portc |= _RTS; // Set RTS# high (inactive) at this stage txsta = 0x04; // Enable UART module and set 8,n,1 - Set BRGH to 1 rcsta.SPEN = 0x01; // Enable serial port switch (BaudRate) // Set Baud rate { case 9600: spbrg = 0x81; break; case 19200: spbrg = 0x40; break; case 57600: spbrg = 0x15; break; case 115200: spbrg = 0x0A; break; default: spbrg = 0x81; // Default to 9600 if not a valid achievable Baud rate } spbrgh = 0x00; if (Handshaking == FLOW_RTS_CTS) { portc &= ~_RTS; // If handshaking is required, set RTS# active (low) FLOW_CONTROL_ENABLED = 0x01; // Set flow control enabled flag } serial_resetrxfifo(); // Reset Rx FIFO pir1.RCIF = 0x00; // Clear Rx interrupt flag pie1.RCIE = 0x01; // Enable interrupts for Rx byte intcon.GIE = 0x01; intcon.PEIE = 0x01; rcsta.CREN = 0x01; // Enable receiver } //***************************************************************************************** // // Send a byte directly from the transmit register // //***************************************************************************************** void serial_sendbyte(char TX_BYTE) { if (FLOW_CONTROL_ENABLED == 0x01) { while (portc & _CTS); // Wait for CTS# to become active (low) } while (!txsta.TRMT); // Wait for TSR to be empty txreg = TX_BYTE; // Move byte to send into TXREG txsta.TXEN = 1; // Start transmission while (!txsta.TRMT); // Wait for TSR to be empty - indicates sending is complete } //***************************************************************************************** // // Read a byte directly from the receive character register // //***************************************************************************************** void serial_receivebyte(char RX_BYTE) { RX_BYTE = rcreg; pir1.RCIF = 0x00; // Clear Rx interrupt flag } //***************************************************************************************** // // Add a byte from the receive character register to the receive FIFO buffer // //***************************************************************************************** void serial_addtorxfifo() { if (RX_FIFO_COUNT >= RX_FIFO_SIZE) // If Rx FIFO is full { RX_STATUS = RX_STATUS | RX_FIFO_OVERRUN; // Set overrun flag if (FLOW_CONTROL_ENABLED == 0x01) // If using flow control { portc |= _RTS; // Set RTS# inactive (high) } return; } if (RX_FIFO_COUNT >= RX_FIFO_UPPER_LIMIT) // Keep some space in the FIFO for bytes already being sent { if (FLOW_CONTROL_ENABLED == 0x01) // If using flow control { portc |= _RTS; // Set RTS# inactive (high) } } intcon.GIE = 0x00; // Disable interrupts intcon.PEIE = 0x00; *RX_FIFO_HEAD_PTR++ = rcreg; // Add received byte to RX_FIFO and then increment head pointer RX_FIFO_COUNT++; // Increment byte count if (RX_FIFO_HEAD_PTR >= RX_FIFO_END) // If head pointer is greater than allocated FIFO end address { RX_FIFO_HEAD_PTR = RX_FIFO_START; // Wrap-around buffer to start address again (circuluar FIFO) } if ((RX_FIFO_COUNT < RX_FIFO_UPPER_LIMIT) && (FLOW_CONTROL_ENABLED == 0x01)) // If Rx FIFO is not full { portc &= ~_RTS; // Set RTS# active (low) } intcon.GIE = 0x01; // Re-enable interrupts intcon.PEIE = 0x01; } //***************************************************************************************** // // Read a byte out of the receive FIFO buffer // //***************************************************************************************** void serial_readfromrxfifo(char * RX_FIFO_BYTE) { if (RX_FIFO_COUNT == 0) // If FIFO is empty { return; // Exit - no data to read } intcon.GIE = 0x00; // Disable interrupts intcon.PEIE = 0x00; *RX_FIFO_BYTE = *RX_FIFO_TAIL_PTR++; // Read a byte from the FIFO at position given by RX_FIFO_TAIL_PTR and then increment tail pointer RX_FIFO_COUNT--; // Decrement byte count if (RX_FIFO_TAIL_PTR >= RX_FIFO_END) // If tail pointer is greater than allocated FIFO end address { RX_FIFO_TAIL_PTR = RX_FIFO_START; // Wrap-around buffer to start address again (circuluar FIFO) } intcon.GIE = 0x01; // Re-enable interrupts intcon.PEIE = 0x01; if ((RX_FIFO_COUNT < RX_FIFO_LOWER_LIMIT) && (FLOW_CONTROL_ENABLED == 0x01)) // If using flow control { portc &= ~_RTS; // Set RTS# active (low) } } /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // // VNC1L VDAP commands go here // /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// //***************************************************************************************** // // Echo command using E - same for short command set or long command set // //***************************************************************************************** void VNC1L_Big_E() { serial_sendbyte('E'); // Send 'E' serial_sendbyte(0x0D); // Send carriage return } //***************************************************************************************** // // Echo command using e - same for short command set or long command set // //***************************************************************************************** void VNC1L_Small_e() { serial_sendbyte('e'); // Send 'e' serial_sendbyte(0x0D); // Send carriage return } //***************************************************************************************** // // Synchronise to the VNC1L using the e and E echo commands // //***************************************************************************************** char VNC1L_Sync() { char LoopCount = 0x00; char FIFOByte1 = 0x00; char FIFOByte2 = 0x00; char Got_Big_E = 0x00; char Got_Small_e = 0x00; serial_resetrxfifo(); // Reset FIFO before synchronising while ((Got_Big_E == 0x00) && (LoopCount < 0xFF)) { VNC1L_Big_E(); // Send Big E echo LoopCount++; // Increment loop count while (RX_FIFO_COUNT < 0x02) { delay_ms(100); // Wait for 2 byte response } if (RX_FIFO_COUNT > 0x00) // If data is available { while (RX_FIFO_COUNT > 0x02) // Read all data available except last 2 bytes { serial_readfromrxfifo(&FIFOByte1); // If more than 2 bytes available, read surplus ones } serial_readfromrxfifo(&FIFOByte1); // Check that remaining 2 bytes are 'E' and 0x0D serial_readfromrxfifo(&FIFOByte2); if ((FIFOByte1 == 'E') && (FIFOByte2 == 0x0D)) { Got_Big_E = 0x01; } else { delay_ms(10); // Wait a bit and retry synchronisation } } } if (Got_Big_E == 0x01) { VNC1L_Small_e(); while (RX_FIFO_COUNT < 0x02) { delay_ms(10); // Wait for 2 byte response } serial_readfromrxfifo(&FIFOByte1); // Check that remaining 2 bytes are 'e' and 0x0D serial_readfromrxfifo(&FIFOByte2); if ((FIFOByte1 == 'e') && (FIFOByte2 == 0x0D)) { Got_Small_e = 0x01; // If small e found, then synchronised } } return Got_Small_e; } //***************************************************************************************** // // Look for disk - assumes long command set being used // //***************************************************************************************** char VNC1L_FindDisk() // Return 0x01 if disk is found, else return 0x00 { char FIFOByte1 = 0x00; char FIFOByte2 = 0x00; char FIFOByte3 = 0x00; char FIFOByte4 = 0x00; char FIFOByte5 = 0x00; serial_sendbyte(0x0D); // Send carriage return while (RX_FIFO_COUNT < 0x05); // Wait until at least 5 bytes in the Rx FIFO serial_readfromrxfifo(&FIFOByte1); // Read bytes out of Rx FIFO serial_readfromrxfifo(&FIFOByte2); serial_readfromrxfifo(&FIFOByte3); serial_readfromrxfifo(&FIFOByte4); serial_readfromrxfifo(&FIFOByte5); if ((FIFOByte1 == 'D') && (FIFOByte2 == ':') && (FIFOByte3 == 0x5C) && (FIFOByte4 == '>') && (FIFOByte5 == 0x0D)) // Check for prompt { return 0x01; // If prompt found, then return disk available } else { while (RX_FIFO_COUNT > 0x00) { serial_readfromrxfifo(&FIFOByte1); // Read any additional bytes out of the FIFO } return 0x00; // Return no disk available } } //***************************************************************************************** // // Open file for write command (OPW) using long command set // //***************************************************************************************** void VNC1L_OpenFile() { char FIFOByte = 0x00; serial_sendbyte('O'); // Send 'O' serial_sendbyte('P'); // Send 'P' serial_sendbyte('W'); // Send 'W' serial_sendbyte(' '); // Send ' ' serial_sendbyte('h'); // Send 'h' serial_sendbyte('e'); // Send 'e' serial_sendbyte('l'); // Send 'l' serial_sendbyte('l'); // Send 'l' serial_sendbyte('o'); // Send 'o' serial_sendbyte('.'); // Send '.' serial_sendbyte('t'); // Send 't' serial_sendbyte('x'); // Send 'x' serial_sendbyte('t'); // Send 't' serial_sendbyte(0x0D); // Send carriage return while (RX_FIFO_COUNT < 0x05) // Wait until at least 5 bytes in the Rx FIFO { delay_ms(10); // Wait for 5 byte response("D:\>" + 0x0D) } while (RX_FIFO_COUNT > 0x00) { serial_readfromrxfifo(&FIFOByte); // Read response bytes form FIFO } } //***************************************************************************************** // // Write to file command (OPW) using long command set // //***************************************************************************************** void VNC1L_WriteToFile() { char FIFOByte = 0x00; serial_sendbyte('W'); // Send 'W' serial_sendbyte('R'); // Send 'R' serial_sendbyte('F'); // Send 'F' serial_sendbyte(' '); // Send ' ' serial_sendbyte(0x00); // Send 0x00 - Number of bytes to write MSB serial_sendbyte(0x00); // Send 0x00 serial_sendbyte(0x00); // Send 0x00 serial_sendbyte(0x0E); // Send 0x0E - Number of bytes to write LSB serial_sendbyte(0x0D); // Send carriage return serial_sendbyte('H'); // Send 'H' serial_sendbyte('e'); // Send 'e' serial_sendbyte('l'); // Send 'l' serial_sendbyte('l'); // Send 'l' serial_sendbyte('o'); // Send 'o' serial_sendbyte(' '); // Send ' ' serial_sendbyte('W'); // Send 'W' serial_sendbyte('o'); // Send 'o' serial_sendbyte('r'); // Send 'r' serial_sendbyte('l'); // Send 'l' serial_sendbyte('d'); // Send 'd' serial_sendbyte('!'); // Send '1' serial_sendbyte(0x0D); // Send carriage return serial_sendbyte(0x0A); // Send line feed serial_sendbyte(0x0D); // Send carriage return while (RX_FIFO_COUNT < 0x05) // Wait until at least 5 bytes in the Rx FIFO { delay_ms(10); // Wait for 5 byte response("D:\>" + 0x0D) } while (RX_FIFO_COUNT > 0x00) { serial_readfromrxfifo(&FIFOByte); // Read response bytes form FIFO } } //***************************************************************************************** // // Close file command (CLF) using long command set // //***************************************************************************************** void VNC1L_CloseFile() { char FIFOByte = 0x00; serial_sendbyte('C'); // Send 'C' serial_sendbyte('L'); // Send 'L' serial_sendbyte('F'); // Send 'F' serial_sendbyte(' '); // Send ' ' serial_sendbyte('h'); // Send 'h' serial_sendbyte('e'); // Send 'e' serial_sendbyte('l'); // Send 'l' serial_sendbyte('l'); // Send 'l' serial_sendbyte('o'); // Send 'o' serial_sendbyte('.'); // Send '.' serial_sendbyte('t'); // Send 't' serial_sendbyte('x'); // Send 'x' serial_sendbyte('t'); // Send 't' serial_sendbyte(0x0D); // Send carriage return while (RX_FIFO_COUNT < 0x05) // Wait until at least 5 bytes in the Rx FIFO { delay_ms(10); // Wait for 5 byte response("D:\>" + 0x0D) } while (RX_FIFO_COUNT > 0x00) { serial_readfromrxfifo(&FIFOByte); // Read response bytes form FIFO } } //***************************************************************************************** //***************************************************************************************** //***************************************************************************************** // // Interrupt routines go here // Use receive character interrupt to add received characters to the RX FIFO // //***************************************************************************************** //***************************************************************************************** //***************************************************************************************** void interrupt() { if (pir1.RCIF) { serial_addtorxfifo(); // Add received byte to Rx FIFO pir1.RCIF = 0x00; // Clear RCIF flag } } //***************************************************************************************** //***************************************************************************************** //***************************************************************************************** // // Main line code // //***************************************************************************************** //***************************************************************************************** //***************************************************************************************** void main() { char Sync = 0x00; VNC1L_State = VNC1L_Idle; // Initialise state to idle serial_init(9600, FLOW_RTS_CTS); // Initialise Baud rate and flow control delay_s(1); // Allow some time for power up if (VNC1L_Sync() == Synchronised) // Synchronise to Vinculum { while (1) // Main state machine - do not return from here { switch (VNC1L_State) { case VNC1L_Idle: Sync = VNC1L_Sync(); if (VNC1L_FindDisk() == GotDisk) // Check for disk { VNC1L_State = VNC1L_DiskAvailable; // Update state to indicate disk found } break; case VNC1L_DiskAvailable: VNC1L_OpenFile(); // Send open file for write command (OPW) - file name "hello.txt" VNC1L_State = VNC1L_WriteFile; // Update state to indicate file has been created and can be written break; case VNC1L_WriteFile: VNC1L_WriteToFile(); // Send write file command (WRF) - write "Hello World!" VNC1L_State = VNC1L_EndFile; // Update state to indicate file has been written and can now be closed break; case VNC1L_EndFile: VNC1L_CloseFile(); // Send close file command (CLF) VNC1L_State = VNC1L_WaitForRemove; // Update state to indicate witing for disk to be removed break; case VNC1L_WaitForRemove: Sync = VNC1L_Sync(); if (VNC1L_FindDisk() != GotDisk) // Check for disk { VNC1L_State = VNC1L_Idle; // Update state to indicate disk has been removed and return to idle } break; default: VNC1L_State = VNC1L_Idle; // Default to idle state } } } }