/*************************************************************************
LEVELR.C
This program is level sensor.
It transmits in a variant of 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
|
14
----------
| |
| |-6--- in from Seatalk (transistor buffer)
| |
| |-13-- LCD D4
| 16F628 |-12-- LCD D3
| |-11-- LCD D2
| |-10-- LCD D1
| |-9--- LCD EN
8MHz XTAL-15-| |-8--- LCD RS
XTAL-16-| |
----------
5
|
Gnd
***************************************************************************/
/* The following include should contain 16F84 or 16F628. */
#include < 16F628.h >
#include < jonsinc.h >
#fuses HS, NOPROTECT, NOPUT, NOWDT, BROWNOUT, MCLR, NOLVP
// LCD STUFF
#define LCD_D7 PIN_B7
#define LCD_D6 PIN_B6
#define LCD_D5 PIN_B5
#define LCD_D4 PIN_B4
#define LCD_EN PIN_B3
#define LCD_RS PIN_B2
#define FIRST_LINE 0x00
#define SECOND_LINE 0x40
#define THIRD_LINE 0x10
#define FOURTH_LINE 0x50
#define CLEAR_DISP 0x01
#define CURS_ON 0x0e
#define CURS_OFF 0x0c
#define NINTH_BIT 7
#define SEATALK_MSGNUM 0xFE
#define NUL 0
#use delay ( clock = 8000000 )
#use standard_io ( B )
#use standard_io ( A )
#use rs232 ( BAUD = 4800, RCV = PIN_B0, BITS = 9, ERRORS )
// proto statements
void LCD_Init ( void );
void LCD_SetPosition ( unsigned int cX );
void LCD_PutChar ( unsigned int cX );
void LCD_PutCmd ( unsigned int cX );
void LCD_PulseEnable ( void );
void LCD_SetData ( unsigned int cX );
//*****************************************************************
// MAIN CODE
void main ( void )
{
char cCmd, cCnt, cAdr, cLevel, cError;
// INITIAL MESSAGE
delay_ms ( 200 ); // wait enough time after Vdd rise
LCD_Init();
LCD_PutCmd ( CLEAR_DISP );
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "TANK LEVELS" );
LCD_SetPosition ( SECOND_LINE + 0 );
printf ( LCD_PutChar, "Water 1" );
LCD_SetPosition ( THIRD_LINE + 0 );
printf ( LCD_PutChar, "Water 2" );
LCD_SetPosition ( FOURTH_LINE + 0 );
printf ( LCD_PutChar, "Waste" );
while ( TRUE )
{
cCmd = NUL;
while ( cCmd != SEATALK_MSGNUM )
{
cCmd = getc();
}
if ( ( bit_test ( rs232_errors, NINTH_BIT ) == HIGH ) )
{
cCnt = getc(); // get count bit
if ( ( bit_test ( rs232_errors, NINTH_BIT ) == HIGH ) )
{
cError = YES; // should not have received a command byte here
}
if ( cCnt != 0x01 )
{
cError = YES; // count should have been 01
}
cAdr = getc(); // get address bit
if ( ( bit_test ( rs232_errors, NINTH_BIT ) == HIGH ) )
{
cError = YES; // should not have received a command byte here
}
cLevel = getc(); // get level bit
if ( ( bit_test ( rs232_errors, NINTH_BIT ) == HIGH ) )
{
cError = YES; // should not have received a command byte here
}
switch ( cAdr & 0x03 ) // mask on address bits 0 & 1
{
case 0x00:
{
LCD_SetPosition ( SECOND_LINE + 8 );
break;
}
case 0x01:
{
LCD_SetPosition ( THIRD_LINE + 8 );
break;
}
case 0x02:
{
LCD_SetPosition ( FOURTH_LINE + 8 );
break;
}
case 0x03:
{
// error
}
}
printf ( LCD_PutChar, "%3u%%", cLevel );
}
}
}
//*****************************************************************************
void LCD_Init ( void )
{
LCD_SetData ( 0x00 );
output_low ( LCD_RS );
LCD_SetData ( 0x03 ); // init with specific nibbles to start 4-bit mode
LCD_PulseEnable();
LCD_PulseEnable();
LCD_PulseEnable();
LCD_SetData ( 0x02 ); // set 4-bit interface
LCD_PulseEnable(); // send dual nibbles hereafter, MSN first
LCD_PutCmd ( 0x2C ); // function set (all lines, 5x7 characters)
LCD_PutCmd ( 0x0C ); // display ON, cursor off, no blink
LCD_PutCmd ( 0x01 ); // clear display
LCD_PutCmd ( 0x06 ); // entry mode set, increment
}
void LCD_SetPosition ( unsigned int cX )
{
// this subroutine works specifically for 4-bit Port A
LCD_SetData ( swap ( cX ) | 0x08 );
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) );
LCD_PulseEnable();
}
void LCD_PutChar ( unsigned int cX )
{
// this subroutine works specifically for 4-bit Port A
output_high ( LCD_RS );
LCD_SetData ( swap ( cX ) ); // send high nibble
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) ); // send low nibble
LCD_PulseEnable();
output_low ( LCD_RS );
}
void LCD_PutCmd ( unsigned int cX )
{
// this subroutine works specifically for 4-bit Port A
LCD_SetData ( swap ( cX ) ); // send high nibble
LCD_PulseEnable();
LCD_SetData ( swap ( cX ) ); // send low nibble
LCD_PulseEnable();
}
void LCD_PulseEnable ( void )
{
output_high ( LCD_EN );
delay_us ( 10 );
output_low ( LCD_EN );
delay_ms ( 5 );
}
void LCD_SetData ( unsigned int cX )
{
output_bit ( LCD_D4, cX & 0x01 );
output_bit ( LCD_D5, cX & 0x02 );
output_bit ( LCD_D6, cX & 0x04 );
output_bit ( LCD_D7, cX & 0x08 );
}