blankfield 14 Posted November 20, 2012 Share Posted November 20, 2012 Hi everyone,I have idea to use msp430 and hacked atx power supply as a car battery charger. Simply msp430 will be used to control of charge process, different charging current, time, precharging and swichable load for accurate voltage measurement will be available.Done:* hacked atx to 17V* enclosure* schematics* pcb* functional codeTo do:* firmware developmentIf anyone have some suggestions fell free to post within this topic, I'll be grateful.Simple description of operation:At first voltage measurement is initiated, user set type of car battery, capacity and charge strategy. While charging is started PWM signal control P-MOSFET to set correct current, feedback is taken from 5mR shunt resistor trough RC low-pass filter and software implementation of 3 order Chebyshev LP filter. Every 5 minutes charge process stops, load resistor is applied and voltage reading is taken. During charge process user can see actual voltage, current, elapsed time and estimated period to end of charging. If battery will be accidentally disconnected mcu stops charging process. [uPDATE] 22.12.2012 ====================================================== Results. ATX2CHARGER is now working, I've successfully charged some car batteries. In near future I need to finish firmware, add rest of options etc. Tomorrow I'll add attachments (sorce of firmware and gerbers). Final schematics: PCB: Tests of working unit: All media: http://imageshack.us/g/1/9930724/ http://imageshack.us/g/1/9930688/ main.c: /*==================================================================== * * ATX2CHARGER v1.0 * 2012 by blankfield <blank0field@gmail.com> * MSP430G2553 @ 16MHz, 3,3V * ===================================================================*/ /*==================================================================== * * headers and includes * ===================================================================*/ #include <msp430.h> #include <ti/mcu/msp430/csl/CSL.h> #include <stdio.h> #include <string.h> #include "msp430g2553.h" #include "main.h" #include "LCD2x16.h" /*==================================================================== * * constants and definitions * ===================================================================*/ //charge indicator animation const char bat0[8] = {0x0A, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F}; const char bat1[8] = {0x0A, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x1F}; const char bat2[8] = {0x0A, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x1F, 0x1F}; const char bat3[8] = {0x0A, 0x1F, 0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x1F}; const char bat4[8] = {0x0A, 0x1F, 0x11, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; const char bat5[8] = {0x0A, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; #define SWSTART BIT7 //P2.7 - START #define SWF1 BIT7 //P1.7 - F1 #define SWF2 BIT6 //P1.6 - F2 #define FIRNtap 15 //fir lp filter number of tap #define Vref (float)3.3 #define ADCRES (float)1023 #define R7resistor (float)4650 #define R9resistor (float)1005 #define R15resistor (float)0.005 #define Uconstant (float)((Vref/ADCRES)*((R7resistor+R9resistor)/R9resistor)) #define Iconstant (float)((Vref/ADCRES)/(R15resistor)) enum enumdisplay_menu {startup, measure_acc, settings, chargeing_params, chargeing_time, summary}; enum enumcharge_type {emergency, normal, mainterance_free}; enum enumaccu_voltage {U6V, U12V}; enum enumstate {idle, charge, discharge}; #pragma DATA_SECTION(ee_charge_type, ".mydata"); #pragma DATA_SECTION(ee_accu_capacity, ".mydata"); #pragma DATA_SECTION(ee_accu_voltage, ".mydata"); #pragma DATA_ALIGN(ee_charge_type, 1); #pragma DATA_ALIGN(ee_accu_capacity, 1) #pragma DATA_ALIGN(ee_accu_voltage, 1); /*==================================================================== * * variables * ===================================================================*/ //FLASH "EEPROM" VALUES unsigned char ee_charge_type; unsigned char ee_accu_capacity; unsigned char ee_accu_voltage; struct timeformat { char hours; char minutes; char seconds; }time; int time_prescale=0; int measurments[4]; float measured_charge_current = 0; float measured_charge_voltage = 0; float measured_discharge_current = 0; float measured_discharge_voltage = 0; float measured_idle_current = 0; float measured_idle_voltage = 0; float Iavg = 0; float Imax = 0; float Ucellmax = 2.5; float Ucelloptimum = 2.4; char accu_capacity = 75; char accu_voltage = U12V; char charge_type = normal; //char charge_time = 0; char charge_progress = 0; char elapsed_1s = false; char tempstring[20]; float DCV[10]; char display_level = startup; char state = idle; /*==================================================================== * * main() * ===================================================================*/ int main(int argc, char *argv[]){ CSL_init();// Activate Grace-generated configuration __delay_cycles(5000); init_mcu(); _delay_cycles(1000000); LCD2x16_Initialize(); __delay_cycles(5000); LCD2x16_WriteCommand(0x01); __delay_cycles(5000); ADC10CTL0 |= ENC;//Start ADC TA1CCR1 = 0; while(1){ control(); if (elapsed_1s){ sprintf(tempstring,"%1.2f;%1.2f;%1.2f;%1.2f;%1.2f;%1.2f;", measured_idle_voltage,measured_idle_current, measured_charge_voltage,measured_charge_current, measured_discharge_voltage,measured_discharge_current); int i; for (i=0; i<sizeof(tempstring); i++) { while (!(IFG2 & UCA0TXIFG)); UCA0TXBUF = tempstring[i]; } elapsed_1s=false; } switch(display_level){ case startup: menu_redraw(); __delay_cycles(25000000); display_level = measure_acc; menu_redraw(); break; case measure_acc: menu_redraw(); if (!(P2IN & SWSTART)) { beep_sound(); display_level=settings; menu_redraw(); } if (!(P1IN & SWF1)) { beep_sound(); if (accu_voltage == U12V) accu_voltage = U6V; else accu_voltage = U12V; menu_redraw(); } break; case settings: if (!(P2IN & SWSTART) && true) { beep_sound(); time.seconds=0; time.minutes=0; time.hours=0; Iavg=0; display_level = chargeing_params; state = charge; menu_redraw(); } if (!(P1IN & SWF1)) { beep_sound(); accu_capacity += 5; if (accu_capacity > 200) accu_capacity = 5; menu_redraw(); } if (!(P1IN & SWF2)) { beep_sound(); charge_type++; if (charge_type>=3) charge_type=0; menu_redraw(); } break; case chargeing_params: if (!(P2IN & SWSTART)) { beep_sound(); display_level=summary; state = idle; menu_redraw(); } if (!(P1IN & SWF1)) { beep_sound(); display_level=chargeing_time; menu_redraw(); } break; case chargeing_time: if (!(P2IN & SWSTART)) { beep_sound(); display_level=summary; state = idle; menu_redraw(); } if (!(P1IN & SWF1)) { beep_sound(); display_level=chargeing_time; menu_redraw(); } break; case summary: if (!(P2IN & SWSTART)) { beep_sound(); display_level=measure_acc; menu_redraw(); } break; } } } /*==================================================================== * * menu_redraw() * ===================================================================*/ void menu_redraw(void){ LCD2x16_GoTo(0,0); switch(display_level){ case startup: LCD2x16_WriteText("ATX2CHARGER v1.0"); LCD2x16_GoTo(0,1); LCD2x16_WriteText(" by blankfield"); break; case measure_acc: if (measured_idle_voltage <=1){ LCD2x16_WriteText("Check battery, "); LCD2x16_GoTo(0,1); sprintf(tempstring,"%01.2fV is too low ",measured_idle_voltage); LCD2x16_WriteText(tempstring); } else{ sprintf(tempstring,"Battery U=%02.2fV ",measured_idle_voltage); LCD2x16_WriteText(tempstring); LCD2x16_GoTo(0,1); //sprintf(tempstring,"P=%01.0f I= %02.2fA ", TA1CCR1*0.05, measured_idle_current); sprintf(tempstring,"Type F1:%02.0dV ",(accu_voltage*6)+6); LCD2x16_WriteText(tempstring); } break; case settings: sprintf(tempstring,"Set cap F1:%03.0dAh ",accu_capacity); LCD2x16_WriteText(tempstring); LCD2x16_GoTo(0,1); if (charge_type==normal) LCD2x16_WriteText("F2: standard "); if (charge_type==mainterance_free) LCD2x16_WriteText("F2: mainter.free"); if (charge_type==emergency) LCD2x16_WriteText("F2: emerg.charg."); break; case chargeing_params: sprintf(tempstring,"U=%02.1fV I=%02.1fA", measured_charge_voltage, measured_charge_current); LCD2x16_WriteText(tempstring); LCD2x16_GoTo(0,1); sprintf(tempstring,"U=%02.1fV I=%02.1fA", measured_discharge_voltage, measured_discharge_current); break; case chargeing_time: sprintf(tempstring,"Elapsed %2.0f:%2.0f:%2.0f", time.hours, time.minutes, time.seconds); LCD2x16_WriteText(tempstring); LCD2x16_GoTo(0,1); sprintf(tempstring,"Iavg=%2.2f ", Iavg); LCD2x16_WriteText(tempstring); break; case summary: LCD2x16_WriteText("Finished! "); LCD2x16_GoTo(0,1); sprintf(tempstring,"%2.0f:%2.0f:%2.0f @ %2.2f", time.hours, time.minutes, time.seconds, Iavg); LCD2x16_WriteText(tempstring); break; } } /*==================================================================== * * control() * ===================================================================*/ void control(void){ switch (state){ case idle: TA1CCR1=0; TA1CCR2=0; break; case charge: TA1CCR2=0; if ((measured_charge_voltage < (Ucelloptimum*3*(accu_voltage+1))) && (measured_charge_current < (accu_capacity/5/(charge_type+1))) && (TA1CCR1<1999)){ TA1CCR1++; } if (((measured_charge_voltage > (Ucelloptimum*3*(accu_voltage+1))) || (measured_charge_current > (accu_capacity/5/(charge_type+1)))) && (TA1CCR1>1)){ TA1CCR1--; } if (TA1CCR1 <=100 && measured_charge_voltage >= (Ucelloptimum*3*(accu_voltage+1))){ state = idle; display_level = summary; beep_sound(); } break; case discharge: TA1CCR1=0; TA1CCR2=1000; break; } } /*==================================================================== * * init_mcu() * ===================================================================*/ void init_mcu(void){ WDTCTL = WDTPW + WDTHOLD; char name[18]="AT+NAMEATX2CHARGER"; int i; for (i=0; i<18; i++) { while (!(IFG2 & UCA0TXIFG)); UCA0TXBUF = name[i]; } } /*==================================================================== * * measure() * interrupts from ADC * ===================================================================*/ void measure(void){ time_prescale++; if (time_prescale==999){ elapsed_1s=true; time.seconds++; //increase every 1s if(time.seconds==60){ time.seconds=0; time.minutes++; if(time.minutes==60){ time.minutes=0; time.hours++; } } } switch (state){ case idle: measured_idle_current = measurments[3]*Iconstant; measured_idle_voltage = measurments[0]*Uconstant; break; case charge: measured_charge_current = measurments[3]*Iconstant; Iavg = (Iavg + measured_charge_current)/2; measured_charge_voltage = measurments[0]*Uconstant; break; case discharge: measured_discharge_current = measurments[3]*Iconstant; measured_discharge_voltage = measurments[0]*Uconstant; break; } return; } /*==================================================================== * * beep_sound() * ===================================================================*/ void beep_sound(void){ TA0CCR1 = 1000; __delay_cycles(500000); TA0CCR1 = 0; return; } /*==================================================================== * * fir() * ===================================================================== WinFilter version 0.8 http://www.winfilter.20m.com akundert@hotmail.com Filter type: Low Pass Filter model: Chebyshev Filter order: 3 Sampling Frequency: 1000 Hz Cut Frequency: 6.000000 Hz Pass band Ripple: 1.000000 dB Coefficents Quantization: float Z domain Zeros z = -1.000000 + j 0.000000 z = -1.000000 + j 0.000000 z = -1.000000 + j 0.000000 Z domain Poles z = 0.981540 + j -0.000000 z = 0.990073 + j -0.036073 z = 0.990073 + j 0.036073 ***************************************************************/ float fir(float NewSample) { float FIRCoef[FIRNtap] = { 0.06499156895458164900, 0.06567519555537033900, 0.06626048168634461600, 0.06674435292740793500, 0.06712913054423083600, 0.06742809591680600000, 0.06768722943723189100, 0.06816788995605341200, 0.06768722943723189100, 0.06742809591680600000, 0.06712913054423083600, 0.06674435292740793500, 0.06626048168634461600, 0.06567519555537033900, 0.06499156895458164900 }; static float x[FIRNtap]; //input samples float y=0; //output sample int n; //shift the old samples for(n=FIRNtap-1; n>0; n--) x[n] = x[n-1]; //Calculate the new output x[0] = NewSample; for(n=0; n<FIRNtap; n++) y += FIRCoef[n] * x[n]; return y; } /*==================================================================== * * flashEraseSegment(int FarPtr) * ===================================================================*/ void flashEraseSegment(int FarPtr){ int *Flash_ptr; // local Flash pointer Flash_ptr = (int *) FarPtr; // Initialize Flash pointer FCTL1 = FWKEY + ERASE; FCTL3 = FWKEY; *Flash_ptr = 0; // dummy write to start erase while (FCTL3 & BUSY ); FCTL1 = FWKEY; FCTL3 = FWKEY + LOCK; } /*==================================================================== * * saveSettings(void) * ===================================================================*/ void saveSettings(void){ flashEraseSegment((int) 0xC000); FCTL1 = FWKEY + WRT; FCTL3 = FWKEY; ee_charge_type = (char)charge_type; ee_accu_capacity = (char)accu_capacity; ee_accu_voltage = (char)accu_voltage; FCTL1 = FWKEY; FCTL3 = FWKEY + LOCK; } /*==================================================================== * * loadSettings(void) * ===================================================================*/ void loadSettings(void){ charge_type = (char)ee_charge_type; accu_capacity = (char)ee_accu_capacity; accu_voltage = (char)ee_accu_voltage; } main.h: #ifndef MAIN_H_ #define MAIN_H_ #define false 0 #define true 1 void menu_redraw(void); void control(void); void init_mcu(void); void buttons(void); void measure(void); void beep_sound(void); float fir(float NewSample); void flashEraseSegment(int FarPtr); void saveSettings(void); void loadSettings(void); #endif /* MAIN_H_ */ LCD2x16.c: // ----------------------------------------------------------------#include "LCD2x16.h"#include "msp430g2553.h"#define RS BIT3 // RS - P2.3 RW - GND#define EN BIT5 // EN - P2.5#define D4 BIT4 // D4 - P1.4#define D5 BIT5 // D5 - P1.5#define D6 BIT0 // D6 - P2.0#define D7 BIT1 // D7 - P2.1//Polish language characters//E6 B9 B3 EA F3 F1 9F 9C A5 BF CA C6 D1 A3 8C D3 AF 8F//? ? ? ? ? atx2charger-firmware.zip gerbers-mirror.zip gerbers.zip Automate, nura100, Rickta59 and 4 others 7 Quote Link to post Share on other sites
bobnova 59 Posted November 21, 2012 Share Posted November 21, 2012 Interesting concept, I like it. 17v seems excessive, most cars charge at 13.7 to 14.7v, over 15v with significant amps can/will boil the battery. The final voltage at the battery is what matters of course, the PSU may droop down to that point. Have you given it a test run yet? What sort of amperage are you expecting into the battery? Quote Link to post Share on other sites
abecedarian 330 Posted November 21, 2012 Share Posted November 21, 2012 Some information about charging lead-acid batteries, particularly deep cycle: http://www.pacificpowerbatteries.com/aboutbatts/Deep%20Cycle%20Battery%20FAQ/dcfaq6.html (sorry for the link- don't want to leech) You're definitely going to want current limiting - a self-resetting thermal breaker could suffice; and reverse voltage protection- I've seen trained mechanics hook up chargers and other things backwards. Rickta59 1 Quote Link to post Share on other sites
blankfield 14 Posted November 21, 2012 Author Share Posted November 21, 2012 Hi, ...17v seems excessive, most cars charge at 13.7 to 14.7v, over 15v with significant amps can/will boil the battery. This is only maximal voltage to drive mosfet (Uds) to achieve desired current. PWM will drop this value. Have you given it a test run yet? What sort of amperage are you expecting into the battery? I've made test with resistive load and light bulbs but not with battery, I've measured up to 9A not more because my DMM limit. You're definitely going to want current limiting - a self-resetting thermal breaker could suffice; and reverse voltage protection- I've seen trained mechanics hook up chargers and other things backwards. I have to add some reverse protection diode's on ISENSE and USENSE in parallel and one diode in series with 5V rail. This should protect msp430. MOSFET should protect secondary side of atx, I have to make some simulations in LTspice to check this. I didn't mention that charge process is started only when at least 2V (positive = good polarity) are present on battery terminals. By the way thanks for constructive comments. Quote Link to post Share on other sites
abecedarian 330 Posted November 21, 2012 Share Posted November 21, 2012 ... I have to add some reverse protection diode's on ISENSE and USENSE in parallel and one diode in series with 5V rail. This should protect msp430. MOSFET should protect secondary side of atx, I have to make some simulations in LTspice to check this. I didn't mention that charge process is started only when at least 2V (positive = good polarity) are present on battery terminals. By the way thanks for constructive comments. I wasn't just referring to protecting the equipment. Batteries don't like being charged backwards either. The self resetting breaker would help some there as if the charger can deliver10A @ 15v and the battery is strong enough to produce 5A @ 12v, you're looking at least a 14A current across the charger output (unless my math is wrong: 10*15=150W, 5*12=60W, 150+60=210, 210/12=17.5A @ 12v, 210/15=14A @ 15v). Been a while since I worked on automotive charging circuits so forgive me if I'm wrong; please correct me if you will. Quote Link to post Share on other sites
blankfield 14 Posted November 22, 2012 Author Share Posted November 22, 2012 210/12=17.5A @ 12v, 210/15=14A @ 15v In case of wrong polarity with mosfet switched on, a car battery can provide lot more amps. In this case current is limited only by series resistance of battery, wires, Rds(ON), atx transformer secondary side winding etc. I've made some simulations and conclusion is only one: don't switch this mosfet when battery is in wrong polarity, it's easy to make software protections for this situation. Wrong polarity for circuit is safety, I don't want to add reverse protection diode on high current path because of power losses. abecedarian 1 Quote Link to post Share on other sites
nura100 0 Posted November 23, 2012 Share Posted November 23, 2012 Nice Project ! The prototype looks promising, but there are no details about how to hack the ATX supply to output 17V. Can you provide the details of hack. Thanks, Nura Quote Link to post Share on other sites
blankfield 14 Posted November 24, 2012 Author Share Posted November 24, 2012 Can you provide the details of hack. Nura In few words it depends on kind of PWM switching controller which ATX supply is using. I have tested models with TL494 and SG6105 IC's both with success. At first you need to remove all components from 3.3V, 5V, -5V rails, then "upgrade" components in 12V rail to new required voltages and power. After this and based on datasheet you need to provide correct voltages on feedback inputs of switching controller basically from resistor dividers, this step bypass OV, UV, OVC protections for missing rails. Via value change in resistor dividers of feedback it's easy to set output voltage. When I finish a project I will try to provide more detail and complete materials about this topic. Probably final schematic in attachment. Quote Link to post Share on other sites
nura100 0 Posted November 26, 2012 Share Posted November 26, 2012 Thanks for the info, changing P.D to output higher voltage seems simple but do i have to rewind the trafo with new turns ? Nura Quote Link to post Share on other sites
thanhtran 10 Posted December 18, 2012 Share Posted December 18, 2012 What's the highest voltage one can achieve with modifying the ATX power supply? At sometime I needed 32.4v and 36v to charge my battery pack (A123 cells - for an RC helicopter), and there was no charger available at the time for this kind of voltage, so I had to incorporate a step-up regulator (to 50v max from 12v) and a MSP430 to modify the feedback voltage of the regulator. http://www.rcgroups.com/forums/showthread.php?t=928136& Quote Link to post Share on other sites
bobnova 59 Posted December 19, 2012 Share Posted December 19, 2012 How much modifying? If you're willing to do some creative modifactions to the feedback bits and replace all the capacitors on the second side you might be able to get a decent bit more. I don't know that anybody has tried. I seriously doubt you'd get 30+ volts though, the transformer is wound to take ~380v and make it 12v after all. The 12V filter caps are all rated at 16V, as such going over 13.5-14ish without replacing them isn't an especially good idea. Quote Link to post Share on other sites
blankfield 14 Posted December 22, 2012 Author Share Posted December 22, 2012 Thanks for the info, changing P.D to output higher voltage seems simple but do i have to rewind the trafo with new turns ? Nura Hi, my project basis only on modification of some feedback resistors, no need to play with trafo. What's the highest voltage one can achieve with modifying the ATX power supply? At sometime I needed 32.4v and 36v to charge my battery pack (A123 cells - for an RC helicopter), and there was no charger available at the time for this kind of voltage, so I had to incorporate a step-up regulator (to 50v max from 12v) and a MSP430 to modify the feedback voltage of the regulator. http://www.rcgroups.com/forums/showthread.php?t=928136& Up to ~17V for typical ATX PSU is maximum value. Above this value you need to rewind or made a new transformer. In my opinion it's to tricky, especially to disassemble fragile core (2 "E" shapes glued together). thanhtran 1 Quote Link to post Share on other sites
blankfield 14 Posted December 23, 2012 Author Share Posted December 23, 2012 I've edited first post, now all materials are included. abecedarian 1 Quote Link to post Share on other sites
thanhtran 10 Posted January 9, 2013 Share Posted January 9, 2013 Looks really nice. Thanks for the answer about modifying the ATX Power supply. Quote Link to post Share on other sites
Arch_ETS 0 Posted March 28, 2013 Share Posted March 28, 2013 Whoa, pretty impressive. Is it functioning fine? Really interesting project. Isn't PWM stressing battery somehow? EDIT: I was reading code, probably it is just lack of my knowledge, but in code R7resistor - 4650, while in schematic 5600ohm. :? EDIT2: So, I managed to blow up my ATX three times in a row, but fixed that only twice. Luckily I got one more. Meanwhile I was checking LCD code, and Polish character stuff (commented one) was unclear for me. I wanted to add some Latvian chars, like ? {0x1F, 0x000, 0x1F, 0x11, 0x1F, 0x11, 0x11, 0x000};, but couldn't compile, got an error about "const char". So I changed it to just "char". Then it compiled fine. What else should I do to successfully print out these chars on LCD? Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.