/****************************************************************
STEAMGEN07.C
This is a steam generator.
WORKING CODE
ERRORS - count the number of quick flashes
1 - not drained at beginning
2 - not filled within time limit during initial fill cycle
3 - not filled within time limit during rinse cycle
4 - not filled to limit during heating within time limit
5 - over temperature during preheat or soak cycles
6 - under temperature after preheat cycle
LED FLASH
SLOW - filling, preheating, draining
SOLID - soak mode
FAST - stop and wait (warn) period
---------
+5--20-|Vdd C0|-11--- Heat-A relay
| C1|-12--- Heat-B relay
Gnd--08-|Vss C2|-13--- Heat - SSR
Gnd--19-|Vss C3|-14--- Fill
4MHz--10-|Xtal C4|-15--- Drain
--09-|Xtal |
| | PROGRAMMING CONNECTOR
| B7|-28--DATA
| B6|-27--CLK
| MCLR|-01--MCLR
| | GND
| 16F876 |
| |
Level --21-|B0 C5|-16-- LED1
Sw1 --22-|B1 C6|-17-- LED2
Sw2 --23-|B2 B4|-25-- LED RETURNS
DS1820 --03-|A1 B3|-24-- BEEPER
| B5|-26-- COOLING FAN
---------
**********************************************************/
#include < 16F876.H >
#include < jonsinc.h >
#fuses XT, NOPROTECT, PUT, NOWDT, BROWNOUT, NOLVP, NOCPD, NOWRT
#define RELAY_HEAT_A PIN_C0
#define RELAY_HEAT_B PIN_C1
#define RELAY_SSR PIN_C2
#define RELAY_FILL PIN_C3
#define RELAY_DRAIN PIN_C4
#define LED_1 PIN_C5
#define LED_2 PIN_C6
#define SWITCH_LEVEL PIN_B0
#define SWITCH_1 PIN_B1
#define SWITCH_2 PIN_B2
#define BEEPER PIN_B3
#define LED_RETURN PIN_B4
#define DS1820_DATA_PIN PIN_A1
#define FAN PIN_B5
//============================
#define PERCENT_100 4
#define PERCENT_75 3
#define PERCENT_50 2
#define PERCENT_25 1
#define PERCENT_0 0
#define LED_FLASH_NONE 0
#define LED_FLASH_SLOW 1
#define LED_FLASH_FAST 2
#define LED_INIT_FLASH_COUNT 16
#define MINIMUM_INTERVAL 1
#define HIGH_STEAM 1
#define LOW_STEAM 2
//============================
#define STATE_START 0
#define STATE_INITIAL_CHECKS 1
#define STATE_READ_SWITCHES 2
#define STATE_FILL 3
#define STATE_PREHEAT 4
#define STATE_SOAK 5
#define STATE_WARN 6
#define STATE_DRAIN 7
#define STATE_ERROR 9
//============================
#define ERROR_NONE 0
#define ERROR_NOT_DRAINED 1
#define ERROR_NOT_FILLED_DURING_FILL 2
#define ERROR_NOT_FILLED_DURING_RINSE 3
#define ERROR_FILL_ABSENCE 4
#define ERROR_OVERTEMP 5
#define ERROR_UNDERTEMP 6
//============================
// times in seconds
#define TIME_FILL_LIMIT 240
#define TIME_DRAIN 160
#define TIME_PREBOIL 360
#define TIME_PREHEAT_SHOWER 600
#define TIME_SOAK_MODE 1200
#define TIME_STOP_AND_WAIT 130
#define TIME_FILL_ABSENCE 300
#define TIME_TEMPERATURE_MEAS 10
// times in mS
#define TIME_ADD_WATER_INCR 1000
#define TIME_ADD_WATER_DELAY 100
#define TIME_DRAIN_COOL_FILL 10000
#define TIME_INITIAL_OVERFILL 3000
// times in uS
#define BEEPER_PERIOD_DELAY 518
// number of cycles for beeper
#define BEEP_CYCLES 500
//============================
// DS1820 temperature sensor
#define DS1820_SKIP_ROM 0xCC
#define DS1820_READ_SCRATCHPAD 0xBE
#define DS1820_CONVERT_T 0x44
// temperature in degrees C
#define TEMPERATURE_DRAIN_LIMIT 65
#define TEMPERATURE_OVER 105
#define TEMPERATURE_UNDER 90
//============================
#use delay ( clock=6000000 )
#use standard_io ( A )
#use standard_io ( B )
#use standard_io ( C )
void CheckSwitches ( void );
void AddWaterIfNecessary ( void );
void FlashErrorCode ( cError );
void AllRelaysOff ( void );
void Beep ( char cCount );
void SetHeaterPower ( char cLevel );
char ReadTemperature ( void );
void ResetDS1820 ( void );
void WriteDS1820 ( void );
void ReadDS1820 ( void );
void WaitForConversion ( void );
static char cInterruptCount;
static long iSecondCount, iFillOvertimeCount;
static char cSwitch1Count, cSwitch1on;
static char cSwitch2Count, cSwitch2on;
static char cError, cState, cMode, cOff, cResetCount;
static char cLedFlash, cLedOn, cSSRon, cSSRcount, cDebugMode;
static char cPower, cDutyCycleCount, cDutyCycleState;
static char cShiftBit,cDataOut, cTemperatureSecondCount;
static long iDataIn;
//*************************************************************************
#int_rtcc
void TimerInterrupt ( void ) // 21.760mS tick, 46 interrupts per second
{
// ONE-SECOND TICK
if ( cInterruptCount++ == 47 ) // a little more than one second so flashes divide evenly
{
cInterruptCount = 0; // restart interrupt count
iSecondCount++; // increment second count
cTemperatureSecondCount++; // increment temperature second count
iFillOvertimeCount++; // increment overtime count
}
// SOLID STATE RELAY POWER CYCLING
if ( cDutyCycleCount++ >= 4 ) // is it 84mS yet? (sets the overall duty cycle granularity)
{
cDutyCycleCount = 0; // reset
if ( cDutyCycleState == cPower ) // is heat cycle done yet?
{
output_low ( RELAY_SSR ); // turn solid state relay off
}
else
{
if ( cDutyCycleState == 0 ) // if first state
{
output_high ( RELAY_SSR ); // turn solid state relay on
}
}
if ( cDutyCycleState++ >= 3 ) // increment state
{
cDutyCycleState = 0; // reset
}
}
// LED FLASH/SOLID CONTROL
switch ( cLedFlash )
{
case LED_FLASH_NONE:
{
cLedOn = YES;
break;
}
case LED_FLASH_SLOW:
{
if ( ( cInterruptCount % 24 ) == 0 )
{
cLedOn ^= 1;
}
break;
}
case LED_FLASH_FAST:
{
if ( ( cInterruptCount % 8 ) == 0 )
{
cLedOn ^= 1;
}
break;
}
}
if ( cLedOn == YES )
{
output_low ( LED_RETURN ); // LED on
}
else
{
output_high ( LED_RETURN ); // LED off
}
// SWITCH 1 SENSE
if ( input ( SWITCH_1 ) == LOW ) // if pressed
{
if ( cSwitch1Count++ >= 92 ) // increment count
{
cSwitch1Count = 92; // prevent wrap, limit to highest needed
}
}
else // when button is released
{
if ( cSwitch1Count >= 92 ) // if was pressed for two seconds
{
cOff = YES; // signal OFF
}
else
{
if ( cSwitch1Count >= 2 ) // else if was pressed momentarily
{
cSwitch1on = YES; // signal normal press
}
}
cSwitch1Count = 0; // switch up, restart
}
// SWITCH 2 SENSE
if ( input ( SWITCH_2 ) == LOW ) // if pressed
{
if ( cSwitch2Count++ >= 92 ) // increment count
{
cSwitch2Count = 92; // prevent wrap, limit to highest needed
}
}
else // when button is released
{
if ( cSwitch2Count >= 92 ) // if was pressed for two seconds
{
cOff = YES; // signal OFF
}
else
{
if ( cSwitch2Count >= 2 ) // else if was pressed momentarily
{
cSwitch2on = YES; // signal normal press
}
}
cSwitch2Count = 0; // switch up, restart
}
// RESET (PRESS BOTH SWITCHES SIMULTANEOUSLY)
if ( ( input ( SWITCH_1 ) == LOW ) && ( input ( SWITCH_2 ) == LOW ) ) // if pressed for three seconds
{
if ( cResetCount++ >= 143 ) // increment count
{
cResetCount = 143; // cap it
}
}
else // when button is released
{
if ( cResetCount >= 143 ) // if was pressed for five seconds
{
reset_cpu();
}
cResetCount = 0; // switch up, restart
}
}
//****************************************************************************
void main ( void )
{
char cCnt, cTemp;
delay_ms ( 500 ); // wait enough time after VDD rise
setup_counters ( RTCC_INTERNAL, RTCC_DIV_128 );
cSSRcount = 0; // force SSR off
cSSRon = 0;
AllRelaysOff();
port_b_pullups ( ON );
cDutyCycleState = 0;
cDutyCycleCount = 0;
cPower = 0;
// flash LEDs
output_low ( LED_RETURN ); // LED solid on
for ( cCnt = 0; cCnt < LED_INIT_FLASH_COUNT; cCnt++ )
{
output_high ( LED_1 );
delay_ms ( 30 );
output_low ( LED_1 );
output_high ( LED_2 );
delay_ms ( 30 );
output_low ( LED_2 );
}
// DEBUG MODE (hold all buttons during power up, before interrupts are enabled)
if ( ( input ( SWITCH_1 ) == LOW ) || ( input ( SWITCH_2 ) == LOW ) )
{
while ( ( input ( SWITCH_1 ) == LOW ) || ( input ( SWITCH_2 ) == LOW ) ) // wait until all buttons released
cDebugMode = ON;
}
else
{
cDebugMode = OFF;
}
enable_interrupts ( INT_RTCC ); // turn on timer interrupt
enable_interrupts ( GLOBAL ); // enable interrupts
cSwitch1Count = 0;
cSwitch2Count = 0;
cResetCount = 0;
cSwitch1on = NO;
cSwitch2on = NO;
cOff = NO;
ReadTemperature(); // dummy read to clear sensor
cState = STATE_READ_SWITCHES;
// DEBUG LOOP===============================================================
// get out of this mode by doing a reset
if ( cDebugMode == ON )
{
cLedFlash = LED_FLASH_NONE;
output_high ( LED_1 );
delay_ms ( 300 );
output_low ( LED_1 );
output_high ( LED_2 );
delay_ms ( 300 );
output_low ( LED_2 );
while ( TRUE )
{
// get float switch status
if ( input ( SWITCH_LEVEL ) == LOW )
{
output_high ( LED_2 );
}
else
{
output_low ( LED_2) ;
}
// switch 1 opens fill valve
if ( input ( SWITCH_1 ) == LOW )
{
output_high ( RELAY_FILL );
}
else
{
output_low ( RELAY_FILL );
}
// switch 1 opens drain valve
if ( input ( SWITCH_2 ) == LOW )
{
output_high ( RELAY_DRAIN );
}
else
{
output_low ( RELAY_DRAIN );
}
}
}
// end DEBUG loop ======================================================
Beep ( 1 ); // beep to signal ready for use
cMode = 0; // default no switches
// MAIN LOOP================================================================
while ( TRUE ) // do main loop forever
{
switch ( cState )
{
case STATE_READ_SWITCHES:
{
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_NONE;
output_low ( LED_1 );
output_low ( LED_2 );
output_low ( FAN ); // turn off the SSR cooling fan
CheckSwitches();
delay_ms ( 100 );
cOff = NO; // ignore a switch pressed too long
if ( cMode != 0 ) // if a switch was pressed
{
cState = STATE_INITIAL_CHECKS;
}
break;
}
case STATE_INITIAL_CHECKS:
{
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_SLOW;
// check thermistor (ensure not overtemp, and record actual temp)
// check water level (should be empty, otherwise drain)
if ( input ( SWITCH_LEVEL ) == LOW ) // if water level is high
{
output_high ( RELAY_DRAIN ); // open drain valve
iSecondCount = 0;
while ( iSecondCount < TIME_DRAIN ); // allow drain time
output_low ( RELAY_DRAIN ); // close drain valve
}
delay_ms ( 1000 );
if ( input ( SWITCH_LEVEL ) == LOW ) // if water level is still high
{
cError = ERROR_NOT_DRAINED;
cState = STATE_ERROR;
}
else
{
cState = STATE_FILL; // otherwise go to fill state
}
break;
}
case STATE_FILL:
{
// turn on FILL valve
// watch the level sensor, time the fill
// turn off the FILL valve
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_SLOW;
output_high ( RELAY_FILL ); // open fill valve
iSecondCount = 0;
while ( TRUE )
{
if ( iSecondCount >= TIME_FILL_LIMIT )
{
cError = ERROR_NOT_FILLED_DURING_FILL;
cState = STATE_ERROR;
break;
}
if ( cOff == YES ) // if button was pressed for two seconds
{
cMode = 0;
cState = STATE_DRAIN;
break;
}
if ( input ( SWITCH_LEVEL ) == LOW ) // is water level up to proper level yet?
{
delay_ms ( TIME_INITIAL_OVERFILL ); // over fill slightly
cState = STATE_PREHEAT;
break; // break out early when level switch senses water level
}
}
output_low ( RELAY_FILL ); // close fill valve
delay_ms ( 1000 );
break;
}
case STATE_PREHEAT:
{
// turn on the heat relay
// monitor the temperature (for immediate heat rise over existing, and overheat)
// time the high heat
// turn on the fan
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_SLOW;
iSecondCount = 0;
cTemperatureSecondCount = 0;
iFillOvertimeCount = 0;
SetHeaterPower ( PERCENT_100 ); // apply power
while ( TRUE )
{
AddWaterIfNecessary();
if ( iSecondCount >= ( TIME_PREBOIL + TIME_PREHEAT_SHOWER ) )
{
if ( ReadTemperature() < TEMPERATURE_UNDER )
{
cError = ERROR_UNDERTEMP;
cState = STATE_ERROR;
break;
}
else
{
Beep ( 5 ); // to signal ready
cState = STATE_SOAK;
break;
}
}
if ( cOff == YES ) // if button was pressed for two seconds
{
cMode = 0;
cState = STATE_DRAIN;
break;
}
if ( iFillOvertimeCount >= TIME_FILL_ABSENCE )
{
cError = ERROR_FILL_ABSENCE;
cState = STATE_ERROR;
break;
}
if ( ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS ) && ( ReadTemperature() > TEMPERATURE_OVER ) )
{
cError = ERROR_OVERTEMP;
cState = STATE_ERROR;
}
}
break;
}
case STATE_SOAK:
{
// monitor the temperature (for overheat)
// time the cycle
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_NONE;
iSecondCount = 0;
iFillOvertimeCount = 0;
while ( TRUE )
{
AddWaterIfNecessary();
CheckSwitches();
switch ( cMode ) // allows changes during SOAK cycle
{
case HIGH_STEAM:
{
SetHeaterPower ( PERCENT_100 ); // apply power
break;
}
case LOW_STEAM:
{
SetHeaterPower ( PERCENT_50 ); // apply power;
break;
}
}
if ( iSecondCount >= TIME_SOAK_MODE )
{
cState = STATE_WARN;
break;
}
if ( cOff == YES ) // if button was pressed for two seconds
{
cState = STATE_DRAIN;
break;
}
if ( iFillOvertimeCount >= TIME_FILL_ABSENCE )
{
cError = ERROR_FILL_ABSENCE;
cState = STATE_ERROR;
break;
}
if ( ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS ) && ( ReadTemperature() > TEMPERATURE_OVER ) )
{
cError = ERROR_OVERTEMP;
cState = STATE_ERROR;
}
}
break;
}
case STATE_WARN:
{
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_FAST;
iSecondCount = 0;
cMode = 0; // default no switches
while ( TRUE )
{
// beep once per minute
if ( ( iSecondCount % 60 == 0 ) ) // beep once per minute
{
Beep ( 1 );
delay_ms ( 100 ); // ensure it doesn't get here twice in the same iSecondCount value
}
// if timed out
if ( iSecondCount >= TIME_STOP_AND_WAIT )
{
cMode = 0; // default no switches
cState = STATE_DRAIN;
break;
}
if ( cOff == YES ) // if button was pressed for two seconds
{
cState = STATE_DRAIN;
break;
}
AddWaterIfNecessary();
CheckSwitches();
if ( cMode != 0 ) // if a switch was pressed
{
cState = STATE_SOAK; // repeat the soak cycle
break;
}
}
break;
}
case STATE_DRAIN:
{
SetHeaterPower ( OFF ); // heater power off
cError = ERROR_NONE; // default
cLedFlash = LED_FLASH_SLOW;
delay_ms ( 1000 );
// check temperature
if ( ReadTemperature() <= TEMPERATURE_DRAIN_LIMIT ) // if cool enough to drain immediately
{
// open for the complete drain time
output_high ( RELAY_DRAIN ); // open drain valve
iSecondCount = 0;
while ( iSecondCount < TIME_DRAIN ); // drain time
output_low ( RELAY_DRAIN ); // close drain valve
delay_ms ( 1000 );
cMode = 0; // default no switches
cState = STATE_READ_SWITCHES; // no rinse cycle necessary
}
else
{
// open FILL valve for a few seconds to cool off tank
output_high ( RELAY_FILL ); // open fill valve
delay_ms ( TIME_DRAIN_COOL_FILL );
output_low ( RELAY_FILL ); // close fill valve
// wait until it cools enough for drain valve and piping
while ( TRUE )
{
if ( cTemperatureSecondCount > TIME_TEMPERATURE_MEAS ) // if time to measure
{
if ( ReadTemperature() <= TEMPERATURE_DRAIN_LIMIT ) // if cool enough to drain
{
break;
}
}
}
// open for the complete drain time
output_high ( RELAY_DRAIN ); // open drain valve
iSecondCount = 0;
while ( iSecondCount < TIME_DRAIN ); // drain time
output_low ( RELAY_DRAIN ); // close drain valve
delay_ms ( 1000 );
// rinse cycle
output_high ( RELAY_FILL ); // open fill valve
iSecondCount = 0;
while ( iSecondCount < TIME_FILL_LIMIT ) // maximum fill time
{
if ( input ( SWITCH_LEVEL ) == LOW ) // is water level up to proper level yet?
{
break; // break out early when level switch senses water level
}
}
output_low ( RELAY_FILL ); // close fill valve
delay_ms ( 1000 );
if ( input ( SWITCH_LEVEL ) == HIGH ) // after filling, is water level not up to proper level?
{
cError = ERROR_NOT_FILLED_DURING_RINSE;
cState = STATE_ERROR;
}
else
{
// rinse cycle drain
output_high ( RELAY_DRAIN ); // open drain valve
iSecondCount = 0;
while ( iSecondCount < TIME_DRAIN ); // allow drain time
output_low ( RELAY_DRAIN ); // close drain valve
cMode = 0; // default no switches
cState = STATE_READ_SWITCHES;
}
}
break;
}
case STATE_ERROR:
{
// always turn off everything
AllRelaysOff();
FlashErrorCode ( cError );
// don't change state so it will come directly back here until reset is pressed
}
}
}
}
void CheckSwitches ( void )
{
if ( cSwitch1on == YES )
{
cSwitch1on = NO;
cSwitch2on = NO;
cMode = HIGH_STEAM;
output_high ( LED_1 );
output_low ( LED_2 );
}
if ( cSwitch2on == YES )
{
cSwitch1on = NO;
cSwitch2on = NO;
cMode = LOW_STEAM;
output_low ( LED_1 );
output_high ( LED_2 );
}
}
void AddWaterIfNecessary ( void )
{
if ( input ( SWITCH_LEVEL ) == HIGH ) // low water
{
if ( cMode == LOW_STEAM ) // if low power mode
{
SetHeaterPower ( PERCENT_75 ); // apply more power during water add
}
output_high ( RELAY_FILL ); // open fill valve
delay_ms ( TIME_ADD_WATER_INCR );
output_low ( RELAY_FILL ); // close fill valve
delay_ms ( TIME_ADD_WATER_DELAY );
}
else
{
iFillOvertimeCount = 0; // reset count if water is up to level
}
}
void FlashErrorCode ( char cError )
{
char cCnt;
cLedFlash = LED_FLASH_NONE; // no flashing from interrupt
for ( cCnt = 0; cCnt < cError; cCnt++ )
{
output_high ( LED_1 );
output_high ( LED_2 );
delay_ms ( 100 );
output_low ( LED_1 );
output_low ( LED_2 );
delay_ms ( 200 );
}
delay_ms ( 2000 );
}
void AllRelaysOff ( void )
{
output_low ( RELAY_HEAT_A );
output_low ( RELAY_HEAT_B );
output_low ( RELAY_SSR );
output_low ( RELAY_FILL );
output_low ( RELAY_DRAIN );
output_low ( FAN );
}
void Beep ( char cCount )
{
char cCnt;
long iCnt;
for ( cCnt = 1; cCnt <= cCount; cCnt++ )
{
disable_interrupts ( GLOBAL ); // so beeper isn't buzzy due to interruption
for ( iCnt = 0; iCnt < BEEP_CYCLES; iCnt++ )
{
output_high ( BEEPER );
delay_us ( BEEPER_PERIOD_DELAY / 2 );
output_low ( BEEPER );
delay_us ( BEEPER_PERIOD_DELAY / 2 );
}
enable_interrupts ( GLOBAL );
delay_ms ( 1000 );
}
}
void SetHeaterPower ( char cLevel )
{
if ( cLevel == OFF )
{
output_low ( RELAY_HEAT_A ); // turn off both relays
output_low ( RELAY_HEAT_B );
cPower = OFF; // power handled by interrupt
output_low ( FAN ); // turn off the SSR cooling fan
}
else
{
output_high ( FAN ); // turn on the SSR cooling fan
output_low ( RELAY_HEAT_A ); // apply power
delay_ms ( 500 );
output_low ( RELAY_HEAT_B ); // apply power
delay_ms ( 500 );
cPower = cLevel; // power handled by interrupt
}
}
char ReadTemperature ( void )
{
// initiate temperature reading
ResetDS1820();
cDataOut = DS1820_SKIP_ROM;
WriteDS1820();
cDataOut = DS1820_CONVERT_T;
WriteDS1820();
// wait until acquired
WaitForConversion();
// retrieve temperature reading
ResetDS1820();
cDataOut = DS1820_SKIP_ROM;
WriteDS1820();
cDataOut = DS1820_READ_SCRATCHPAD;
WriteDS1820();
ReadDS1820();
cTemperatureSecondCount = 0; // reset count
return ( char ) ( iDataIn / 2 );
}
void ResetDS1820 ( void )
{
output_low ( DS1820_DATA_PIN ); // low
delay_us ( 500 ); // reset pulse width
output_float ( DS1820_DATA_PIN ); // high
delay_us ( 500 ); // presence pulse width
}
void WriteDS1820 ( void ) // ~70uS per bit
{
disable_interrupts ( GLOBAL );
for ( cShiftBit = 1; cShiftBit <= 8; ++cShiftBit )
{
output_low ( DS1820_DATA_PIN );
delay_us ( 5 );
output_bit ( DS1820_DATA_PIN, shift_right ( &cDataOut, 1, 0 ) );
delay_us ( 60 );
output_float ( DS1820_DATA_PIN );
delay_us ( 5 ); // recovery time between slots
}
enable_interrupts ( GLOBAL );
}
void ReadDS1820 ( void ) // ~70uS per bit
{
iDataIn = 0;
disable_interrupts ( GLOBAL );
for ( cShiftBit = 1; cShiftBit <= 16; ++cShiftBit )
{
output_low ( DS1820_DATA_PIN );
delay_us ( 5 );
output_float ( DS1820_DATA_PIN );
delay_us ( 5 );
shift_right ( &iDataIn, 2, input ( DS1820_DATA_PIN ) ); // sample bit
delay_us ( 55 ); // includes recovery time between slots
}
enable_interrupts ( GLOBAL );
ResetDS1820(); // terminate remainder of scratchpad register transmission
}
void WaitForConversion ( void )
{
disable_interrupts ( GLOBAL );
while ( TRUE )
{
output_low ( DS1820_DATA_PIN );
delay_us ( 5 );
output_float ( DS1820_DATA_PIN );
delay_us ( 5 );
if ( input ( DS1820_DATA_PIN ) == 1 )
{
break;
}
delay_us ( 55 );
}
enable_interrupts ( GLOBAL );
}