/****************************************************************************
TACHTC.c
This program is a tachometer transmitter.
It transmits in Seatalk(tm) network protocol.
The following Seatalk protocol is extracted from Thomas Knauf's web site:
www.thomasknauf.de/seatalk.htm
Message protocol
* Each 4800 baud message contains between 3 and 18 characters:
* COMMAND byte (the only byte with the command-bit set)
ATTRIBUTE byte, specifying the total length of the message in the least significant nibble:
Most significant 4 bits: 0 or part of a data value
Least significant 4 bits: Number of additional bytes beyond the mandatory data byte
DATA byte (mandatory, meaning than the smallest message is 3 bytes)
DATA bytes (optional, up to 15 additional data bytes, meaning that longest messages is 18 bytes)
Serial Data Transmission
11 bits are transmitted for each byte:
* 1 Start bit (0V)
* 8 Data Bits (least significant bit transmitted first, bit ON = +12V)
* 1 Command/Data bit (+12V if command byte, 0V if other)
* 1 Stop bit (+12V)
Collision Management
Bus should be idle for at least 2mS (+12V for at least 10/4800 seconds).
Listens to it's own transmission and recognizes when its message has
been corrupted by a second talker. In this case it abandons the remaining
bytes in the message, waits for the bus to become free again, and then
retransmits the whole message.
CODES
------------------------------
FE 01 0x yy Tank address x is yy percent full
+5 +5
| |
14 4
----------
| |-17-- out to Seatalk (transistor driver)
| |-18-- in from Seatalk (transistor buffer)
| |
IN ----9-| |
| 16F628 |
| |
ADR ---12-|A0 |
ADR ---13-|A1 |
4MHz XTAL-15-| A2|-1--- LED
XTAL-16-| |
----------
5
|
Gnd
***************************************************************************/
/* The following include should contain 16F84 or 16F628. */
#include < 16F628.h >
#include < jonsinc.h >
#fuses HS, NOPROTECT, PUT, NOWDT, BROWNOUT, NOMCLR, NOLVP
#use fast_io ( A )
#use standard_io ( B )
#use delay ( clock = 8000000, restart_wdt )
#byte PORT_A = 5
#byte PORT_B = 6
#bit TX_OUT = PORT_A.0
#bit RX_IN = PORT_A.1
#bit LED = PORT_A.2
#define ADDR_0 PIN_B6
#define ADDR_1 PIN_B7
#define SEATALK_MSGNUM 0xFD
#define CMD 1
#define DATA 0
// TACHOMETER DEFINES ================
#define START_TACH 0
#define RUN_TACH 1
#define DONE_TACH 2
// Pulley factor is alternator pulley diameter divided by crank pulley diameter
#define ENGINE_DIA 3.625
#define ALT_DIA 2.875
#define PULLEY_FACTOR ALT_DIA/ENGINE_DIA
// Obtain from manufacturers data sheet (6, 8, 10, etc.)
#define ALTERNATOR_POLES 10
void SendMsg ( int32 int32Data );
char SendByte ( char cError, char cCommand, char cData );
char SendBit ( cBit );
void CheckBus ( void );
static char cAddress;
static char cBuffer [ 10 ];
void main ( void )
{
char cX, cY, cLed;
char cCnt;
float fRpm;
int32 int32Count;
long iLevel;
delay_ms ( 150 ); // wait for 75mS nom PUT
TX_OUT = LOW; // allow output to float
LED = LOW;
set_tris_a ( 0b11111010 ); // A0, A2 are outputs, A1 is input
output_low ( PROBE_POWER ); // default off
setup_counters ( RTCC_INTERNAL, WDT_2304MS ); // 256 * 4uS = 1.024mS timer wrap
port_b_pullups ( TRUE );
cAddress = input_b();
cAddress &= 0b11000000; // mask on bits 6 and 7
cAddress >>= 6; // shift address to 0 bit index
for ( cY = 0; cY < cAddress + 1; cY++ ) // blink the card address upon initialization
{
for ( cX = 0; cX < 5; cX++ )
{
LED = ON;
delay_ms ( 18 );
LED = OFF;
delay_ms ( 18 );
}
delay_ms ( 200 );
}
for ( cX = 0; cX < 10; cX++ )
{
cBuffer [ cX ] = 0; // initialize averaging buffer
}
for ( cX = 0; cX < cAddress; cX++ )
{
delay_ms ( 5 ); // variable startup delay based on address
}
cLed = LOW;
while ( TRUE ) // do forever
{
restart_wdt();
enable_interrupts ( INT_CCP1 ); // CCP1 interrupt
cTachState = DONE_TACH;
if ( !cSkip ) // do until button is pressed
{
int32Count = 0;
for ( cCnt = 0; cCnt < 20; cCnt++ ) // accumulate 20 readings
{
cTachState = START_TACH; // allow interrupt to start
while ( cTachState != DONE_TACH ) // wait for timing to complete
{
if ( get_timer1() > 60000 ) // timeout counter //??? divide this by 8 since 8 prescaler is being used???
{
iTachCount = 0; // zero everything out
int32Count = 0;
break; // don't wait any longer
}
}
int32Count += iTachCount; // otherwise accumulate
}
int32Count /= 20; // get average of those 20 readings
}
disable_interrupts ( INT_CCP1 ); // CCP1 interrupt
}
SendMsg ( iLevel );
LED = cLed;
delay_ms ( 1000 );
cLed ^= 1;
}
}
void SendMsg ( int32 int32Data )
{
char cData0, cData1, cData2, cData3, cError;
cData0 = ( char ) ( int32Data & 0x000000ff );
cData1 = ( char ) ( ( int32Data / 256 ) & 0x000000ff );
cData2 = ( char ) ( ( int32Data / 65536 ) & 0x000000ff );
cData3 = ( char ) ( ( int32Data / 167772216 ) & 0x000000ff );
do {
CheckBus(); //wait for bus to be idle
cError = SendByte ( NO, CMD, SEATALK_MSGNUM ); // command
cError = SendByte ( cError, DATA, 0x04 ); // 4 extra data bytes (7 total)
cError = SendByte ( cError, DATA, cAddress ); // card address
cError = SendByte ( cError, DATA, cData0 ); // data
cError = SendByte ( cError, DATA, cData1 );
cError = SendByte ( cError, DATA, cData2 );
cError = SendByte ( cError, DATA, cData3 );
} while ( cError == YES ); // repeat if message was corrupted
}
char SendByte ( char cError, char cCommand, char cData )
{
char cX;
if ( cError != YES )
{
cError = SendBit ( HIGH ); // start bit (0V)
for ( cX = 0; cX < 8; cX++ )
{
cError = SendBit ( ~cData & 0x01 ); // LSB data bit
cData >>= 1; // shift right
}
cError = SendBit ( cCommand ? LOW : HIGH ); // set if command byte, clear if data byte
cError = SendBit ( LOW ); // stop bit (+12V)
}
return ( cError );
}
char SendBit ( cBit )
{
char cX, cY;
// this code adjusted to give 208uS bit times (4800 baud)
TX_OUT = cBit; // send bit to output
for ( cX = 0; cX < 8; cX++ )
{
delay_us ( 14 );
if ( RX_IN == !cBit ) // check if output bit is corrupted by another talker
{
return ( HIGH ); // return collision error
}
}
return ( LOW ); // return no error
}
void CheckBus ( void )
{
char cX;
for ( cX = 0; cX < 255; cX++ ) // assumes output is floating to +12V for ~5mS
{
if ( RX_IN == HIGH ) // check if output bit is corrupted by another talker
{
cX = 0; // reset count to zero
restart_wdt(); // CCS compiler doesn't put CLRWDT into short delay_us, apparently
}
delay_us ( 7 );
}
}