Tweaked the firmware and made a number of major changes to reduce current in all modes as well as number of instructions executed when not in standby mode--i.e., when liquid has been detected or if a probe error has occurred. Among the changes are:
- Only the 31kHz LFINTOSC is used for all modes. With the PWM module taking care of energizing the piezo transducer it's no longer necessary to use the 500kHz clock and toggle it on and off in the ISR. The 500kHz clock is the default frequency upon reset and is used only during initialization of various SFRs.
- WDT timeout is permanently set to 512 ms.
- 8 probe readings instead of 5 are stored in
PXXval. Because number of instructions has been reduced, even with just a 31kHz clock, time interval between probe reads is usually less than 10ms. So debouncing the probes takes less than 100ms. - States
_begin,_insta_plo, and_insta_phi, and functionsInstaPLO(),InstaPHI(), andDisableAudiblehave been discarded. - Audible indicator is now only disabled when entering standby mode. It isn't necessary to disable it in any other mode since only standby mode needs it shut down.
- Variables
PLOlevel,PHIlevel,plo,phi,buzzinghave been discarded. These have been found to be redundant and to increase execution time. - Timer1 interrupt is always enabled; only global interrupt is enabled/disabled when audible indicator is enabled/disabled
- Timer0 is no longer used. There is now no fixed time between reads when not in standby mode, although the time between probe reads has been determined to be 9.5 to 9.8ms depending on the mode, excluding interrupt service. Measured ISR execution time when low probe is immersed or when both low and high probes are immersed is ~3.1ms or ~3.2ms depending on whether the buzzer is being turned on or off.
- Probe reading and determination of status/mode have been highly optimized. And for standby mode no function calls outside main() are made, in order to reduce execution time.
- Although the use of
switch / caseis more elegant, given the way mikroC implements it, execution time takes a tad longer than using a series ofifstatements. So in the ISR the probe error routine now usesif / elsestatements. ISR times using switch / case are ~4.8ms, ~5.3ms, ~5.8ms, ~6.4ms for the first beep, first pause, second beep, second pause, respectively. Withif / elsetimes are ~4.3ms, ~4.9ms, ~5.6ms and ~ 5.4ms, respectively. - Because of the 31kHz clock, the buzzer frequency of 2.15kHz can no longer be attained with the PWM module. The best that can obtained is 1.94kHz.
- Left shifting of bits in
PXXvalis now done after determination of status/mode
/*
Liquid Level Indicator version 3
May 2012
processor = PIC12LF1840
compiler = mikroC v5.6.0
configuration word:
INTOSC with I/O on clk pin
enabled: power up timer, WDT via SWDTEN, MCLR, stack over/underflow
all else disabled
CONFIG1 :$8007 : 0x09CC
CONFIG2 :$8008 : 0x1613
To minimize power consumption BOR should be disabled and MCLR enabled (so that RA3 is not left floating)
Internal oscillator starts at 500kHz after power up then switched to 31kHz LFINTOSC after initialization of SFRs and remains at that setting throughout
*/
#define int8 unsigned char
#define on 1
#define off 0
#define input 1 // for TRISx
#define output 0 // for TRISx
#define analog 1 // for ANSELx
#define digital 0 // for ANSELx
#define buzz LATA.f2
#define an_plo ANSELA.f1
#define an_phi ANSELA.f4
#define tris_plo TRISA.f1
#define tris_phi TRISA.f4
#define wpu_plo WPUA.f1
#define wpu_phi WPUA.f4
#define ch_plo 0 // comparator input channel for low probe
#define ch_phi 1 // comparator input channel for high probe
#define t1h_fill 256-15 // TMR1H initial value for audible indicator when low probe immersed or low and high probe immersed
#define t1h_beep1 256-30 // TMR1H initial value for first beep during probe error
#define t1h_pause1 256-19 // TMR1H initial value for first pause during probe error
#define t1h_beep2 256-30 // TMR1H initial value for second beep during probe error
#define t1h_pause2 256-122 // TMR1H initial value for second pause during probe error
#define osc31khz 0b0 // 31kHz LFINTOSC, for use with OSCCON
#define wdt512ms 0b10011 // WDT time out = 512ms, for use with WDTCON
int8 PLOval; // stores the last eight low probe readings
int8 PHIval; // stores the last eight high probe readings
enum {_bouncing, _standby, _plo_immersed, _plophi_immersed, _phi_immersed}
STATE = _bouncing, // current state
PREVSTATE = _bouncing; // previous state
enum {_beep1, _pause1, _beep2, _pause2} STATEPERROR; // for use with buzzer sound pattern when probe error detected
// ===========================================================================================
// Functions
// ===========================================================================================
void IniReg()
{
ANSELA = digital;
TRISA = output;
PORTA = 0;
an_plo = analog;
an_phi = analog;
tris_plo = input;
tris_phi = input;
WPUA = 0; // disable individual pull ups
OPTION_REG = 0; // global pull ups enabled
DACCON0 = 0; // DAC off, DAC is not output on DACOUT pin, Vdd as positive source
//DACCON1 = 0b10000; // 0x10, Vref = 16/32 = 50% of Vdd
DACCON1 = 0b11000; // 0x18, Vref = 24/32 = 75% of Vdd
CM1CON0 = 0b10; // comparator off, comp output polarity not inverted, comp output internal only,
// comparator in low power low speed mode, hysteresis enabled
CM1CON1 = 0b10000; // comparator interrupts disabled, C1VP connected to DAC, C1VN connected to C1N0-
// piezoelectric transducer empirically determined to be loudest at 2.15kHz
// Current draw of the transducer has been determined to be directly proportional to duty cycle.
// Audio volume of the transducer is proportional to duty cycle, but whether it is linear, logarithmic, or otherwise, is unknown
// PWM Period = (PR2 + 1) x 4 x Tosc x (TMR2 Prescale Value), where Tosc = 1/Fosc
// Duty Cycle = (CCPR1L:CCP1CON<5:4>) / [4 x (PR2 + 1)]
// Given PR2 = 3, timer2 prescale = 1, CCPR1L:CCP1CON<5:4> = 4, Fosc = 31kHz,
// PWM period = 516us (freq = 1.937kHz) and duty cycle = 25%
T2CON = 0; // prescale = 1:1, postscale = 1:1, timer2 off
PR2 = 3;
CCPR1L = 0b1; // with CCPR1L = 0b1 and CCP1CON<5:4> = 0b00, CCPR1L:CCP1CON<5:4> = 0b100 = 4 decimal
CCP1CON = 0b1100; // PWM mode with P1A active high, P1B disabled, CCP1CON<5:4> = 0b00
// initialize probe readings to zero and levels to zero
PLOval = 0;
PHIval = 0;
PIE1.TMR1IE = 1; // timer1 interrupt enabled
INTCON.PEIE = 1; // peripheral interrupt enabled. Global interrupt is by default disabled upon any reset. GIE is enabled only when timer1 is enabled (i.e., when buzzer needs to be sounded)
OSCCON = osc31khz; // default INTOSC frequency = 500khz. Change to 31kHz LFINTOSC after initialization.
WDTCON = wdt512ms; // WDT time out = 512ms. This is the sleep time between probe reads when in Standby mode
// longer sleep times would be better from a power consumption perspective
// but would be too long when the user starts using the unit for its intended purpose -- to detect liquid and as soon as possible
} // void IniReg()
// turn on buzzer
void BuzzOn()
{
TMR2 = 0;
PIR1.TMR2IF = 0;
T2CON.TMR2ON = 1;
}
// turn off buzzer
void BuzzOff()
{
buzz = off;
T2CON.TMR2ON = 0;
}
// audible indicator is armed, with the particular sequence of sounds emitted depending on
// whether liquid has reached low probe, low and high probe, or just the high probe
void EnableAudible()
{
TMR1L = 0;
PIR1.TMR1IF = 0;
INTCON.GIE = 1;
BuzzOn();
}
// both low and high probes not immersed
// PLOval = PHIval = 0
void Standby()
{
STATE = _standby;
BuzzOff(); // following three statements disable audible indicator
T1CON.TMR1ON = 0; //
INTCON.GIE = 0; //
wpu_plo = 1; // enable weak pullup for low probe
wpu_phi = 1; // enable weak pullup for high probe
}
// low probe immersed -- cup nearly full
void PLOimmersed()
{
if (PREVSTATE != _plo_immersed)
{
STATE = _plo_immersed;
TMR1H = t1h_fill;
T1CON = 0b1100001; // Timer1 clock source is system clock (FOSC), prescale = 1:4, timer1 oscillator off, timer1 on
// given 31kHz clock and TMR1H ini value = 15, timer1 tick is ~500ms
EnableAudible();
}
}
// low and high probes both immersed -- cup is full
void PLOPHIimmersed()
{
if (PREVSTATE != _plophi_immersed)
{
STATE = _plophi_immersed;
TMR1H = t1h_fill;
T1CON = 0b1000001; // Timer1 clock source is system clock (FOSC), prescale = 1:1, timer1 oscillator off, timer1 on
// given 31kHz clock and TMR1H ini value = 15, timer1 tick is ~125ms
EnableAudible();
}
}
// probe error -- only high probe is immersed
void PHIimmersed()
{
if (PREVSTATE != _phi_immersed)
{
STATE = _phi_immersed;
TMR1H = t1h_beep1;
T1CON = 0b1000001; // Timer1 clock source is system clock (FOSC), prescale = 1:1, timer1 oscillator off, timer1 on
EnableAudible();
STATEPERROR = _beep1;
}
}
void DetermineState()
{
asm clrwdt
// ===============================
// Read Probes -- for modes other than standby
// Individual weak pull ups are enabled prior to reading and disabled after reading to minimize current draw since the probes are immersed and weak pull ups will draw current
// DAC, weak pull ups, and comparator are turned on before reading and then turned off afterwards to minimize power consumption
// current reading stored in PXXval.f0
// ===============================
CM1CON0.C1ON = 1; // turn on comparator (current draw ~4.5uA)
DACCON0.DACEN = 1; // turn on DAC (current draw ~19uA)
wpu_plo = 1; // enable low probe weak pull up
CM1CON1.C1NCH = ch_plo;
if (CMOUT)
PLOval.f0 = 1;
wpu_plo = 0; // disable low probe weak pull up
wpu_phi = 1; // enable high probe weak pull up
CM1CON1.C1NCH = ch_phi;
if (CMOUT)
PHIval.f0 = 1;
wpu_phi = 0; // disable high probe weak pull up
DACCON0.DACEN = 0; // turn off DAC
CM1CON0.C1ON = 0; // turn off comparator
// ===============================
// Determine state/mode
// ===============================
if (!PHIval)
{
if (!PLOval)
Standby();
else if (PLOval == 0xFF)
PLOimmersed();
}
else if (PHIval == 0xFF)
{
if (PLOval == 0xFF)
PLOPHIimmersed();
else if (!PLOval)
PHIimmersed();
}
// all bits (readings) of PXXval can now be shifted left
// discard PXXval.f7 and make PXXval.f0 ready for the new reading
PLOval <<= 1;
PHIval <<= 1;
PREVSTATE = STATE;
} // void DetermineState()
void interrupt()
{
if (PIR1.TMR1IF)
{
if (STATE == _phi_immersed)
{
if (++STATEPERROR > _pause2)
STATEPERROR = _beep1;
if (!STATEPERROR)
{
BuzzOn();
TMR1H = t1h_beep1; // given 31kHz, system clock as clock source, prescale = 1:1, TMR1H ini value = 30, timer1 tick is ~250ms
}
else if (STATEPERROR == _pause1)
{
BuzzOff();
TMR1H = t1h_pause1; // given 31kHz, system clock as clock source, prescale = 1:1, TMR1H ini value = 19, timer1 tick is ~160ms
}
else if (STATEPERROR == _beep2)
{
BuzzOn();
TMR1H = t1h_beep2; // given 31kHz, system clock as clock source, prescale = 1:1, TMR1H ini value = 30, timer1 tick is ~250ms
}
else
{
BuzzOff();
TMR1H = t1h_pause2; // given 31kHz, system clock as clock source, prescale = 1:1, TMR1H ini value = 122, timer1 tick is ~1000ms
}
} // if (STATE == _phi_immersed)
else // if (STATE == _plo_immersed || STATE == _plophi_immersed)
{
TMR1H = t1h_fill;
if (T2CON.TMR2ON)
BuzzOff();
else
BuzzOn();
}
PIR1.TMR1IF = 0;
} // if (PIR1.TMR1IF)
} // void interrupt()
void main()
{
IniReg();
while(1)
{
if (STATE != _standby)
DetermineState(); // just one function call to reduce execution time
else
{
asm sleep // WDT is automatically cleared right before sleep and after waking up
// MCU wakes up upon WDT timeout
// Reduction of MCU awake time during Standby mode reduces current draw
// Ways by which instructions executed when MCU is awake are reduced:
// 1. Probe reading is stored in PXXval only if reading is high
// 2. Weak pull ups are kept on since they don't consume current unless probes are immersed.
// 3. Standby mode is when PLOval = PHIval = PLOlevel = PHIlevel = 0 therefore we need only monitor for instantaneous readings that are high
// 4. Reduction/elimination of function calls and using inline code instead
// DAC and voltage comparator are turned on before reading and then turned off afterwards to minimize power consumption
DACCON0.DACEN = 1; // turn on DAC
CM1CON0.C1ON = 1; // turn on comparator
CM1CON1.C1NCH = ch_phi;
if (CMOUT)
{
STATE = _bouncing;
PLOval.f0 = 1;
}
CM1CON1.C1NCH = ch_plo;
if (CMOUT)
{
STATE = _bouncing;
PHIval.f0 = 1;
}
CM1CON0.C1ON = 0; // turn off comparator
DACCON0.DACEN = 0; // turn off DAC
}
} // while(1)
} // void main()
No comments:
Post a Comment