//
//
// 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;
}