Jump to content

blankfield

Members
  • Content Count

    14
  • Joined

  • Last visited

  • Days Won

    4

Reputation Activity

  1. Like
    blankfield got a reaction from zeke in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  2. Like
    blankfield got a reaction from legailutin in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  3. Like
    blankfield got a reaction from thanhtran in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    Hi, my project basis only on modification of some feedback resistors, no need to play with trafo.
     
     
     
    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).
  4. Like
    blankfield got a reaction from rubfi in MSP430 Launchpad + Android = Pinball   
    Really nice project especially from software side, Android is fantastic OS because of free development add-on for eclipse. If you're using UART it is easy to make wireless communication via bluetooth. I did something similar with this module and based on this Java code - it's good point to start with android.
  5. Like
    blankfield reacted to rubfi in MSP430 Launchpad + Android = Pinball   
    Find here a small guide for building the Android side of the app (The launchpad side can be found on the original post)
     
    Android App Setup
     
    Download Android Serialport API code (I used the revision 54 for the pinball proof of concept)
    $ mkdir build $ cd build $ svn checkout -r54 http://android-serialport-api.googlecode.com/svn/trunk/ android-serialport-api  Download Vector Pinball code (rev  04ee044b27)
    $ git clone https://github.com/dozingcat/Vector-Pinball.git $ cd Vector-Pinball/ $ git checkout 04ee044b27 Apply the modifications to the Vector Pinball (MSP430Pinball.patch is attached to this post, copy it to build dir) 
    patch -p1 < ../MSP430Pinball.patch Copy serial port code and libs on the Vector Pinball tree.
    $ cp -r ../android-serialport-api/android-serialport-api/project/src/android_serialport_api/ src/ $ rm -rf src/android_serialport_api/sample/ $ cp -r ../android-serialport-api/android-serialport-api/project/libs/armeabi/libserial_port.so libs/armeabi/ $ cp -r ../android-serialport-api/android-serialport-api/project/libs/armeabi-v7a/libserial_port.so libs/armeabi-v7a/ You may need to modify "src/android_serial_port/SerialPort.java" for pointing the "su" path to the "su" command on your Android device. In my case, I did:
    $ vim src/android_serialport_api/SerialPort.java - su = Runtime.getRuntime().exec("/system/bin/su"); + su = Runtime.getRuntime().exec("/system/xbin/su"); To import the modified VectorPinball project to your Android Development environment (In eclipse  File > New >> Project... >> Android project from existing code -> Root directory = <path_to_vector_pinball> )
     
    Adding Serial Port emulation over USB to your OTG supporting Android Tablet
    (in my case an Allwiner A10 based)
     
    Download the kernel source code (I used the lichee3 branch in order to create a module compatible with the kernel already in the device)
    $ git clone -b lichee-3.0.8-sun4i git://github.com/linux-sunxi/linux-sunxi.git Configure the kernel, for adding CDC_ACM support
    $ make ARCH=arm sun4i_crane_defconfig $ make ARCH=arm menuconfig Device Drivers >> USB Support >> <M> USB Modem (CDC ACM) support Compile the kernel and the modules
    $ export PATH=$PATH:/<path_to_your_cross_compiler>/bin (i.e "export PATH=$PATH:/opt/arm-2010q1/bin" ) $ export CROSS_COMPILE=arm-none-linux-gnueabi- $ make ARCH=arm $ make ARCH=arm modules Copy the cdc-acm.ko module to your Android device and install it
    $ scp drivers/usb/class/cdc-acm.ko root@<ip>:/data/local/ ( i.e $ scp drivers/usb/class/cdc-acm.ko root@192.168.0.11:/data/local/ ) (I used DropBear SSH Server II on Android as SSH server) After copying cdc-acm.ko, you should ssh your Android device and execute as root "insmod cdc-acm.ko". (you may need to do this step every time you reboot your tablet). Check if /dev/ttyACM0 has been created after loading cdc-acm.ko in order to see if the module was installed correctly.
     
    From Eclipse build the apk or launch the BouncyActivity app.   Hopefully, following these steps it works for you, and you can build awesome apps using MSP430 Launchpad and Android.
    MSP430Pinball_VectorPinball.patch.gz
  6. Like
    blankfield got a reaction from cubeberg in [ ENDED ] Nov-Dec 2012 - 43oh Project of the Month Contest   
    It's time to submit own project for the POTM contest.  It will be my first attempt .
     
    Project name: ATX2CHARGER
    Short description: Switch your ATX into a car battery charger with msp430g2553
    Link: http://forum.43oh.com/topic/2906-atx2charger-switch-your-atx-into-a-car-battery-charger-with-msp430-updated/?p=24584
     
    Karol (aka blankfield)
  7. Like
    blankfield got a reaction from abecedarian in [ ENDED ] Nov-Dec 2012 - 43oh Project of the Month Contest   
    It's time to submit own project for the POTM contest.  It will be my first attempt .
     
    Project name: ATX2CHARGER
    Short description: Switch your ATX into a car battery charger with msp430g2553
    Link: http://forum.43oh.com/topic/2906-atx2charger-switch-your-atx-into-a-car-battery-charger-with-msp430-updated/?p=24584
     
    Karol (aka blankfield)
  8. Like
    blankfield got a reaction from abecedarian in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    I've edited first post, now all materials are included.
  9. Like
    blankfield got a reaction from abecedarian in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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.






  10. Like
    blankfield got a reaction from abecedarian in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  11. Like
    blankfield got a reaction from bluehash in [ ENDED ] Nov-Dec 2012 - 43oh Project of the Month Contest   
    It's time to submit own project for the POTM contest.  It will be my first attempt .
     
    Project name: ATX2CHARGER
    Short description: Switch your ATX into a car battery charger with msp430g2553
    Link: http://forum.43oh.com/topic/2906-atx2charger-switch-your-atx-into-a-car-battery-charger-with-msp430-updated/?p=24584
     
    Karol (aka blankfield)
  12. Like
    blankfield got a reaction from gwdeveloper in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  13. Like
    blankfield reacted to bluehash in How to simple get simetrical 8-15V from 5V USB rail with one DC/DC converter?   
    Welcome to Stellarisiti!
    Try the Boost calculator from Lady Ada.
  14. Like
    blankfield got a reaction from nura100 in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  15. Like
    blankfield got a reaction from Rickta59 in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
  16. Like
    blankfield got a reaction from Automate in ATX2CHARGER - switch your ATX into a car battery charger with MSP430 [UPDATED]   
    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 code


    To do:
    * firmware development


    If 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
×
×
  • Create New...