You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
8.3 KiB
355 lines
8.3 KiB
#include <avr/io.h>
|
|
#include <util/delay.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <avr/sleep.h>
|
|
|
|
#define TEMP_PIN 0
|
|
#define IMPULS0_PIN 1
|
|
#define IMPULS1_PIN 2
|
|
#define TAST_PIN 3
|
|
|
|
#define FET0_PIN 0
|
|
#define FET1_PIN 1
|
|
|
|
#define TIMEOUT 51 /* 2 seconds */
|
|
|
|
#define LED_GREEN_TEMP 300
|
|
//#define LED_RED_TEMP 1000
|
|
#define LED_RED_TEMP 850
|
|
|
|
#define TARGET_TEMP_MIN 300
|
|
#define TARGET_TEMP_MAX 1050
|
|
|
|
uint8_t setting_timeout = 0;
|
|
|
|
uint8_t adc_pos = 0;
|
|
uint16_t adc_sum = 0;
|
|
|
|
uint16_t target = 0;
|
|
uint16_t temperature = 0; // all temperatures are in tenth of degrees celsius
|
|
|
|
|
|
uint8_t heat_pwm = 0; // softpwm counter
|
|
uint8_t heat_power = 0; // requested heating power; 0..255
|
|
//uint8_t heat_on[2] = {0, 128};
|
|
//uint8_t heat_off[2] = {128, 0};
|
|
|
|
uint8_t input_state = 0;
|
|
uint8_t dev_state = 0; // whether the device is on or off
|
|
uint8_t led_is_on = 0; // whether led is on or off; used to start/stop timer 0
|
|
|
|
#define TOGGLE_TIMEOUT 5
|
|
#define TEN_SECONDS 256
|
|
#define ONE_MINUTE 1536
|
|
#define ONE_HOUR 92160
|
|
|
|
uint8_t toggle_timeout = 0;
|
|
|
|
uint16_t startup = TEN_SECONDS;
|
|
|
|
uint8_t eeprom_write = 0;
|
|
uint16_t eeprom_clk = ONE_MINUTE;
|
|
|
|
uint32_t shutdown = 2 * (uint32_t) ONE_HOUR;
|
|
|
|
/* gray code to change lookup table, index is old state .. new state */
|
|
int8_t state_change[16] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0};
|
|
//int8_t state_change[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
|
|
|
|
|
|
ISR(TIM1_OVF_vect)
|
|
{
|
|
/* timeout for displaying target temperature */
|
|
if(toggle_timeout) {toggle_timeout--;}
|
|
if(setting_timeout) {setting_timeout--;}
|
|
if(startup) {startup--;}
|
|
if(eeprom_clk) {eeprom_clk--;}
|
|
else
|
|
{
|
|
eeprom_write = 1;
|
|
eeprom_clk = ONE_MINUTE;
|
|
}
|
|
if(shutdown) {shutdown--;}
|
|
else {dev_state = 0;}
|
|
|
|
/* incremtening softpwm, toggling output if needed */
|
|
heat_pwm++;
|
|
if(heat_pwm == 0 && heat_power > 0)
|
|
{
|
|
PORTB |= (1 << FET0_PIN);
|
|
}
|
|
else if(heat_pwm > heat_power)
|
|
{
|
|
PORTB &= ~(1 << FET0_PIN);
|
|
}
|
|
if(heat_pwm == 128 && heat_power > 0)
|
|
{
|
|
PORTB |= (1 << FET1_PIN);
|
|
}
|
|
else if( ((heat_pwm + 128) & 0xff) > heat_power )
|
|
{
|
|
PORTB &= ~(1 << FET1_PIN);
|
|
}
|
|
|
|
/* if(heat_pwm == heat_on[0]) {PORTB |= (1 << FET0_PIN);}
|
|
if(heat_pwm == heat_off[0]) {PORTB &= ~(1 << FET0_PIN);}
|
|
if(heat_pwm == heat_on[1]) {PORTB |= (1 << FET1_PIN);}
|
|
if(heat_pwm == heat_off[1]) {PORTB &= ~(1 << FET1_PIN);}
|
|
*/
|
|
}
|
|
|
|
ISR(PCINT0_vect)
|
|
{
|
|
uint8_t new_state = (PINA & ((1 << IMPULS1_PIN) | (1 << IMPULS0_PIN) | (1 << TAST_PIN))) >> 1;
|
|
uint8_t diff = new_state ^ input_state;
|
|
if(!diff) {return;}
|
|
|
|
if(!toggle_timeout && diff & (1 << (TAST_PIN - 1)) && new_state & (1 << (TAST_PIN - 1)))
|
|
{
|
|
dev_state = !dev_state;
|
|
toggle_timeout = TOGGLE_TIMEOUT;
|
|
if(dev_state) {
|
|
device_on();
|
|
} else {
|
|
device_off();
|
|
}
|
|
|
|
}
|
|
|
|
int8_t change = state_change[((input_state & 3) << 2) | (new_state & 3)];
|
|
if(change)
|
|
{
|
|
target += change * 6;//0.6K * 4 per step 10; // 1 celsius per step
|
|
if(target < TARGET_TEMP_MIN) {target = TARGET_TEMP_MIN;}
|
|
else if(target > TARGET_TEMP_MAX) {target = TARGET_TEMP_MAX;}
|
|
//if(target < LED_GREEN_TEMP) {target = LED_GREEN_TEMP;}
|
|
//else if(target > LED_RED_TEMP) {target = LED_RED_TEMP;}
|
|
setting_timeout = TIMEOUT;
|
|
}
|
|
|
|
shutdown = 2 * (uint32_t) ONE_HOUR;
|
|
input_state = new_state;
|
|
}
|
|
|
|
inline uint8_t control_output(void)
|
|
{
|
|
if(temperature - 50 > target)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(temperature + 50 > target)
|
|
{
|
|
return (target - temperature + 50) * 240 / 100;
|
|
}
|
|
else
|
|
{
|
|
return 255;//192;
|
|
//return (uint8_t)((uint16_t)((uint32_t)((uint32_t)255*target)/temperature)/TARGET_TEMP_MAX);
|
|
}
|
|
}
|
|
|
|
// Measure: 5V --- 10k --- --- PTY81-121 -- GND
|
|
// |
|
|
// uC ADC pin (with 1.11V reference)
|
|
inline uint16_t linearize_temp(uint16_t temp_in)
|
|
{
|
|
uint16_t temp_out = TARGET_TEMP_MAX; //burning
|
|
// made from datasheet by:
|
|
// for i in 677 740 807 877 951 1029 1111 1196 1286 1378 1475 1575 1679 1786 1896 2003 2103 2189; do calc 5*$i/9860*65472/1.11; done
|
|
static const uint16_t coeffs[] PROGMEM =
|
|
{
|
|
// in, out
|
|
20250, 0, // -20 celsius
|
|
22134, 0, // -10 celsius
|
|
24138, 0, // 0 celsius
|
|
26232, 100, // 10 celsius
|
|
28445, 200,
|
|
30778, 300,
|
|
33231, 400, // 40 celsius
|
|
35773, 500, // ..
|
|
38465, 600,
|
|
41216, 700,
|
|
44118, 800,
|
|
47109, 900,
|
|
50220, 1000,
|
|
53420, 1100,
|
|
56711, 1200,
|
|
59911, 1300,
|
|
62902, 1400,
|
|
65474, 1500, // 150 celsius
|
|
65535, TARGET_TEMP_MAX // 152 celsius, made-up, not from datasheet
|
|
};
|
|
uint8_t i=2;
|
|
for(; i < sizeof(coeffs) / sizeof(coeffs[0]); i += 2)
|
|
{
|
|
if(temp_in < pgm_read_word(&coeffs[i]))
|
|
{
|
|
temp_out = (uint16_t) (
|
|
pgm_read_word(&coeffs[i-1]) +
|
|
(uint32_t) (
|
|
(uint32_t) (temp_in - pgm_read_word(&coeffs[i-2])) *
|
|
(pgm_read_word(&coeffs[i+1])-pgm_read_word(&coeffs[i-1]))
|
|
) /
|
|
(pgm_read_word(&coeffs[i])-pgm_read_word(&coeffs[i-2])));
|
|
break;
|
|
}
|
|
}
|
|
return temp_out;
|
|
}
|
|
|
|
inline void io_init(void)
|
|
{
|
|
PORTA = (1 << TAST_PIN) | (1 << IMPULS1_PIN) | (1 << IMPULS0_PIN);/* enable pullups on inputs */
|
|
PORTB = 0;
|
|
DDRA = (1 << PA7);/* led outputs ... */
|
|
DDRB = (1 << PB2) | (1 << FET0_PIN) | (1 << FET1_PIN);/* ... and FET output */
|
|
input_state = PINA & ((1 << IMPULS1_PIN) | (1 << IMPULS0_PIN) | (1 << TAST_PIN));
|
|
}
|
|
|
|
inline void led_init(void)
|
|
{
|
|
TCCR0A = (1 << COM0A1) | (1 << COM0B1) | (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);/* pwm enable: fast, A inverted, B not */
|
|
TCCR0B = (1 << CS00);/* no prescaler */
|
|
OCR0A = OCR0B = 0;
|
|
}
|
|
|
|
inline void led_set(uint16_t value)
|
|
{
|
|
uint8_t pwmval;
|
|
if (LED_GREEN_TEMP <= value && value < LED_RED_TEMP) {
|
|
pwmval = ((uint32_t) (value-LED_GREEN_TEMP) * 255)/(LED_RED_TEMP-LED_GREEN_TEMP);
|
|
} else if (value <= LED_GREEN_TEMP) {
|
|
pwmval = 0;
|
|
} else {
|
|
pwmval = 255;
|
|
}
|
|
OCR0A = pwmval;
|
|
OCR0B = pwmval;
|
|
}
|
|
|
|
inline void led_off(void)
|
|
{
|
|
// OCR0A = 0;
|
|
// OCR0B = 255;
|
|
}
|
|
|
|
inline void device_off(void)
|
|
{
|
|
TCCR0B = 0; // stop pwm
|
|
TCCR0A = 0; // disconnect outputs;
|
|
ADCSRA &= ~(1<<ADEN); // disable adc
|
|
heat_power = 0; // turn off heating
|
|
}
|
|
|
|
inline void device_on(void)
|
|
{
|
|
led_init();
|
|
ADCSRA |= (1<<ADEN); // enable adc
|
|
ADCSRA |= (1<<ADSC); // start a conversion to be sure
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void adc_init(void)
|
|
{
|
|
ADMUX = (1 << REFS1) | (TEMP_PIN << MUX0);/* Vref = 1.1, pin selection */
|
|
ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADPS2);/* adc enable, free running mode, prescaler 16 */
|
|
DIDR0 = (1 << TEMP_PIN);/* disable digital input on adc pin */
|
|
/* result in (ADCH << 8) | ADCL, accessable as ADC */
|
|
}
|
|
|
|
inline void heat_init(void)
|
|
{
|
|
TCCR1A = (1 << WGM11) | (1 << WGM10);
|
|
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);/* some mode allowing to set timer TOP, no prescaler */
|
|
TIMSK1 = (1 << TOIE1);/* interupt on overflow */
|
|
OCR1A = 39063;
|
|
|
|
/* read eeprom */
|
|
while(EECR & (1 << EEPE)) {;}
|
|
EEAR = 0;
|
|
EECR = (1 << EERE);
|
|
target = EEDR << 8;
|
|
EEARL = 1;
|
|
EECR |= (1 << EERE);
|
|
target |= EEDR;
|
|
}
|
|
|
|
inline void input_init(void)
|
|
{
|
|
GIMSK = (1 << PCIE0);/* interrupt on change on PORT A */
|
|
PCMSK0 = (1 << TAST_PIN) | (1 << IMPULS1_PIN) | (1 << IMPULS0_PIN);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
io_init();
|
|
adc_init();
|
|
heat_init();
|
|
input_init();
|
|
led_init();
|
|
sei();
|
|
|
|
device_off(); // initial state
|
|
|
|
for(;;)
|
|
{
|
|
if(ADCSRA & (1 << ADIF))
|
|
{
|
|
/* new adc result */
|
|
ADCSRA |= (1 << ADIF);
|
|
adc_sum += ADC;
|
|
if(++adc_pos == 64)
|
|
{
|
|
temperature = linearize_temp(adc_sum);
|
|
if(temperature < 400)
|
|
{
|
|
startup = 2 * TEN_SECONDS;
|
|
}
|
|
adc_sum = 0;
|
|
adc_pos = 0;
|
|
if(dev_state)
|
|
{
|
|
heat_power = control_output();
|
|
if(startup && heat_power > 96) {heat_power = 96;}
|
|
}
|
|
else
|
|
{
|
|
heat_power = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if(!dev_state) {led_off();}
|
|
// now done in interrupt
|
|
else if(setting_timeout) {led_set(target);}
|
|
else {led_set(temperature);}
|
|
|
|
if(eeprom_write && dev_state)
|
|
{
|
|
while(EECR & (1 << EEPE)) {;}
|
|
EEARL = 0;
|
|
EEDR = (target >> 8) & 0xff;
|
|
EECR |= (1 << EEMPE);
|
|
EECR |= (1 << EEPE);
|
|
|
|
while(EECR & (1 << EEPE)) {;}
|
|
EEARL = 1;
|
|
EEDR = target & 0xff;
|
|
EECR |= (1 << EEMPE);
|
|
EECR |= (1 << EEPE);
|
|
|
|
eeprom_write = 0;
|
|
}
|
|
if(!dev_state) {
|
|
set_sleep_mode(SLEEP_MODE_IDLE);
|
|
sleep_mode();
|
|
// timer1 interrupt will wake us again, as will PCINT
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|