General purpose PIC16F876A controlled power supply
CONSTANT VOLTAGE, 0<25V OR CONSTANT CURRANT, 1, 2, 4 and 8 Amp ver.
SPECIFICATIONS VOLTAGE: - Ajustable from 0 < 25 Volts at a resolution of 100 mV. CURRENT: choice of 4 ranges; - 0 < 1.000A at a resolution of 1 mA. JP1 and JP2 open, C1 = 2000uF filter cap., R7 = 0.5 Ohm, T1 = 24V, 1Amp. - 0 < 2.000A at a resolution of 2 mA. JP1 ground and JP2 open, C1 = 4000uF filter cap., R7 = 0.25 Ohm, T1 = 24V, 2Amp. - 0 < 4.000A at a resolution of 4 mA. JP1 open and JP2 ground, C1 = 8000uF filter cap., R7 = 0.125 Ohm, T1 = 24V, 4Amp. - 0 < 8.000A at a resolution of 8 mA. JP1 and JP2 ground, C1 = 16000uF filter cap., R7 = 0.0625 Ohm, T1 = 24V, 8Amp. 2 lignes LCD display: - The top line shows the settings, the maximum current stays displayed at all times, it is not necessary to short the output terminals to set the maximum current. - The bottom line shows the measured values. - The arrows at the bottom center line show who is in control, the voltage or the current. Ajustments: - Voltage, steps of 1 V and 0.1 V. - Current, steps of 1, 2, 4, 8 mA and 50, 100, 200, 400 mA depending on the Amp range. - Memorisation of the last settings before power off, restarting with thoses same settings. View the schematic Parts list C1 = 2000uF per AMP, 40V (adjust capacity to power supply amps) C2,C3,C12,C15,C18,C21 = 0.1uF CER C4 = 0.01uF CER C5A,C5B,C5C,C5D = 0.01uF CER C6,C7 = 0.05uF CER C8 = Not used C9,C11,C19,C20 = 1uF CER C10 = 470uF 15V EL RADIAL C13 = 4.7F 50V EL RADIAL C14 = 0.001uF CER C16,C17 = 22pF CER D1,D2,D4,D5 = 1N4148 D3 = 1N4004 Br1 = 1<10A BRIDGE RECTIFIER (adjust seize to power supply amps) DZ1 = 1N4751 30V ZENER IC1 = LM317KCS TO220 IC2 = LM337LZ TO92 IC3 = LT1491CN special HV opamp, do not substitute IC4 = PIC16F876A Q1 = 2N3906 Q2A to Q2D = TIP142TU 1 to 5 transistors (adjust quantity to power supply amps) Q3A to Q3D = 2N3904 (adjust quantity to power supply amps) X1 = 20 mHz HC49 DigiKey PN: 300-6042-ND LED1 = T1 3/4 RED OR GREEN LED LCD = LCD, 2X16, HD44780 intelligent controller DigiKey PN: 67-1758-ND insulate from case P1 = 500 Ohms P2 = 10 kOhms P3,P4 = 5 kOhms R1 = 1 kOhms 10W R2 = 1.5 kOhms R3,R11,R12,R13,R14,R16,R17,R25,R26,R27 = 10 kOhms R4 = 56 Ohms R5A to R5D = 0.22 Ohms (adjust quantity to power supply amps) R6,R28,R29 = 47k Ohms R7 = as per schematic (adjust value to power supply amps) R8 = 240 Ohms R9 = 270 Ohms R10 = 390 Ohms R15 = 39 kOhms R18 = 39 kOhms R19 = 8.2 kOhms R20,R22,R23,R32 = 1 kOhms R21 = 5.6 kOhms R24 = 33 Ohms 1/2W R30,R31 = 4.7 kOhms SW1 = SPST min toggle SW2,SW3,SW4,SW5,SW6,SW7,SW8,SW9 = NO_SP push button T1 = 24 V 1<8A (adjust seize to power supply amps) T2 = 6.3 V, 150 mA MISC: 3 Terminals, Transistors Heat Sink, up to: 1A = 30W, 2A = 60W, 4A = 120W, 8A = 240W to dissipate Line filter, Case PCB, IC sockets... View the PCB View the PCB + parts CALIBRATION LCD: - Adjust P2 near ground for a nice contrast. VOLTAGE: - Adjust P1 for a supply of 5.12V. - Measure the voltage output terminals, adjust P3 to for the same voltage reading on the PS LCD and the meter. CURRENT: - Install a 10 Ohm resistor in series with an ammeter in series on the output terminals. - adjust P4 for a correct Amp reading on the LCD. NOTE I recommand the use of a PIC16F876A, it is lower in cost and has a better EEDATA memory. The source program in "C" and the object code (HEX) for the PIC16F876A in alim_v3.zip. //-------------------------- ALIM V3 ---------------------------------------- // ALIMENTATION VARIABLE CONSTANT VOLTAGE, CONSTANT COURANT, 0 < 25V // choix de 4 gammes de courant, 0<1A, 2A, 4A et 8A VERSION: 3.0 // PAR VE2EMM avril 04 //--------------------------------------------------------------------------- #include <16F876a.h> #device ADC=16 // les 10 bits de gauche sont valable #use delay(clock=20000000) #fuses HS,NOWDT,PUT,BROWNOUT,NOPROTECT,NOLVP,NOCPD,NOWRT //#rom 0x2100={0x00,0x00,0x00,0x00,0x00} // zero les premieres 4 cases du EEDATA #use fast_io(A) #use fast_io(B) #use fast_io(C) //------------------------- VARIABLES ------------------- INT8 i; // 0 < + 255 int8 commande; // 0 < + 255 int8 car; // 0 < + 255 int8 nibble; // 0 < + 255 int8 boutons; // 0 < + 255 int8 bit_status; // 0 < + 255 int16 disp_a; // pour gamme de l'affichage Amp int8 disp_ma; // pour gamme de l'affichage mAmp //----------- signed int16 volt_moy[16]; signed int16 amp_moy[16]; signed int16 volt_set=0; // -32768 < +32767 signed int16 volt_lue=0; // " " signed int16 amp_set=0; // " " signed int16 amp_lue=0; // " " //------------------------- FONCTIONS --------------- void pic_ini(void); void lcd_ini(void); void write_cmd_lcd(int8); // LCD void write_car_lcd(INT8); // LCD void send_nibble(int8); // LCD void busy_status(void); // LCD void lire_voltage(void); // lire et afficher E void lire_courant(void); // lire et afficher I void lire_PB(void); // lire les boutons, changer les consignes de voltage et de courant void set_range(void); void comparer_Iset_Ilue(void); // determiner qui est en controle, E ou I //********************************* MAIN *********************************** void main(void) { pic_ini(); lcd_ini(); for(i=0;i<=15;i++) volt_moy[i]=0; for(i=0;i<=15;i++) amp_moy[i]=0; //------- afficher ecran d"acceuil ------- write_cmd_lcd(128); // adresse du debut de la 1 re ligne printf(write_car_lcd,"ALIMENTATION V3 "); write_cmd_lcd(192); // adresse du debut de la 2 ie ligne printf(write_car_lcd," PAR VE2EMM "); for(i=0;i<4;i++) delay_ms(250); // laisser affichage 1 1/2 seconde //---- charger les valeurs du eedata for(i=0;i<2;i++) *(&volt_set+i)=read_eeprom(10+i); // reprendre les consignes if((volt_set>1023)||(volt_set<0))volt_set=0; for(i=0;i<2;i++) *(&_set+i)=read_eeprom(12+i); // du dernier usage if((amp_set>1023)||(amp_set<0))amp_set=0; //---- programme principal while(true) { set_range();// choisir la gamme du courant, 1, 2, 4 ou 8 Amp lire_PB(); // lire les boutons et placer les consignes de voltage et de courant write_cmd_lcd(128); // adresse du debut de la 1ere ligne printf(write_car_lcd,"%01ld,%03ldA SET %02ld,%01ldV",amp_set/disp_a,amp_set*disp_ma,volt_set/40,volt_set/4); SET_PWM1_duty(amp_set); // amp_set SET_PWM2_duty(volt_set); // volt_set lire_voltage(); // lire et afficher lire_courant(); // lire et afficher comparer_Iset_Ilue(); // lire et afficher qui est en controle, E ou I } } //********************************* FIN MAIN ****************************** //************************************ FONCTIONS ***************************** void pic_ini(void) { //-- PORTS output_A(0x00); output_B(0x00); output_C(0x00); set_tris_A(0b11011111); port_B_pullups(true); set_tris_B(0b11111111); set_tris_C(0b00000000); //--- ADC setup_adc_ports(RA0_RA1_RA3_ANALOG); setup_adc(ADC_CLOCK_INTERNAL); //--- PWM SET_PWM1_duty(0); // amp_set SET_PWM2_duty(0); // volt_set setup_timer_2(t2_div_by_16,255,1); setup_ccp1(CCP_PWM_PLUS_3); setup_ccp2(CCP_PWM_PLUS_3); } //------------------------------------------------- void lcd_ini(void) { delay_ms(100); // attendre que le 5V soit stabilise output_bit(PIN_A5,0); // E clock output_bit(PIN_C0,0); // R/W R=1,W=0 output_bit(PIN_C3,0); // R/S 0 pour commande, 1 pour data for(i=1;i<=4;i++) {send_nibble(0x30); delay_ms(5);} // reset du LCD send_nibble(0x20); // interface 4 bits delay_us(43); write_cmd_lcd(0x28); // interface 4 bits, 5X7 write_cmd_lcd(0x08); // display off write_cmd_lcd(0x0C); // display on write_cmd_lcd(0x06); // increment no display shift } //*********************** Fonctions du LCD ***************************** void write_cmd_lcd(commande) { busy_status(); // LCD pret? output_bit(PIN_C3,0); // R/S 0 pour commande send_nibble(commande); // placer commande dans LCD swap(commande); send_nibble(commande); // placer commande dans LCD } //-------------- afficher un caractere sur le LCD -------------------------- void write_car_lcd(car) { busy_status(); // LCD pret? output_bit(PIN_C3,1); // R/S 1 pour data send_nibble(car); // placer commande dans LCD swap(car); send_nibble(car); // placer commande dans LCD } //-------------------- Envoyer un nibble ---------------- void send_nibble(nibble) { output_bit(PIN_C0,0); // R/W R=1, W=0 output_bit(PIN_A5,0); // E line low delay_cycles(3); output_bit(PIN_A5,1); // pulser E high nibble=nibble&0xF0; // vider les 4 bits du bas de la byte nibble=nibble|(input_C()&0x0F); // charger C0-C3 output_C(nibble); delay_cycles(3); output_bit(PIN_A5,0); // E low } //----------------------- void busy_status() // tester si le LCD est pret a accepter un data { do { set_tris_C(0b11110000); output_bit(PIN_A5,0); // E off output_bit(PIN_C0,1); // R/W R=1,W=0 output_bit(PIN_C3,0); // R/S 0 pour commande, 1 pour data delay_cycles(3); output_bit(PIN_A5,1); // pulser E high delay_cycles(3); bit_status=input_C()&0X80; // lire status delay_us(2); output_bit(PIN_A5,0); // E off delay_cycles(3); output_bit(PIN_A5,1); // pulser E high delay_us(1); // faire semblant de lire les 4 bits de status du bas output_bit(PIN_A5,0); // E off delay_cycles(3); } while(bit_status==0x80); set_tris_C(0b00000000); } //***************************** Fin des fonctions LCD ******************************** //************************************************************************************ void lire_voltage() // lire et afficher { SET_ADC_CHANNEL(0); delay_ms(1); for(i=15;i>=1;i--) volt_moy[i]=volt_moy[i-1]; volt_moy[0]=READ_ADC()/64; // tasser a droite la lecture dans les 2 bytes, 6 bits volt_lue=0; for(i=0;i<=15;i++) volt_lue=volt_lue+volt_moy[i]; volt_lue=volt_lue/16; volt_lue=volt_lue+2;// compenser pour le restant de la division a l'affichage //if(volt_lue<=7)volt_lue=0; write_cmd_lcd(203); // adresse du 3/4 de la 2ie ligne printf(write_car_lcd,"%02ld,%01ldV",volt_lue/40,volt_lue/4); } //---------------------------- void lire_courant() // lire et afficher, tester si I depasse la limite et est en controle { SET_ADC_CHANNEL(1); delay_ms(1); for(i=15;i>=1;i--) amp_moy[i]=amp_moy[i-1]; amp_moy[0]=READ_ADC()/64; // tasser a droite la lecture dans les 2 bytes, 6 bits amp_lue=0; for(i=0;i<=15;i++) amp_lue=amp_lue+amp_moy[i]; amp_lue=amp_lue/16; amp_lue=amp_lue+1; write_cmd_lcd(192); // adresse du debut de la 2ie ligne printf(write_car_lcd,"%01ld,%03ldA",amp_lue/disp_a,amp_lue*disp_ma); } //---------------------------- void set_range() { if((input(pin_A2)==1)&&(input(pin_A4)==1)){disp_a=1000;disp_ma=1;} if((input(pin_A2)==0)&&(input(pin_A4)==1)){disp_a=500;disp_ma=2;} if((input(pin_A2)==1)&&(input(pin_A4)==0)){disp_a=250;disp_ma=4;} if((input(pin_A2)==0)&&(input(pin_A4)==0)){disp_a=125;disp_ma=8;} } //-------------------------------------------------------------- void lire_PB() // lire les boutons et setter voltage et le courant { boutons=input_b(); if(boutons!=0xFF) { switch(boutons) { case 0b01111111: // grande reduction du voltage ************* volt_set=volt_set-40; if(volt_set<=0) volt_set=0; break; case 0b10111111: // grande reduction du courant amp_set=amp_set-50; if(amp_set<=0) amp_set=0; break; case 0b11011111: // grande augmentation du voltage ********** volt_set=volt_set+40; if(volt_set>=1000) volt_set=1000; break; case 0b11101111: // grande augmentation du courant amp_set=amp_set+50; if(amp_set>=1000) amp_set=1000; break; case 0b11110111: // petite augmentation du voltage ********** volt_set=volt_set+4; if(volt_set>=1000) volt_set=1000; break; case 0b11111011: // petite augmentation du courant amp_set=amp_set+1; if(amp_set>=1000) amp_set=1000; break; case 0b11111101: // petite reduction du voltage ************* volt_set=volt_set-4; if(volt_set<=0) volt_set=0; break; case 0b11111110: // petite reduction du courant amp_set=amp_set-1; if(amp_set<=0) amp_set=0; break; default: // plus qu'un bouton pese break; } for(i=0;i<2;i++) write_eeprom(10+i,*(&volt_set+i)); for(i=0;i<2;i++) write_eeprom(12+i,*(&_set+i)); delay_ms(350); // taux de repetition } } //------------------------------- comparer_Iset_Ilue() { write_cmd_lcd(198); // milieu de la 2ie ligne if (amp_lue<(amp_set-1)) printf(write_car_lcd," >>> "); else printf(write_car_lcd," <<< "); } //******************************** Fin des fonctions **************************************