搜索
bottom↓
回复: 28

PID锂电充电器

[复制链接]

出0入0汤圆

发表于 2012-6-14 10:45:03 | 显示全部楼层 |阅读模式
本帖最后由 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"
#define  LEDM_1  PORTD|= (1<<PD3)      //充满指示
#define  LEDM_0  PORTD&=~(1<<PD3)
#define  LEDC_1   PORTD|= (1<<PD4)    //充电指示
#define  LEDC_0   PORTD&=~(1<<PD4)
#define  FDKG_1   PORTD|= (1<<PD5)    //放电开关
#define  FDKG_0   PORTD&=~(1<<PD5)
#define  ACKEY_1   PORTC|= (1<<PC5)    //交流开关
#define  ACKEY_0   PORTC&=~(1<<PC5)
unsigned char  hour,cent,second,time_ms;
int  Error[4];      //误差值
int  vpwm ,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);
}
/*
void  bit_bcd (unsigned int tmpda)     //转换BCD码
{       
       play_data[3]= SEG7_2[tmpda/1000%10];
           play_data[2]= SEG7_2[tmpda/100%10];
       play_data[1]= SEG7_2[tmpda/10%10];
       play_data[0]= SEG7_2[tmpda%10];
}
void  bit_bcd_1 (unsigned int tmpda)     //转换BCD码
{       
       display_data[0]= SEG7_1[tmpda/1000%10];
           display_data[1]= SEG7_1[tmpda/100%10];
       display_data[2]= SEG7_1[tmpda/10%10];
       display_data[3]= SEG7_1[tmpda%10];
}*/
#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[0]=70-bat_A;
                                                          pwm=vpwm+(Error[0]/12)+(Error[1]/24);//
                                                          vpwm=pwm;
                                                          Error[1]=Error[0];
                                                          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[0]=245-bat_A;
                                                          pwm=vpwm+(Error[0]/12)+(Error[1]/24);//
                                                          vpwm=pwm;
                                                          Error[1]=Error[0];
                                                          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[0]=12600-vdd_BAT1;
                                pwm=vpwm+(Error[0]/12)+(Error[1]/24);
                                vpwm=pwm;
                                Error[1]=Error[0];
                                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)     //key  ac/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[0]=70-bat_A;
                                 //sumError += Error[0];
                                  Error[2]=Error[0]-Error[1];
                                  pwm=vpwm+(Error[0]/12)+(Error[2]/24);
                                  vpwm=pwm;
                                  Error[1]=Error[0];
                                  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[0]=245-bat_A;
                                  Error[2]=Error[0]-Error[1];
                                  pwm=vpwm+(Error[0]/12)+(Error[2]/24);//+(sumError/20);
                                  vpwm=pwm;
                                  Error[1]=Error[0];
                                  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[0]=12600-vdd_BAT1;
                                Error[2]=Error[0]-Error[1];
                                pwm=vpwm+(Error[0]/12)+(Error[2]/24);
                                vpwm=pwm;
                                Error[1]=Error[0];
                                OCR1A=pwm;
                       
                            }
                                break;
          case 4:  
                           TCCR1A = 0x00;//停止定时器
                   LEDM_1 ;      //无电池或电池严重欠压
                   LEDC_0 ;
                           delay_ms (100);
                    break;
           default:  break;
          }
   }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2012-6-14 10:58:39 | 显示全部楼层
欢迎关注!

出0入0汤圆

发表于 2012-6-14 11:24:51 | 显示全部楼层
一直想搞个类似的充电器。多谢分享!

出0入0汤圆

发表于 2012-6-19 22:08:46 | 显示全部楼层
很经典的东西。能最终调试出来肯定能学到不少东西。

出0入0汤圆

发表于 2012-6-19 22:34:02 | 显示全部楼层
不错 不错

出0入0汤圆

发表于 2012-6-20 12:06:59 | 显示全部楼层
学习,不错 不错!!!

出0入8汤圆

发表于 2012-6-22 14:16:57 | 显示全部楼层
mark         

出0入0汤圆

发表于 2012-6-22 21:04:25 | 显示全部楼层
学习了,支持分享

出0入0汤圆

发表于 2012-6-22 21:31:50 | 显示全部楼层
问下·····这个需要PID么?

出0入4汤圆

发表于 2012-6-23 07:28:54 | 显示全部楼层
建议你将PID部分去掉,将代码再反复动几次大手术甚至推倒重来,参考atmel的一个例程吧,他的网站上有。

出85入4汤圆

发表于 2012-6-30 18:22:09 | 显示全部楼层
不知道楼主的充电器使用的效果怎么样?

出0入0汤圆

发表于 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的数值


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

出0入0汤圆

发表于 2012-7-27 22:43:54 | 显示全部楼层

出0入0汤圆

 楼主| 发表于 2012-7-30 16:22:18 | 显示全部楼层
谢谢13楼的建议,值得好好学习!
头像被屏蔽

出0入0汤圆

发表于 2012-10-1 23:18:24 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入85汤圆

发表于 2012-10-1 23:40:31 | 显示全部楼层
使用内阻小的锂电池充电例如三洋18650,充满电测下电压看看能做到多少?MAX1879做的可以做到4.200XV

出0入4汤圆

发表于 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;
          }
     }
}

出0入0汤圆

发表于 2012-10-2 17:06:56 | 显示全部楼层
很好很 强大!!!!!

出0入0汤圆

发表于 2012-10-4 23:55:27 | 显示全部楼层
楼主辛苦了!!!!!!!!!!!!

出0入0汤圆

发表于 2012-10-5 10:24:41 | 显示全部楼层
那么厉害了。也可以中文写程序了。

出0入0汤圆

发表于 2013-4-18 19:44:54 | 显示全部楼层
MARK MARK MARK

出0入0汤圆

发表于 2013-4-19 16:32:05 | 显示全部楼层
想问下楼主要设计一个充电器的话应该注意些什么东西呢?
那个PID算法在充电电路里面具体用在哪一块方面呢?

出0入0汤圆

发表于 2013-4-22 13:44:10 | 显示全部楼层
ding....................

出0入0汤圆

发表于 2013-4-22 14:09:19 | 显示全部楼层
mark
      

出0入0汤圆

发表于 2013-5-26 16:35:23 | 显示全部楼层
关注中,留着以后测试用

出0入0汤圆

发表于 2016-4-8 13:25:03 | 显示全部楼层
楼主, 我给你提个建议,代码最好用函数封装,然后最好规范一点,而且关键处注释写好,不然你写的就是自己能看懂, ,别人要琢磨半天才能看懂!

出0入4汤圆

发表于 2016-5-25 16:31:23 | 显示全部楼层
顶一个

出0入0汤圆

发表于 2018-4-18 08:27:24 | 显示全部楼层
这个帖子好,教学相长,赞!

出0入0汤圆

发表于 2018-7-25 12:45:05 | 显示全部楼层
值得称赞!~
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-23 14:51

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表