/Designs/HAM_Constructions/IBP20/SW/V_1_1/ibp2.c
0,0 → 1,1247
//**********************************************************************
// INFO BATTERY PACK
//**********************************************************************
// (c) OK1XGL 2008
// verze 1.0 - uvodni verze
// 1.1 - referencni teplota pro samovybijeni se uklada pri zadani doby mereni samovybijeni
//
//
 
#include "ibp2.h"
 
 
// nastavi orientaci a klidovy stav portu
//
void init_ports (void) {
set_tris_a (0b00101011);
output_a (0b000000000);
 
set_tris_b (0b11010000);
output_b (0b00100000);
port_b_pullups (FALSE);
}
 
 
//////////////////////////////////////////////////
//
// iterrupty
// cele preruseni zabere 33+xx+30 cyklu
//
//////////////////////////////////////////////////
 
// periodicky casovac
//
#INT_TIMER1
void tmr1_int (void) {
// natahovani casovace
set_timer1 (S_TICK_TIME);
s_tick = 1;
 
if (l_timer == 0) {
l_timer = L_TICK_TIME;
l_tick = 1;
blink = ~blink;
if (blink)
slow_blink = ~slow_blink;
}
else
l_timer--;
}
 
 
 
///////////////////////////////////////////////////
//
// FUNKCE PRO PRACI S TEPLOMEREM
//
//////////////////////////////////////////////////
 
// testuje pritomnost teplomeru, pri pritomnosti vraci 1
// zabere 1060 taktu
//
int8 TM_present() {
int1 present;
 
output_low (TM_PIN);
output_fixed (TM_PIN);
delay_us (500);
disable_interrupts (GLOBAL);
output_float (TM_PIN);
if(!input (TM_PIN)) {
enable_interrupts (GLOBAL);
return (0);
}
delay_us (65);
present = input(TM_PIN);
enable_interrupts (GLOBAL);
delay_us (440);
if (present)
return (0);
else
return (1);
}
 
 
// precte bajt z teplomeru
//
int8 TM_read_byte() {
int8 i,data;
 
for(i = 0; i < 8; ++i) {
disable_interrupts (GLOBAL);
output_low (TM_PIN);
output_fixed (TM_PIN);
delay_us (2);
output_float (TM_PIN);
delay_us (8);
shift_right (&data,1,input(TM_PIN));
enable_interrupts (GLOBAL);
delay_us (55);
}
return (data);
}
 
 
// zapise bajt do teplomeru
//
void TM_write_byte(int8 data) {
int8 i;
 
for (i = 0 ; i < 8; ++i) {
disable_interrupts (GLOBAL);
output_low (TM_PIN);
output_fixed (TM_PIN);
delay_us (5);
if (shift_right (&data,1,0))
output_float (TM_PIN);
delay_us (60);
output_float (TM_PIN);
delay_us (2);
enable_interrupts (GLOBAL);
}
}
 
 
// vraci 0 pri ukonceni mereni
// vraci 1 pri rozpracovanem mereni
// zabere max 1100 taktu na 1 cyklus
// potrebuje 10 volani pro zmereni teploty
//
int8 measure_temp () {
static int8 phase;
static int8 tmp;
int8 tmp2;
 
switch (phase) {
case 0:
if (! TM_present ()) { // over pritomnost teplomeru
phase = 0;
TB_val = TB_DEFAULT;
return (0); // teplomer neni pritomen, dal nelze pokracovat
}
break;
 
case 1:
TM_write_byte (0xcc); // preskoc na dalsi sadu prikazu
break;
 
case 2:
TM_write_byte (0x44); // prikaz spust mereni teploty
output_high (TM_PIN); // zapni tvrdy pullup
output_fixed (TM_PIN); //
break;
 
case 3:
case 4:
case 5: // cekej nejmene 750 ms
break;
 
case 6:
output_float (TM_PIN); // vyppni tvrdy pullup
output_low (TM_PIN); //
if (! TM_present ()) { // over pritomnost teplomeru
phase = 0;
TB_val = TB_DEFAULT;
return (0); // teplomer neni pritomen, dal nelze pokracovat
}
break;
 
case 7:
TM_write_byte (0xcc); // preskoc na dalsi sadu prikazu
break;
 
case 8:
TM_write_byte (0xbe); // prikaz precti zmerenou teplotu
break;
 
case 9:
tmp = TM_read_byte () >> 4; // precti LSB bajt s teplotou a zahod destiny stupne
break;
 
case 10:
tmp |= TM_read_byte () << 4; // precti MSB bajt s teplotou a sloz s LSB
tmp2 = tmp & 0x7F; //
if (tmp2 < 80) { // teplota je v moznem rozsahu (nahrada kontroly CRC - bylo by casove prilis dlouhe)
if (tmp & 0x80)
TB_val = 0; // zapornou teplotu povazujeme za nulovou
else
TB_val = tmp2;
}
phase = 0;
return (0);
break;
 
default:
phase = 0;
TB_val = TB_DEFAULT;
return (0);
}
phase++;
return (1);
}
 
 
 
//////////////////////////////////////////////////////
//
// FUNNKCE PRO MERENI NAPETI A PROUDU
//
//////////////////////////////////////////////////////
 
// meri velikost proudu z/do baterie a prumeruje z 8 mereni
// zabere max 1760 taktu (1760 us)
//
int16 measure_IB (void) {
int16 out;
 
set_adc_channel (IB_CAN);
delay_us (100);
out = read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
delay_us (100);
out += read_adc (ADC_START_AND_READ);
out = out >> 3;
return (out);
}
 
 
// meri velikost napeti baterie a provadi klouzavy prumer z 8 mereni
// zabere max 320 taktu (320 us)
//
int16 measure_VB (void) {
static int16 smpl [8];
static int8 i;
int16 out;
 
if (i >= 8-1)
i = 0;
else
i++;
 
set_adc_channel (VB_CAN);
delay_us (100);
smpl [i] = read_adc (ADC_START_AND_READ);
 
out = smpl [0] + smpl [1] + smpl [2] + smpl [3] +
smpl [4] + smpl [5] + smpl [6] + smpl [7];
out = out >> 3;
return (out);
}
 
 
// meri velikost vstupniho napeti zdroje pro nabijeni
// zabere max 200 taktu (200 us)
//
int16 measure_VS (void) {
int16 out;
 
set_adc_channel (VS_CAN);
delay_us (100);
out = read_adc (ADC_START_AND_READ);
return (out);
}
 
 
// meri vystupni napeti OZ pro mereni proudu pri nulovem odberu - pro kompenzaci ofsetu OZ
//
int16 measure_offset (void) {
int16 out;
int8 i;
 
// vypni vsechny spotrebice
init_ports ();
disable_interrupts (GLOBAL);
ADIE = 1;
PEIE = 1;
delay_ms (500);
 
// mer napeti OZ a vypocti prumer z 8 mereni
set_adc_channel (IB_CAN);
delay_us (100);
out = 0;
for (i = 0; i < 8; i++) {
read_adc (ADC_START_ONLY);
sleep ();
out += read_adc (ADC_READ_ONLY);
delay_us (100);
restart_wdt ();
}
out = out >> 3;
 
ADIE = 0;
PEIE = 0;
enable_interrupts (GLOBAL);
return (out);
}
 
 
/////////////////////////////////////////////////
//
// FUNKCE PRO PRACI S LED
//
/////////////////////////////////////////////////
 
// rozsviti vsechny led
//
void leds_all_on () {
output_high (LED4_R);
output_high (LED3_Y);
output_high (LED2_Y);
output_high (LED1_G);
}
 
 
// zhasne vsechny led
//
void leds_all_off () {
output_low (LED4_R);
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G);
}
 
 
// signalizuje kapacitu baterie pri nabijeni
// vraci cislo odpovidajici stavu nabiti
// zabere max 40 cyklu
//
int8 leds_chrg (void) {
if (B_cap >= B_cap_4_4) {
leds_all_on ();
return (4); // baterie plne nabita
}
 
if (B_cap > B_cap_3_4) {
output_high (LED4_R);
output_high (LED3_Y);
output_high (LED2_Y);
if (slow_blink)
output_high (LED1_G);
else
output_low (LED1_G);
return (3);
}
 
if (B_cap > B_cap_2_4) {
output_high (LED4_R);
output_high (LED3_Y);
if (slow_blink)
output_high (LED2_Y);
else
output_low (LED2_Y);
output_low (LED1_G);
return (2);
}
 
if (B_cap > B_cap_1_4) {
output_high (LED4_R);
if (slow_blink)
output_high (LED3_Y);
else
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G);
return (1);
}
 
if (slow_blink)
output_high (LED4_R);
else
output_low (LED4_R);
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G);
return (0);
}
 
 
// signalizuje chybu
//
void leds_err () {
output_low (LED1_G);
output_low (LED4_R);
if (blink) {
output_high (LED2_Y);
output_high (LED3_Y);
}
else {
output_low (LED2_Y);
output_low (LED3_Y);
}
}
 
 
// signalizuje neznamou aktualni kapacitu baterie
//
void leds_invalid_cap () {
output_low (LED2_Y);
output_low (LED3_Y);
if (blink) {
output_high (LED1_G);
output_high (LED4_R);
}
else {
output_low (LED1_G);
output_low (LED4_R);
}
}
 
 
// signalizuje zbyvajici kapacitu baterie pri vybijeni
//
void leds_dis () {
if (B_cap < B_cap_1_4) {
output_low (LED1_G);
output_low (LED2_Y);
output_low (LED3_Y);
output_high (LED4_R);
return;
}
 
if (B_cap < B_cap_2_4) {
output_low (LED1_G);
output_low (LED2_Y);
output_high (LED3_Y);
output_low (LED4_R);
return;
}
 
if (B_cap < B_cap_3_4) {
output_low (LED1_G);
output_high (LED2_Y);
output_low (LED3_Y);
output_low (LED4_R);
return;
}
 
output_high (LED1_G);
output_low (LED2_Y);
output_low (LED3_Y);
output_low (LED4_R);
}
 
 
// signalizuje temer vybitou baterii
//
void leds_VB_low () {
output_low (LED1_G);
output_low (LED2_Y);
output_low (LED3_Y);
if (slow_blink)
output_high (LED4_R);
else
output_low (LED4_R);
}
 
 
// zobrazi cislo v rozsahu 0 - 9 v binarnim kodu na ledkach
//
void num_to_leds (int8 num) {
 
switch (num) {
case 0:
output_low (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 1:
output_high (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 2:
output_low (LED4_R); // nejnizsi bit
output_high (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 3:
output_high (LED4_R); // nejnizsi bit
output_high (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 4:
output_low (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_high (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 5:
output_high (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_high (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 6:
output_low (LED4_R); // nejnizsi bit
output_high (LED3_Y);
output_high (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 7:
output_high (LED4_R); // nejnizsi bit
output_high (LED3_Y);
output_high (LED2_Y);
output_low (LED1_G); // nejvyssi bit
break;
case 8:
output_low (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_low (LED2_Y);
output_high (LED1_G); // nejvyssi bit
break;
case 9:
output_high (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_low (LED2_Y);
output_high (LED1_G); // nejvyssi bit
break;
default:
output_low (LED4_R); // nejnizsi bit
output_low (LED3_Y);
output_low (LED2_Y);
output_low (LED1_G); // nejvyssi bit
}
}
 
 
///////////////////////////////////////////////////////
//
// FUNKCE PRO CTENI TLACITEK A KONTAKTU
//
///////////////////////////////////////////////////////
 
// je pripojena zatez?
// vraci 1 pri pripojene zatezi
//
int1 is_load () {
int1 load;
port_b_pullups (TRUE);
delay_us (100);
load = input (LOAD_ON); // precti stav kontaktu pripojeni zateze
port_b_pullups (FALSE);
return (load);
}
 
 
// cte stav nastavovacich tlacitek
// vraci masky stisknutych tlacitek, 0 = klidovy stav
//
int8 read_keys () {
static int1 up_key_old;
static int1 set_key_old;
static int8 keys;
int1 key_state;
 
key_state = input (UP);
if (key_state != up_key_old)
up_key_old = key_state;
else
if (! key_state)
keys |= UP_MASK;
else
keys &= ~UP_MASK;
 
key_state = input (SET);
if (key_state != set_key_old)
set_key_old = key_state;
else
if (! key_state)
keys |= SET_MASK;
else
keys &= ~SET_MASK;
return (keys);
}
 
 
///////////////////////////////////////////////////
//
// VYKONNE FUNKCE
//
///////////////////////////////////////////////////
 
// vybijeni baterie
//
void do_discharge () {
int16 IB_val;
int16 I_zero;
int16 VB_val;
int16 dec_cap;
int1 load, load_old;
int1 err;
 
#ifdef DEBUG
int16 max,min;
int16 cnt;
#endif
 
// signalizuj neduveryhodnost zbyvajici kapacity baterie
if (invalid_cap) {
blink = 1;
leds_invalid_cap ();
delay_ms (500);
leds_all_off ();
}
 
// nastaveni AD prevodniku: reference je 2.5V posun mereni proudu o 2,51V => cim vetsi prout, tim nizzsi cislo z prevodniku
setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VREF);
setup_vref (VREF_HIGH | VREF_A2 | 8); // na A2 vystup reference 2.51V
delay_ms (1);
 
I_zero = measure_offset (); // precti klidovou hodnotu, tedy offset OZ - nulovy odebirany proud
 
output_high (BATT_ON);
leds_dis (); // signalizuj zbyvajici kapacitu bateie
 
err = 0;
blink = 1;
l_tick = 0;
s_tick = 0;
 
// jeden pruchod smycku zabere max 2000 taktu
for(;;) {
restart_wdt ();
if (s_tick) {
s_tick = 0;
IB_val = measure_IB (); // mer proud odebirany z baterie
if (IB_val == 0 ) {
// proudove pretizeni
output_low (BATT_ON); // odpoj baterii
err = 1; // signalizuj chybu
}
dec_cap = I_zero - IB_val; // zbav se ofsetu OZ
if (B_cap > dec_cap)
B_cap = B_cap - dec_cap; // uprav zbyvajici kapacitu baterie
 
if (! err)
VB_val = measure_VB (); // napeti baterie je mozne merit jen pokud neni odpojena
 
#ifdef DEBUG
if (IB_val > max)
max = IB_val;
if (IB_val < min)
min = IB_val;
cnt++;
#endif
}
 
if (l_tick) {
l_tick = 0;
if (! is_load ()) {
return; // koncime, zatez odpojena
}
 
if (VB_val < VB_MIN) {
// baterie zcela vybita
B_cap = 0; // nastav prazdnou kapacitu
invalid_cap = 0; // aktualni kapacita baterie je nyni znama
return;
}
 
if (err) {
leds_err (); // signalizuj chybu
}
else {
if (VB_val < VB_LOW) {
leds_VB_low (); // signalizuj temer vybitou baterii
}
else {
leds_dis (); // signalizuj zbyvajici kapacitu bateie
}
}
 
#ifdef DEBUG
printf ("cnt:%lu z:%lu max:%ld delta:%u dec:%lu\n\r",cnt,I_zero,max,(int8)(max-min),dec_cap);
max = 0;
min = 1024;
cnt = 0;
#endif
}
}
}
 
 
// proces nabijeni baterie
//
void do_charge () {
int1 chrg;
int1 err;
int16 I_zero;
int16 IB_val;
int16 inc_cap;
int32 fast_chrg;
int8 pwm;
int16 duty;
 
#ifdef DEBUG
int16 max,min;
int16 vs_val;
#endif
 
// vypocti mez do ktere budeme nabijet vyssim proudem
// fast_chrg = (B_cap_4_4 - B_cap) / 64;
fast_chrg = (B_cap_4_4 - B_cap) / 72;
fast_chrg *= chrg_eff;
fast_chrg += B_cap;
#ifdef DEBUG
// printf ("%lu %u %lu %lu\n\r\n\r",B_cap_4_4, chrg_eff,B_cap, fast_chrg);
#endif
 
err = 0;
s_tick = 0;
l_tick = 0;
pwm = 0;
chrg = 0;
 
// nastaveni AD prevodniku, reference je VDD = 5V , zadny posun napeti pri mereni proudu => cim vyssi proud, tim vyssi cislo z prevodniku
setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);
setup_vref (VREF_LOW | VREF_A2 | 2); // na A2 vystup reference 0.416V
delay_ms (1);
I_zero = measure_offset (); // precti klidovou hodnotu, tedy ofset OZ - nulovy nabijeci proud
 
if (invalid_cap == 0 &&
TB_val < TB_MAX &&
TB_VAL > TB_MIN) {
// kapacita baterie je znama a teplota baterie je v mezich
output_high (BATT_ON); // pripoj baterii
output_high (CHRG_ON); // pripoj zdroj nabijeni
delay_ms (500); // cekej na ustaleni menice
chrg = 1;
}
 
if (measure_VS () < VS_MIN) { // kontrola napeti zatizeneho menice
// napajeci zdroj je prilis mekky
err = 1; // signalizuj chybu
chrg = 0; // ukonci nabijeni
output_low (CHRG_ON); // odpoj zdroj nabijeni
output_low (BATT_ON); // odpoj baterii
delay_ms (300); // cekej na ustaleni menice
}
 
// jeden pruchod smyckou zabere max 3000 taktu
for (;;) {
restart_wdt ();
if (s_tick) {
s_tick = 0;
IB_val = measure_IB (); // mer proud dodavany do baterie 1760 taktu
if (IB_val > I_zero) // zbav se ofsetu OZ
IB_val -= I_zero;
else
IB_val = 0;
// nasledujici sekvence zabere max 300 taktu
if (chrg) {
if (IB_val == 0) { // tece vubec nejaky nabijeci proud ?
// NE netece
err = 1; // signalizuj chybu
chrg = 0; // ukonci nabijeni
output_low (CHRG_ON);
output_low (BATT_ON);
}
else {
// ANO tece
inc_cap = IB_val * chrg_eff; // uprav koeficientem ucinnosti nabijeni
inc_cap = inc_cap >> (6-1); // zbav se vynasobeni 64x a vynasob dvema, protoze krok proudu pri nabijeni je dvojnasobny proti vybijeni
B_cap = B_cap + inc_cap; // uprav kapacitu baterie
}
}
#ifdef DEBUG
if (IB_val > max)
max = IB_val;
if (IB_val < min)
min = IB_val;
#endif
}
 
if (l_tick) {
l_tick = 0;
 
measure_temp (); // mer teplotu baterie - 1100 taktu
 
if (measure_VS () < VS_MIN)
return; // koncime, napajeci zdroj byl odpojen
 
if (is_load ())
return; // koncime, je pripojena zatez
 
if (invalid_cap)
leds_invalid_cap (); // signalizace nezname aktualni kapacity baterie - nutno nejdive baterii vybit
else
if (err)
leds_err (); // signalizace chyby
else {
// signalizace nabiti baterie
if ( leds_chrg () == 4 || TB_val > TB_MAX) {
// baterie je plne nabita
output_low (CHRG_ON);
output_low (BATT_ON);
chrg = 0;
B_cap = B_cap_4_4;
}
else {
if (B_cap > fast_chrg)
duty = chrg_01C; // temer nabito dale nabijime 0.1C
else
duty = chrg_02C; // nabijeni proudem max. 0.2C
 
if (pwm == 0) {
output_high (CHRG_ON);
chrg = 1;
}
else {
if (pwm >= duty / IB_val) { // 400 taktu
output_low (CHRG_ON);
chrg = 0;
}
}
pwm++;
}
}
 
#ifdef DEBUG
restart_wdt ();
// printf ("z:%lu h:%ld add:%lu cap:%lu T:%u\n\r",I_zero,max,inc_cap,B_cap,TB_val);
// printf ("pwm:%u z:%lu max:%lu d:%lu\n\r",pwm,I_zero,max,max-min);
// printf ("%lu %lu\n\r",B_cap, fast_chrg);
printf ("z:%lu max:%lu delta:%u inc:%lu\n\r",I_zero,max,(int8)(max-min),inc_cap);
 
max = 0;
min = 1024;
#endif
}
}
}
 
 
// upravi kapacitu baterie dle samovybijeni
// zabere max. 2000 cyklu
//
void selfdischarge (int8 day_cnt) {
int8 k2;
int16 temp;
int32 dec_cap;
 
// realizuje upravu koeficientu samovybijeni podle teploty k2 = k * Z * delta_TB
// zmena Z je 1.05 na stupen
if (TB_avr24 > TB_ref)
temp = (3 * (TB_avr24 - TB_ref)) + 64;
else {
temp = 3 * (TB_ref - TB_avr24);
if (temp > 64)
temp = 0;
else
temp = 64 - temp;
}
temp = (temp * k) / 64;
k2 = (int8)temp;
 
// v prvnich 5 dnech je samovybijeni strmejsi
if (day_cnt < 5)
k2 = k2 * 2;
dec_cap = k2 * CAP_BATT;
if (B_cap > dec_cap)
B_cap -=dec_cap;
#ifdef DEBUG
// printf ("k:%u k2:%u dec_cap:%lu cap:%lu ",k,k2,dec_cap,B_cap);
#endif
}
 
 
//////////////////////////////////////////////////////
//
// PRIPRAVNE A NASTAVOVACI FUNKCE
//
//////////////////////////////////////////////////////
 
// pripravi promenne pro beh programu
//
void prepare_var () {
int16 B_cap;
int8 tmp8;
int16 tmp16;
int32 tmp32;
float tmpf;
 
// priprav meze pro signalizaci stavu vybiti baterie
B_cap = read_eeprom (B_CAP_L_ADDR);
B_cap *= 100;
tmp16 = read_eeprom (B_CAP_H_ADDR);
tmp16 *= 1000;
B_cap += tmp16; // kapacita baterie v mAh
 
B_cap_4_4 = B_cap * CAP_BATT;
// B_cap_4_4 = 100 * CAP_BATT;
B_cap_2_4 = B_cap_4_4 / 2;
B_cap_1_4 = B_cap_2_4 / 2;
B_cap_3_4 = B_cap_2_4 + B_cap_1_4;
 
// priprav omezeni nabijecich proudu
tmpf = B_cap / 10;
tmpf *= 256;
tmpf /= 4.88;
chrg_01C = (int16)tmpf;
chrg_02C = chrg_01C * 2;
 
// priprav koeficient ucinnosti nabijeni
// eff = 1+(( read_eeprom (EFF_ADDR) * 5) / 100.0); // koeficient s jednickou
// eff -= 0.1;
// eff = 64 / eff; // ucinnost nabijeni (0-1) nasovena 64
// chrg_eff = (int8)(eff); // ucinnost nabijeni vhodna pro celociselnou aritmetiku
 
chrg_eff = CHRG_EFF_TAB [read_eeprom (EFF_ADDR)]; // toto je vyhodnejsi varianta
 
// priprav koeficient samovybijeni
tmp8 = read_eeprom (LOSS_CAP_H_ADDR) * 10 + read_eeprom (LOSS_CAP_L_ADDR); // ztracena kapacita v procentech
tmp32 = B_cap;
tmp32 *= tmp8;
tmp16 = tmp32 / 100; // ztracena kapacita v mAh
tmp8 = read_eeprom (LOSS_DAY_H_ADDR) * 10 + read_eeprom (LOSS_DAY_L_ADDR); // pocet dni, za kterou se kapacita ztratila
k = tmp16 / tmp8; // strmost poklesu samovybijeni v mAh / den
 
TB_ref = read_eeprom (TB_REF_ADDR); // teplota, pri ktere k samovybijeni dochazelo
 
#ifdef DEBUG
// printf ("chrg_eff:%lu ",B_cap);
// printf ("chrg_eff:%u ",chrg_eff);
// printf ("k:%u ",k);
#endif
}
 
 
// pripravi konstantu pro odmereni 1 hod pomoci wdt
//
prepare_hour_time () {
int32 hour_time_cor;
 
// vypocti potrebny pocet tiku wdt pro odmereni jedne hodiny
hour_time_cor = 400 - hour_time;
hour_time_cor *= HOUR_TIME_WDT;
hour_time_cor /= hour_time;
hour_time = HOUR_TIME_WDT + (int16)hour_time_cor;
}
 
 
// priprav vse pro minimalizaci spotreby ve spanku
//
void prepare_sleep () {
leds_all_off ();
output_low (BATT_ON);
output_low (CHRG_ON);
setup_vref (FALSE);
}
 
 
// nastavi parametry programu
//
void do_set_par () {
int8 par_num; // cislo parametru
int8 par_val; // hodnota parametru
int8 key_timer; // pro osetreni zakmitu nastavovacich tlacitek
int8 keys; // stav nastavovacich tlacitek
int1 set_par; // jsme v rezimu nastavovani hodnoty parametru
int1 wait_release; // cekame na uvolneni tlacitka
 
leds_all_on (); // oznam, ze jsme v nastavovacim rezimu
delay_ms (1000);
 
par_num = 0;
s_tick = 0;
l_tick = 0;
key_timer = 0;
wait_release = 0;
set_par = 0;
num_to_leds (par_num); // zobraz zvoleny parametr
 
for (;;) {
restart_wdt ();
if (s_tick) {
s_tick = 0;
if (key_timer != 0)
key_timer--;
else {
key_timer = 5;
keys = read_keys (); // precti stav tlacitek
if (keys & SET_MASK) {
// tlacitko SET je stisknuto - rezim vyberu parametru
if (set_par) {
if (par_val != read_eeprom (par_num)) { // prechazime z rezimu nastaveni parametru, uloz zvoleny parametr
write_eeprom (par_num, par_val); // uloz jen pokud byl zmenen
if (par_num == LOSS_DAY_H_ADDR ||
par_num == LOSS_DAY_L_ADDR)
write_eeprom (TB_REF_ADDR,TB_avr24); // pokud byla zmenena hodnota ztracene kapacity samovybijenim, uloz TB_avr24 jako refrerencni
}
num_to_leds (par_num); // zobraz zpatky zvoleny parametr
set_par = 0;
}
else {
// vybirame parametr
if (keys & UP_MASK) {
if (! wait_release) {
if (par_num < 6)
par_num++;
else
par_num = 0;
num_to_leds (par_num); // zobrazeni vybraneho parametru
wait_release = 1; // cekame na uvolneni tlacitka UP
}
}
else
wait_release = 0; // tlacitko UP bylo uvolneno
}
}
else {
// tlacitko SET je uvolneno - rezim nastaveni parametru
if (! set_par) {
par_val = read_eeprom (par_num); // prechazime z rezimu vyberu parametru, vyzvedni zvoleny parametr
num_to_leds (par_val); // zobraz hodotu parametru
set_par = 1;
}
else {
// nastavujeme parametr
if (keys & UP_MASK) {
if (! wait_release) {
if (par_val < PAR_MAX_TAB [par_num])
par_val++;
else
par_val = 0;
num_to_leds (par_val); // zobraz hodnotu parametru
wait_release = 1; // cekame na uvolneni tlacitka UP
}
}
else
wait_release = 0;
}
}
}
}
 
if (l_tick) {
l_tick = 0;
if (measure_VS () < VS_MIN)
break; // koncime, napajeci zdroj odpojen
}
}
prepare_var (); // konec nastavovani, aktualizuj promenne programu
}
 
 
 
///////////////////////////////////////////////////
// HLAVNI FUNKCE
///////////////////////////////////////////////////
 
main () {
int1 load_old;
int1 load;
int1 no_load;
int1 start_TB; // spust mrereni teploty baterie
int16 wdt_timer; // pro odmereni hodiny pomoci wdt
int8 hour_timer; // pro odmerovani hodin
int8 day_timer; // pro odmerovani dni od nabiti
int16 TB_avr_tmp; // pro vypocet prumerne teploty
 
 
#ifdef DEBUG
int16 c;
int16 inc_cap,IB_val,I_zero;
int8 val;
#endif
 
init_ports ();
 
if (restart_cause () != WDT_TIMEOUT) {
// mereni skutecne doby behu wdt proved pro vseshny resety krome wdt
setup_oscillator (OSC_4MHZ | OSC_NORMAL);
delay_ms (100);
setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
setup_wdt (WDT_288MS);
hour_time = 0;
set_timer1 (~1000);
restart_wdt ();
for (;;) {
if (TMR1IF) {
hour_time++;
TMR1IF = 0;
set_timer1 (~1000);
}
}
}
 
setup_oscillator (OSC_4MHZ | OSC_NORMAL);
setup_timer_0 (RTCC_INTERNAL| RTCC_DIV_1);
setup_wdt (WDT_288MS);
setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
set_timer1 (S_TICK_TIME);
setup_spi (FALSE);
setup_comparator (NC_NC_NC_NC);
setup_vref (FALSE);
setup_ccp1 (CCP_OFF);
setup_adc (ADC_CLOCK_INTERNAL); // doba prevodu cca 48 uS
setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);
 
enable_interrupts (INT_TIMER1);
enable_interrupts (GLOBAL);
 
prepare_var (); // priprav promenne pro beh programu z udaju o vlastnostech baterie
prepare_hour_time ();
 
 
//////////////////////////////////////////////////
 
// uvodni bliknuti ledkou
output_high (LED1_G);
delay_ms (250);
output_low (LED1_G);
 
 
/*
#ifdef DEBUG
output_high (BATT_ON);
printf ("Ahoj %lu\n\r",hour_time);
 
for (;;) {
restart_wdt ();
if (is_load ())
break; // napajeci zdroj byl pripojen
delay_ms (100);
}
 
B_cap = B_cap_2_4;
invalid_cap = 0;
 
set_timer1 (0);
// zde se meri doba trvani funkci nebo vybraneho kodu
// selfdischarge (0); //OK
do_discharge ();
 
c = get_timer1 ();
// printf ("T:%lu %lu %u \n\r",c,B_cap,chrg_eff);
 
printf ("T:%lu\n\r",c);
output_low (BATT_ON);
delay_ms (250);
for (;;)
restart_wdt ();
#endif
*/
 
#ifdef DEBUG
invalid_cap = 0;
// B_cap = B_cap_3_4;
B_cap = B_cap_2_4;
#else
invalid_cap = 1; // nezname zbyvajici kapacitu baterie
B_cap = 0; // povazujeme ji za vybitou
#endif
 
TB_avr24 = TB_DEFAULT;
TB_avr_tmp = 0;
wdt_timer = 0;
hour_timer = 0;
day_timer = 0;
start_TB = 1;
no_load = 0;
 
// hlavni programova smycka
for (;;) {
restart_wdt ();
prepare_sleep ();
sleep ();
setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);
load = is_load (); // precti stav kontaktu pripojeni zateze
if (load != load_old)
load_old = load; // stav kontaktu neni platny
else {
if (load) {
if (! no_load) {
do_discharge (); // zatez pripojena, vybijime - ma vyssi prioritu
no_load = 1; // dalsi vybijeni je mozne az po odpojeni zateze
wdt_timer = 0;
}
}
else {
no_load = 0; // zatez byl odpojena
if (measure_VS () > VS_MIN) { // ma zdroj pro nabijeni dostatecne napeti?
if (input (SET))
do_charge (); // zatez odpojena a pripojen zdroj pro nabijeni, nabijime - ma nizsi pioritu
else
do_set_par (); // prechazime do rezimu pro nastavovani parametru programu
TB_avr_tmp = 0;
wdt_timer = 0;
hour_timer = 0;
day_timer = 0;
}
}
}
 
// reseni samovybijeni
if (wdt_timer < hour_time)
wdt_timer++;
else {
// uplynula hodina
start_TB = 1; // odstartuj mereni teploty
wdt_timer = 0; // natahni odmerovani hodiny
if (hour_timer < 23)
hour_timer++;
else {
// uplynul den
TB_avr24 = TB_avr_tmp / 24; // vypocti prumernou teplotu za den
if (day_timer < 90)
day_timer++;
else
invalid_cap = 1; // po 90 dnech uz neverime kapacite baterie
selfdischarge (day_timer); // uprav kapacitu baterie s ohledem na samovybijeni
#ifdef DEBUG
// printf ("TB:%u\n\r",TB_avr24);
#endif
TB_avr_tmp = 0;
hour_timer = 0;
}
}
 
// realizace mereni teploty - je v mnoha krocich
if (start_TB) {
if (measure_temp () == 0) {
#ifdef DEBUG
// printf("T:%u ",TB_val);
#endif
TB_avr_tmp += TB_val; // pocitame prumer teploty
start_TB = 0;
}
}
}
 
}
/Designs/HAM_Constructions/IBP20/SW/V_1_1/ibp2.h
0,0 → 1,166
//**********************************************************************
// INFO BATTERY PACK
//**********************************************************************
// (c) OK1XGL 2008
 
 
#include <16F88.h>
#device *=16
#device adc=10
 
#fuses WDT,INTRC_IO, NOPUT, NOMCLR, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG, NOPROTECT, NOFCMEN, NOIESO, CCPB3
#use delay(clock=4000000,RESTART_WDT)
#use FAST_IO (A)
#use FAST_IO (B)
 
 
// definice pinu
 
 
// vystupy LED diody
#define LED1_G PIN_A6
#define LED2_Y PIN_A7
#define LED3_Y PIN_A4
#define LED4_R PIN_B0
 
// analogove vstupy
#define I_SENSE PIN_A0
#define IB_CAN 0
#define V_SENSE PIN_A1
#define VB_CAN 1
#define TM_PIN PIN_B7
 
#define TB_CAN 6
#define REF_25V PIN_A3
 
// vstupy rizeni funkcnosti
#define SOURCE_ON PIN_B6 // analogovy vstup
#define VS_CAN 5
#define LOAD_ON PIN_B4
 
// vystupy rizeni funkcnosti
#define CHRG_ON PIN_B1
#define BATT_ON PIN_B3
 
// nastavovaci signaly
#define SET PIN_A5
#define UP PIN_B7
 
#define UP_MASK 0x01 // maska pro stisknute tlacitko UP
#define SET_MASK 0x02 // maska pro stisknute tlacitko SET
 
// debug seriovka
#ifdef DEBUG
#use rs232 (baud = 9600, xmit = PIN_B5, DISABLE_INTS)
#endif
 
 
 
#define VS_MIN_mV 8000 // minimalni vstupni napeti napajeciho zdroje pro nabijeni
 
#define VB_MIN_mV 10000 // minimalni napeti baterie - dojde k odpojeni
#define VB_LOW_mV 11000 // nizke napeti baterie - upozorneni pred odpojenim
 
#define TB_MAX 45 // max teplota baterie pri nabijeni
#define TB_MIN 5 // min teplota baterie pri nabijeni
#define TB_DEFAULT 25 // teplota baterie, kdyz teplomer neni pouzit
 
#define VREF_DIS_mV 2500
#define VREF_CHRG_mV 5000
 
#define VS_STEP_mV ((VREF_CHRG_mV / 1024.0) * ((8.2 + 1) / 1)) // rozliseni mereni vstupniho napeti mV ()
#define VB_STEP_mV ((VREF_DIS_mV / 1024.0) * ((68 + 10) / 10.0)) // rozliseni mereni napeti baterie v mV (19 mV -> 1.9 mv na clanek)
//#define VS_MIN (int16)(VS_MIN_mV / VS_STEP_mV)
#define VS_MIN (int16)44
#define VB_MIN (int16)(VB_MIN_mV / VB_STEP_mV)
#define VB_LOW (int16)(VB_LOW_mV / VB_STEP_mV)
 
#define I_STEP_mA ((VREF_DIS_mV / 1024.0) * 10 * 0.1) // rozliseni mereni proudu v mV (2.44 mA)
 
#define TICK_mS 4 // tik kratkeho casovace v ms
#define S_TICK_TIME ~4000 // hodnota pro natazeni kratkeho casovace
#define L_TICK_TIME 300 / TICK_mS //
#define HOUR_TIME_WDT 9000 // cas v ms / 400 ms nastav na 1 hod
 
#define CAP_BATT ((60.0 * 60.0 * 1000.0) / (TICK_ms * I_STEP_mA)) // pro prevod kapacity baterie na vnitrni reprezentaci
 
// adresy parametru programu v eeprom
#define B_CAP_H_ADDR 0
#define B_CAP_L_ADDR 1
#define EFF_ADDR 2
#define LOSS_CAP_H_ADDR 3
#define LOSS_CAP_L_ADDR 4
#define LOSS_DAY_H_ADDR 5
#define LOSS_DAY_L_ADDR 6
#define TB_REF_ADDR 7
 
// maximalni hodnoty parametru programu
const int8 PAR_MAX_TAB [8] = {5,
9,
9,
9,
9,
6,
9};
 
// tabulka pro ziskani koeficientu ucinnosti
// hodnoty se ziskaji ze vztahu: 64 / koeficient ucinnosti (pr: 64 / 1.2 = 53)
const int8 CHRG_EFF_TAB [10] = {71, // ucinnost 0.9
67, // 0.95
64, // 1.0
61, // 1.05
58, // 1.10
55, // 1.15
53, // 1.20
51, // 1.25
49, // 1.30
47}; // 1.35
// globalni promenne
int8 TB_val; // teplota baterie
int8 TB_ref; // teplota baterie, pri ktere se merilo samovybijeni
int8 TB_avr24; // prumerna teplota baterie za 24 hod
 
int8 l_timer; // pro dlouhy periodicky casovac
int1 s_tick; // kratky tik periodickeho casovace
int1 l_tick; // dlouhy tik periodickeho casovace
 
int1 blink; // zrcadlo pro rychle blikani ledkami
int1 slow_blink; // zrcadlo pro pomale blikani ledkami
 
int1 invalid_cap; // neplatny zaznam o aktualni kapacite baterie
int32 B_cap; // aktualini kapacita baterie
 
int16 hour_time; // cas pro odmereni 1 hod pomoci wdt
 
// promenne vypocitane z parametru programu
int32 B_cap_4_4; // mez pro plnou kapacitu baterie
int32 B_cap_3_4; // mez pro 3/4 kapacitu baterie
int32 B_cap_2_4; // mez pro 2/3 kapacitu baterie
int32 B_cap_1_4; // mez pro 1/4 kapacitu baterie
 
int16 chrg_01C; // regulace proudu na 0.1C
int16 chrg_02C; // regulace proudu na 0.2C
int8 chrg_eff; // ucinnost nabijeni
 
int8 k; // strmost samovybijeni
 
// definice potrebnych registru CPU
#bit ADIE = 0x8c.6 // povoleni interruptu od AD
#bit PEIE = 0x0b.6 // povoleni interruptu od periferii
#bit ADIF = 0x0c.6
#bit TMR1IF = 0x0c.0 // preteceni casovace 1
 
// Makro pro nastaveni pinu jao vystup (reverzni funke k output_float () )
#define output_fixed(pin) { #asm BCF ((pin) / 8 + 0x80).((pin) % 8) #endasm }
 
 
// defaultni parametry programu ulozene v eeprom
#rom 0x2100={3, // kpacita baterie tisice
5, // kapacita baterie stovky
0, // ucinnost nabijeni ve tvaru 1.xx s krokem 05
3, // ztracena kapacita samovybijenim desitky procent
0, // ztracena kapacita samovybijenim jednotky procent
3, // za dobu desitky dni
0, // za dobu jednotky dni
25} // referencni teplota pri ktere probihalo samovybijeni
/Designs/HAM_Constructions/IBP20/SW/V_1_1/ibp2.hex
0,0 → 1,454
:1000000008308A00CF2C0000FF00030E8301A100FE
:100010007F08A0000A08A8008A01A00E0408A20018
:100020007708A3007808A4007908A5007A08A6003C
:100030007B08A700831383128C308400001C2228C5
:100040000C183528220884002308F7002408F8003B
:100050002508F9002608FA002708FB0028088A006E
:10006000210E8300FF0E7F0E09008A1151280A100D
:100070008A100A1182070534093409340934093415
:100080000634093400340A108A100A1182074734F2
:10009000433440343D343A343734353433343134F6
:1000A0002F34F0308F005F308E002D14AC08031D0C
:1000B00063284B30AC00AD140430AD062D1D622812
:1000C0000830AD066428AC030C108A1122282B30AE
:1000D00065008501D03066002030860083168117C8
:1000E000831200347C3084008313000803198928AC
:1000F0000130F800BF30F7006400F70B7C28F80BE4
:100100007A284A30F700F70B832800006400800B40
:10011000782800341030FE00F701FA01FD0CFC0CC9
:10012000031C9A2883162008F7070318FA0A2108E7
:10013000FA078312FA0CF70CF90CF80CFE0B8E2858
:1001400000348E30F7007D08F8007C08F900FA01D1
:10015000F808031DB5287908F800F9010830F702FE
:10016000F808031DB528F701BD280310F81BBC28AB
:10017000F90DF80DF703B528F813000000348316C5
:10018000200803193029A800240803193029A807DA
:100190000318D1287F30A802031C30290319302905
:1001A000D5288130A807031830292808F700F8015E
:1001B000F901FA012108AC00AC172208AB002308B2
:1001C000AA001830A800A9012A1CFE282708FA074F
:1001D000031CF028F90A031DF028F80A0319A917CF
:1001E0002608F907031CF728F80A0319A917250898
:1001F000A200A2172208F8070318A917A90DF80CE6
:10020000F90CFA0CAC0CAB0CAA0C0310A80BE328ED
:100210000130F70703183029F81B1529AC0DFA0D2A
:10022000F90DF80DF70303193029AC1F2629FA0A36
:10023000031D2629F90A031D2629F80A031D26296C
:10024000F80CF90CFA0CF70A031930292108A90057
:100250002508A906A91F2E29F8173429F8133429CF
:10026000F701F801F901FA01000083120034831646
:100270002008B63CA000FA012108FC00A1170310D9
:10028000A10CA20CA30CFA0CF90CF80CF70CA00BA7
:100290003F29FC1F5629F709F809F909FA09F70A55
:1002A0000319F80A0319F90A0319FA0A8312003428
:1002B000F801F901F701FA0183162108031D6329EA
:1002C000200803197D291030FE000310FC0DFD0DE0
:1002D000F70DFA0D21087A02031D70292008770214
:1002E000031C79292008F702031CFA032108FA02EB
:1002F0000314F80DF90DFE0B65290000831200347C
:10030000831620080319462AAC0024080319462A3C
:10031000AC02031C90297F30AC070318462A9629AB
:100320008130AC02031C462A0319462A2C08F70028
:10033000F801F901FA01AB012108AA00AA17220865
:10034000A9002308A8001930AC002708A802031848
:10035000B9290130A9020318B929AA020318B92939
:10036000AB020318B929AB0AAA0AA90A2708A807E9
:10037000EB292608A9020318D4290130AA02031880
:10038000D429AB020318D429AB0AAA0A2608A90764
:100390002708A807031CEB29A90A031DEB29AA0AB1
:1003A000031DEB29AB0AEB2925088038AA020318A4
:1003B000EA290130AB020318EA29AB0A2508803884
:1003C000AA072608A907031CC829AA0A031DC829C9
:1003D000AB0AC8297A14AC0BEE29F9290310A80D31
:1003E000A90DAA0DAB0D0310FA0DF90DF80DAD0D09
:1003F000A5292D1C012A0310F80CF90CFA0CAD0CE0
:10040000042AF7030319462AAD1B2D2A0310A80D51
:10041000A90DAA0DAB0D2708A8020318192A01304F
:10042000A9020318192AAA020318192AAB02031CED
:100430003C2A2608A9020318242A0130AA0203181C
:10044000242AAB02031C3C2A25088038AA02031880
:100450002D2A0130AB02031C3C2AFA0A031D3C2A58
:10046000F90A031D3C2AF80A031D3C2AF70A03195E
:10047000462AF80CF90CFA0C2108AC002508AC0649
:10048000AC1F442AF8174A2AF8134A2AF701F80140
:10049000F901FA01000083128A11D52B8E30F70082
:1004A00083162008F7022108F9002208F800F9173E
:1004B000F7080319662A0310F908031D622AF808D1
:1004C0000319662AF90CF80CF70B5B2AA11F6D2A99
:1004D000F809F909F80A0319F90A000083128A11C8
:1004E000E82BF701F8017E08031083162018F707A0
:1004F000F70CF80CA018F707F70CF80C2019F70701
:10050000F70CF80CA019F707F70CF80C201AF707EE
:10051000F70CF80CA01AF707F70CF80C201BF707DC
:10052000F70CF80CA01BF707F70CF80C831200343B
:1005300020308316AC00A801A901AA01AB01230851
:10054000FA002208F9002108F8002008F70003103B
:10055000771CB82A2408A80725080318250FA9071F
:1005600026080318260FAA0727080318270FAB072A
:10057000AB0CAA0CA90CA80CFA0CF90CF80CF70C93
:10058000AC0BA72A83120034F701F801F901FA0134
:100590008316A801A901AA01AB0127082604250496
:1005A00024040319032B2030AC000310A00DA10D6F
:1005B000A20DA30DA80DA90DAA0DAB0D27082B02A6
:1005C000031DEC2A26082A02031DEC2A250829020D
:1005D000031DEC2A24082802031CFD2A2408A80273
:1005E0002508031C250FA9022608031C260FAA02B2
:1005F0002708031C270FAB020314F70DF80DF90DA4
:10060000FA0DAC0BD52A0000A830840083138312A6
:100610000034013003178D0083168C130C148312E1
:100620000C080313EB01EA006B08FD006A08FC00EC
:100630008316A1016430A00083128A207908EB00A0
:100640007808EA0003178D0183168C130C148312AB
:100650000C080313EE01ED006E08FD006D08FC00B0
:1006600003308316A100E830A00083128A207908A5
:10067000EE007808ED006D08EA076E0803186E0FAB
:10068000EB076B08FD006A08FC00A1207A088316BE
:10069000A3007908A2007808A1007708A000A701AC
:1006A000A6013430A5009130A4008312BF207A083F
:1006B0008316A3007908A2007808A1007708A0009B
:1006C000831237217A08B7007908B6007808B50098
:1006D0007708B4000310370CBF00360CBE00350C91
:1006E000BD00340CBC0003103F0CC3003E0CC20024
:1006F0003D0CC1003C0CC00040083C07B8003D0860
:10070000B90041080318410FB9073E08BA00420872
:100710000318420FBA073F08BB0043080318430FF2
:10072000BB076B08FD006A08FC008316A1010A30B4
:10073000A000831258217908FA007808FC00790893
:10074000FD00A1207A08F6007908F5007808F40089
:100750007708F30076088316A3007508A2007408D2
:10076000A1007308A000A701A601A5018730A4007D
:100770008312BF207A08F6007908F5007808F400A3
:100780007708F30076088316A3007508A2007408A2
:10079000A1007308A000F630A7002830A6001C3086
:1007A000A5008130A400831280297A08F600790818
:1007B000F5007808F4007708F30076088316A300A4
:1007C0007508A2007408A1007308A00083124E2AC5
:1007D0007908C5007808C4000310440DC600450D13
:1007E000C700023003178D0083168C130C1483127C
:1007F0000C0803134320F800C800033003178D00D2
:1008000083168C130C1483120C08FD00FE000A30B2
:1008100083160313A00083127122043003178D0086
:1008200083168C130C1483120C0878070313EC0046
:10083000F201F1016B08F0006A08EF0072088316FC
:10084000A3007108A2007008A10083126F0883162C
:10085000A000A701A601A50183126C088316A400BD
:10086000831298227A08F2007908F1007808F000E3
:100870007708EF0072088316A3007108A2007008C1
:10088000A10083126F088316A000A701A601A5018D
:100890006430A4008312C4227808EE007708ED00CB
:1008A000053003178D0083168C130C1483120C086B
:1008B000FD00FE000A3083160313A000831271228C
:1008C000063003178D0083168C130C1483120C084A
:1008D00078070313EC006E08FD006D08FC0083161A
:1008E000A10183126C088316A00083125821780896
:1008F000C900073003178D0083168C130C14831264
:100900000C080313AA0000343208903CE5000130C3
:10091000E6003308031C330FE602E701E80168082C
:100920008316A300831267088316A2008312660849
:100930008316A100831265088316A000A701A601F3
:100940002330A5002830A400831298227A08E800FA
:100950007908E7007808E6007708E500680883165C
:10096000A300831267088316A20083126608831609
:10097000A100831265088316A000A701A6018312B7
:1009800033088316A500831232088316A40083124D
:10099000C4227A08E8007908E7007808E6007708BA
:1009A000E50028306507B2006608B300233003185D
:1009B0002430B3078A15832D0610051285130513FD
:1009C0000034DC248611861083169D0183128A155B
:1009D0009D2D8316811364002030F700F70BEE2C59
:1009E0000000000073108312061A73148316811717
:1009F000003073180130F80083120034851305129B
:100A00002D1D052D05170614072D0513061000349E
:100A100067200B138B138B1B0A2D83160C17831265
:100A20000B170230F600FA30FC007220F60B132D83
:100A30000030F8001F08C73978049F006400203098
:100A4000F700F70B212D00000000F401F301F50180
:100A50007508073C031C442D1F1563001F192E2D1C
:100A60001E08FA0083161E08F3077A0803187A0F87
:100A7000F40764002030F700F70B3C2D0000000065
:100A80006400F50A8312282DF40CF30CF40CF30C1B
:100A9000F40CF30C1F30F40583160C1383120B13A4
:100AA000C0308B047308F8007408F9000034310872
:100AB0004302031C722D031D6D2D30084202031CDE
:100AC000722D031D6D2D2F084102031C722D031D75
:100AD0006D2D40082E020318722D05138513051283
:100AE0000614AC2D31083F02031C8D2D031D882DEB
:100AF00030083E02031C8D2D031D882D2F083D025A
:100B0000031C8D2D031D882D3C082E0203188D2DEE
:100B10000513851305160610AC2D31083B02031C86
:100B2000A82D031DA32D30083A02031CA82D031D78
:100B3000A32D2F083902031CA82D031DA32D38084F
:100B40002E020318A82D0513851705120610AC2DCB
:100B5000051785130512061000340030F8001F0831
:100B6000C73978049F0064002030F700F70BB62DDA
:100B7000000000001F151F19BB2D1E08FA00831668
:100B80001E08F3007A08F40064002030F700F70B29
:100B9000C72D0000000083121F151F19CD2D1E0840
:100BA000FA0083161E08F3077A0803187A0FF40771
:100BB00064002030F700F70BDB2D000000008312EB
:100BC0001F151F19E12D1E08FA0083161E08F307D2
:100BD0007A0803187A0FF40764002030F700F70B47
:100BE000EF2D0000000083121F151F19F52D1E08A0
:100BF000FA0083161E08F3077A0803187A0FF40721
:100C000064002030F700F70B032E00000000831271
:100C10001F151F19092E1E08FA0083161E08F30758
:100C20007A0803187A0FF40764002030F700F70BF6
:100C3000172E0000000083121F151F191D2E1E08FD
:100C4000FA0083161E08F3077A0803187A0FF407D0
:100C500064002030F700F70B2B2E000000008312F9
:100C60001F151F19312E1E08FA0083161E08F307E0
:100C70007A0803187A0FF40764002030F700F70BA6
:100C80003F2E0000000083121F151F19452E1E085D
:100C9000FA0083161E08F3077A0803187A0FF40780
:100CA000F40CF30CF40CF30CF40CF30C1F30F405FF
:100CB0007308F8007408F900831200340513061055
:100CC0002D1D652E85170516672E85130512003418
:100CD0002D1E742E2D15FE240230EE00FA30FC007D
:100CE0007220EE0B6E2EDC2483161F129F161B083B
:100CF00080392F389B00C8309D000130FC008312E2
:100D0000722008257908E8007808E700861557253D
:100D10006D112D15AD102D1064002D1C642F2D109C
:100D2000AD257908E6007808E500E508031D9D2E4D
:100D3000E608031D9D2E86116D1565086702EB0000
:100D40006808EC006608031C660FEC02B108031D7E
:100D5000B62EB008031DB62E6C082F02031CC42E3D
:100D6000031DB62E2E086B020318C42E6B08AE02AC
:100D70006C08031C6C0FAF020030031C0130B00282
:100D80000030031C0130B1026D19642F5C08063C71
:100D90000318CC2EDC01CD2EDC0A0830F8001F0829
:100DA000C73978049F0064002030F700F70BD62E77
:100DB0000000000003105C0D4C3E840083131F15DF
:100DC0001F19E02E1E08FA0083161E088000840AF0
:100DD0007A08800083124E084C078316A000831205
:100DE0004D088316A10083124F0803184F0FF92EE8
:100DF000FC2E8316A1078312500883162007A20039
:100E00002108A300831251080318510F082F0B2F3C
:100E10008316A3078312520883162207A40023080F
:100E2000A500831253080318530F172F1A2F831688
:100E3000A5078312540883162407A6002508A700D7
:100E4000831255080318550F262F292F8316A7073D
:100E50008312560883162607A8002708A9008312C4
:100E600057080318570F352F382F8316A9078312F9
:100E7000580883162807AA002908AB0083125908CE
:100E80000318590F442F472F8316AB0783125A08B4
:100E900083162A078312EE0083162B088312EF00B5
:100EA0005B0803185B0FEF07EF0CEE0CEF0CEE0C7A
:100EB000EF0CEE0C1F30EF056E08F8006F08F9001C
:100EC0007908EA007808E900AD1C942FAD10E924F8
:100ED000F808031D6C2F952F6A08023C031C7C2F19
:100EE000031D762F69080C3C031C7C2FB101B00157
:100EF000AF01AE012D12952F6D1D802F5E26942F10
:100F00006A08023C031C932F031D8A2F6908403C8A
:100F1000031C932F051385130512AD1D912F061485
:100F2000922F0610942F57258C2E8A15C12D28300C
:100F3000F8001F08C73978049F0064002030F700CC
:100F4000F70BA02F000000001F151F19A52F1E086A
:100F5000FA0083161E08F3007A08F4007308F800FC
:100F60007408F90083120034861383168613640014
:100F7000A630F700F70BBA2F83120B138B138B1BC2
:100F8000BE2F831686178312861BCB2FC0308B048F
:100F90000030F800E32F64001530F700F70BCE2F78
:100FA0007410861B7414C0308B0464009230F700F8
:100FB000F70BD82F741CE02F0030F800E32FE32F3D
:100FC0000130F800E32F00340614051685170517C5
:020FD0000034EB
:10100000F5017508073C031C23280B138B138B1B5E
:101010000628861383168613640000000000000073
:1010200000000310F40C0318861764001330F70057
:10103000F70B18280000861764000000C0308312E8
:101040008B04F50A01280034F4017408073C031CE2
:101050004A280B138B138B1B2A288613831686139F
:1010600064000000861764000230F700F70B362892
:101070008312861B3D2803103E280314F50CC03054
:101080008B0464001130F700F70B442800000000C7
:10109000F40A25287508F80000344A08003A0319B4
:1010A0007028013A03197D28033A03198128013A6F
:1010B00003198928073A03198928013A031989284D
:1010C000033A03198A28013A03199B280F3A031996
:1010D0009F28013A0319A328033A0319A928C22813
:1010E0008A11B4278A15F808031D7C28CA01193013
:1010F000A9000030F800CB28C828CC30F40000202C
:10110000C8284430F4000020861783168613831203
:10111000C828C82883168617831286138A11B42715
:101120008A15F808031D9A28CA011930A900003051
:10113000F800CB28C828CC30F4000020C828BE30E6
:10114000F4000020C8282420780ECB000F30CB05F7
:10115000C8282420780EF700F030F7057708CB0474
:101160004B087F39F30073084F3C031CBD28CB1F8D
:10117000BB28A901BD287308A900CA010030F800E6
:10118000CB28C828CA011930A9000030F800CB28A4
:10119000CA0A0130F80000342E083402F300350882
:1011A000F4002F08031C2F0FF4023608F500300856
:1011B000031C300FF5023708F6003108031C310F0D
:1011C000F60276088316A3007508A2007408A10031
:1011D0007308A000A701A601A5014830A4008A1148
:1011E0008312C4228A157A08EF007908EE00780885
:1011F000ED007708EC006F088316A30083126E08D9
:101200008316A20083126D088316A10083126C0856
:101210008316A000A701A601A50183124808831622
:10122000A4008A11831298228A157A08EF0079089F
:10123000EE007808ED007708EC002E08EC072F0888
:1012400003182F0FED0730080318300FEE07310891
:101250000318310FEF07E5102D10AD10F0016510E8
:1012600083161F129F121B0880392F389B00E23013
:101270009D000130FC008A11831272208A158A11A8
:1012800008258A157908E7007808E6002D1A5C29F8
:1012900029082C3C031C5C292908053C03185C29FF
:1012A000861586140230F300FA30FC008A11722091
:1012B0008A15F30B542965148A1197278A15790822
:1012C000F4007808F300F408031D772973082B3C19
:1012D000031C7729E5146510861086110230F3008F
:1012E0009630FC008A1172208A15F30B7029640075
:1012F0002D1CCE292D108A11AD258A157908E900FB
:101300007808E80067086902031C9329031D8C29EB
:1013100068086602031893296608E8026708031C38
:10132000670FE9029529E901E801651CCE29E80863
:10133000031DA229E908031DA229E51465108610E2
:101340008611CE296908FD006808FC008316A101FA
:10135000831248088316A0008A1183128A208A15F6
:101360007908EB007808EA00EB0CEA0CEB0CEA0CCD
:10137000EB0CEA0CEB0CEA0CEB0CEA0C0730EB057F
:101380006A08AE076B0803186B0FAF07003003182D
:101390000130B007003003180130B107AD1CD02A6E
:1013A000AD104D208A1197278A157908F400780826
:1013B000F300F408031DE02973082B3C0318D12A1D
:1013C0008A11E9248A15F808031DD12A2D1EEC295B
:1013D0008A11FE248A15D02AE51CF2298A115E267C
:1013E0008A15D02A37083102031C0E2A031D082A49
:1013F00036083002031C0E2A031D082A35082F0266
:10140000031C0E2A031D082A34082E02031C0E2A70
:101410008A11E4278A150430F8007B2A3B08310240
:10142000031C2F2A031D242A3A083002031C2F2AEA
:10143000031D242A39082F02031C2F2A031D242AE6
:101440002E08380203182F2A061405168517AD1D1D
:101450002B2A05172C2A05130330F8007B2A3F0896
:101460003102031C502A031D452A3E083002031C8A
:10147000502A031D452A3D082F02031C502A031D34
:10148000452A2E083C020318502A06140516AD1DE5
:101490004B2A85174C2A851305130230F8007B2A46
:1014A00043083102031C712A031D662A42083002D8
:1014B000031C712A031D662A41082F02031C712A8E
:1014C000031D662A2E0840020318712A0614AD1D5A
:1014D0006B2A05166C2A0512851305130130F800D6
:1014E0007B2AAD1D752A0614762A0610051285136F
:1014F00005130030F8007808043C0319832A2908F2
:101500002D3C03188F2A8610861165103708B1000C
:101510003608B0003508AF003408AE00D02A6F0896
:101520003102031CAA2A031DA52A6E083002031CDF
:10153000AA2A031DA52A6D082F02031CAA2A031D2F
:10154000A52A2E086C020318AA2A4508F2004408AE
:10155000F100AE2A4708F2004608F100F008031D2A
:10156000B42A86146514CF2A7208FD007108FC00A5
:1015700069088316A100831268088316A0008A11E7
:10158000831258218A157908FA007808FA08031D91
:10159000CF2A78087002031CCF2A86106510F00A43
:1015A00077298A15D82D6A08003A0319F32A013AD7
:1015B0000319F82A033A0319FD2A013A0319022BE9
:1015C000073A0319072B013A03190C2B033A0319A5
:1015D000112B013A0319162B0F3A03191B2B013A51
:1015E0000319202B252B0610051285130513292B13
:1015F0000614051285130513292B061005168513ED
:101600000513292B0614051685130513292B06101F
:10161000051285170513292B0614051285170513C6
:10162000292B0610051685170513292B06140516F8
:1016300085170513292B0610051285130517292B6D
:101640000614051285130517292B0610051285139C
:10165000051300348A11E4278A150430EA00FA30B1
:10166000FC008A1172208A15EA0B2F2BE5012D1040
:10167000AD10E701E91069106508EA00D3226400A3
:101680002D1C052C2D10E7080319482BE703052C0A
:101690000530E7006A10861B6A14F701AD1A77144B
:1016A0006A08770601390319592BAD126A18AD166D
:1016B0005E2B6A185D2B5D145E2B5D106A10851A17
:1016C0006A14F7012D1B77146A087706013903198C
:1016D0006D2B2D136A182D17722B6A18712BDD14C0
:1016E000722BDD105D08F8007808E800E81CDF2B9D
:1016F000691CCD2B650803178D0083168C130C1401
:1017000083120C08031366020319C82B650803171C
:101710008D000313660803178C0083168C130C15B9
:10172000831203130B08F7008B138316031755302E
:101730008D00AA308D008C148C189C2B0C1177080E
:10174000831203138B046508053C0319AB2B650852
:10175000063C031DC82B073003178D0003132B080D
:1017600003178C0083168C130C15831203130B08BC
:10177000F7008B138316031755308D00AA308D00A8
:101780008C148C18C12B0C117708831203138B0453
:101790006508EA00D3226910DE2B681CDD2BE918EE
:1017A000DC2B6508053C031CD72BE50AD82BE5018B
:1017B0006508EA00D322E914DE2BE910052C69182C
:1017C000F02B650803178D0083168C130C148312FD
:1017D0000C080313E6006608EA00D3226914052CFE
:1017E000681C042CE918032C65088A1137208A1517
:1017F000F80066020318FE2BE60AFF2BE6016608D6
:10180000EA00D322E914052CE910AD1C162CAD100A
:101810008A1197278A157908EB007808EA00EB0807
:10182000031D162C6A082B3C0318172C3F2B8A111A
:1018300009238A158A15D82D2B082A020318312C62
:101840002A082B02ED000330FE006D088316A0006D
:101850008A11831271228A1540307807E700E80167
:10186000552C2B082A02ED000330FE006D0883166C
:10187000A0008A11831271228A15E8017808E70016
:10188000E808031D472C6708403C03184A2CE80170
:10189000E701552C6708403CE7006808031C680F07
:1018A000532C0030542C003CE8006808FD00670809
:1018B000FC008316A101831249088316A0008A1137
:1018C00083128A208A15790CE800780CE700E80C6E
:1018D000E70CE80CE70CE80CE70CE80CE70CE80C6C
:1018E000E70C0330E8056708E6006508043C031CC4
:1018F0007B2C0310E60DFD016608FC008A11A12077
:101900008A157A088316A3007908A2007808A10036
:101910007708A000A701A6013430A5009130A400EB
:101920008A118312BF208A157A088316A3007908CA
:10193000A2007808A1007708A0008A11831237213D
:101940008A157A08EC007908EB007808EA00770835
:10195000E9006C083102031CCD2C031DBF2C6B0861
:101960003002031CCD2C031DBF2C6A082F02031C60
:10197000CD2C031DBF2C2E0869020318CD2C69083D
:10198000AE026A08031C6A0FAF026B08031C6B0FE0
:10199000B0026C08031C6C0FB1028A150D2E840175
:1019A00083131F308305603083168F001F129F1230
:1019B0001B0880399B0007309C008312CA01CB01B1
:1019C000CC01CD01CE01CF01D001D101D201D30193
:1019D000D401D501D601D701D801D901DA01DB0143
:1019E000DC01AD122D13DD018A1167208A15030871
:1019F0001839F70083160E08033977040E148E1475
:101A0000831503160B3C0319342D60308F006430AE
:101A1000FC008A11831272208A15853090000C30E8
:101A2000F700073081018130840083130008F0390A
:101A30000738800064000008F739F719F039770497
:101A40008000B301B201FC308F0017308E006400BB
:101A50000C1C322DB20A0319B30A0C10FC308F0093
:101A600017308E00282D831660308F000108C7398B
:101A7000083881000C30F700073083128101813073
:101A8000840083130008F0390738800064000008E0
:101A9000F739F719F0397704800085309000F0307D
:101AA0008F005F308E0094120030940083169400F3
:101AB00007309C00050864000230F700F70B5E2D2C
:101AC000000000001C0883120D1383169D01831271
:101AD0009701970183161F1383121F179F178316F1
:101AE0009F1783121F1483161F129F121B08803921
:101AF0002F389B000C14C03083128B048A110923E9
:101B00008A158A11842C8A150517FA30FC008A116F
:101B100072208A1505132D16B101B001AF01AE0177
:101B20001930AB00E401E301E001DF01E101E20172
:101B3000DE155E1164008A11E12C8A15630083169C
:101B40001F129F121B0880392F389B008A118312A5
:101B5000E9248A157818AE2DDE10AF2DDE14F701BA
:101B60005E18F7145E08770602390319BB2D5E1064
:101B7000DE185E14DE2DDE1CC62D5E19C52D8A1101
:101B8000682E8A155E15E001DF01DE2D5E118A11D7
:101B900097278A157908E6007808E500E608031D0E
:101BA000D52D65082C3C0318DE2D851ACC282A2B50
:101BB000E401E301E001DF01E101E2016008330239
:101BC000031CEC2D031DE82D32085F020318EC2DD9
:101BD000DF0A0319E00A102EDE15E001DF016108BB
:101BE000163C031CF52DE10A102E6408FD00630865
:101BF000FC008316A1011830A0008A11831258211D
:101C00008A157808AB006208593C031C092EE20AC9
:101C10000A2E2D166208E5001C2CE401E301E10107
:101C2000DE1D1B2E4D20F808031D1B2E2908E3077F
:0A1C30000318E40ADE119A2D630088
:104200000300050000000300000003000000190087
:04400E001C2FFC3F28
:00000001FF
;PIC16F88