Batt_Charger.txt
Warning: if you clip this code into your program, look out for possible html code slipping in.
REM Battery charger 2, SBC2000-062 REM ss, 11/98 REM ***************************************************************** REM This program was written to be compiled with revision 3 of VSTB. REM Because of this, it defines AIN() as a subroutine, since it only REM became a built-in function in revision 4. If you want to use this REM program, you will have to make the appropriate modifications. REM ***************************************************************** REM Here is a little battery charger project. REM The charger is basically a "buck regulator" where the PWM output of an SBC2000-062 REM controls a series pass transistor into an inductor which charges the battery. REM The charging current is measured and used to adjust the PWM. REM The battery voltage is measured and used to terminate the charge cycle. REM This code illustrates a very workable 3 button interface REM and printing "decimal" numbers to the LCD. SUBROUTINE delay(arg AS INTEGER) LOCAL index AS INTEGER FOR index = 0 TO arg REM NEXT END REM print a number formatted to right with DP SUBROUTINE PRINT_DP(arg AS INTEGER,pos AS INTEGER) LOCAL T1 AS INTEGER LOCAL places AS INTEGER T1 = arg\1000 places = 3 REM print number from right to left one at a time DO LCD_COMMAND(0x80+pos+places) PRINT T1\10 T1=T1/10 places = places - 1 LOOP UNTIL places = 0 LCD_COMMAND(0x80+pos+places+1) PRINT "." T1 = arg/1000 DO LCD_COMMAND(0x80+pos+places-1) PRINT T1\10 T1=T1/10 places = places - 1 LOOP UNTIL T1=0 END REM This uses pins 1,2,3, and 5 of the KEYPAD connector CONSTANT up_key AS INTEGER = 0 CONSTANT down_key AS INTEGER = 4 CONSTANT enter_key AS INTEGER = 8 CONSTANT no_key AS INTEGER = -1 REM get key will return a debounced keypress FUNCTION get_key() AS INTEGER STATIC last_key AS INTEGER DO DO get_key = KEYPAD(0) LOOP UNTIL get_key <> last_key last_key = get_key LOOP UNTIL get_key<>no_key END SUBROUTINE set_time(sec AS INTEGER, mn AS INTEGER, hr AS INTEGER) POKE(37,0):REM day POKE(38,hr):REM hour POKE(39,mn):REM minute POKE(40,sec):REM second END SUBROUTINE wait_one_second() LOCAL now AS INTEGER now = PEEK(40) DO REM LOOP UNTIL now <> PEEK(40) END GLOBAL beep_bit AS BIT = 6,7 SUBROUTINE tweet() beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 beep_bit=0 beep_bit=1 END REM 8 bit PWM at fixed freq - 5 kHz (-062) REM PWM control word SUBROUTINE set_duty_cycle(arg AS INTEGER) CONSTANT VTI_T2CON AS INTEGER = 0x12 CONSTANT VTI_CCPR1L AS INTEGER = 0x15 CONSTANT VTI_CCP1CON AS INTEGER = 0x17 CONSTANT VTI_PR2 AS INTEGER = 0x92 CONSTANT VTI_TRISC AS INTEGER = 0x95 POKE(VTI_T2CON, (PEEK(VTI_T2CON) AND 0xFC) OR 0) POKE(VTI_PR2,255) POKE(VTI_CCPR1L, arg):REM Time hi POKE(VTI_CCP1CON, 0x0C + (1 * 0x10)):REM 2 lsb time hi POKE(VTI_TRISC, PEEK(VTI_TRISC) AND 0xFD) POKE(VTI_T2CON, PEEK(VTI_T2CON) OR 4) END FUNCTION read_PWM() AS INTEGER read_PWM = PEEK(VTI_CCPR1L) END SUBROUTINE adjust_PWM(arg AS INTEGER) LOCAL temp AS INTEGER temp = read_PWM() temp = temp + arg temp = MAX(MIN(temp,255),0) set_duty_cycle(temp) END CONSTANT VTI_ADRES AS INTEGER = 0x1E CONSTANT VTI_ADCON0 AS INTEGER = 0x1F CONSTANT VTI_TRISA AS INTEGER = 0x85 CONSTANT VTI_ADCON1 AS INTEGER = 0x9F CONSTANT VTI_Analog_5 AS INTEGER = 2 REM CONSTANT VTI_Analog_3 AS INTEGER = 4 REM CONSTANT VTI_Analog_0 AS INTEGER = 6 CONSTANT VTI_Fosc_by_32 AS INTEGER = 0x81 GLOBAL VTI_AD_GO AS BIT = VTI_ADCON0,2 REM simple A/D, SBC2000-074/-062 FUNCTION AIN(channel AS INTEGER) AS INTEGER POKE(VTI_ADCON1,VTI_Analog_5) :REM set up port a as 5 analog inputs POKE(VTI_TRISA, PEEK(VTI_TRISA) OR (2^channel)) POKE(VTI_ADCON0, VTI_Fosc_by_32 OR (channel * 8)) VTI_AD_GO = 1 : REM Start the conversion AIN = PEEK(VTI_ADRES) : REM Read the result END REM return battery voltage in millivots REM measured voltage is trimmed with pot REM get enuf reading so result = millivolts FUNCTION get_battery_voltage() AS INTEGER LOCAL index AS INTEGER FOR index = 1 TO 40 get_battery_voltage = get_battery_voltage + AIN(0) NEXT END REM return charging current in millamps REM get enuf reading so result = milliamps REM 5.6 mV/ma REM fix overflow FUNCTION get_current() AS INTEGER LOCAL index AS INTEGER FOR index = 1 TO 3 get_current = get_current + AIN(1) NEXT END GLOBAL number_of_cells AS INTEGER GLOBAL battery_voltage AS INTEGER, cell_voltage AS INTEGER GLOBAL battery_EOC_voltage AS INTEGER, highest_battery_voltage AS INTEGER GLOBAL desired_charge_rate AS INTEGER, actual_charge_rate AS INTEGER GLOBAL capacity AS INTEGER, measured_capacity AS INTEGER GLOBAL mA_seconds AS INTEGER, mA_hours AS INTEGER GLOBAL key AS INTEGER, index AS INTEGER DIM cell_count[8] AS INTEGER CONSTANT = [1,2,3,4,5,6,7,8] DIM cell_voltages[4] AS INTEGER CONSTANT = [1400,1450,3400,3450] DIM charge_rates[6] AS INTEGER CONSTANT = [20,50,100,200,500,1000] REM charger is used on NiCd cells with EOC = 1.40 or 1.45 Volts REM and Lithium Metal cells with EOC = 3.4 or 3.45 Volts REM *************************** Main Program **************************** DO LCD_COMMAND(128) PRINT "Cells? " index = 0 DO number_of_cells = cell_count[index] LCD_COMMAND(192) PRINT number_of_cells," " key = get_key() SELECT key CASE up_key index = MIN(7,MAX(0,index+1)) CASE down_key index = MIN(7,MAX(0,index-1)) ENDSELECT LOOP UNTIL key = enter_key delay(100) LCD_COMMAND(128) PRINT "V/cell? " index = 0 DO cell_voltage = cell_voltages[index] PRINT_DP(cell_voltage,65) key = get_key() SELECT key CASE up_key index = MIN(3,MAX(0,index+1)) CASE down_key index = MIN(3,MAX(0,index-1)) ENDSELECT LOOP UNTIL key = enter_key battery_EOC_voltage = cell_voltage * number_of_cells highest_battery_voltage = 0 delay(100) LCD_COMMAND(128) PRINT "Rate? " index = 0 DO desired_charge_rate = charge_rates[index] LCD_COMMAND(192) PRINT desired_charge_rate," " key = get_key() SELECT key CASE up_key index = MIN(5,MAX(0,index+1)) CASE down_key index = MIN(5,MAX(0,index-1)) ENDSELECT LOOP UNTIL key = enter_key set_duty_cycle(0) set_time(0,0,0) LCD_COMMAND(128) PRINT " V" LCD_COMMAND(192) PRINT " mA" LCD_COMMAND(148) PRINT " mAH" DO REM get battery voltage battery_voltage = get_battery_voltage() highest_battery_voltage = MAX(highest_battery_voltage,battery_voltage) PRINT_DP(battery_voltage,1) REM servo the PWM duty cycle to match the desired charge rate actual_charge_rate = get_current() adjust_PWM(((desired_charge_rate-actual_charge_rate)/5)) IF desired_charge_rate > actual_charge_rate adjust_PWM(1) ELSE adjust_PWM(-1) ENDIF LCD_COMMAND(192) PRINT actual_charge_rate," " REM accumulate the charge into the battery mA_seconds = mA_seconds + actual_charge_rate IF mA_seconds >= 3600 mA_seconds = mA_seconds-3600 mA_hours = mA_hours + 1 ENDIF LCD_COMMAND(148) PRINT mA_hours," " wait_one_second() LOOP UNTIL (battery_voltage>battery_EOC_voltage) OR (battery_voltage<(highest_battery_voltage-5*number_of_cells)) set_duty_cycle(0) LCD_COMMAND(192) PRINT " mAH" LCD_COMMAND(192) PRINT mA_Hours DO PRINT_DP(get_battery_voltage(),1) tweet() LOOP UNTIL KEYPAD(0) <> -1 DO REM LOOP UNTIL KEYPAD(0) = -1 LOOP UNTIL 0