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;
}
}
}
欢迎关注! 一直想搞个类似的充电器。多谢分享! 很经典的东西。能最终调试出来肯定能学到不少东西。 不错 不错 学习,不错 不错!!!{:smile:} mark 学习了,支持分享 问下·····这个需要PID么? 建议你将PID部分去掉,将代码再反复动几次大手术甚至推倒重来,参考atmel的一个例程吧,他的网站上有。 不知道楼主的充电器使用的效果怎么样? 很好的例子,提个小建议!提高代码可读性、可移植性、和规范性:
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的数值
好了,啰嗦了很多,我说的也不应定就正确,希望高手指点!! {:smile:}{:smile:}{:smile:} 谢谢13楼的建议,值得好好学习! 使用内阻小的锂电池充电例如三洋18650,充满电测下电压看看能做到多少?MAX1879做的可以做到4.200XV 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;
}
}
} 很好很 强大!!!!! 楼主辛苦了!!!!!!!!!!!! 那么厉害了。也可以中文写程序了。
MARK MARK MARK 想问下楼主要设计一个充电器的话应该注意些什么东西呢?
那个PID算法在充电电路里面具体用在哪一块方面呢? ding.................... mark
关注中,留着以后测试用 楼主, 我给你提个建议,代码最好用函数封装,然后最好规范一点,而且关键处注释写好,不然你写的就是自己能看懂, ,别人要琢磨半天才能看懂! 顶一个
这个帖子好,教学相长,赞! 值得称赞!~{:victory:}{:victory:}
页:
[1]