📄 charger.c
字号:
/* [KSC4]-------------------------------------------------------------------[]
*
* CHARGER.C - NiMH Battery Charger for ATTiny45, version 1.0
* Part of Entry AT3344 for the Atmel Design Contest 2006
*
* []-----------------------------------------------------------------------[]
*/
#include <avr/io.h> // include I/O defs (port names, pin names, etc)
#include <avr/interrupt.h> // include interrupt support
#include <avr/sleep.h>
#include <avr/pgmspace.h>
/* ------------------------- Configuration Macros ------------------------- */
/* Enable sending debug messages over the serial port */
#define BATT_DEBUG
// ---- Basic Hardware Configuration
/* CPU Clock */
#define F_CPU 2000000L
/* Which ADC channels/MCU Pins are used to measure the battery and mains
voltages. They're usually connected to a resistor network voltage divider
to make the voltage range acceptable to the ADC. For instance, the resistor
values are 220k/100k, making the range 9-16V appear as 2.81-5.00V */
#define ADC_BATTERY_CHANNEL 3
#define ADC_MAIN_POWER_CHANNEL 2
/* Which pin will act as the serial TXD (transmit data) where the logging/
debugging messages will be sent to. */
#define SERIAL_OUT_DDR DDRB
#define SERIAL_OUT_PORT PORTB
#define SERIAL_OUT_BIT PB1
/* Which pin is connected to the charger enable function. In this project,
when pulled *down* it enables an LM317-based constant-current regulator
that recharges the battery. This enable port has a resistor pullup, so it's
disabled by default even if the MCU is dead. */
#define BATTERY_CHARGER_DDR DDRB
#define BATTERY_CHARGER_PORT PORTB
#define BATTERY_CHARGER_BIT PB0
/* Serial transmission stuff -- basic speed, phase (speed multiplier -- was
needed for reception, but I have removed the reception code) and
appropriate calculations for the timer comparator */
#define BAUD_RATE 1200
#define PHASE 4
#define TIMER0_PRESCALER 8
#define PHASE_MASK (PHASE-1)
#define TIMER0_TOP (F_CPU/TIMER0_PRESCALER/PHASE/BAUD_RATE)
/* Serial Transmit Fifo size. Bytes to be transmitted are first put in here
then transmitted later */
#define TXFIFOSIZE 80
#if TIMER0_TOP > 255
#error TIMER0_TOP too large for this baud rate, increase PHASE
#endif
/* ---------------- Utility Macros & Macro-Defined Functions -------------- */
#define TRUE 1
#define FALSE 0
/* Macros for better readability of Analog-to-Digital Converter operations */
#define adc_enable() (ADCSRA |= _BV(ADEN))
#define adc_busy() (ADCSRA & _BV(ADSC))
#define adc_start() ADCSRA |= _BV(ADSC)
#define adc_set_channel(c) \
(ADMUX = ((ADMUX & ~(_BV(MUX3)|_BV(MUX2)|_BV(MUX1)|_BV(MUX0))) \
|((c)<<MUX0)))
#define adc_value() (ADCL|((ADCH & 3)<<8))
#define adc_set_prescaler(v) (ADCSRA = (ADCSRA & 0xF8) | (v))
#define adc_set_reference(v) (ADMUX = (ADMUX & 0x4F) | (((v)&4)<<2) | \
(((v)&3)<<6) )
#define ADC_CHANNELS 4
#define ADC_CH_0 0
#define ADC_CH_1 1
#define ADC_CH_2 2
#define ADC_CH_3 3
#define ADC_CH_2DIFF2_1X 4
#define ADC_CH_2DIFF2_20X 5
#define ADC_CH_2DIFF3_1X 6
#define ADC_CH_2DIFF3_20X 7
#define ADC_CH_TEMP_SENSOR 15
#define ADC_PS_CLK_4 2
#define ADC_PS_CLK_8 3
#define ADC_PS_CLK_16 4
#define ADC_REF_VCC_DISC 0
#define ADC_REF_1_1V_DISC 2
#define ADC_REF_2_56V_DISC 6
/* ----------------------- Global Variables & Declarations ---------------- */
volatile enum BatteryChargingState {
BatteryNotCharging,
BatteryInitialSense, BatterySoftCharging,
BatteryFastCharging, BatteryTrickleCharging
} charging_state;
unsigned char max_battery_charge_time;
short last_voltage,voltage;
unsigned char quell_charging;
unsigned short duty_cycle;
unsigned long avg_accum;
volatile unsigned char fifo[TXFIFOSIZE];
volatile unsigned char txhead,txtail;
volatile unsigned char xmitbyte,xmitbit,phase;
volatile unsigned short cnt;
volatile unsigned short gcount;
volatile unsigned char has_new_second;
volatile unsigned char hours,minutes,seconds;
/* --------------------------- Function Prototypes ------------------------ */
void xmit(unsigned char c);
void xmit_crlf(void);
void xmit_timestamp(void);
void xmit_u8as2d(unsigned char val);
void xmit_str_p(const prog_char *p);
void do_charge_control(void);
unsigned short getVoltage(unsigned char ch);
void xmit_k1_u8as5dot2f(unsigned short val);
/* ------------------------- Macro-Defined Functions ---------------------- */
/* Conveniente macros to have the ADC measure battery and mains voltages.
'Mains' refers to the 12-18VDC input to the circuit after the power brick,
not the 110-240VAC from the outlet. */
#define get_battery_voltage() getVoltage(ADC_BATTERY_CHANNEL)
#define get_mains_voltage() getVoltage(ADC_MAIN_POWER_CHANNEL)
/* Macros to transmit a zero or one bit over the serial port */
#define xmit0() SERIAL_OUT_PORT &= ~_BV(SERIAL_OUT_BIT)
#define xmit1() SERIAL_OUT_PORT |= _BV(SERIAL_OUT_BIT)
/* Looks like it's reversed, but it is not. The hardware works in such a
way that the charger is disabled when the pin is high */
#define disable_charger() BATTERY_CHARGER_PORT |= _BV(BATTERY_CHARGER_BIT)
#define enable_charger() BATTERY_CHARGER_PORT &= ~_BV(BATTERY_CHARGER_BIT)
/* Sets the battery charger 1Hz PWM duty cycle, which controls the 'strength'
of the charging operation */
#define set_duty_cycle(x) (duty_cycle=((100-(x))/100.0*BAUD_RATE))
/* Helper macro to automatically define a string in program space and have
it transmitted */
#define xmit_pstr(x) xmit_str_p(PSTR(x))
/* ------------------------------ Main Program ---------------------------- */
int main(void)
{
// Set the main clock prescaler to BASE_CLOCK/4, yielding 2.0MHz
// for the CPU clock
CLKPR = 0x80;
CLKPR = 2;
SERIAL_OUT_DDR |= _BV(SERIAL_OUT_BIT);
BATTERY_CHARGER_DDR |= _BV(BATTERY_CHARGER_BIT);
TCCR0A = 2;
#if TIMER0_PRESCALER == 8
TCCR0B = 2;
#else
TCCR0B = 1;
#endif
OCR0A = TIMER0_TOP;
TIMSK = _BV(OCIE0A);
adc_set_prescaler(ADC_PS_CLK_16);
adc_set_reference(ADC_REF_VCC_DISC);
adc_enable();
disable_charger();
set_duty_cycle(0);
sei();
xmit_pstr("\r\n\r\nWelcome to AT3344's Battery Charger "
"Monitor/Debug Facility v1.0\r\n\r\n");
for (;;) {
if (has_new_second) {
has_new_second=FALSE;
do_charge_control();
//xmit_timestamp(); xmit_crlf();
}
}
}
/* Transmits a line break */
void xmit_crlf(void)
{
xmit(13); xmit(10);
}
/* Transmits the current time */
void xmit_timestamp(void)
{
xmit_u8as2d(hours);
xmit(':');
xmit_u8as2d(minutes);
xmit(':');
xmit_u8as2d(seconds);
}
/* Converts 'val' to a 2-digit decimal number and transmits it. If the
value is larger than 2 digits, transmits four '#'-signs instead to
flag overflow */
void xmit_u8as2d(unsigned char val)
{
unsigned char c;
if (val<100) {
c=val/10;
c+='0'; xmit(c);
c=val%10+'0';
xmit(c);
} else {
xmit('#'); xmit('#');
}
}
/* Converts 'val' to a 4-digit decimal number and transmits it. If the
value is larger than 4 digits, transmits four '#'-signs instead to
flag overflow */
void xmit_u8as4d(unsigned short val)
{
if (val<10000) {
xmit_u8as2d(val/100);
xmit_u8as2d(val%100);
} else {
xmit('#'); xmit('#');
xmit('#'); xmit('#');
}
}
/* Converts 'val' (an averaged voltage from the 'do_charger_control' function
further down) to millivolts and the prints it as a fixed-point value
XX.XX in volts. It is used when debugging to log the battery voltage */
void xmit_k1_u8as5dot2f(unsigned short val)
{
/* This essentially multiplies by 1.953125, which is the correct
conversion factor from ADC units to millivolts, considering
the ADC reference voltage of 5V, the x8 scaling due to averaging
and the 220k/110kohm voltage divider where the ADC is connected.
Change the hardware and you'll have to change this. The bit gymnastics
is just to avoid needing floating point stuff that would make the
resulting object code so much larger. */
unsigned short v=(val<<1)-(val>>4)+(val>>6);
xmit_u8as2d(v/1000);
xmit('.');
xmit_u8as2d((v%1000)/10);
}
/* Transmits a zero-terminated string pointed by 'p' in program space (the
MCU internal flash memory). Used in conjunction witht eh xmit_pstr macro
for convenient use. */
void xmit_str_p(const prog_char *p)
{
unsigned char c;
for (;;) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -