Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 05/21/2017 in all areas

  1. 1 point
    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...