This is a battery characterizer for NiMH and NiCD cells.
Four 5V ADC inputs (1.2V cells) and one 8V input (3.6 or 4.8V cells) collect samples.
Control and display is done via two pushbuttons and an LCD display.
Menus and results are viewed on the display.
//=============================================================
// crystal frequency (Hz)
#define CRYSTAL_FREQ 4000000
// actual power supply voltage (volts)
#define VDD 5.02
// cell nominal voltage (volts)
#define CELL_NOM_VOLTAGE 1.2
// voltage at which mAh accumlation stops (volts)
#define CUTOFF_VOLTAGE 1.0
// actual load resistances (ohms)
#define LOAD_0_OHMS 4.9
#define LOAD_1_OHMS 4.9
#define LOAD_2_OHMS 4.9
#define LOAD_3_OHMS 4.9
#define LOAD_4_OHMS 19.5
// actual FET RDS(on) resistance (ohms)
#define FET_RDS_OHMS 0.17
// actual scaling resistors R1 and R2 for channel 5 (ohms)
#define R1 5350
#define R2 10220
// display time for logo screens (normally 750 mS)
#define LOGO_TIME 750
// wait time for which voltage is displayed after a cell is inserted into a socket (seconds)
#define INITIAL_DELAY 2
// wait time after which display goes into rolling status mode (seconds)
#define STATUS_DELAY 10
// screen cycle time when in rolling status mode (seconds)
#define SCREEN_CYCLE_DELAY 2
// screen cycle time when in cell detail mode (mS)
#define DETAIL_CYCLE_DELAY 1500
// restart IRQ timer at adjusted value for 1-second accuracy (0-255, higher makes shorter IRQ tick)
#define IRQ_RESTART_TICK 3
// time at cutoff voltage required to end test (seconds, normally 60)
#define AUTOSTOP_TIME 60
// button time after which unit will display cell details (31mS interrupt counts, normally 16, ~= 0.5 second)
#define BUTTON_DETAIL_TIME 16
// button time after which unit will reset (31mS interrupt counts, normally 128, ~= 4 seconds)
#define BUTTON_RESET_TIME 128
// round to nearest 10's or 100's
#define ROUNDNUM 100
// socket definitions
#define SOCKET_1 0
#define SOCKET_5 4
// overvoltage definitions
#define CH_1_4_OVERVOLTAGE 2.0
#define CH_5_OVERVOLTAGE 6.0
//=============================================================
#int_timer0
void TimerInterrupt ( void ) // 32.768mS tic @ 4MHz, ~31 interrupts per second
{
// ONE-SECOND TICK
if ( cInterruptCount1s++ >= 31 ) // if one second yet
{
cInterruptCount1s = 0;
for ( cY = SOCKET_1; cY <= SOCKET_5; cY++ )
{
cAutoStopCount [ cY ]++;
if ( cLogging [ cY ] == YES )
{
if ( cSampleCount [ cY ]++ >= SAMPLE_INTERVAL_SEC - 1 ) // if sample time yet
{
iRunMinutes [ cY ]++; // accumulate minutes
cSampleFlag [ cY ] = ON; // signal time to sample
cSampleCount[ cY ] = 0; // start count over
}
}
}
}
// INITIAL DELAY TIMER (delay during which voltage is displayed)
if ( cInitialDelayCount < INITIAL_DELAY * 32 ) // hold at end of delay
{
cInitialDelayCount++; // otherwise increment
}
// STATUS DELAY (time after which in rolling status display mode)
if ( iInterruptStatusCount >= ( STATUS_DELAY * 32 ) ) // if ready to change screen yet
{
cStatusMode = YES;
}
else
{
cStatusMode = NO;
iInterruptStatusCount++;
}
// SCREEN CYCLE (screen cycle rate once in status display mode)
if ( cInterruptScreenCycleCount++ >= ( SCREEN_CYCLE_DELAY * 32 ) ) // if ready to change screen yet
{
if ( ( cStatusMode == YES ) && ( cCellDetailWanted == NO ) ) // if ready to change screen yet
{
cSocketDisp++; // point to next cell
if ( cSocketDisp > SOCKET_5 ) // if last socket
{
cSocketDisp = SOCKET_1; // wrap to first socket
}
}
cInterruptScreenCycleCount = 0;
}
// SWITCH HANDLER (momentary cycles which cell, hold one second displays cell details, hold four seconds resets)
if ( input ( BUTTON ) == LOW ) // if button is low
{
if ( cSwitchCount < 128 ) // hold at 128 (~4 seconds)
{
cSwitchCount++; // otherwise increment
}
}
else
{
if ( cSwitchCount > 2 ) // filter out glitches
{
// If button is held for a really long time, do cold reset
if ( cSwitchCount == BUTTON_RESET_TIME )
{
reset_cpu();
}
// If button is held for shorter time
if ( ( cSwitchCount > BUTTON_DETAIL_TIME ) && ( cSwitchCount < BUTTON_RESET_TIME ) )
{
cCellDetailWanted = YES; // signal that switch was pressed
cInterruptScreenCycleCount = 0;
iInterruptStatusCount = 0; // stop status cycling
cSkip = NO;
}
// If button press is pressed only momentarily
if ( cSwitchCount <= BUTTON_DETAIL_TIME )
{
cSocketDisp++; // point to next cell
if ( cSocketDisp > SOCKET_5 ) // if last socket
{
cSocketDisp = SOCKET_1; // wrap to first socket
}
cSkip = YES;
cCellDetailWanted = NO;
}
cInterruptScreenCycleCount = 0;
iInterruptStatusCount = 0; // stop status cycling
cSwitchCount = 0; // reset
}
else
{
cSwitchCount = 0; // switch was released prematurely, restart count
}
}
// Adjust timing
set_rtcc ( IRQ_RESTART_TICK ); // restart at adjusted value for 1-second accuracy (higher to make shorter tick)
}
while ( TRUE ) // do forever
{
cSkip = NO; // reset
// CALCULATION SECTION, USES CELL POINTER "cSocket" ///////////////////////////////
for ( cSocket = SOCKET_1; cSocket <= SOCKET_5; cSocket++ )
{
// read cell voltage
fV = ReadAdc( cSocket ); // read voltage at this cell
// if ( fVopen > fOverVoltage [ cSocket ] )
// {
// cCellPresent [ cSocket ] = NO;
// printf ( LCD_PutChar, "#%u Overvoltage!!", cSocketNum );
// }
if ( fV <= 0.4 ) // check if this socket is empty
{
cCellPresent [ cSocket ] = NO;
cSocketState [ cSocket ] = CELL_STATE_READY;
}
else // if socket is not empty
{
fPresentVoltage [ cSocket ] = fV; // save present measurement
switch ( cSocketState [ cSocket ] )
{
case CELL_STATE_READY:
{
if ( cCellPresent [ cSocket ] == NO ) // if socket was previously empty
{
// do all the preliminary stuff one time before changing to TEST state
// now that the voltage is greater than empty socket,
// read cell again to ensure voltage is stable after a delay
fLastV = 0; // restart
for ( cX = 0; cX < 10; cX++ )
{
fV = ReadAdc( cSocket ); // read voltage at this cell
if ( ( fV < fLastV - 0.010 ) || ( fV > fLastV + 0.010 ) )
{
cX = 0; // restart count
}
fLastV = fV; // update
DelayMs ( 10 );
}
// determine number of cells in socket
fStartVoltageUnloaded [ cSocket ] = fV; // save unloaded start voltage
cX = ( char ) ( fV / CELL_NOM_VOLTAGE ); // calculate number of cells
if ( cX < 1 )
{
cX = 1; // one cell minimum, just in case so cutoff isn't ever zero volts
}
cCellCount [ cSocket ] = cX; // save count
// calculate cutoff voltage
fCutoffVoltage [ cSocket ] = ( ( float ) ( cCellcount [ cSocket ] ) ) * CUTOFF_VOLTAGE; // calculate cutoff voltage based on the number of cells
// measure internal resistance
LoadControl ( cSocket, ON ); // load cell
DelayMs ( 100 ); // stabilize time
fVload = ReadAdc( cSocket ); // read ADC
LoadControl ( cSocket, OFF ); // unload cell
fStartVoltageLoaded [ cSocket ] = fVLoad; // save loaded start voltage
fIntRes [ cSocket ] = ( fV - fVload ) / ( fVload / fLoadRes [ cSocket ] ); // calc R and store it
// initialize test
cSampleCount [ cSocket ] = 0; // reset
fAccumMa [ cSocket ] = 0; // reset accumulated mAh
cAutoStopCount [ cSocket ] = 0; // reset
cLogging [ cSocket ] = YES; // allow interrupt to flag accumulating
iInterruptStatusCount = 0; // stop rolling status display for awhile
cSocketDisp = cSocket; // display this cell immediately
cInitialDelayCount = 0; // initialize delay for voltage display
iRunMinutes [ cSocket ] = 0; // reset test duration
cSocketState [ cSocket ] = CELL_STATE_TEST; // put in TEST state
}
break;
}
case CELL_STATE_TEST:
{
if ( fV <= fCutoffVoltage [ cSocket ] ) // check if below cutoff voltage yet
{
// finish up, change state to DONE
if ( cAutoStopCount [ cSocket ] >= AUTOSTOP_TIME ) // need this many seconds in a row to end
{
cLogging [ cSocket ] = NO; // stop interrupt from flagging accumulating
LoadControl ( cSocket, OFF ); // unload cell
cSocketState [ cSocket ] = CELL_STATE_DONE;
}
}
else // if still above autostop voltage
{
// still testing, reset stop count
cAutoStopCount [ cSocket ] = 0; // reset count
}
break;
}
}
cCellPresent [ cSocket ] = YES;
}
// is it time to sample this particular cell?
if ( cSampleFlag [ cSocket ] == ON )
{
cSampleFlag [ cSocket ] = OFF; // reset flag
fX = fV / fLoadRes [ cSocket ]; // calc mA for this sample
fAccumMa [ cSocket ] += fX; // accumulate mA
}
} // end of calculation section
// DISPLAY SECTION, USES CELL POINTER "cSocketDisp" /////////////////////////////////
LCD_SetPosition ( FIRST_LINE + 0 );
// check if any cells in any sockets at all
if ( ( cCellPresent [ 0 ] == NO ) && ( cCellPresent [ 1 ] == NO ) && ( cCellPresent [ 2 ] == NO ) && ( cCellPresent [ 3 ] == NO ) && ( cCellPresent [ 4 ] == NO ) )
{
printf ( LCD_PutChar, " Sockets empty " );
for ( cX = SOCKET_1; cX <= SOCKET_5; cX++ ) // preset
{
LoadControl ( cX, OFF );
}
}
else // some cells are in sockets
{
for ( cX = 0; cX <= 5; cX++ )
{
if ( cCellPresent [ cSocketDisp ] == NO ) // find next socket that has a cell
{
cSocketDisp++;
if ( cSocketDisp > SOCKET_5 ) // if last socket
{
cSocketDisp = SOCKET_1; // wrap to first socket
}
}
else
{
break; // found a cell, skip out
}
}
// check if detail screens are wanted
if ( cCellDetailWanted == YES )
{
while ( TRUE )
{
if ( cSkip == YES ) { break; }
// display cell number and status
printf ( LCD_PutChar, "Cell #%u: ", cSocketDisp + 1 );
if ( cSocketState [ cSocketDisp ] == CELL_STATE_TEST )
{
printf ( LCD_PutChar, "in TEST" );
}
if ( cSocketState [ cSocketDisp ] == CELL_STATE_DONE )
{
printf ( LCD_PutChar, "is DONE" );
}
DelayMs ( DETAIL_CYCLE_DELAY );
// display number of cells
if ( cSkip == YES ) { break; }
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "Num cells: %u ", cCellCount [ cSocketDisp ] );
DelayMs ( DETAIL_CYCLE_DELAY );
// display initial unloaded voltage
if ( cSkip == YES ) { break; }
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "Init Vopen: %01.2f", fStartVoltageUnloaded [ cSocketDisp ] );
DelayMs ( DETAIL_CYCLE_DELAY );
// display present or final loaded voltage
if ( cSkip == YES ) { break; }
if ( cSocketState [ cSocketDisp ] == CELL_STATE_TEST )
{
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "Vloaded: %01.2f ", fPresentVoltage [ cSocketDisp ] );
DelayMs ( DETAIL_CYCLE_DELAY );
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "Int Res: %01.2f%c ", fIntRes [ cSocketDisp ], OHM_SYM );
DelayMs ( DETAIL_CYCLE_DELAY );
}
// display average mA load
if ( cSkip == YES ) { break; }
LCD_SetPosition ( FIRST_LINE + 0 );
fX = ( fPresentVoltage [ cSocketDisp ] + ( fStartVoltageLoaded [ cSocketDisp ] - fPresentVoltage [ cSocketDisp ] ) / 2 );
if ( cSocketDisp == 0 )
{
fX /= LOAD_0_OHMS;
}
if ( cSocketDisp == 1 )
{
fX /= LOAD_1_OHMS;
}
if ( cSocketDisp == 2 )
{
fX /= LOAD_2_OHMS;
}
if ( cSocketDisp == 3 )
{
fX /= LOAD_3_OHMS;
}
if ( cSocketDisp == 4 )
{
fX /= LOAD_4_OHMS;
}
// convert to long
printf ( LCD_PutChar, "Avg Load: %04.0fmA ", fX * 1000 );
DelayMs ( DETAIL_CYCLE_DELAY );
// display accumulated mAh
if ( cSkip == YES ) { break; }
LCD_SetPosition ( FIRST_LINE + 0 );
if ( cSocketState [ cSocketDisp ] == CELL_STATE_TEST )
{
printf ( LCD_PutChar, "Accum" );
}
if ( cSocketState [ cSocketDisp ] == CELL_STATE_DONE )
{
printf ( LCD_PutChar, "Final" );
}
iX = ( long ) ( fAccumMa [ cSocketDisp ] * 1000 / SAMPLE_INTERVAL_SEC );
printf ( LCD_PutChar, ": %lumAh ", iX );
DelayMs ( DETAIL_CYCLE_DELAY );
// display test time
if ( cSkip == YES ) { break; }
LCD_SetPosition ( FIRST_LINE + 0 );
printf ( LCD_PutChar, "Time %02.1f hrs ", ( float ) iRunMinutes [ cSocketDisp ] / 60 );
DelayMs ( DETAIL_CYCLE_DELAY );
iInterruptStatusCount = 0; // stop status cycling
cCellDetailWanted = NO;
break;
}
}
else
{
switch ( cSocketState [ cSocketDisp ] )
{
case CELL_STATE_TEST:
{
if ( cInitialDelayCount < INITIAL_DELAY * 32 ) // if cell was just inserted in socket
{
// display initial unloaded voltage for a short time
fV = ReadAdc( cSocketDisp ); // read voltage at this cell
printf ( LCD_PutChar, "T#%u meas: %01.2fV ", cSocketDisp + 1, fStartVoltageUnloaded [ cSocketDisp ] );
}
else
{
// display present mAh thereafter
LoadControl ( cSocketDisp, ON ); // turn load on
iX = ( long ) ( fAccumMa [ cSocketDisp ] * 1000 / SAMPLE_INTERVAL_SEC );
printf ( LCD_PutChar, "T#%u %lumAh ", cSocketDisp + 1, iX );
}
break;
}
case CELL_STATE_DONE:
{
// display result
fX = fAccumMa [ cSocketDisp ] * 1000 / SAMPLE_INTERVAL_SEC;
iX = Round ( fX, ROUNDNUM ); // store final mAh value
printf ( LCD_PutChar, "D#%u %lumAh %01.1f%c ", cSocketDisp + 1, iX, fIntRes [ cSocketDisp ], OHM_SYM );
break;
}
}
} // end of display section
}
}
}
separate void DelayMs ( long iDelay )
{
long iX;
for ( iX = 0; iX < iDelay; iX++ )
{
delay_ms ( 1 );
if ( cSkip == YES )
{
//cSkip = NO; // reset flag
break;
}
}
}
separate long Round ( float fNum, int cNearest )
{
long iM, iX;
iX = ( long ) fNum;
if ( iX >= ( long ) cNearest ) // if greater than rounding factor, otherwise return actual number
{
iM = iX % cNearest; // fractional part
iX = iX / cNearest;
if ( iM < ( cNearest / 2 ) )
{
iX = iX * cNearest; // bump down
}
else
{
iX = ( iX + 1 ) * cNearest; // bump up
}
}
return ( iX );
}
float ReadAdc ( unsigned int cChannel )
{
long iAdcVal;
char cCnt;
set_adc_channel ( cChannel );
delay_us ( 50 );
iAdcVal = 0;
for ( cCnt = 0; cCnt < 10; cCnt++ ) // average over 10 readings
{
iAdcVal += read_adc();
DelayMs ( 5 );
}
// return average of ten readings, scaled to proper range
return ( ( ( float ) iAdcVal ) / ( float ) 10 / ( float ) 1024 * ( float ) VDD / fMultiplier [ cChannel ] );
}
Tak kedze ja som vzdy prekladal z ASM a nie z C tak neviem ani aky soft mam pouzit. Keby dakto vedel aspon nazov toho programu. ja som si stiahol daco ako Pic C compiler,lenze v tom neviem nic robit.
Založen: Dec 26, 2004 Příspěvky: 1430 Bydliště: Košice - okolie
Zaslal: so listopad 08 2008, 20:36 Předmět:
ono je problem, nieje C ako C pre pic ine je mikroC ine C compiller treba skusat ktore to skusne a prelozi do hex, v tychto programoch musis najprv vytvorit projekt a do projektu vlozit subor a tak to prelozi
Založen: Nov 22, 2007 Příspěvky: 425 Bydliště: Doma
Zaslal: ne listopad 09 2008, 2:19 Předmět:
Vždyť ti to ale píše, že mu schází hlavičkové soubory. Ten "jonsinc.h" si musíš stáhnout tady: http://www.vermontficks.org/pic.htm . Na stránce dole máš tabulku projektů a hned v prvním řádku máš napsané, že ten soubor potřebuješ a máš si jej stáhnout. Další je "18f252.h". To je hlavičkový soubor procesoru a ten je jako součást překladače. Musíš stáhnout novější verzi překladače, nebo potřebný soubor někde pohledat na webu a nacpat do adresáře ../devices.
A akym programom to mam kompilovat? alebo ich mam viac ale neviem ktory je na to vhodny. Skusal som vo vsetkych pridat ten subor ale vzdy tam hlasili dake chyby. Kazdy dake ine.
Založen: Nov 22, 2007 Příspěvky: 425 Bydliště: Doma
Zaslal: ne listopad 09 2008, 14:51 Předmět:
Když použiješ PCW z druhého obrázku a podíváš se do adresáře, kde je nainstalovaný, tak v podadresáři "devices" musíš mít oba soubory "*.h" . Tam je překladač hledá. Jinak hlásí chybu, že něco nenašel. Kolegové ti už psali dřív, že každý překladač "umí" nějaké procesory. Které to jsou, to najdeš na příslušné stránce a obvykle i v nápovědě. Pokud máš jenom nějaké demo, tak je omezena funkčnost buď v délce zpracovávaného programu, nebo neumí všechny procesory, nebo nezapíše na disk výsledný HEX kod. Jinak se dál s tím musíš poprat sám. Číst chybový výstup překladače a hledat příčinu chyby.
ahoj, v priloze je onen zkompilovany projekt.
v src jsou nasledujici chyby:
1) protoze onen .h soubor davas pravdepodobne do stejneho adresare jako .c, musis tam mit #include "jonsinc.h"
ne #include <jonsinc.h>
2) jednou tam je cCellcount misto cCellCount
3) jednou tam je fVLoad misto fVload
Mozem sa este spytat v com si to kompiloval? Som skusal opravit to co si pisal ze je zle a stale mi to neslapalo. Pise mi ze nepozna dake prikazy nap. USE...
Časy uváděny v GMT + 1 hodina Jdi na stránku 1, 2Další
Strana 1 z 2
Nemůžete odesílat nové téma do tohoto fóra. Nemůžete odpovídat na témata v tomto fóru. Nemůžete upravovat své příspěvky v tomto fóru. Nemůžete mazat své příspěvky v tomto fóru. Nemůžete hlasovat v tomto fóru. Nemůžete připojovat soubory k příspěvkům Můžete stahovat a prohlížet přiložené soubory
Informace na portálu Elektro bastlírny jsou prezentovány za účelem vzdělání čtenářů a rozšíření zájmu o elektroniku. Autoři článků na serveru neberou žádnou zodpovědnost za škody vzniklé těmito zapojeními. Rovněž neberou žádnou odpovědnost za případnou újmu na zdraví vzniklou úrazem elektrickým proudem. Autoři a správci těchto stránek nepřejímají záruku za správnost zveřejněných materiálů. Předkládané informace a zapojení jsou zveřejněny bez ohledu na případné patenty třetích osob. Nároky na odškodnění na základě změn, chyb nebo vynechání jsou zásadně vyloučeny. Všechny registrované nebo jiné obchodní známky zde použité jsou majetkem jejich vlastníků. Uvedením nejsou zpochybněna z toho vyplývající vlastnická práva. Použití konstrukcí v rozporu se zákonem je přísně zakázáno. Vzhledem k tomu, že původ předkládaných materiálů nelze žádným způsobem dohledat, nelze je použít pro komerční účely! Tento nekomerční server nemá z uvedených zapojení či konstrukcí žádný zisk. Nezodpovídáme za pravost předkládaných materiálů třetími osobami a jejich původ. V případě, že zjistíte porušení autorského práva či jiné nesrovnalosti, kontaktujte administrátory na diskuzním fóru EB.