ARDUINO电池充电器:如何一次保存行星一个电池! 你知道所有的单身电池(碱性人)都有吗? 他们的标签说"Do Not Recharge"事实上是收费!
当然有些不会,有些人会收取比这一点更好 其他。但即使正在从抛弃电池中再使用似乎是一个 better way to go.
无论是不是的标准 电池是重新收费的,而不是首先给它一个目视检查。如果 它有粉状残留物,然后它已经死了,所以在当地重新循环它 电池重新循环。
确定重新收费的下一件事是电池是否 电压下降到0.8V以下。如果它太低,那么你不太可能得到 任何地方。您可以用伏特计或仅使用项目来衡量这一点 下面这会自动为您(我将级别设置为0.75 电压测量不是绝对准确的(电压regs通常有一个 ±10%调节,所以你不'T井完全一下 - 也是参考电压i 使用的是使用两个分隔电阻器'另外10%)。它不是 对充电碱性电池绝对关键。
首先,当CHRG和HCHRG浮动时,R1拉动晶体管 基础高,因此没有电流流量=关闭。
将CHRG降低导致21.3mA的恒定电流,然后拉动 HCRG低导致33mA的充电电流增加。
基本操作是开始在低电流下对电池充电 期间,然后充电在一个高电流中另一个时期。如果电池 电压增加,在高电荷期间,然后进行充电,如果没有,那么 充电完成(只要电池在可接受的电压内 limits).
当克拉被拉出时,r2,r2和r3形成电阻 将基极电压设置的分频器
3.28V:5 *((1E3 + 560)/(1E3 + 560 + 820))
因此,晶体管基部的电压,即R1,是:
1.722:5 - 3.26
基础发射极二极管下降为0.7V,因此叶1V掉落在R4上。 因此,这允许您定义充电电流即47R 电阻显示的电阻将是
21.3MA:1.0 / 47
当HCHRG被拉低时(无论克格),电阻器R3短路 出台。因此,现在跨越(仅)R2的电压是:
2.74V:5 *((1E3)/(1E3 + 820)))
这跨越晶体管基部的电压,即R1,是:
2.25V:5 - 2.74
现在R1上的电压降是:
1.55:2.25 - 0.7
这导致充电电流:
33MA:1.55 / 47
由R8和R9形成的分压器将用于ADC的电压减半 参考,因此ADC可以读取的最大值是2.5V,因为我们 只需要测量高达约1.6V。还具有较低的ISF电压 意味着ADC的每个位具有较低的值i.e.更精细的分辨率 - 而不是4.88mv它为每位2.44mV,因此ADC可以看到较小的电压 电池的变化。
您只能使用LCD显示器或仅使用LED。你会得到一个绿色 (通过)或红色(失败)光线充电结束。 LCD显示更多 电池电压电压等信息,起始电压 电池和经过的时间。它还显示了状态变量(真的 对于debug),可以让您知道代码在哪里。此外,串口 输出显示发生的事情的短信。所以液晶液晶不是真的 对于操作至关重要,但如果您单独使用单位,则是有用的。
您可以使用串行监视器(在Arduino IDE中)观察操作 还报告串行数据显示状态机操作,ADC值和 voltages.
版本:1.8.3.
用于普通的库 Hitachi HD44780. display is called 液晶和下面的过程将用不同的代码替换它 这样你就可以使用一个串行驱动的液晶显示器 74HC595. 串行到平行芯片。
在原始库中,您需要以下代码以初始化液晶类的实例化:
LiquidCrystal lcd(lcd_rs,lcd_e,lcd_d4,lcd_d5,lcd_d6,lcd_d7);
这是一个4位接口,通常用作默认值
并行接口(可以使用8位接口但是
没有很大的优势)。所以在该界面中,您需要共6个
引脚驱动芯片。
当您使用SPI版本的LiquincryStal接口时,初始方法是:
LiquidCrystal lcd(spi_ss_pin.);
这看起来只使用一个引脚,但实际上它使用标准SPI引脚:
spi_ss_pin.只是用户定义的从属选择引脚
其他人是在Arduino Board上的固定销。所以通过添加一个
74HC595以驾驶那些原始的针脚,在第一个初始化者中看到,你
可以节省三个引脚。不仅如此,这些SPI引脚可用于驱动
其他SPI设备(具有不同的SS信号)。
如果你不't have an 74HC595. 然后,您可以使用与下面的相同的草图,但如图所示加入LCD 这里 (4位并行接口) - 和唐't更改图书馆(下面)。这 SPI库使用与正常库相同的功能,因此代码 素描不会改变。
液晶库被替换为允许SPI操作:
从以下方式下载(SPI)SpilectcryStal库:
//playground.arduino.cc/Main/LiquidCrystal#Download
然后转到您的图书馆位置:
C:\用户\<username>\ documents \ arduino \图书馆
重命名Directory tumpercrystal to silitercrystal_orig。
然后解压缩下载的文件,这应该导致新文件夹
液晶
您可以在此处找到原理图(接线图74HC595至HITACHI HD44780):
//playground.arduino.cc/Main/LiquidCrystal#Connections
那里'没有改变引脚的工具(你可以改变图书馆 代码虽然要将其更改为您自己的74HC595 - HD44780连接 desired).
arduino.电池充电器代码由简单的状态机形成。 这些是主要国家:
闲置,蝙蝠,充电,upd_charge,测试,完成,失败,等待_start
您可以或多或少地从左到右阅读这些,看看代码是什么 doing.
首先在空闲状态中,系统设置为充电。
然后在BATFND状态下,检查电压(在低电荷期间)查看 如果存在有效电池。
如果超出范围,则状态将失败。
如果在范围内,则状态设置为充电。
在充电状态下,低电荷电流持续30秒。在末尾 ADC读数存储在变量LowChRGADC中。
30秒后,状态设置为High_charge和高电流输出 activated.
状态在30秒后更改为High_charge。
另外两次测试是(一直)。
电池达到最大电压
1.状态设置为完成。
电池超出范围
2.状态设置为失败。
30秒后,状态设置为测试
状态在30秒后改变了测试。
另外两次测试是(一直)。
电池达到最大电压
1.状态设置为完成。
电池超出范围
2.状态设置为失败。
这是决定终止过程的地方或 继续。首先,如果当前的ADC值(在高电荷结束时 与低电流充电期结束时的ADC值相比)不是 high enough (<9.77mv)然后电池被认为是充电的 状态设置为完成。
自从它已经在此处测试了电池(超出范围)的状态 在各州收费和High_charge中不断测试。
一些电池在最终值周围悬停,但改变其电压输出 在高电荷时足够,以便算法可以'停下来。检测这一点 状态(超过几次充电和高电荷周期)需要存储ADC值 因此,包含最后10个低充电ADC值的滚动移位寄存器是 used for detection.
在函数test_avg()中,测试阵列prevadc []以查看是否存在 值全部在10个读数的平均值的值范围内。如果它们是, 然后,状态机进入了消费的状态I.E.这表明有 been no 'real'在最后几个充电周期内更换电池电压。
在每个测试阶段之后,阵列移位,最新的低电荷ADC value is inserted.
如果上述情况都不是真的,那么状态机就会返回 IDLE.
下一个州是:
完成(如果电池电压足够高)或
完成了没有平均电池的变化或
闲置(如果其他人不是真的)。
设置控制和LED :(绿色LED点亮以指示成功)和 充电已关闭。
下一个状态是wait_start。
设置控件和LED :(红色LED点亮以指示故障)和 充电已关闭。
下一个状态是wait_start。
这是允许重启检测按钮按下的唯一状态 输入引脚。按下时,初始化各种变量以进行完整 restart.
按下状态时,状态设置为空闲。
// AA and AAA charger
// Could use for others with different charge currents
// i.e. different emitter resistors.
//
// LiquidCrystal library used here is not the original
// download an enhanced version from
// //playground.arduino.cc/Main/LiquidCrystal
// LiquidCrystal_1.zip allows SPI operation.
//
// V1.00
// Copyright John Main
// Free for non-commercial use.
//
#include <SPI.h>
#include <LiquidCrystal.h> // Enhanced for SPI operation.
#define START_PIN 12
#define RLED_PIN A4
#define GLED_PIN A5
#define VBATT_PIN A0
#define CHRG_PIN 2
#define HIGH_PIN 3
#define spi_ss_pin. 10
#define 收费_ON pinMode(CHRG_PIN,OUTPUT);digitalWrite(CHRG_PIN,LOW);
#define 收费_OFF pinMode(CHRG_PIN,INPUT);
#define HIGH_ON pinMode(HIGH_PIN,OUTPUT);digitalWrite(HIGH_PIN,LOW);
#define HIGH_OFF pinMode(HIGH_PIN,INPUT);
#define RLED_ON digitalWrite(RLED_PIN,HIGH);
#define RLED_OFF digitalWrite(RLED_PIN,LOW);
#define GLED_ON digitalWrite(GLED_PIN,HIGH);
#define GLED_OFF digitalWrite(GLED_PIN,LOW);
#define SPC Serial.print(' ');
#define VBATMAX 1.60 // Maximum and minimum acceptable battery.
#define VBATMIN 0.75 // voltage to allow charging to start.
#define VREFANA 2.50 // Reference voltage (AREF). Measure this with DMM.
#define CHRG_ms 30000 // Time period of normal charging time (ms).
#define HIGH_ms 30000 // Time period of normal higher power charging time (ms).
#define PREV_ADC 10 // Number of nearly same ADC readings to declare done.
typedef enum { 闲置的, 蝙蝠队, 收费, HIGH_CHARGE., 测试,
完成的, 失败, wait_start. } state_t;
enum { BATTFULL, BATTAVGSTABLE, BATTBAD, BATTNORISE };
// Initialize for SPI with sspin, also known as RCLK or LATCH.
// For the lcd other pins are the standard SPI pins.
液晶 LCD(SPI_SS_PIN);
///////////////////////////////////////////////////
void setup() {
pinMode(VBATT_PIN,INPUT);
pinMode(START_PIN,INPUT_PULLUP);
pinMode(CHRG_PIN,OUTPUT);
pinMode(HIGH_PIN,OUTPUT);
pinMode(GLED_PIN,OUTPUT);
pinMode(RLED_PIN,OUTPUT);
收费_OFF
HIGH_OFF
RLED_OFF
GLED_OFF
analogReference(EXTERNAL);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
Serial.begin(57600);
Serial.println("Battery Charger ");
showStartVolts();
}
///////////////////////////////////////////////////
// Print start volts to LCD & serial(for reference).
void showStartVolts(void) {
float v = getVana( analogRead(VBATT_PIN) ) ;
lcd.setCursor(11, 0);
lcd.print( v,3 );
Serial.print("\nStart volts: ");
Serial.println( v,3 );
}
///////////////////////////////////////////////////
void reason(uint8_t c) {
Serial.print("End reason: ");
switch(c) {
case BATTFULL : Serial.println("Max volts."); break;
case BATTAVGSTABLE : Serial.println("No avg change."); break;
case BATTBAD : Serial.println("Out of range.");
case BATTNORISE : Serial.println("No Hchrg rise.");
}
}
///////////////////////////////////////////////////
void loop() {
static uint32_t loop_time = 0;
if (millis()-loop_time>250) { // No need execute too fast
action();
loop_time = millis();
}
}
///////////////////////////////////////////////////
float getVana(uint16_t adc) {
return adc * VREFANA / 1024;
}
///////////////////////////////////////////////////
uint8_t checkBattBad(float Vana) {
if ( Vana<VBATMIN || Vana>VBATMAX ) return 1;
return 0;
}
///////////////////////////////////////////////////
uint8_t checkBattFinished(float Vana) {
if ( Vana>=VBATMAX-0.1 ) return 1;
return 0;
}
///////////////////////////////////////////////////
void showSerialElapsedTime(uint32_t r_time) {
Serial.print("Time:");
Serial.print(r_time/1000/60);
Serial.println(" mins.");
}
///////////////////////////////////////////////////
void showState(state_t state) {
// This state has no text, to leave FINISHED or FAIL on LCD.
if (state == wait_start.) return;
// Show current state
lcd.setCursor(0, 0);
lcd.print(" "); // 11 chars clear debug area.
lcd.setCursor(0, 0);
switch( state) {
case 闲置的 : lcd.print("IDLE");break;
case 蝙蝠队 : lcd.print("BATFND");break;
case 收费 : lcd.print("CHRG");break;
case HIGH_CHARGE. : lcd.print("H-CHRG");break;
case 测试 : lcd.print("TEST");break;
case 完成的 : lcd.print ("FINSHD");break;
case 失败 : lcd.print("FAIL");break;
// case WAIT_START : lcd.print("WAIT");break;
default: break;
}
}
///////////////////////////////////////////////////
void showTime(uint32_t timeVal_ms) { // could use sprinf - this = less memory.
uint8_t lcdpos;
uint16_t tmin,tsec;
uint32_t t;
t = timeVal_ms;
tmin = (t/1000/60); // Could use sprintf but smaller code using:
if (tmin >= 1) { // Print minutes since reset
lcdpos = 12;
if (tmin>99) lcdpos = 10;
else if (tmin>9) lcdpos = 11;
lcd.setCursor(lcdpos, 1);
lcd.print(tmin);
lcd.print(':');
}
// Print seconds since reset.
tsec = (t/1000) % 60;
lcd.setCursor(14, 1);
if (tsec<10)
lcd.print('0');
lcd.print(tsec);
}
///////////////////////////////////////////////////
// -1 resets the store
//
uint8_t test_avg(uint16_t adc) {
uint8_t i,match,allmatch;
static uint16_t prevADC[PREV_ADC];
uint16_t avg;
if (adc==-1) {
for(i=0;i<PREV_ADC;i++) prevADC[i]=0;
return;
}
// Get average value
avg = 0;
Serial.print("AVG:");
for (i=0;i<PREV_ADC;i++) {
Serial.print(prevADC[i]); SPC;
avg+=prevADC[i];
}
avg += 5; // =0.5 after division by 10
avg /=10;
Serial.print("Avg ");Serial.println(avg);
// If any are zero then not filled: set avg zero to stop err. match
for (i=0;i<PREV_ADC;i++) {
if (prevADC[i]==0) {
avg = 0;
Serial.println("Avg zet ZERO");
break;
}
}
// Check if has not increased over last n readings - if so exit.
allmatch = 1;
for (i=0;i<PREV_ADC;i++) {
match = 0;
// Detect close matches: lowChrgADC+/-2 (+/-4.88mV)
if ( avg-1 <= prevADC[i] && prevADC[i] <= avg+1 ) match=1;
// Here if the value is one of 3: lowChrgADC+/-1, then match is high
if (!match) { allmatch = 0; break; }
}
// Update rolling store. // Array indices 0 ~ (n-1) shift.
for (i=0;i<PREV_ADC-1;i++) prevADC[i]=prevADC[i+1];
prevADC[PREV_ADC-1] = adc;
// allmatch is 1 if all have matched for all elements.
if (allmatch && avg!=0) { // Zero is a special case.
reason(BATTAVGSTABLE);
return 1; // All the same so indicate finished.
}
return 0;
}
///////////////////////////////////////////////////
void action(void) {
static uint32_t s_time = millis();
static uint32_t r_time = s_time; // Relative, start times.
static uint32_t timewas = 0, timewas_state=r_time;
static state_t state = 闲置的;
static uint16_t lowChrgADC=0;
static uint8_t timer_on=1;
uint16_t adc=0;
float Vana;
static uint8_t done_once = 0;
if (!done_once) {
test_avg(-1); // Reset store.
done_once=1;
}
r_time = millis() - s_time; // Use time relative to start
if (timer_on) showTime(r_time); // Time since start: to LCD.
adc = analogRead(VBATT_PIN);
lcd.setCursor(0, 1);
lcd.print("Vb:");
lcd.print(Vana=getVana(adc),3);
switch( state) {
case 闲置的 :
state = 蝙蝠队;
Serial.println("IDLE>BATFND");
收费_ON // Start charge to detect battery.
HIGH_OFF
RLED_OFF
GLED_OFF
showSerialElapsedTime(r_time);
break;
case 蝙蝠队 : // Battery ok?
Serial.println("BATFND>CHARGE");
if ( checkBattBad(Vana) ) {
state = 失败; // Battery is dead or not present.
Serial.println("FAIL in BATFND");
} else if (Vana>=VBATMIN && Vana<=VBATMAX) {
state = 收费;
}
break;
case 收费 :
if ( checkBattBad(Vana) ) { reason(BATTBAD) ; state = 失败; }
if ( checkBattFinished(Vana) ) { reason(BATTFULL); state = 完成的; }
else if (r_time - timewas_state>CHRG_ms) {
Serial.println("CHARGE>HCHARGE");
lowChrgADC = adc; // Store value just before high charge time.
HIGH_ON
state = HIGH_CHARGE.;
timewas_state = r_time;
}
break;
case HIGH_CHARGE. :
if ( checkBattBad(Vana) ) { reason(BATTBAD) ; state = 失败; }
if ( checkBattFinished(Vana) ) { reason(BATTFULL); state = 完成的; }
else if ( r_time - timewas_state>HIGH_ms) {
Serial.println("HCHARGE>TEST");
state = 测试;
timewas_state = r_time;
}
break;
case 测试 :
Serial.println("TEST>IDLE/FINISHED");
Serial.println("Cur adc,lowChrgADC");
Serial.print(adc); SPC; Serial.println(getVana(adc),3);
Serial.print(lowChrgADC);SPC;Serial.println(getVana(lowChrgADC),3);
if ( adc > (lowChrgADC+4) ) { // Gone up by > (2.5/1024)*4=9.77mV
state= 闲置的;
} else { // No rise so batery charge maxed out.
reason(BATTNORISE);
state = 完成的;
}
if ( test_avg(lowChrgADC) ) state = 完成的;
break;
case 失败 : // Error condition so stop charging battery.
Serial.println("FAIL");
收费_OFF
HIGH_OFF
timer_on = 0;
RLED_ON
GLED_OFF
state = wait_start.;
break;
case 完成的 :
Serial.print("FINISHED:");
Serial.println(Vana);
showSerialElapsedTime(r_time);
收费_OFF
HIGH_OFF
RLED_OFF
GLED_ON
timer_on = 0;
state = wait_start.;
break;
case wait_start. :
if ( digitalRead(START_PIN)==0) {
state = 闲置的;
s_time = millis();
r_time = millis()-s_time;
timewas_state = r_time;
timer_on = 1;
test_avg(-1); // Reset store.
lcd.clear();
showStartVolts();
}
break;
}
showState(state);
// Show raw adc value
lcd.setCursor(7, 0);
lcd.print(adc);lcd.print(' ');
}
定义vrefana是 名义上2.5V,但我使用数字万用表测量并放置 代码中的值,给出更准确的阅读。如果你不't 想要这样做,留下价值为2.5。
使用ADS1115精度16位ADC进行教程进行低功耗。
arduino.模拟输出:如何创建最精确的PWM模拟输出以及如何创建模拟PWM正弦波。
了解DigitalWrite()的工作原理......现在使用17倍宏宏!
如何使用TCS230(/ TCS3200)彩色检测器芯片并轻松将其添加到您的任何项目中。
使用ADXL345 Acellerometer,您可以检测到16G!您还可以了解如何使用它来点击检测等。
HMC5883L - 如何制作数字罗盘,了解HMC5883L和QMC5883L之间的差异以及它们是否兼容。
新的! Comments
让你说到你刚刚阅读的东西!留下下面的框中的评论。