gaobao_1 发表于 2012-6-14 10:45:03

PID锂电充电器

本帖最后由 gaobao_1 于 2012-6-14 10:54 编辑

用m8做的锂电充电器大家发表观点
/***************2012/04/20初写*******************/
/***************2012/05/10调试*******************/
/***********没用中断方式的充电器**************/
/***************2012/05/10试用版*******************/
#include<iom8v.h>
#include "macros.h"
#include"delay.h"
//#include"LCMXX.H"
#defineLEDM_1PORTD|= (1<<PD3)      //充满指示
#defineLEDM_0PORTD&=~(1<<PD3)
#defineLEDC_1   PORTD|= (1<<PD4)    //充电指示
#defineLEDC_0   PORTD&=~(1<<PD4)
#defineFDKG_1   PORTD|= (1<<PD5)    //放电开关
#defineFDKG_0   PORTD&=~(1<<PD5)
#defineACKEY_1   PORTC|= (1<<PC5)    //交流开关
#defineACKEY_0   PORTC&=~(1<<PC5)
unsigned charhour,cent,second,time_ms;
intError;      //误差值
intvpwm ,pwm,sumError=0;
unsigned char   mode;          //工作模式
unsigned int   vdd_tmp;      //ADC_BAT电压
unsigned int   vdd_BAT1;      //ADC_BAT电压
unsigned int   vdd_BAT0;      //ADC_BAT电压
unsigned int   bat_A;      //ADC_电流通渠道
unsigned int   ADC_tmp;      //ADC_BAT电压
unsigned long   ADC_BAT;
unsigned int read_2_56adc(unsigned char ad_chanel)
{ unsigned int ui_AD_data;
   ADMUX=ad_chanel;                //外2.56V参考电压
   delay_us(100);
   ADCSRA=1<<ADEN|1<<ADSC|6<<ADPS0;   //启动AD 64分频
   while (!(ADCSRA&(1<<ADIF)));      //等待A/D完成
   ADCSRA|=(1<<ADIF);               //写1清除标志位
   ui_AD_data=ADC;
   return (ui_AD_data);
}
/*
voidbit_bcd (unsigned int tmpda)   //转换BCD码
{       
       play_data= SEG7_2;
           play_data= SEG7_2;
       play_data= SEG7_2;
       play_data= SEG7_2;
}
voidbit_bcd_1 (unsigned int tmpda)   //转换BCD码
{       
       display_data= SEG7_1;
           display_data= SEG7_1;
       display_data= SEG7_1;
       display_data= SEG7_1;
}*/
#pragma interrupt_handler timer2:5
//T/C2中断服务
void timer2(void)
{
/***********************************/
time_ms++;
if(time_ms==2)
   {time_ms=0;second++;}
if(second==60)
   {second=0;
   cent++;
   }
if(cent==60)
   {cent=0;
   hour++;
   }
if(hour==24)
   {hour=0;
   }
    _SEI();

}
//端口初始化
void port_init(void)
{
PORTB = 0x08;   //key_PB3输入
DDRB= 0x07;
PORTC = 0x00;
DDRC= 0x30;   //PC5输出
PORTD = 0x00;
DDRD= 0xff;
}


//定时T1初始化
void timer1_init(void)
{
TCCR1B = 0x00;//停止定时器
TIMSK |= 0x00;//中断允许
OCR1A = 0x0000;//匹配A值
//ICR1= 0x012c;//输入捕捉匹配值
TCCR1A = 0x81;   //8位pwm
TCCR1B|=!(1<<WGM12)|1<<CS10;   //系统时钟
}
void init_timer2_ocr(void)
{
ASSR=(1<<AS2);
TCCR2=0x04;   //64分频
TIMSK=(1<<TOIE2);
TCNT2=0x00;
while (ASSR&(1<<OCR2UB));
}

void init_devices(void)
{
CLI(); //禁止所有中断
MCUCR= 0x00;
MCUCSR = 0x80;//禁止JTAG
GICR   = 0x00;
port_init();
timer1_init();
//init_timer2_ocr();
SEI();//开全局中断
}
//主函数
void main(void)
{ unsigned char i;
init_devices();
delay_ms( 200 ) ;
   TCCR1A = 0x00;//停止定时器
   delay_us(20);
   FDKG_1;
   delay_ms(6);
   vdd_tmp=read_2_56adc(0x00);
   vdd_BAT0=(vdd_tmp*17)/10;
   FDKG_0;
   ADC_tmp=vdd_BAT0/10;
   vpwm=250*ADC_tmp/150;   //计算PWM值1500是电源电压
   OCR1A=vpwm;
   if(vdd_BAT0<300){mode=0;
                  TCCR1A = 0x81;//停止定时器
                                        LEDC_1 ;
                      LEDM_0 ;      //无电池或电池严重欠压
                                        OCR1A=30;
                                        delay_ms(100);
                                        TCCR1A = 0x00;//停止定时器
                                        LEDC_0 ;
                                        delay_ms(60);
                                   }
   else if(vdd_BAT0<900){mode=1;
                         TCCR1A = 0x81;
                         LEDM_0;      //备充电状态
                                          LEDC_1;
                                                 
                                                for(i=0;i<60;i++)
                            {
                                      delay_05ms();
                                           vdd_tmp=read_2_56adc(0x01);
                                      vdd_BAT1=vdd_tmp*17;
                                                   vdd_tmp=read_2_56adc(0x00);
                                                           vdd_BAT0=vdd_tmp*17;
                                                       
                                                           if(vdd_BAT1>vdd_BAT0)
                                                             {bat_A=(vdd_BAT1-vdd_BAT0);
                                                          Error=70-bat_A;
                                                          pwm=vpwm+(Error/12)+(Error/24);//
                                                          vpwm=pwm;
                                                          Error=Error;
                                                          OCR1A=pwm;
                                                           }
                                                         }
                        }
   else if (vdd_BAT0<1259){ mode=2;
                                                 TCCR1A = 0x81;
                            LEDM_0;      //恒流充电状态
                                LEDC_1;
                                      
                                                   for(i=0;i<60;i++)
                               {
                                      delay_05ms();
                                          vdd_tmp=read_2_56adc(0x01);
                                      vdd_BAT1=vdd_tmp*17;
                                                   vdd_tmp=read_2_56adc(0x00);
                                                           vdd_BAT0=vdd_tmp*17;
                                                       
                                                           if(vdd_BAT1>vdd_BAT0)
                                                             {bat_A=(vdd_BAT1-vdd_BAT0);
                                                          Error=245-bat_A;
                                                          pwm=vpwm+(Error/12)+(Error/24);//
                                                          vpwm=pwm;
                                                          Error=Error;
                                                          OCR1A=pwm;
                                                           }
                                                         }
                        }
   else{ mode=3;
         LEDM_1;      //恒压充电状态
             LEDC_0;
                   vpwm=30;
                   OCR1A=vpwm;
                   TCCR1A = 0x81;
                   for(i=0;i<40;i++)
                   {
                           delay_ms (2);
                   vdd_tmp=read_2_56adc(0x01);
               vdd_BAT1=vdd_tmp*17;
                                Error=12600-vdd_BAT1;
                                pwm=vpwm+(Error/12)+(Error/24);
                                vpwm=pwm;
                                Error=Error;
                                OCR1A=pwm;
                                }
          }
               
while(1)
{ TCCR1A = 0x00;//停止定时器
   delay_us(20);
   FDKG_1;      //放电测量电池电压
   delay_ms(6);
   vdd_tmp=read_2_56adc(0x00);
   ADC_BAT=vdd_tmp;
   vdd_BAT0=(unsigned int)((ADC_BAT*171)/100);
   //vdd_BAT0=(vdd_tmp*17)/10;
   FDKG_0;
   if(vdd_BAT0<300){mode=0;
                  TCCR1A = 0x00;//停止定时器
                      LEDM_0 ;      //无电池或电池严重欠压
                             LEDC_0 ;
                                  
                                   }
   else if(vdd_BAT0<900){mode=1;
                        LEDM_0;      //备充电状态
                                           LEDC_1;
                                                  TCCR1A = 0x81;
                        }
   else if (vdd_BAT0<1259){mode=2;
                           LEDM_0;      //恒流充电状态
                               LEDC_1;
                                       TCCR1A = 0x81;
                        }
   else if(vdd_BAT0<1261) { mode=3;
                           LEDM_1;      //恒压充电状态
                               LEDC_0;
                                     TCCR1A = 0x81;
                           }
   else    { mode=4;
             LEDM_1;      //停止恒压充电状态
                 LEDC_0;
                     TCCR1A = 0x00;
            }
if((PINB&0x08)==0)   //keyac/dc变换
      {
          PORTC ^= 0x20;
          }
   if((PINC&0x20)==0x20)
   { mode=0;
       }
    switch(mode)
      {
       case 0:TCCR1A = 0x00;//停止定时器
                   LEDM_0 ;      //无电池或电池严重欠压
                   LEDC_0 ;
                           delay_ms (120);
                    break;
           case 1: for(i=0;i<240;i++)
                   {
                           delay_05ms ();
                   vdd_tmp=read_2_56adc(0x01);
               vdd_BAT1=vdd_tmp*17;
                   vdd_tmp=read_2_56adc(0x00);
                           vdd_BAT0=vdd_tmp*17;
                          
                           if(vdd_BAT1>vdd_BAT0)
                             {bat_A=(vdd_BAT1-vdd_BAT0);
                                  Error=70-bat_A;
                               //sumError += Error;
                                  Error=Error-Error;
                                  pwm=vpwm+(Error/12)+(Error/24);
                                  vpwm=pwm;
                                  Error=Error;
                                  OCR1A=pwm;
                               }
                          }
                break;
       case 2:
                               for(i=0;i<240;i++)
                   {
                             delay_05ms();
                   vdd_tmp=read_2_56adc(0x01);
               vdd_BAT1=vdd_tmp*17;
                   vdd_tmp=read_2_56adc(0x00);
                           vdd_BAT0=vdd_tmp*17;
                          
                           if(vdd_BAT1>vdd_BAT0)
                             {bat_A=(vdd_BAT1-vdd_BAT0);
                                  Error=245-bat_A;
                                  Error=Error-Error;
                                  pwm=vpwm+(Error/12)+(Error/24);//+(sumError/20);
                                  vpwm=pwm;
                                  Error=Error;
                                  OCR1A=pwm;
                               
                                  }
                          }
                    break;
           case 3:
                           for(i=0;i<90;i++)
                   {
                          delay_ms (2);
                    vdd_tmp=read_2_56adc(0x01);
                vdd_BAT1=vdd_tmp*17;
                                Error=12600-vdd_BAT1;
                                Error=Error-Error;
                                pwm=vpwm+(Error/12)+(Error/24);
                                vpwm=pwm;
                                Error=Error;
                                OCR1A=pwm;
                       
                          }
                                break;
          case 4:
                           TCCR1A = 0x00;//停止定时器
                   LEDM_1 ;      //无电池或电池严重欠压
                   LEDC_0 ;
                           delay_ms (100);
                    break;
           default:break;
          }
   }
}

gaobao_1 发表于 2012-6-14 10:58:39

欢迎关注!

good200xyz 发表于 2012-6-14 11:24:51

一直想搞个类似的充电器。多谢分享!

BCE312 发表于 2012-6-19 22:08:46

很经典的东西。能最终调试出来肯定能学到不少东西。

xiangye 发表于 2012-6-19 22:34:02

不错 不错

dory_m 发表于 2012-6-20 12:06:59

学习,不错 不错!!!{:smile:}

prow 发表于 2012-6-22 14:16:57

mark         

zssssha 发表于 2012-6-22 21:04:25

学习了,支持分享

huayuliang 发表于 2012-6-22 21:31:50

问下·····这个需要PID么?

sunliezhi 发表于 2012-6-23 07:28:54

建议你将PID部分去掉,将代码再反复动几次大手术甚至推倒重来,参考atmel的一个例程吧,他的网站上有。

zzy9903 发表于 2012-6-30 18:22:09

不知道楼主的充电器使用的效果怎么样?

jackielau 发表于 2012-6-30 19:14:07

很好的例子,提个小建议!提高代码可读性、可移植性、和规范性:
1、一些同样功能的代码段可以写成函数调用,看着方便!
2、对于一些寄存器设置可以写一个专门的宏,比较清晰
比如:TCCR1A = 0x00;//停止定时器
可以写成
#define T1_STOP() TCCR1A = 0x00
调用时直接写:T1_STOP() ;
3、一些状态可以定义成宏,
switch(mode)
{
      case 0:
                break;
       case 1:
               break;
}
可以改成
switch(mode)
{
      case STATE1:
                break;
       case STATE2:
               break;
}
这种形式,STATE1要用比较易懂的词,比如恒压状态:STATE_CV,横流状态:STATE_CC
4、一些比较的数值也可以用宏预先计算出来,比如
if(vdd_BAT0<300)中三百的意思不直观
可以
#define BAT_VOLT(x)x*5/100      (***公式是我自己瞎写的)
这样BAT_VOLT(3)代表3V,看程序时可以很快知道低于3V是某某状态,以后修改程序事也更方便,只需要改x的数值


好了,啰嗦了很多,我说的也不应定就正确,希望高手指点!!

jason520 发表于 2012-7-27 22:43:54

{:smile:}{:smile:}{:smile:}

gaobao_1 发表于 2012-7-30 16:22:18

谢谢13楼的建议,值得好好学习!

215661599 发表于 2012-10-1 23:18:24

marshallemon 发表于 2012-10-1 23:40:31

使用内阻小的锂电池充电例如三洋18650,充满电测下电压看看能做到多少?MAX1879做的可以做到4.200XV

sunliezhi 发表于 2012-10-2 00:31:50

void 充电控制(void)
{
   t_batt = measure_temp();
   if ((t_batt > 充电起始温度) && (t_batt < 充电终止温度))
   {
          switch (充电状态)
          {
          case START:
                  u_batt = measure_volt();
                  if ((t_batt < 恒流充电起始温度) | | (u_batt < 恒流充电起始电压))
                  {
                        充电时间变量 = 预充时间;
                        充电状态 = 预充态;
                  }
                  else if (u_batt < 恒流充电终止电压)
                  {
                        充电时间变量 = 恒流充电时间;
                        充电状态 = 恒流态;
                  }
                  else
                  {
                        充电时间变量 = 恒压充电时间;
                        充电状态 = 恒压态;
                  }
                  // break;
          case 预充态:
                  if (充电时间变量)
                  {
                         start_pwm(xxx);
                         curr_adj(xxx);
                         u_batt = measure_volt();
                         if ((t_batt >= 恒流起始温度) && (u_batt >= 恒流起始电压))
                         {
                              充电状态 = 恒流态;
                              充电时间变量 = 恒流充电时间;
                         }
                  }
                  else
                  {
                         充电控制标志 |= 预充超时出错;
                         充电状态 = END;
                  }
                  break;
          case 恒流态:
                  if (充电时间变量)
                  {
                         ...
                  }
                  else
                  {
                         ...
                  }
                  break;
          case 恒压态:
                  if (充电时间变量)
                  {
                         ...
                  }
                  else
                  {
                         ...
                  }
                  break;
          case END:
                  stop_pwm();
                  ...
                  break;
          }
   }
}

venus5712 发表于 2012-10-2 17:06:56

很好很 强大!!!!!

liudaolunhui 发表于 2012-10-4 23:55:27

楼主辛苦了!!!!!!!!!!!!

cheky77 发表于 2012-10-5 10:24:41

那么厉害了。也可以中文写程序了。

xiongxie007 发表于 2013-4-18 19:44:54

MARK MARK MARK

wlkobe 发表于 2013-4-19 16:32:05

想问下楼主要设计一个充电器的话应该注意些什么东西呢?
那个PID算法在充电电路里面具体用在哪一块方面呢?

lianfutiana 发表于 2013-4-22 13:44:10

ding....................

2005n2005 发表于 2013-4-22 14:09:19

mark
      

freethink168@ 发表于 2013-5-26 16:35:23

关注中,留着以后测试用

dhw5qq 发表于 2016-4-8 13:25:03

楼主, 我给你提个建议,代码最好用函数封装,然后最好规范一点,而且关键处注释写好,不然你写的就是自己能看懂, ,别人要琢磨半天才能看懂!

MagicYang 发表于 2016-5-25 16:31:23

顶一个

evanwangyi2018 发表于 2018-4-18 08:27:24

这个帖子好,教学相长,赞!

changshs 发表于 2018-7-25 12:45:05

值得称赞!~{:victory:}{:victory:}
页: [1]
查看完整版本: PID锂电充电器