'Power Monitor. 14/02/14. GM 'This program is a coulomb counter which measures current and time and calculates charge used 'from a battery. This is then subtracted from the remaining battery capacity. 'Battery capacity is saved in NV storage representing the 'remaining battery capacity in uAh. 'The running current is sampled at 0.1 sec intervals and averaged over 1 sec. 'The sampled current over 1 sec is subtracted from the saved remaining capacity. 'The remaining capacity var is reset to a max value manually at the completion of a charge. 'The remaining capacity is saved to nv ram at power down.It is again read at power up. 'Port c.0 (pin 17) is a spare input ADC or GP i/p with pullup 'Port c.1 (pin 18) is the ADC for measuring current 'Port c.2 (pin 1)is an ADC for detecting powerdown 'Ports b.0-3 are outputs to the display as a BCD digit representing the 10s digit of percentage remaining. 'Ports b.4-6 are spare outputs 'b.7 is the display power switch, active low symbol adc1=b0 symbol adc2=b1 symbol voltag=b2 'adc reg for battery voltage and power loss detection symbol bshad=b3 'shadow Reg for b.7 display power symbol dlev=b4 symbol percap=b5 'display percentage var as a single 10's digit. ie 9=90% symbol loopcnt=b6 'loop counter symbol maxcapu=b7 'upper byte of max battery capacity in mAh*100 (0.1AH)units. value set in declarations.Lower is assmuned to be 0 symbol remcapu=w4 'upper byte of capacity remaining in 0.05AH (or 50mAH) units, 10AH=200 symbol remcapl=w5 'least significant word in uAh units limited to 49,999 to match remcapu symbol avgcur=w6 'average current value from ADC readings symbol vtemp=w7 'temp calc var symbol dispwr=b.7 'output display power switch, active low symbol dacpin=c.2 'define dac display output symbol rescap=pinC.7 'input reset of remaining capacity following charge, active low symbol dispsel=pinC.6 'future display select function, high=batt capacity, low=voltage start0: 'task 1 start 'set initial vars '!!!!SET BATTERY CAPACITY HERE!!!! maxcapu=10 'default upper MSD >>>set here<<< for the battery maximum capacity in mAhx100 (0.1Ah) units, test at 10Ah=100 'remcapu=maxcapu*2 'Upper value set here for remaining capacity in 0.05AH units, stored/retrieved from nv, test with maxcapu value 'remcapl=120 'test lower value set here for remaining capacity in 0.05Ah units up to a max of 50,000 modulo cto remcapu read 0,WORD remcapu 'get last saved remaining capacity from nv ram read 2,WORD remcapl let dirsB = %11111111 'set Port B (display) to outputs low dacpin 'make sure dac pin is set to o/p dacsetup %10100000 'setup dac 'main loop adcr: avgcur=0 'reset average current in preparation for loops for loopcnt= 1 to 10 'read 10 samples and average reading over 1 second readadc c.1, adc1 'read current 'convert reading to amps. 'ASC712/20A is 100mV per Amp and with 0Amp at 50%VCC 'ADC reads 256 steps and is 127 units at 50% Vcc, therefore subtract 127 for 0 amp 'adc reads 255 steps for 5V, therefore 1 step=19.6mV. 'Therefore 100mV/19.6mV=5.1 steps per Amp or 1A/5.1=0.196A per step. 'ASC712 Max swing is +4.5V. Therefore Max I = 2V/.0196=102 units '0.2A resolution is not sufficient to measure small currents such as background current. 'However, the motor usually draws much more when used at all. 'Therefore, we can estimate background I and add it as a constant rather than measure hi res. '10 samples are calculated individually because we will be in a wait loop anyway and we have plenty of code space. 'It also helps to average and to balance sample times rather than do all conversions and peukert after sampling. if adc1 < 127 then goto adcr 'current is in the wrong sense, ie charging. abort average. In future, make this line a charge addition adc1=adc1-127 'remove offset from Isense, ie 127units=0 Amp. vtemp=adc1*196 'convert to amps.Units *0.196=A. Max 102*0.196=20A.scale up by 1000 to allow 'integers to be used. (102*196=20000, 1mA resolution) vtemp=vtemp/100 'bring resolution back to amps*10 ie 20A=200 with 100mA resolution ie 100mA unitys 'apply peukerts, max count 255, ie 25.5 Amp if vtemp > 50 then 'test for 5.0amp (in 100mA units) vtemp=vtemp*11 'apply 110% ie x1.1 but need to use 1.1 x 10 for integer math. vtemp=vtemp/10 'remove 10 scale to restore 0.1A units for 8 bits, 20A=200 with 0.1A res goto p1 'loop to end 'other peukert values can go here and exit to P1 endif p1: avgcur=avgcur + vtemp 'exit pooint for peukert tests. Store running sum for average. Make sure count does not exceed 65,000. 20A=200 pause 100 'pause 100ms per loop. Adust pause var as needed here to get overall 100ms perloop. 10 loops = 1 sec p2: next loopcnt 'go an loop again avgcur=avgcur/10 'obtain average 'convert from Amp/sec (or coulomb) units to Ah units. Asec/3600=Ah. avgcur=avgcur*100 'multiply by 100 to allow interger math. 20A=200*100=20000 avgcur=avgcur/36 'convert secs to A hours (adds to 100 scale above by 100 here). 20A=20000/36=555 avgcur=avgcur*10 'scale up to increase resolution. Total multiply 10E5. 20Asec=5550 'avgcur holds the 1 sec sample current converted to Ah*10E-5 units. 5550=5,550uAh '20Amp at the sensor for 1 sec = 5,550uAh. 1 Amp/sec=277.5uAh 'FUTURE add background running current here so that we do not need to use hi res ADC 'FUTURE add aging here in future 'check if there is enough remaining capacity in the lower register to subtract current sample if remcapl>avgcur then goto decap 'not enough left in lower reg. check capacity in upper reg if remcapu =0 then remcapl=0 'set to 0 as all capacity is consumed percap=0 goto disp1 'no remaining capacity else remcapu=remcapu-1 'decrement upper reg and reload lower reg. remcapl=50000 'Some capacity in remcapl may be lost. dont want to add iit to remainder in case of overflow endif decap: remcapl=remcapl-avgcur 'subtract present value from remaining capacity goto disp1 'display remaining capacity 'display of remaining capacity 'The dispaly is a single 4 bit BCD digit sent to Port B.4-7 (high nibble) as %/10 disp1: 'convert Asec to % and display avgcur=remcapu*100 'scale for % 'need to add remcapl to remcapu otherwise batteries will have pesemistic indication percap=avgcur/maxcapu percap=percap/2 'allow for difference between maxcapu and remcapu of x2. 'set analogue o/p 'scale percap to 31. 31*percap/10 vtemp=percap * 31 'use vtemp Word because 100%*31 exceeds byte dlev=vtemp/100 'correct scaling daclevel dlev 'set digital output single digit %x10 percap=percap/10 'extract percentage*10% digit if percap < 1 then goto minval 'if <10% then get attention if percap > 9 then goto maxval 'cannot show 100% with 1 digit then use 9 goto disp2 minval: 'remainin capacity is < 10% show alert 'to be done goto disp2 maxval: 'remaining capacity > 100% show "9" for 100% percap=9 goto disp2 disp2: 'get value of port B so that the display power setting can be preserved if bshad=0 then 'if shadow reg is off, then display is off let pinsB=percap | %10000000 'set outputs of Port B lower 4 bits for diagnosis else let pinsB=percap 'b.7 will be off as an active low under normal conditions, set low 4 bits endif goto adcr 'end of task 1 'task 2 will be the remcap manual reset following battery charging start1: pause 500 lp2: if rescap =1 then goto lp2 'test for switch press, high is not pressed 'must be pressed if not high remcapu=maxcapu*2 remcapl=0 'reset remcap goto lp2 'task 3 will be the power down monitor and save remcap to nv mem start2: pause 500 low dispwr 'set display power on bshad=1 'set shadow reg for b.7 lp1: readadc c.0,voltag 'check i/p voltage if voltag < 100 then if bshad=1 then 'check if we have already saved remcap to avoid eeprom wear high dispwr 'turn display off to save power 'save remcap to nv ram write 0,WORD remcapu write 2,WORD remcapl bshad=0 'reset shadow register endif else ' voltage ok bshad=1 'set display ON low dispwr endif goto lp1 end