// // // Name: cutdown.c // // // Revision History: // // M. Gray 1 Oct 2003 V1.00 Initial release. // // // COPYRIGHT (c) 2003 Michael Gray, KD7LMO // // 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 // // Hardware specific configuration. #include <16f84a.h> #fuses HS,NOWDT,NOPROTECT,NOPUT // These compiler directives set the clock and baud rate information. #use delay(clock=4897000) #use rs232(baud=4800, xmit=PIN_B7) #use fast_io(A) #use fast_io(B) // We define types that are used for all variables. These are // declared because compilers have different sizes for int and long. typedef int1 bool; typedef unsigned int uint8; //typedef signed long int16; typedef unsigned long uint16; // Public methods for each class. #define IO_DEBUG PIN_A0 #define IO_LED PIN_A3 #define IO_RX PIN_B0 #define IO_WIRE1 PIN_B2 #define IO_WIRE2 PIN_B3 #define PWM_PRI_COUNT 478 #define PWM_DUTY_CYCLE_DELTA 14 /** * Calculate the CRC-16 CCITT of <b>buffer</b> that is <b>length</b> bytes long. * * @param buffer Pointer to data buffer. * @param length number of bytes in data buffer * * @return CRC-16 of buffer[0 .. length] */ uint16 crc16 (uint8 *buffer, uint16 length) { uint8 i, bit; uint16 crc, value; crc = 0xffff; for (i = 0; i < length; ++i) { value = buffer[i]; for (bit = 0; bit < 8; ++bit) { crc ^= (value & 0x01); crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 ); value = value >> 1; } // END for } // END for return crc ^ 0xffff; } // Counts the length of time the input bit remains in the same state. uint8 subBitCount; // Number of bits we have accumulated before we manchester decode the input stream. uint8 decodeCount; // The last two bits we have received. We look at these bits to decode the manchester bit stream. uint8 decodeBits; // The last byte we decoded from the input stream. We must process this is data in 16-bit times. uint8 outputByte; // The number of bits we have accumulated in rxByte. Once we have 8, we have a complete byte. uint8 bitCount; // Temporary register to hold each data bit as we receive it. uint8 rxByte; // Accumulator that shifts each received bit through it until we sync with the unique word. uint8 accum; // The number of bits lefts in outputByte. uint8 shiftCount; // Timer that determines when we turn off the LED and cut down wires. uint16 powerDown; // Flag that is set when outputByte contains the latest 8-bits of data. boolean dataReady; // Set to the last input state from the receiver. boolean lastState; uint16 wirePWMDutyCycle; uint16 wirePWMAccum; // This ISR gets call every 209uS #INT_RTCC void timer0Interrupt() { // Check the state of the input pin. if (input(IO_RX)) { if (!lastState) { subBitCount = 2; lastState = true; } else if (subBitCount != 0) if (--subBitCount == 0) { subBitCount = 4; // We have detected a 1 bit, so save it. decodeBits = (decodeBits << 1) | 0x01; ++decodeCount; } // END if } else { if (lastState) { subBitCount = 2; lastState = false; } else if (subBitCount != 0) if (--subBitCount == 0) { subBitCount = 4; // We have detected a bit, so save it. decodeBits = (decodeBits << 1); ++decodeCount; } // END if } // END if-else // Manchester decode the bit stream. if (decodeCount == 2) { switch (decodeBits) { // Resync the bit stream. case 0x00: decodeCount = 1; break; // A 0 to 1 transition indicates we have a 0 data bit. case 0x01: // Reset the counter that tracks the number of bits we look at. decodeCount = 0; // Clear the bit accumulator. decodeBits = 0x00; // Save a 0 in the output regsiter. rxByte = rxByte << 1; // Once we have a full byte, save it and start over. if (++bitCount == 8) { outputByte = rxByte; rxByte = 0x00; bitCount = 0; dataReady = true; } // END if break; // A 1 to 0 transition indicates we have a 1 data bit. case 0x02: // Reset the counter that tracks the number of bits we look at. decodeCount = 0; // Clear the bit accumulator. decodeBits = 0x00; // Save a 1 in the output register rxByte = (rxByte << 1) | 0x01; // Once we have a full byte, save it and start over. if (++bitCount == 8) { outputByte = rxByte; rxByte = 0x00; bitCount = 0; dataReady = true; } // END if break; // Resync the bit stream. case 0x03: decodeCount = 1; decodeBits = 0x01; break; } // END switch } // END if // Counter to determine when to cycle LED. if (powerDown != 0) if (--powerDown == 0) { output_low (IO_WIRE1); output_low (IO_WIRE2); output_low (IO_LED); wirePWMDutyCycle = 0; wirePWMAccum = 0; } // PWM the cutdown wire output. if (wirePWMDutyCycle != 0) { // Wrap the accumulator at the PRI rate and fire the wires. if (++wirePWMAccum == PWM_PRI_COUNT) { wirePWMAccum = 0; output_high (IO_WIRE1); output_high (IO_WIRE2); // Increase the PWM duty cycle until we reach maximum. if (wirePWMDutyCycle < PWM_PRI_COUNT) { wirePWMDutyCycle += PWM_DUTY_CYCLE_DELTA; if (wirePWMDutyCycle > PWM_PRI_COUNT) wirePWMDutyCycle = PWM_PRI_COUNT; } // END if } // END if // Once we reach the duty cycle, shut it off. if (wirePWMAccum == wirePWMDutyCycle) { output_low (IO_WIRE1); output_low (IO_WIRE2); } // END if } // END if } uint8 timerSyncChar(uint8 sync) { uint8 i; while (!dataReady); dataReady = false; shiftCount = 8; for (i = 0; i < 8; ++i) { accum = (accum << 1) | ((outputByte & 0x80) ? 0x01 : 0x00); outputByte = outputByte << 1; if (accum == sync) shiftCount = i; } // END for if (shiftCount == 8) return false; else return true; } uint8 timerGetChar() { uint8 i, value; while (!dataReady); dataReady = false; value = 0; for (i = 0; i < 8; ++i) { accum = (accum << 1) | ((outputByte & 0x80) ? 0x01 : 0x00); outputByte = outputByte << 1; if (i == shiftCount) value = accum; } return value; } #define MODE_FRAMESYNC_1 1 #define MODE_FRAMESYNC_2 2 #define MODE_FRAMESYNC_3 3 #define MODE_FRAMESYNC_4 4 #define MODE_COMMAND 5 #define MODE_LENGTH 6 #define MODE_DATA 7 #define MODE_CRC_1 8 #define MODE_CRC_2 9 #define COMMAND_SELFTEST 0x21 #define COMMAND_CUTDOWN 0x22 #define INDEX_COMMAND 0 #define INDEX_LENGTH 1 #define INDEX_DATA_START 2 #byte OPTION_REG = 0x81 int main() { uint8 mode, accum, commandIndex, commandBuffer[12]; uint16 crc; boolean runFlag; // Set the output state before we configure the port direction. output_a (0x00); output_b (0x00); // Configure the I/O port direction. set_tris_a (0x00); set_tris_b (0x51); // Set all the state variables. accum = 0x00; bitCount = 0; dataReady = false; decodeBits = 0x00; decodeCount = 0; outputByte = 0x00; shiftCount = 0; rxByte = 0x00; powerDown = 9600; wirePWMDutyCycle = 0; wirePWMAccum = 0; // Set the timer to fire at regular intervals. set_timer0(0); // setup_timer_0( RTCC_INTERNAL | RTCC_DIV_4 ); setup_timer_0( RTCC_INTERNAL ); #asm bsf OPTION_REG,3 #endasm // Setup our interrupts. enable_interrupts(INT_TIMER0); enable_interrupts(GLOBAL); // Turn on the LED to indicate successful startup. It will turn off in 2 seconds. output_high (IO_LED); // Setup the state machine. runFlag = true; mode = MODE_FRAMESYNC_1; accum = 0; // State machine to decode the data stream. The stream contains:\ // 0xcccc Preamble // 0x18477025 Unique word // 0x21, 0x22 Command, cut down or self test // nn Data Length // 1 .. nn Data, up to 10 bytes // xx xx 16-bit CRC while (runFlag) { switch (mode) { case MODE_FRAMESYNC_1: if (timerSyncChar(0x18)) mode = MODE_FRAMESYNC_2; break; case MODE_FRAMESYNC_2: if (timerGetChar() != 0x47) mode = MODE_FRAMESYNC_1; else mode = MODE_FRAMESYNC_3; break; case MODE_FRAMESYNC_3: if (timerGetChar() != 0x70) mode = MODE_FRAMESYNC_1; else mode = MODE_FRAMESYNC_4; break; case MODE_FRAMESYNC_4: if (timerGetChar() != 0x25) mode = MODE_FRAMESYNC_1; else mode = MODE_COMMAND; break; case MODE_COMMAND: commandBuffer[INDEX_COMMAND] = timerGetChar(); if (commandBuffer[INDEX_COMMAND] != COMMAND_CUTDOWN && commandBuffer[INDEX_COMMAND] != COMMAND_SELFTEST) mode = MODE_FRAMESYNC_1; else mode = MODE_LENGTH; break; case MODE_LENGTH: commandBuffer[INDEX_LENGTH] = timerGetChar(); commandIndex = INDEX_DATA_START; if (commandBuffer[INDEX_LENGTH] > 10) mode = MODE_FRAMESYNC_1; else if (commandBuffer[INDEX_LENGTH] == 0) mode = MODE_CRC_1; else mode = MODE_DATA; break; case MODE_DATA: commandBuffer[commandIndex] = timerGetChar(); if (++commandIndex == commandBuffer[INDEX_LENGTH] + 2) mode = MODE_CRC_1; break; case MODE_CRC_1: commandBuffer[commandIndex++] = timerGetChar(); mode = MODE_CRC_2; break; case MODE_CRC_2: // Save the LSB of the CRC. commandBuffer[commandIndex] = timerGetChar(); // Calcualte the CRC of the buffer. crc = crc16 (commandBuffer, commandIndex - 1); // If the CRC is valid process the command. if ((crc >> 8) == commandBuffer[commandIndex - 1] && (crc & 0xff) == commandBuffer[commandIndex]) switch (commandBuffer[INDEX_COMMAND]) { case COMMAND_SELFTEST: if (powerDown == 0) { output_high (IO_LED); powerDown = 2400; } // END if break; case COMMAND_CUTDOWN: if (powerDown == 0) { output_high (IO_LED); powerDown = 60000; // Start out with a duty cycle of 3mS wirePWMDutyCycle = PWM_DUTY_CYCLE_DELTA; wirePWMAccum = 0; output_high (IO_WIRE1); output_high (IO_WIRE2); } break; } // END switch // No matter what, start the search over. mode = MODE_FRAMESYNC_1; break; } // END switch } // END while return 0; }