commit
					aaaec8ff40
				
				 43 changed files with 3010 additions and 0 deletions
			
			
		@ -0,0 +1,67 @@
					 | 
				
			||||
# Makefile
 | 
				
			||||
 | 
				
			||||
DEVICE     = attiny88
 | 
				
			||||
CLOCK      = 4000000
 | 
				
			||||
PROGRAMMER = -c buspirate -P /dev/ttyUSB0
 | 
				
			||||
OBJECTS    = reflow.o display.o font.o i2c.o menu.o spi.o therm.o timer.o config.o beep.o control.o fields.o profile.o
 | 
				
			||||
SRC        = $(OBJECTS:%.o=%.c)
 | 
				
			||||
#FUSES      = -U lfuse:w:0xEC:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
 | 
				
			||||
FUSES      = -U lfuse:w:0xEC:m -U hfuse:w:0xDF:m -U efuse:w:0x07:m
 | 
				
			||||
 | 
				
			||||
# For computing fuse byte values for other devices and options see
 | 
				
			||||
# the fuse bit calculator at http://www.engbedded.com/fusecalc/
 | 
				
			||||
 | 
				
			||||
#AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
 | 
				
			||||
AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -V
 | 
				
			||||
# Don't use -Werror in the link stage to avoid a compiler bug which is fixed in gcc 4.8.3
 | 
				
			||||
CFLAGS = -Werror
 | 
				
			||||
COMPILE = avr-gcc -std=c99 -Wall -Wl,-Map,$@.map -Os -g -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -mcall-prologues -flto -fuse-linker-plugin
 | 
				
			||||
 | 
				
			||||
# symbolic targets:
 | 
				
			||||
all:	reflow.hex | 
				
			||||
 | 
				
			||||
.c.o: | 
				
			||||
	$(COMPILE) $(CFLAGS) -c $< -o $@
 | 
				
			||||
 | 
				
			||||
.S.o: | 
				
			||||
	$(COMPILE) -x assembler-with-cpp -c $< -o $@
 | 
				
			||||
 | 
				
			||||
.c.s: | 
				
			||||
	$(COMPILE) $(CFLAGS) -S $< -o $@
 | 
				
			||||
 | 
				
			||||
flash: | 
				
			||||
	$(AVRDUDE) -U flash:w:reflow.hex:i
 | 
				
			||||
 | 
				
			||||
fuse: | 
				
			||||
	$(AVRDUDE) $(FUSES)
 | 
				
			||||
 | 
				
			||||
install: all flash fuse | 
				
			||||
 | 
				
			||||
# if you use a bootloader, change the command below appropriately:
 | 
				
			||||
load: all | 
				
			||||
	bootloadHID reflow.hex
 | 
				
			||||
 | 
				
			||||
clean: | 
				
			||||
	rm -f reflow.hex reflow.elf $(OBJECTS)
 | 
				
			||||
 | 
				
			||||
# file targets:
 | 
				
			||||
#reflow.elf: $(SRC)
 | 
				
			||||
#	$(COMPILE) $(LDFLAGS) -o reflow.elf $(SRC)
 | 
				
			||||
 | 
				
			||||
reflow.elf: $(OBJECTS) | 
				
			||||
	$(COMPILE) $(LDFLAGS) -o reflow.elf $(OBJECTS)
 | 
				
			||||
 | 
				
			||||
reflow.hex: reflow.elf | 
				
			||||
	rm -f reflow.hex
 | 
				
			||||
	avr-objcopy -j .text -j .data -O ihex reflow.elf reflow.hex
 | 
				
			||||
	avr-size --format=avr --mcu=$(DEVICE) reflow.elf
 | 
				
			||||
# If you have an EEPROM section, you must also create a hex file for the
 | 
				
			||||
# EEPROM and add it to the "flash" target.
 | 
				
			||||
 | 
				
			||||
# Targets for code debugging and analysis:
 | 
				
			||||
disasm:	reflow.elf | 
				
			||||
	avr-objdump -d reflow.elf
 | 
				
			||||
 | 
				
			||||
cpp: | 
				
			||||
	$(COMPILE) -E reflow.c
 | 
				
			||||
 | 
				
			||||
@ -0,0 +1,24 @@
					 | 
				
			||||
/* beep.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
#define DDR_BEEP DDRB | 
				
			||||
#define PORT_BEEP PORTB | 
				
			||||
 | 
				
			||||
#define PIN_BEEP 1 | 
				
			||||
 | 
				
			||||
void beep_on(void) | 
				
			||||
{ | 
				
			||||
    TIMSK1 = 0; | 
				
			||||
    TCCR1A = (1<<COM1A0); | 
				
			||||
    TCCR1B = (1<<CS10) | (1<<WGM12); /* clkIO / 1, CTC mode */ | 
				
			||||
    DDR_BEEP |= (1<<PIN_BEEP); | 
				
			||||
    OCR1A = 1000; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void beep_off(void) | 
				
			||||
{ | 
				
			||||
    DDR_BEEP &= ~(1<<PIN_BEEP); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,9 @@
					 | 
				
			||||
/* beep.h */ | 
				
			||||
 | 
				
			||||
#ifndef _BEEP_H_ | 
				
			||||
#define _BEEP_H_ | 
				
			||||
 | 
				
			||||
void beep_on(void); | 
				
			||||
void beep_off(void); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,33 @@
					 | 
				
			||||
/* button.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/interrupt.h> | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
uint8_t bounce_timer; | 
				
			||||
uint8_t button; | 
				
			||||
 | 
				
			||||
#define BOUNCE_TIMER_RESET 10 | 
				
			||||
 | 
				
			||||
void button_init(void) | 
				
			||||
{ | 
				
			||||
    bounce_timer = 0; | 
				
			||||
    button = 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
ISR(TIMER2_vect) | 
				
			||||
{ | 
				
			||||
    uint8_t port = PORTD; | 
				
			||||
    uint8_t old_button; | 
				
			||||
    port = (~port >> 4); | 
				
			||||
    if (bounce_timer) | 
				
			||||
	bounce_timer--; | 
				
			||||
    else | 
				
			||||
	if (port) | 
				
			||||
	    button = 1 + ctz(port); | 
				
			||||
	else | 
				
			||||
	    button = 0; | 
				
			||||
 | 
				
			||||
    if (button != old_button) | 
				
			||||
	bounce_timer = BOUNCE_TIMER_RESET; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,8 @@
					 | 
				
			||||
/* button.h */ | 
				
			||||
 | 
				
			||||
#ifndef _BUTTON_H_ | 
				
			||||
#define _BUTTON_H_ | 
				
			||||
 | 
				
			||||
extern uint8 button; | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,11 @@
					 | 
				
			||||
/* common.h */ | 
				
			||||
 | 
				
			||||
#ifndef _COMMON_H_ | 
				
			||||
#define _COMMON_H_ | 
				
			||||
 | 
				
			||||
#include <inttypes.h> | 
				
			||||
#include "types.h" | 
				
			||||
 | 
				
			||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,15 @@
					 | 
				
			||||
/* config.c */ | 
				
			||||
 | 
				
			||||
#include "config.h" | 
				
			||||
 | 
				
			||||
config_t config = { | 
				
			||||
    127, | 
				
			||||
    1, /* beep */ | 
				
			||||
    3, /* thermocouple */ | 
				
			||||
    0, /* frequency */ | 
				
			||||
    0, /* units */ | 
				
			||||
    0x080, | 
				
			||||
    0x040, | 
				
			||||
    0xfc0 | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
@ -0,0 +1,25 @@
					 | 
				
			||||
/* config.h */ | 
				
			||||
 | 
				
			||||
#ifndef _CONFIG_H_ | 
				
			||||
#define _CONFIG_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
/* So there's a total of 64 bytes of EEPROM.
 | 
				
			||||
 * This is going to be a squeeze. Unless we can | 
				
			||||
 * program the profiles into program flash or something. | 
				
			||||
 */ | 
				
			||||
typedef struct config { | 
				
			||||
    uint8_t contrast; | 
				
			||||
    unsigned int beep:1; | 
				
			||||
    unsigned int thermocouple_type:3; | 
				
			||||
    unsigned int frequency:1; | 
				
			||||
    unsigned int units:4; | 
				
			||||
    signed int p:12; | 
				
			||||
    signed int i:12; | 
				
			||||
    signed int d:12; | 
				
			||||
} config_t; | 
				
			||||
 | 
				
			||||
extern config_t config; | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,122 @@
					 | 
				
			||||
/* control.c */ | 
				
			||||
 | 
				
			||||
#include <util/atomic.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "timer.h" | 
				
			||||
#include "config.h" | 
				
			||||
#include "therm.h" | 
				
			||||
#include "beep.h" | 
				
			||||
 | 
				
			||||
static temp_t last_temp; | 
				
			||||
static volatile bool preheat; | 
				
			||||
static bool control_running; | 
				
			||||
static uint32_t control_start_time; | 
				
			||||
static int16_t integral; | 
				
			||||
static int16_t control_output; | 
				
			||||
 | 
				
			||||
temp_t temperature_at_time(uint32_t time); | 
				
			||||
 | 
				
			||||
int16_t __attribute__ ((noinline)) clip16(int16_t val, int16_t min, int16_t max) | 
				
			||||
{ | 
				
			||||
    if (val < min) | 
				
			||||
	val = min; | 
				
			||||
    if (val > max) | 
				
			||||
	val = max; | 
				
			||||
    return val; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint32_t control_now(void) | 
				
			||||
{ | 
				
			||||
    uint32_t this_seconds; | 
				
			||||
 | 
				
			||||
    ATOMIC_BLOCK(ATOMIC_FORCEON) { | 
				
			||||
	this_seconds = seconds; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (preheat) | 
				
			||||
	control_start_time = this_seconds; | 
				
			||||
    return this_seconds - control_start_time; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void control_start(void) | 
				
			||||
{ | 
				
			||||
    preheat = TRUE; | 
				
			||||
    last_temp = therm_temp(); | 
				
			||||
    integral = 0; | 
				
			||||
    control_running = TRUE; | 
				
			||||
    control_output = 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#define TIME_UNIT 1 | 
				
			||||
#define INTEGRAL_MAX (CONTROL_MAX * 2) | 
				
			||||
#define INTEGRAL_MIN (-(INTEGRAL_MAX)) | 
				
			||||
 | 
				
			||||
#define CONTROL_MAX (10 * 128) | 
				
			||||
#define CONTROL_MIN (0) | 
				
			||||
 | 
				
			||||
/* P = 1.00 means 1 degree C leads to full-scale output
 | 
				
			||||
 * This is represented by config.p == 128 | 
				
			||||
 * The calculations done here are in units of 0.1 degrees | 
				
			||||
 */ | 
				
			||||
 | 
				
			||||
#define ABS(x) ((x) < 0 ? -(x) : (x)) | 
				
			||||
#define INT_MAX 32767 | 
				
			||||
#define INT_MIN (-32768) | 
				
			||||
 | 
				
			||||
#if 0 | 
				
			||||
int16_t multiply_clip(int16_t a, int16_t b) | 
				
			||||
{ | 
				
			||||
    if ((b == 0) || (ABS(a) > INT_MAX / ABS(b))) | 
				
			||||
	return ((a < 0) ^ (b < 0)) ? INT_MIN : INT_MAX; | 
				
			||||
    return a * b; | 
				
			||||
} | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void control_poll(void) | 
				
			||||
{ | 
				
			||||
    temp_t temp = therm_temp(); | 
				
			||||
    temp_t profile_temp; | 
				
			||||
    int16_t error; | 
				
			||||
    int16_t temp_diff = temp - last_temp; | 
				
			||||
    int16_t maxgain; | 
				
			||||
    int16_t p, i, d; | 
				
			||||
 | 
				
			||||
    last_temp = temp; | 
				
			||||
 | 
				
			||||
    if (!control_running) | 
				
			||||
	return; | 
				
			||||
 | 
				
			||||
    profile_temp = temperature_at_time(control_now()); | 
				
			||||
    if (profile_temp == INVALID_TEMPERATURE) { | 
				
			||||
	control_running = FALSE; | 
				
			||||
	output0 = 0; | 
				
			||||
	beep_off(); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    error = (int16_t)(profile_temp - temp); | 
				
			||||
 | 
				
			||||
    if (error <= 0) { | 
				
			||||
	beep_on(); | 
				
			||||
	preheat = FALSE; | 
				
			||||
    } else { | 
				
			||||
	beep_off(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    maxgain = (error == 0) ? INT_MAX : ((2*CONTROL_MAX) / error); | 
				
			||||
    maxgain = ABS(maxgain); | 
				
			||||
 | 
				
			||||
    p = clip16(config.p, -maxgain, maxgain); | 
				
			||||
    i = clip16(config.i, -maxgain, maxgain); | 
				
			||||
    d = clip16(config.d, -maxgain, maxgain); | 
				
			||||
 | 
				
			||||
    integral += i * error * TIME_UNIT; | 
				
			||||
    integral = clip16(integral, INTEGRAL_MIN, INTEGRAL_MAX); | 
				
			||||
 | 
				
			||||
    control_output += p * error + integral + d * temp_diff / TIME_UNIT; | 
				
			||||
    control_output = clip16(control_output, CONTROL_MIN, CONTROL_MAX); | 
				
			||||
 | 
				
			||||
    output0 = (TIMER_MAX/5) * control_output / (CONTROL_MAX/5); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,12 @@
					 | 
				
			||||
/* control.h */ | 
				
			||||
 | 
				
			||||
#ifndef _CONTROL_H_ | 
				
			||||
#define _CONTROL_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void control_start(void); | 
				
			||||
void control_poll(void); | 
				
			||||
uint32_t control_now(void); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,140 @@
					 | 
				
			||||
/* display.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/pgmspace.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "i2c.h" | 
				
			||||
#include "font.h" | 
				
			||||
 | 
				
			||||
#define ADDRESS 0x3c | 
				
			||||
 | 
				
			||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) | 
				
			||||
 | 
				
			||||
#define HORIZONTAL_FLIP 0 | 
				
			||||
#define VERTICAL_FLIP 0 | 
				
			||||
 | 
				
			||||
#define COM_ALT 1 | 
				
			||||
#define COM_LR_REMAP 0 | 
				
			||||
 | 
				
			||||
#define DISPLAY_INVERT 0 | 
				
			||||
 | 
				
			||||
#define DEFAULT_CONTRAST 0x7f | 
				
			||||
 | 
				
			||||
/* Init sequence from SSD1306 datasheet */ | 
				
			||||
const uint8 display_init_seq[] = { | 
				
			||||
	0xa8, 0x3f, /* Multiplex ratio */ | 
				
			||||
	0xd3, 0x00, /* Display offset */ | 
				
			||||
	0x40, /* Display start line */ | 
				
			||||
	0xa0 | HORIZONTAL_FLIP, | 
				
			||||
	0xc0 | (VERTICAL_FLIP << 3), | 
				
			||||
	0xda, 0x02 | (COM_ALT << 4) | (COM_LR_REMAP << 5), | 
				
			||||
	0x81, DEFAULT_CONTRAST, | 
				
			||||
	0xa4, /* Disable entire display on */ | 
				
			||||
	0xa6 | DISPLAY_INVERT, | 
				
			||||
	0xd5, 0x80, /* clock divide ratio */ | 
				
			||||
	0x8d, 0x14, /* charge pump setting */ | 
				
			||||
	0xaf  /* display on */ | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
uint8 curpos; | 
				
			||||
 | 
				
			||||
#if 0 | 
				
			||||
void delay(uint8_t count) | 
				
			||||
{ | 
				
			||||
    volatile uint8_t i; | 
				
			||||
 | 
				
			||||
    while (count--) | 
				
			||||
	for (i = 255; i; i--) | 
				
			||||
	    ; | 
				
			||||
} | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void display_command(uint8_t command) | 
				
			||||
{ | 
				
			||||
    i2c_set_xor(0); | 
				
			||||
    i2c_write(ADDRESS, 0x80, 1, &command); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_init(void) | 
				
			||||
{ | 
				
			||||
    uint8 i; | 
				
			||||
 | 
				
			||||
    i2c_init(); | 
				
			||||
 | 
				
			||||
    for (i = 0; i < ARRAY_SIZE(display_init_seq); i++) | 
				
			||||
	display_command(display_init_seq[i]); | 
				
			||||
 | 
				
			||||
    curpos = 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_data(uint8_t len, uint8_t *data) | 
				
			||||
{ | 
				
			||||
    i2c_write(ADDRESS, 0x40, len, data); | 
				
			||||
    curpos += len; | 
				
			||||
#if 0 | 
				
			||||
    /* This bit is only needed if you are relying on wraparound. */ | 
				
			||||
    if (curpos > 127) | 
				
			||||
	curpos -= 128; | 
				
			||||
#endif | 
				
			||||
    //delay(255);
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
/* col = graphics column, row = text rows
 | 
				
			||||
 * 0-127                  0-7 | 
				
			||||
 */ | 
				
			||||
void display_setposition(uint8_t col, uint8_t row) | 
				
			||||
{ | 
				
			||||
    display_command(0xb0 + (row & 0x0f)); | 
				
			||||
    display_command(0x00 + (col & 0x0f)); | 
				
			||||
    display_command(0x10 + ((col >> 4) & 0x0f)); | 
				
			||||
    curpos = col; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_setinverse(bool on) | 
				
			||||
{ | 
				
			||||
    i2c_set_xor(on ? 0xff : 0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putchar(char c) | 
				
			||||
{ | 
				
			||||
    uint8_t *data = font_getchar(c); | 
				
			||||
    display_data(6, data); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putstr(char *s) | 
				
			||||
{ | 
				
			||||
    while (*s) | 
				
			||||
	display_putchar(*(s++)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putstr_P(const char *s) | 
				
			||||
{ | 
				
			||||
    char c; | 
				
			||||
    while ((c = pgm_read_byte(s++)) != '\0') | 
				
			||||
	display_putchar(c); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#ifdef FAST_CLEAR | 
				
			||||
const uint8_t zero_data[] = {0, 0, 0, 0, 0, 0, 0, 0}; | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void display_clearline(void) | 
				
			||||
{ | 
				
			||||
    uint8_t remaining = 128 - curpos; | 
				
			||||
 | 
				
			||||
#ifdef FAST_CLEAR | 
				
			||||
    while (remaining) { | 
				
			||||
	uint8_t todo = remaining; | 
				
			||||
	if (todo > sizeof(zero_data)) | 
				
			||||
	    todo = sizeof(zero_data); | 
				
			||||
	display_data(todo, (uint8_t *)zero_data); | 
				
			||||
	remaining -= todo; | 
				
			||||
    } | 
				
			||||
#else | 
				
			||||
    uint8_t zero = 0; | 
				
			||||
 | 
				
			||||
    while (remaining--) | 
				
			||||
	display_data(1, &zero); | 
				
			||||
#endif | 
				
			||||
} | 
				
			||||
@ -0,0 +1,18 @@
					 | 
				
			||||
/* display.h */ | 
				
			||||
 | 
				
			||||
#ifndef _DISPLAY_H_ | 
				
			||||
#define _DISPLAY_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void display_init(void); | 
				
			||||
void display_data(uint8_t len, uint8_t *data); | 
				
			||||
void display_setposition(uint8_t col, uint8_t row); | 
				
			||||
#define display_settextpos(col, row) display_setposition((col)*6, row) | 
				
			||||
void display_setinverse(bool on); | 
				
			||||
void display_putchar(char c); | 
				
			||||
void display_putstr(char *s); | 
				
			||||
void display_putstr_P(const char *s); | 
				
			||||
void display_clearline(void); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,268 @@
					 | 
				
			||||
/* fields.c */ | 
				
			||||
 | 
				
			||||
#include <stddef.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/pgmspace.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "fields.h" | 
				
			||||
#include "menu.h" | 
				
			||||
#include "config.h" | 
				
			||||
 | 
				
			||||
uint8_t field_row; // XXX ?
 | 
				
			||||
uint8_t field_values[DISPLAY_CHARS]; | 
				
			||||
 | 
				
			||||
static uint8_t field_index(char c) | 
				
			||||
{ | 
				
			||||
    return ((uint8_t)(c) & (uint8_t)15)-1; | 
				
			||||
} | 
				
			||||
//#define field(c) fields[((uint8_t)(c) & (uint8_t)15)-1]
 | 
				
			||||
#define field(c) fields[field_index(c)] | 
				
			||||
 | 
				
			||||
uint8_t field_length(char c) | 
				
			||||
{ | 
				
			||||
    return field(c).length; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool field_is_text(char c) | 
				
			||||
{ | 
				
			||||
    return field(c).display == NULL; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
PGM_P field_text(char c, uint8_t n) | 
				
			||||
{ | 
				
			||||
    return get_string(&field(c).display[n]); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t field_display_entries(char c) | 
				
			||||
{ | 
				
			||||
    return field(c).display_entries; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool is_field(char c) { | 
				
			||||
    return (c > 0) && (c < 32); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool is_editable(char c) { | 
				
			||||
    return (c > 16) && (c < 32); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
PGM_P get_string(PGM_P const *addr) | 
				
			||||
{ | 
				
			||||
    PGM_P ptr; | 
				
			||||
    memcpy_P(&ptr, addr, sizeof(PGM_P)); | 
				
			||||
    return ptr; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t find_field(uint8_t n) | 
				
			||||
{ | 
				
			||||
    char c; | 
				
			||||
    uint8_t index = 0; | 
				
			||||
    char type = 0; | 
				
			||||
 | 
				
			||||
    while ((c = menu_getchar(index)) != '\0') { | 
				
			||||
	index++; | 
				
			||||
	if (type && (type == c)) | 
				
			||||
	    continue; | 
				
			||||
	if (is_field(c)) { | 
				
			||||
	    type = c; | 
				
			||||
	    if (n == 0) | 
				
			||||
		return index-1; | 
				
			||||
	    n--; | 
				
			||||
	} else { | 
				
			||||
	    type = 0; | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
    return 255; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t find_field_length_by_place(uint8_t index) | 
				
			||||
{ | 
				
			||||
    uint8_t orig_index = index; | 
				
			||||
    char type = menu_getchar(index); | 
				
			||||
    uint8_t tlen = field_length(type); | 
				
			||||
 | 
				
			||||
    while (menu_getchar(index) == type) | 
				
			||||
	index += tlen; | 
				
			||||
 | 
				
			||||
    return index - orig_index; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t find_field_length(uint8_t index) | 
				
			||||
{ | 
				
			||||
    return find_field_length_by_place(find_field(index)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t find_field_number(uint8_t index) | 
				
			||||
{ | 
				
			||||
    uint8_t n, p; | 
				
			||||
    for (n = 0; (p = find_field(n)) != 255; n++) | 
				
			||||
    { | 
				
			||||
	if (p == index) | 
				
			||||
	    return n; | 
				
			||||
    } | 
				
			||||
    return 255; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t find_editable_field(uint8_t index, bool left) | 
				
			||||
{ | 
				
			||||
    int8_t increment = left?(-1):1; | 
				
			||||
    char c; | 
				
			||||
    uint8_t len; | 
				
			||||
 | 
				
			||||
    if (index >= 255) | 
				
			||||
	return 255; | 
				
			||||
 | 
				
			||||
    len = strlen_P(get_string(&menu_current_p->text[field_row])); | 
				
			||||
 | 
				
			||||
    if (left && (index >= len)) | 
				
			||||
	index = len-1; | 
				
			||||
 | 
				
			||||
    while ((c = menu_getchar(index)) != '\0') { | 
				
			||||
	if (is_editable(c)) | 
				
			||||
	    return index; | 
				
			||||
	index += increment; | 
				
			||||
    } | 
				
			||||
    return 255; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void write_field_enum(uint8_t field, uint8_t val) | 
				
			||||
{ | 
				
			||||
    uint8_t p = find_field(field); | 
				
			||||
    field_values[p] = val; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t read_field_enum(uint8_t field) | 
				
			||||
{ | 
				
			||||
    uint8_t p = find_field(field); | 
				
			||||
    return field_values[p]; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint16_t read_field_uint16(uint8_t field) | 
				
			||||
{ | 
				
			||||
    uint8_t i; | 
				
			||||
    uint16_t val = 0; | 
				
			||||
 | 
				
			||||
    uint8_t p = find_field(field); | 
				
			||||
    uint8_t l = find_field_length(field); | 
				
			||||
 | 
				
			||||
    for (i = 0; i < l; i++) { | 
				
			||||
	val = val * 10; | 
				
			||||
	val += field_values[p+i]; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return val; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void write_field_uint16(uint8_t field, uint16_t val) | 
				
			||||
{ | 
				
			||||
    uint8_t i; | 
				
			||||
 | 
				
			||||
    uint8_t p = find_field(field); | 
				
			||||
    uint8_t l = find_field_length(field); | 
				
			||||
 | 
				
			||||
    for (i = l; i; i--) { | 
				
			||||
	field_values[p+i-1] = val % 10; | 
				
			||||
	val = val / 10; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void write_field_integer_part(uint8_t field, int32_t val) | 
				
			||||
{ | 
				
			||||
    if (val < 0) | 
				
			||||
	val = -val; | 
				
			||||
 | 
				
			||||
    write_field_uint16(field, val >> 12); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void write_field_fractional_part(uint8_t field, int32_t val) | 
				
			||||
{ | 
				
			||||
    uint8_t i; | 
				
			||||
    uint16_t v; | 
				
			||||
 | 
				
			||||
    uint8_t p = find_field(field); | 
				
			||||
    uint8_t l = find_field_length(field); | 
				
			||||
 | 
				
			||||
    if (val < 0) | 
				
			||||
	val = -val; | 
				
			||||
 | 
				
			||||
    v = val & 0xfff; | 
				
			||||
 | 
				
			||||
    for (i = 0; i < l; i++) { | 
				
			||||
	uint8_t digit; | 
				
			||||
	v = v * 10; | 
				
			||||
	digit = (v >> 12) % 10; | 
				
			||||
	v = v - (digit << 12); | 
				
			||||
	field_values[p+i] = digit; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
int32_t round_dp(int32_t n, uint8_t dp) | 
				
			||||
{ | 
				
			||||
    int32_t rval = 0x800; | 
				
			||||
    int i; | 
				
			||||
 | 
				
			||||
    /* Round away from zero. Remove this bit
 | 
				
			||||
     * to round up | 
				
			||||
     */ | 
				
			||||
    if (n < 0) | 
				
			||||
	rval = -rval; | 
				
			||||
 | 
				
			||||
    for (i = 0; i < dp; i++) | 
				
			||||
	rval = rval / 10; | 
				
			||||
 | 
				
			||||
    return n + rval; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint32_t read_field_fracint(int8_t index, bool frac) | 
				
			||||
{ | 
				
			||||
    int32_t val, v_int, v_frac; | 
				
			||||
    uint8_t sign = read_field_enum(index++); | 
				
			||||
 | 
				
			||||
    v_int = read_field_uint16(index++); | 
				
			||||
 | 
				
			||||
    if (frac) { | 
				
			||||
	uint8_t l_frac; | 
				
			||||
	v_frac = read_field_uint16(index); | 
				
			||||
	l_frac = find_field_length(index); | 
				
			||||
 | 
				
			||||
	v_frac = v_frac << 12; | 
				
			||||
	while (l_frac--) | 
				
			||||
	    v_frac = v_frac / 10; | 
				
			||||
    } else { | 
				
			||||
        v_frac = 0; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    val = (v_int << 12) + v_frac; | 
				
			||||
 | 
				
			||||
    if (sign == FIELD_SIGN_NEGATIVE) | 
				
			||||
	val = -val; | 
				
			||||
 | 
				
			||||
    return val; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void write_field_fracint(uint8_t index, bool sign, bool frac, int32_t val) | 
				
			||||
{ | 
				
			||||
    uint8_t l_frac; | 
				
			||||
 | 
				
			||||
    l_frac = find_field_length(index+1+(sign?1:0)); | 
				
			||||
 | 
				
			||||
    val = round_dp(val, frac?l_frac:0); | 
				
			||||
 | 
				
			||||
    if (sign) | 
				
			||||
	write_field_enum(index++, (val<0)?FIELD_SIGN_NEGATIVE:FIELD_SIGN_POSITIVE); | 
				
			||||
 | 
				
			||||
    write_field_integer_part(index++, val); | 
				
			||||
    if (frac) | 
				
			||||
	write_field_fractional_part(index, val); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
void write_field_temperature(uint8_t index, bool frac, temp_t k_temp) | 
				
			||||
{ | 
				
			||||
    int32_t temp = temperature_from_kelvin(k_temp); | 
				
			||||
    write_field_fracint(index, TRUE, frac, temp); | 
				
			||||
    write_field_enum(index+2+(frac?1:0), config.units); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,53 @@
					 | 
				
			||||
/* fields.h */ | 
				
			||||
 | 
				
			||||
#ifndef _FIELDS_H_ | 
				
			||||
#define _FIELDS_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "avr/pgmspace.h" | 
				
			||||
 | 
				
			||||
#define DISPLAY_CHARS 21 | 
				
			||||
 | 
				
			||||
extern uint8_t field_values[DISPLAY_CHARS]; | 
				
			||||
 | 
				
			||||
typedef struct field { | 
				
			||||
    PGM_P const *display; | 
				
			||||
    uint8_t display_entries; | 
				
			||||
    uint8_t length; | 
				
			||||
} field_t; | 
				
			||||
 | 
				
			||||
//#define field(c) fields[((uint8_t)(c) & (uint8_t)15)-1]
 | 
				
			||||
 | 
				
			||||
//#define field_length(c) (field(c).length)
 | 
				
			||||
 | 
				
			||||
#define FIELD_SIGN_NEGATIVE 0 | 
				
			||||
#define FIELD_SIGN_POSITIVE 1 | 
				
			||||
 | 
				
			||||
uint8_t field_length(char c); | 
				
			||||
bool field_is_text(char c); | 
				
			||||
PGM_P field_text(char c, uint8_t n); | 
				
			||||
uint8_t field_display_entries(char c); | 
				
			||||
bool is_field(char c); | 
				
			||||
bool is_editable(char c); | 
				
			||||
PGM_P get_string(PGM_P const *addr); | 
				
			||||
uint8_t find_field(uint8_t n); | 
				
			||||
uint8_t find_field_length_by_place(uint8_t index); | 
				
			||||
uint8_t find_field_length(uint8_t index); | 
				
			||||
uint8_t find_field_number(uint8_t index); | 
				
			||||
uint8_t find_editable_field(uint8_t index, bool left); | 
				
			||||
void write_field_enum(uint8_t field, uint8_t val); | 
				
			||||
uint8_t read_field_enum(uint8_t field); | 
				
			||||
uint16_t read_field_uint16(uint8_t field); | 
				
			||||
void write_field_uint16(uint8_t field, uint16_t val); | 
				
			||||
 | 
				
			||||
#define read_field_uint8(x) read_field_uint16(x) | 
				
			||||
#define write_field_uint8(x, y) write_field_uint16(x, y) | 
				
			||||
 | 
				
			||||
void write_field_integer_part(uint8_t field, int32_t val); | 
				
			||||
void write_field_fractional_part(uint8_t field, int32_t val); | 
				
			||||
int32_t round_dp(int32_t n, uint8_t dp); | 
				
			||||
uint32_t read_field_fracint(int8_t index, bool frac); | 
				
			||||
void write_field_fracint(uint8_t index, bool sign, bool frac, int32_t val); | 
				
			||||
void write_field_temperature(uint8_t index, bool frac, temp_t k_temp); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,123 @@
					 | 
				
			||||
/* font.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/pgmspace.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
// standard ascii 5x7 font
 | 
				
			||||
// defines ascii characters 0x20-0x7F (32-127)
 | 
				
			||||
static const uint8_t PROGMEM font[] = { | 
				
			||||
	0x00, 0x00, 0x00, 0x00, 0x00,// (space)
 | 
				
			||||
	0x00, 0x00, 0x5F, 0x00, 0x00,// !
 | 
				
			||||
	0x00, 0x07, 0x00, 0x07, 0x00,// "
 | 
				
			||||
	0x14, 0x7F, 0x14, 0x7F, 0x14,// #
 | 
				
			||||
	0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
 | 
				
			||||
	0x23, 0x13, 0x08, 0x64, 0x62,// %
 | 
				
			||||
	0x36, 0x49, 0x55, 0x22, 0x50,// &
 | 
				
			||||
	0x00, 0x05, 0x03, 0x00, 0x00,// '
 | 
				
			||||
	0x00, 0x1C, 0x22, 0x41, 0x00,// (
 | 
				
			||||
	0x00, 0x41, 0x22, 0x1C, 0x00,// )
 | 
				
			||||
	0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
 | 
				
			||||
	0x08, 0x08, 0x3E, 0x08, 0x08,// +
 | 
				
			||||
	0x00, 0x50, 0x30, 0x00, 0x00,// ,
 | 
				
			||||
	0x08, 0x08, 0x08, 0x08, 0x08,// -
 | 
				
			||||
	0x00, 0x60, 0x60, 0x00, 0x00,// .
 | 
				
			||||
	0x20, 0x10, 0x08, 0x04, 0x02,// /
 | 
				
			||||
	0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
 | 
				
			||||
	0x00, 0x42, 0x7F, 0x40, 0x00,// 1
 | 
				
			||||
	0x42, 0x61, 0x51, 0x49, 0x46,// 2
 | 
				
			||||
	0x21, 0x41, 0x45, 0x4B, 0x31,// 3
 | 
				
			||||
	0x18, 0x14, 0x12, 0x7F, 0x10,// 4
 | 
				
			||||
	0x27, 0x45, 0x45, 0x45, 0x39,// 5
 | 
				
			||||
	0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
 | 
				
			||||
	0x01, 0x71, 0x09, 0x05, 0x03,// 7
 | 
				
			||||
	0x36, 0x49, 0x49, 0x49, 0x36,// 8
 | 
				
			||||
	0x06, 0x49, 0x49, 0x29, 0x1E,// 9
 | 
				
			||||
	0x00, 0x36, 0x36, 0x00, 0x00,// :
 | 
				
			||||
	0x00, 0x56, 0x36, 0x00, 0x00,// ;
 | 
				
			||||
	0x00, 0x08, 0x14, 0x22, 0x41,// <
 | 
				
			||||
	0x14, 0x14, 0x14, 0x14, 0x14,// =
 | 
				
			||||
	0x41, 0x22, 0x14, 0x08, 0x00,// >
 | 
				
			||||
	0x02, 0x01, 0x51, 0x09, 0x06,// ?
 | 
				
			||||
	0x32, 0x49, 0x79, 0x41, 0x3E,// @
 | 
				
			||||
	0x7E, 0x11, 0x11, 0x11, 0x7E,// A
 | 
				
			||||
	0x7F, 0x49, 0x49, 0x49, 0x36,// B
 | 
				
			||||
	0x3E, 0x41, 0x41, 0x41, 0x22,// C
 | 
				
			||||
	0x7F, 0x41, 0x41, 0x22, 0x1C,// D
 | 
				
			||||
	0x7F, 0x49, 0x49, 0x49, 0x41,// E
 | 
				
			||||
	0x7F, 0x09, 0x09, 0x01, 0x01,// F
 | 
				
			||||
	0x3E, 0x41, 0x41, 0x51, 0x32,// G
 | 
				
			||||
	0x7F, 0x08, 0x08, 0x08, 0x7F,// H
 | 
				
			||||
	0x00, 0x41, 0x7F, 0x41, 0x00,// I
 | 
				
			||||
	0x20, 0x40, 0x41, 0x3F, 0x01,// J
 | 
				
			||||
	0x7F, 0x08, 0x14, 0x22, 0x41,// K
 | 
				
			||||
	0x7F, 0x40, 0x40, 0x40, 0x40,// L
 | 
				
			||||
	0x7F, 0x02, 0x04, 0x02, 0x7F,// M
 | 
				
			||||
	0x7F, 0x04, 0x08, 0x10, 0x7F,// N
 | 
				
			||||
	0x3E, 0x41, 0x41, 0x41, 0x3E,// O
 | 
				
			||||
	0x7F, 0x09, 0x09, 0x09, 0x06,// P
 | 
				
			||||
	0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
 | 
				
			||||
	0x7F, 0x09, 0x19, 0x29, 0x46,// R
 | 
				
			||||
	0x46, 0x49, 0x49, 0x49, 0x31,// S
 | 
				
			||||
	0x01, 0x01, 0x7F, 0x01, 0x01,// T
 | 
				
			||||
	0x3F, 0x40, 0x40, 0x40, 0x3F,// U
 | 
				
			||||
	0x1F, 0x20, 0x40, 0x20, 0x1F,// V
 | 
				
			||||
	0x7F, 0x20, 0x18, 0x20, 0x7F,// W
 | 
				
			||||
	0x63, 0x14, 0x08, 0x14, 0x63,// X
 | 
				
			||||
	0x03, 0x04, 0x78, 0x04, 0x03,// Y
 | 
				
			||||
	0x61, 0x51, 0x49, 0x45, 0x43,// Z
 | 
				
			||||
	0x00, 0x00, 0x7F, 0x41, 0x41,// [
 | 
				
			||||
	0x02, 0x04, 0x08, 0x10, 0x20,// "\"
 | 
				
			||||
	0x41, 0x41, 0x7F, 0x00, 0x00,// ]
 | 
				
			||||
	0x04, 0x02, 0x01, 0x02, 0x04,// ^
 | 
				
			||||
	0x40, 0x40, 0x40, 0x40, 0x40,// _
 | 
				
			||||
	0x00, 0x01, 0x02, 0x04, 0x00,// `
 | 
				
			||||
	0x20, 0x54, 0x54, 0x54, 0x78,// a
 | 
				
			||||
	0x7F, 0x48, 0x44, 0x44, 0x38,// b
 | 
				
			||||
	0x38, 0x44, 0x44, 0x44, 0x20,// c
 | 
				
			||||
	0x38, 0x44, 0x44, 0x48, 0x7F,// d
 | 
				
			||||
	0x38, 0x54, 0x54, 0x54, 0x18,// e
 | 
				
			||||
	0x08, 0x7E, 0x09, 0x01, 0x02,// f
 | 
				
			||||
	0x08, 0x14, 0x54, 0x54, 0x3C,// g
 | 
				
			||||
	0x7F, 0x08, 0x04, 0x04, 0x78,// h
 | 
				
			||||
	0x00, 0x44, 0x7D, 0x40, 0x00,// i
 | 
				
			||||
	0x20, 0x40, 0x44, 0x3D, 0x00,// j
 | 
				
			||||
	0x00, 0x7F, 0x10, 0x28, 0x44,// k
 | 
				
			||||
	0x00, 0x41, 0x7F, 0x40, 0x00,// l
 | 
				
			||||
	0x7C, 0x04, 0x18, 0x04, 0x78,// m
 | 
				
			||||
	0x7C, 0x08, 0x04, 0x04, 0x78,// n
 | 
				
			||||
	0x38, 0x44, 0x44, 0x44, 0x38,// o
 | 
				
			||||
	0x7C, 0x14, 0x14, 0x14, 0x08,// p
 | 
				
			||||
	0x08, 0x14, 0x14, 0x18, 0x7C,// q
 | 
				
			||||
	0x7C, 0x08, 0x04, 0x04, 0x08,// r
 | 
				
			||||
	0x48, 0x54, 0x54, 0x54, 0x20,// s
 | 
				
			||||
	0x04, 0x3F, 0x44, 0x40, 0x20,// t
 | 
				
			||||
	0x3C, 0x40, 0x40, 0x20, 0x7C,// u
 | 
				
			||||
	0x1C, 0x20, 0x40, 0x20, 0x1C,// v
 | 
				
			||||
	0x3C, 0x40, 0x30, 0x40, 0x3C,// w
 | 
				
			||||
	0x44, 0x28, 0x10, 0x28, 0x44,// x
 | 
				
			||||
	0x0C, 0x50, 0x50, 0x50, 0x3C,// y
 | 
				
			||||
	0x44, 0x64, 0x54, 0x4C, 0x44,// z
 | 
				
			||||
	0x00, 0x08, 0x36, 0x41, 0x00,// {
 | 
				
			||||
	0x00, 0x00, 0x7F, 0x00, 0x00,// |
 | 
				
			||||
	0x00, 0x41, 0x36, 0x08, 0x00,// }
 | 
				
			||||
	0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
 | 
				
			||||
	0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
 | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
uint8_t font_char[6]; | 
				
			||||
 | 
				
			||||
uint8_t *font_getchar(char ch) | 
				
			||||
{ | 
				
			||||
    uint8_t c = ch - 32; | 
				
			||||
 | 
				
			||||
    if (c > (127-32)) | 
				
			||||
	c = 0; | 
				
			||||
 | 
				
			||||
    memcpy_P(font_char, font+5*c, 5); | 
				
			||||
 | 
				
			||||
    font_char[5] = 0; | 
				
			||||
 | 
				
			||||
    return font_char; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,8 @@
					 | 
				
			||||
/* font.h */ | 
				
			||||
 | 
				
			||||
#ifndef _FONT_H_ | 
				
			||||
#define _FONT_H_ | 
				
			||||
 | 
				
			||||
uint8_t *font_getchar(char c); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,115 @@
					 | 
				
			||||
/* soldertimer.c */ | 
				
			||||
 | 
				
			||||
#include <stdio.h> | 
				
			||||
#include <stdint.h> | 
				
			||||
 | 
				
			||||
#include <stddef.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <time.h> | 
				
			||||
 | 
				
			||||
 | 
				
			||||
typedef struct profile_line { | 
				
			||||
    int32_t temperature; /* store degrees C * 4096 */ | 
				
			||||
    unsigned int time; | 
				
			||||
} profile_line_t; | 
				
			||||
 | 
				
			||||
typedef struct profile { | 
				
			||||
    char name[21]; | 
				
			||||
    int32_t start_temp; | 
				
			||||
    profile_line_t lines[6]; | 
				
			||||
} profile_t; | 
				
			||||
 | 
				
			||||
#define DEGREES * 4096 | 
				
			||||
 | 
				
			||||
profile_t profile = {"Lead-free solder", 25 DEGREES, { | 
				
			||||
	{ 150 DEGREES, 100 }, | 
				
			||||
	{ 200 DEGREES, 100 }, | 
				
			||||
	{ 250 DEGREES,  40 }, | 
				
			||||
	{ 250 DEGREES,  30 }, | 
				
			||||
	{ 100 DEGREES,  30 }, | 
				
			||||
	{ 0, 0 } } | 
				
			||||
    }; | 
				
			||||
 | 
				
			||||
static int32_t expand_profile_temperature(uint8_t row) | 
				
			||||
{ | 
				
			||||
    int32_t temp; | 
				
			||||
 | 
				
			||||
    if (row > 1) | 
				
			||||
	temp = profile.lines[row-2].temperature; | 
				
			||||
    else | 
				
			||||
	temp = profile.start_temp; | 
				
			||||
 | 
				
			||||
    return temp; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static uint32_t profile_time(uint8_t line) | 
				
			||||
{ | 
				
			||||
    uint32_t time = profile.lines[line].time; | 
				
			||||
    return time; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static int finished; | 
				
			||||
 | 
				
			||||
static int32_t temperature_at_time(uint32_t time) { | 
				
			||||
    uint32_t time0, time1; | 
				
			||||
    int32_t temp0, temp1; | 
				
			||||
    uint8_t i; | 
				
			||||
 | 
				
			||||
    temp1 = expand_profile_temperature(1); | 
				
			||||
    time1 = 0; | 
				
			||||
 | 
				
			||||
    i = 0; | 
				
			||||
    do { | 
				
			||||
	uint32_t ptime; | 
				
			||||
	temp0 = temp1; | 
				
			||||
	time0 = time1; | 
				
			||||
	ptime = profile_time(i); | 
				
			||||
	if (ptime > 0) { | 
				
			||||
	    temp1 = expand_profile_temperature(2+i); | 
				
			||||
	    time1 += ptime; | 
				
			||||
	} | 
				
			||||
	i++; | 
				
			||||
	if (i >= 6) | 
				
			||||
	    break; | 
				
			||||
    } while (time > time1); | 
				
			||||
 | 
				
			||||
    if (time > time1) { | 
				
			||||
	finished = 1; | 
				
			||||
	temp0 = temp1; | 
				
			||||
	time0 = time1; | 
				
			||||
	time1 += 60; /* Arbitrary number here */ | 
				
			||||
	temp1 = expand_profile_temperature(1); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (time > time1) | 
				
			||||
	return expand_profile_temperature(1); | 
				
			||||
 | 
				
			||||
    return temp0 + (temp1 - temp0) * (int32_t)(time - time0) / (int32_t)(time1 - time0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
int main(void) { | 
				
			||||
    time_t start; | 
				
			||||
    time_t now; | 
				
			||||
    time_t end; | 
				
			||||
 | 
				
			||||
    finished = 0; | 
				
			||||
 | 
				
			||||
    time(&start); | 
				
			||||
 | 
				
			||||
    start = start + 10; | 
				
			||||
 | 
				
			||||
    while (!finished) { | 
				
			||||
	int32_t temp; | 
				
			||||
	struct timespec interval = {0, 2000000}; | 
				
			||||
	time(&now); | 
				
			||||
	if (now < start) | 
				
			||||
	    temp = temperature_at_time(0); | 
				
			||||
	else | 
				
			||||
	    temp = temperature_at_time(now - start); | 
				
			||||
	printf("\r%4ds       %.1f degrees", (int)(now - start), (double)temp / 4096); | 
				
			||||
	fflush(stdout); | 
				
			||||
	nanosleep(&interval, NULL); | 
				
			||||
    } | 
				
			||||
    printf("\n"); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,95 @@
					 | 
				
			||||
/* i2c.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <util/twi.h> | 
				
			||||
#include <avr/interrupt.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "beep.h" | 
				
			||||
 | 
				
			||||
//#define i2c_wait() do { uint16_t count = 0; while (!(TWCR & (1<<TWINT))) if (count++ > 1000) break; } while (0)
 | 
				
			||||
//#define i2c_status() (TWSR & 0xF8)
 | 
				
			||||
#define i2c_check(status) if (i2c_status() != (status)) goto ERROR | 
				
			||||
 | 
				
			||||
static void i2c_wait(void) | 
				
			||||
{ | 
				
			||||
    uint16_t count = 0; | 
				
			||||
    while (!(TWCR & (1<<TWINT))) | 
				
			||||
        if (count++ > 1000) | 
				
			||||
	    break; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static __attribute__ ((noinline)) uint8_t i2c_status(void) | 
				
			||||
{ | 
				
			||||
    return TWSR & 0xF8; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t i2c_xor; | 
				
			||||
 | 
				
			||||
void i2c_init(void) | 
				
			||||
{ | 
				
			||||
    TWSR = 0;  /* prescaler TWPS = 0 */ | 
				
			||||
    TWBR = 10; /* minimum value allowed */ | 
				
			||||
	       /* f_SCL = clk / (16+(2*TWBR*TWPS)) */ | 
				
			||||
 | 
				
			||||
    TWCR = (1<<TWEN); | 
				
			||||
 | 
				
			||||
    i2c_xor = 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//#define i2c_start() do { TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); } while (0)
 | 
				
			||||
//#define i2c_stop()  do { TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); } while (0)
 | 
				
			||||
//#define i2c_txbyte(data) do { TWDR = data; TWCR = (1 << TWINT) | (1<<TWEN); } while (0)
 | 
				
			||||
 | 
				
			||||
static void i2c_start(void) | 
				
			||||
{ | 
				
			||||
    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void i2c_stop(void) | 
				
			||||
{ | 
				
			||||
    TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static __attribute__((noinline)) void i2c_txbyte(uint8_t data) | 
				
			||||
{ | 
				
			||||
    TWDR = data; | 
				
			||||
    TWCR = (1 << TWINT) | (1<<TWEN); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void i2c_set_xor(uint8_t xor) | 
				
			||||
{ | 
				
			||||
    i2c_xor = xor; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool i2c_write(uint8_t address, uint8_t prefix, uint8_t bytes, uint8_t *data) | 
				
			||||
{ | 
				
			||||
    uint8 i; | 
				
			||||
 | 
				
			||||
    i2c_start(); | 
				
			||||
    i2c_wait(); | 
				
			||||
    i2c_check(TW_START); | 
				
			||||
 | 
				
			||||
    i2c_txbyte((address << 1) + 0); | 
				
			||||
    i2c_wait(); | 
				
			||||
 | 
				
			||||
    i2c_check(TW_MT_SLA_ACK); | 
				
			||||
 | 
				
			||||
    i2c_txbyte(prefix); | 
				
			||||
    i2c_wait(); | 
				
			||||
    i2c_check(TW_MT_DATA_ACK); | 
				
			||||
 | 
				
			||||
    for (i = 0; i < bytes; i++) { | 
				
			||||
	i2c_txbyte(data[i] ^ i2c_xor); | 
				
			||||
	i2c_wait(); | 
				
			||||
	i2c_check(TW_MT_DATA_ACK); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    i2c_stop(); | 
				
			||||
    return TRUE; | 
				
			||||
 | 
				
			||||
    ERROR: | 
				
			||||
    i2c_stop(); | 
				
			||||
    return FALSE; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,12 @@
					 | 
				
			||||
/* i2c.h */ | 
				
			||||
 | 
				
			||||
#ifndef _I2C_H_ | 
				
			||||
#define _I2C_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void i2c_init(void); | 
				
			||||
bool i2c_write(uint8_t address, uint8_t prefix, uint8_t bytes, uint8_t *data); | 
				
			||||
void i2c_set_xor(uint8_t xor); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,970 @@
					 | 
				
			||||
/* menu.c */ | 
				
			||||
 | 
				
			||||
//#include <stdio.h>
 | 
				
			||||
 | 
				
			||||
#include <stddef.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/pgmspace.h> | 
				
			||||
#include <util/atomic.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "menu.h" | 
				
			||||
#include "display.h" | 
				
			||||
#include "timer.h" | 
				
			||||
#include "therm.h" | 
				
			||||
#include "config.h" | 
				
			||||
#include "beep.h" | 
				
			||||
#include "fields.h" | 
				
			||||
#include "profile.h" | 
				
			||||
#include "control.h" | 
				
			||||
 | 
				
			||||
uint8_t menu_current; | 
				
			||||
uint8_t menu_row; | 
				
			||||
uint8_t field_row; | 
				
			||||
uint8_t menu_next; | 
				
			||||
uint8_t menu_redraw; | 
				
			||||
bool edit_dirty; | 
				
			||||
 | 
				
			||||
#define REDRAW_NONE 0 | 
				
			||||
#define REDRAW_FIELDS 1 | 
				
			||||
#define REDRAW_ALL 2 | 
				
			||||
 | 
				
			||||
uint8_t run_status; | 
				
			||||
 | 
				
			||||
bool menu_editing; | 
				
			||||
uint8_t menu_editing_field; | 
				
			||||
 | 
				
			||||
menu_t *menu_current_p; | 
				
			||||
 | 
				
			||||
const char str_b[] PROGMEM = "B"; | 
				
			||||
const char str_e[] PROGMEM = "E"; | 
				
			||||
const char str_j[] PROGMEM = "J"; | 
				
			||||
const char str_k[] PROGMEM = "K"; | 
				
			||||
const char str_n[] PROGMEM = "N"; | 
				
			||||
const char str_r[] PROGMEM = "R"; | 
				
			||||
const char str_s[] PROGMEM = "S"; | 
				
			||||
const char str_t[] PROGMEM = "T"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_thermocouple_types[] PROGMEM = { | 
				
			||||
    str_b, | 
				
			||||
    str_e, | 
				
			||||
    str_j, | 
				
			||||
    str_k, | 
				
			||||
    str_n, | 
				
			||||
    str_r, | 
				
			||||
    str_s, | 
				
			||||
    str_t | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_negative[] PROGMEM = "-"; | 
				
			||||
const char str_positive[] PROGMEM = "+"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_posneg[] PROGMEM = { | 
				
			||||
    str_negative, | 
				
			||||
    str_positive | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_off[] PROGMEM = "Off"; | 
				
			||||
const char str_on[]  PROGMEM = "On"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_boolean[] PROGMEM = { | 
				
			||||
    str_off, | 
				
			||||
    str_on | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_50hz[] PROGMEM = "50Hz"; | 
				
			||||
const char str_60hz[] PROGMEM = "60Hz"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_frequency[] PROGMEM = { | 
				
			||||
    str_50hz, | 
				
			||||
    str_60hz | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_degc[]  PROGMEM = "\047C"; | 
				
			||||
const char str_degf[]  PROGMEM = "\047F"; | 
				
			||||
/* str_k already defined */ | 
				
			||||
const char str_degde[] PROGMEM = "\047De"; | 
				
			||||
const char str_degn[]  PROGMEM = "\047N"; | 
				
			||||
const char str_degr[]  PROGMEM = "\047R"; | 
				
			||||
const char str_degre[] PROGMEM = "\047Re"; | 
				
			||||
const char str_degro[] PROGMEM = "\047Ro"; | 
				
			||||
const char str_mev[]   PROGMEM = "meV"; | 
				
			||||
 | 
				
			||||
#define TEMPERATURE_CELSIUS 0 | 
				
			||||
#define TEMPERATURE_FAHRENHEIT 1 | 
				
			||||
#define TEMPERATURE_KELVIN 2 | 
				
			||||
#define TEMPERATURE_DELISLE 3 | 
				
			||||
#define TEMPERATURE_NEWTON 4 | 
				
			||||
#define TEMPERATURE_RANKINE 5 | 
				
			||||
#define TEMPERATURE_REAUMUR 6 | 
				
			||||
#define TEMPERATURE_ROMER 7 | 
				
			||||
#define TEMPERATURE_MEV 8 | 
				
			||||
 | 
				
			||||
PGM_P const enum_units[] PROGMEM = { | 
				
			||||
    str_degc, | 
				
			||||
    str_degf, | 
				
			||||
    str_k, | 
				
			||||
    str_degde, | 
				
			||||
    str_degn, | 
				
			||||
    str_degr, | 
				
			||||
    str_degre, | 
				
			||||
    str_degro, | 
				
			||||
    str_mev | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_seconds[] PROGMEM = "s"; | 
				
			||||
const char str_minutes[] PROGMEM = "m"; | 
				
			||||
const char str_hours[] PROGMEM = "h"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_time_units[] PROGMEM = { | 
				
			||||
    str_seconds, | 
				
			||||
    str_minutes, | 
				
			||||
    str_hours | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_0[]   PROGMEM = "0"; | 
				
			||||
const char str_1[]   PROGMEM = "1"; | 
				
			||||
const char str_2[]   PROGMEM = "2"; | 
				
			||||
const char str_3[]   PROGMEM = "3"; | 
				
			||||
const char str_4[]   PROGMEM = "4"; | 
				
			||||
const char str_5[]   PROGMEM = "5"; | 
				
			||||
const char str_6[]   PROGMEM = "6"; | 
				
			||||
const char str_7[]   PROGMEM = "7"; | 
				
			||||
const char str_8[]   PROGMEM = "8"; | 
				
			||||
const char str_9[]   PROGMEM = "9"; | 
				
			||||
 | 
				
			||||
PGM_P const enum_digits[] PROGMEM = { | 
				
			||||
    str_0, | 
				
			||||
    str_1, | 
				
			||||
    str_2, | 
				
			||||
    str_3, | 
				
			||||
    str_4, | 
				
			||||
    str_5, | 
				
			||||
    str_6, | 
				
			||||
    str_7, | 
				
			||||
    str_8, | 
				
			||||
    str_9, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_start[]     PROGMEM = "Start?"; | 
				
			||||
const char str_running[]   PROGMEM = "Run"; | 
				
			||||
const char str_finished[]  PROGMEM = "Done"; | 
				
			||||
//const char str_error[]     PROGMEM = "Error";
 | 
				
			||||
 | 
				
			||||
PGM_P const enum_status[] PROGMEM = { | 
				
			||||
    str_start, | 
				
			||||
    str_running, | 
				
			||||
    str_finished | 
				
			||||
//    str_error
 | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
#define RUN_STATUS_READY 0 | 
				
			||||
#define RUN_STATUS_RUNNING 1 | 
				
			||||
#define RUN_STATUS_FINISHED 2 | 
				
			||||
#define RUN_STATUS_ERROR 3 | 
				
			||||
 | 
				
			||||
const char str_empty[]     PROGMEM = ""; | 
				
			||||
const char str_fault[]     PROGMEM = "FAULT"; | 
				
			||||
//const char str_open[]      PROGMEM = "Connect thermocouple";
 | 
				
			||||
//const char str_chiprange[] PROGMEM = "Cold junction range";
 | 
				
			||||
//const char str_temprange[] PROGMEM = "Thermocouple range";
 | 
				
			||||
 | 
				
			||||
PGM_P const enum_faultstatus[] PROGMEM = { | 
				
			||||
    str_empty, | 
				
			||||
    str_fault | 
				
			||||
//    str_open,
 | 
				
			||||
//    str_chiprange,
 | 
				
			||||
//    str_temprange
 | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_run_profile[]  PROGMEM = "Run profile"; | 
				
			||||
const char str_edit_profile[] PROGMEM = "Edit profile"; | 
				
			||||
const char str_configure[]    PROGMEM = "Configure"; | 
				
			||||
const char str_faultstatus[]  PROGMEM = "\x0a"; | 
				
			||||
const char str_oven[]         PROGMEM = "Oven       \x08\x02\x02\x02\x02.\x02\x04\x04\x04"; | 
				
			||||
const char str_ambient[]      PROGMEM = "Ambient    \x08\x02\x02\x02\x02.\x02\x04\x04\x04"; | 
				
			||||
 | 
				
			||||
PGM_P const menu_main[] PROGMEM = { | 
				
			||||
    str_run_profile, | 
				
			||||
    str_edit_profile, | 
				
			||||
    str_configure, | 
				
			||||
    NULL, | 
				
			||||
    str_faultstatus, | 
				
			||||
    NULL, | 
				
			||||
    str_oven, | 
				
			||||
    str_ambient | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_placeholder[] PROGMEM = "Placeholder"; | 
				
			||||
const char str_selecting_profile[]      PROGMEM = "Selecting profile"; | 
				
			||||
const char str_str[]      PROGMEM = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"; | 
				
			||||
const char str_editstr[]      PROGMEM = "\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11"; | 
				
			||||
 | 
				
			||||
PGM_P const menu_select_profile[] PROGMEM = { | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str, | 
				
			||||
    str_str | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_editing_profile[]      PROGMEM = "Editing profile"; | 
				
			||||
const char str_name_field[]      PROGMEM = "> Name field <"; | 
				
			||||
const char str_start_temperature[]      PROGMEM = "Start temp   \x18\x12\x12\x12\x12\x04<<"; | 
				
			||||
const char str_edit_field[] PROGMEM = "\x02\x02.\x02\x02/ \x12\x12\x12\x12\x13 \x18\x12\x12\x12\x12\x04<<"; | 
				
			||||
 | 
				
			||||
PGM_P const menu_edit_profile[] PROGMEM = { | 
				
			||||
    str_editstr, | 
				
			||||
    str_start_temperature, | 
				
			||||
    str_edit_field, | 
				
			||||
    str_edit_field, | 
				
			||||
    str_edit_field, | 
				
			||||
    str_edit_field, | 
				
			||||
    str_edit_field, | 
				
			||||
    str_edit_field, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_contrast[]      PROGMEM = "Contrast          \x12\x12\x12"; | 
				
			||||
const char str_beep[]          PROGMEM = "Beep              \x15>>"; | 
				
			||||
const char str_thermocouple[]  PROGMEM = "Thermocouple        \x16"; | 
				
			||||
const char str_frequency[]     PROGMEM = "Frequency        \x17>>>"; | 
				
			||||
const char str_units[]         PROGMEM = "Units             \x14>>"; | 
				
			||||
const char str_p[]             PROGMEM = "P               \x18\x12.\x12\x12"; | 
				
			||||
const char str_i[]             PROGMEM = "I               \x18\x12.\x12\x12"; | 
				
			||||
const char str_d[]             PROGMEM = "D               \x18\x12.\x12\x12"; | 
				
			||||
 | 
				
			||||
PGM_P const menu_configuration[] PROGMEM = { | 
				
			||||
    str_contrast, | 
				
			||||
    str_beep, | 
				
			||||
    str_thermocouple, | 
				
			||||
    str_frequency, | 
				
			||||
    str_units, | 
				
			||||
    str_p, | 
				
			||||
    str_i, | 
				
			||||
    str_d | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
const char str_running_profile[]      PROGMEM = "\x09\x09\x09\x09\x09\x09\x12\x12\x12\x12\x13\x08\x02\x02\x02\x02.\x02\x04<<"; | 
				
			||||
 | 
				
			||||
PGM_P const menu_run_profile[] PROGMEM = { | 
				
			||||
    str_running_profile, | 
				
			||||
    NULL, | 
				
			||||
    NULL, | 
				
			||||
    NULL, | 
				
			||||
    NULL, | 
				
			||||
    NULL, | 
				
			||||
    NULL, | 
				
			||||
    NULL | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
static void main_menu_fields(uint8_t row); | 
				
			||||
static void configuration_fields(uint8_t row); | 
				
			||||
static void save_configuration_fields(uint8_t row); | 
				
			||||
static void select_profile_fields(uint8_t row); | 
				
			||||
static void edit_profile_fields(uint8_t row); | 
				
			||||
static void save_profile_fields(uint8_t row); | 
				
			||||
static void update_run_status(uint8_t row); | 
				
			||||
 | 
				
			||||
menu_t menus[] = { | 
				
			||||
    { menu_main, 0, 2, main_menu_fields, NULL }, | 
				
			||||
    { menu_select_profile, 0, 7, select_profile_fields, NULL }, | 
				
			||||
    { menu_configuration, 0, 7, configuration_fields, save_configuration_fields }, | 
				
			||||
    { menu_edit_profile, 0, 7, edit_profile_fields, save_profile_fields }, | 
				
			||||
    { menu_run_profile, 7, 7, update_run_status, NULL } | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
#define TEMPERATURE_UNIT 0x1000L | 
				
			||||
 | 
				
			||||
int8_t temp_n[] = {1, 9, 1, -3, 33, 9, 4, 21, 10}; | 
				
			||||
int8_t temp_d[] = {1, 5, 1, 2, 100, 5, 5, 40, 116}; | 
				
			||||
int32_t temp_off[] = {0, 32*TEMPERATURE_UNIT, 1118822, -614400, 0, 2013880, 0, 30720, 96450}; | 
				
			||||
 | 
				
			||||
int32_t temperature_from_kelvin(temp_t temp) | 
				
			||||
{ | 
				
			||||
    uint8_t units = config.units; | 
				
			||||
    int32_t temp32 = temp; | 
				
			||||
    int32_t source = ((temp32-2732)<<12) / 10; | 
				
			||||
    int32_t result; | 
				
			||||
    result = source * temp_n[units] / temp_d[units] + temp_off[units]; | 
				
			||||
//    printf("temp = %d, source = %d (%x), result = %d (%x)\n", temp, source, source, result, result);
 | 
				
			||||
    return result; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t temperature_to_kelvin(int32_t source) | 
				
			||||
{ | 
				
			||||
    uint8_t units = config.units; | 
				
			||||
    int32_t result = (source - temp_off[units]) * temp_d[units] / temp_n[units]; | 
				
			||||
 | 
				
			||||
    return therm_reduce(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void update_run_status(uint8_t row) | 
				
			||||
{ | 
				
			||||
    temp_t temp = therm_temp(); | 
				
			||||
    write_field_enum(0, run_status); | 
				
			||||
    write_field_uint16(1, control_now()); | 
				
			||||
    write_field_enum(2, 0); | 
				
			||||
    write_field_temperature(3, TRUE, temp); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void main_menu_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    temp_t temp; | 
				
			||||
 | 
				
			||||
    switch (row) { | 
				
			||||
    case 4: | 
				
			||||
	write_field_enum(0, therm_fault()?1:0); | 
				
			||||
	return; | 
				
			||||
    case 6: | 
				
			||||
	temp = therm_temp(); | 
				
			||||
	break; | 
				
			||||
    case 7: | 
				
			||||
	temp = therm_coldtemp(); | 
				
			||||
	break; | 
				
			||||
    default: | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    write_field_temperature(0, TRUE, temp); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void configuration_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    if (row == 0) { | 
				
			||||
	write_field_uint8(0, config.contrast); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (row > 4) { | 
				
			||||
	int16_t value16; | 
				
			||||
	int32_t val; | 
				
			||||
	switch (row) { | 
				
			||||
	case 5: | 
				
			||||
	    value16 = config.p; | 
				
			||||
	    break; | 
				
			||||
	case 6: | 
				
			||||
	    value16 = config.i; | 
				
			||||
	    break; | 
				
			||||
	case 7: | 
				
			||||
	    value16 = config.d; | 
				
			||||
	    break; | 
				
			||||
	default: | 
				
			||||
	    return; | 
				
			||||
	} | 
				
			||||
	val = (int32_t)value16 << 5; | 
				
			||||
	write_field_fracint(0, TRUE, TRUE, val); | 
				
			||||
    } else { | 
				
			||||
	uint8_t value8; | 
				
			||||
	switch (row) { | 
				
			||||
	case 1: | 
				
			||||
	    value8 = config.beep; | 
				
			||||
	    break; | 
				
			||||
	case 2: | 
				
			||||
	    value8 = config.thermocouple_type; | 
				
			||||
	    break; | 
				
			||||
	case 3: | 
				
			||||
	    value8 = config.frequency; | 
				
			||||
	    break; | 
				
			||||
	case 4: | 
				
			||||
	    value8 = config.units; | 
				
			||||
	    break; | 
				
			||||
	} | 
				
			||||
	write_field_enum(0, value8); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void save_configuration_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    if (row == 0) { | 
				
			||||
	config.contrast = read_field_uint8(0); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (row > 4) { | 
				
			||||
	int16_t value16 = read_field_fracint(0, TRUE) >> 5; | 
				
			||||
	switch (row) { | 
				
			||||
	case 5: | 
				
			||||
	    config.p = value16; | 
				
			||||
	    break; | 
				
			||||
	case 6: | 
				
			||||
	    config.i = value16; | 
				
			||||
	    break; | 
				
			||||
	case 7: | 
				
			||||
	    config.d = value16; | 
				
			||||
	    break; | 
				
			||||
	} | 
				
			||||
    } else { | 
				
			||||
	uint8_t value8 = read_field_enum(0); | 
				
			||||
	switch (row) { | 
				
			||||
	case 1: 
 | 
				
			||||
	    config.beep = value8; | 
				
			||||
	    break; | 
				
			||||
	case 2: 
 | 
				
			||||
	    config.thermocouple_type = value8; | 
				
			||||
	    break; | 
				
			||||
	case 3: 
 | 
				
			||||
	    config.frequency = value8; | 
				
			||||
	    break; | 
				
			||||
	case 4: 
 | 
				
			||||
	    config.units = value8; | 
				
			||||
	    break; | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void select_profile_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    profile_select(row); | 
				
			||||
    memcpy(field_values, &profile_p->name, PROFILE_NAME_LENGTH); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void save_profile_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    int32_t temp; | 
				
			||||
    temp_t k_temp; | 
				
			||||
    uint8_t n; | 
				
			||||
 | 
				
			||||
    if (row == 0) { | 
				
			||||
	memcpy(profile_p->name, field_values, PROFILE_NAME_LENGTH); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    n = find_field_number(find_editable_field(0, FALSE)); | 
				
			||||
 | 
				
			||||
    temp = read_field_fracint(n+((row == 1)?0:2), FALSE); | 
				
			||||
 | 
				
			||||
    k_temp = temperature_to_kelvin(temp); | 
				
			||||
    set_profile_temperature(k_temp); | 
				
			||||
 | 
				
			||||
    if (row == 1) | 
				
			||||
	return; | 
				
			||||
 | 
				
			||||
    set_profile_time(row-2, read_field_uint16(n), read_field_enum(n+1)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void edit_profile_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    int32_t unit_temp, prev_temp, step; | 
				
			||||
    temp_t temp; | 
				
			||||
    uint16_t time; | 
				
			||||
    uint8_t time_units; | 
				
			||||
    if (row == 0) { | 
				
			||||
	memcpy(field_values, profile_p->name, PROFILE_NAME_LENGTH); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    temp = get_profile_temperature(row); | 
				
			||||
    write_field_temperature(((row == 1)?0:4), FALSE, temp); | 
				
			||||
    if (row == 1) | 
				
			||||
	return; | 
				
			||||
 | 
				
			||||
    unit_temp = temperature_from_kelvin(temp); | 
				
			||||
    prev_temp = temperature_from_kelvin(get_profile_temperature(row-1)); | 
				
			||||
 | 
				
			||||
    time = profile_get_time(row-2); | 
				
			||||
    time_units = profile_get_time_units(row-2); | 
				
			||||
 | 
				
			||||
    if (time == 0) | 
				
			||||
	step = 0; | 
				
			||||
    else | 
				
			||||
	step = (unit_temp - prev_temp) / time; | 
				
			||||
 | 
				
			||||
    write_field_fracint(0, FALSE, TRUE, step); | 
				
			||||
    write_field_uint16(2, time); | 
				
			||||
    write_field_enum(3, time_units); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#define MENU_MAIN 0 | 
				
			||||
#define MENU_SELECT_PROFILE 1 | 
				
			||||
#define MENU_CONFIGURATION 2 | 
				
			||||
#define MENU_EDIT_PROFILE 3 | 
				
			||||
#define MENU_RUN_PROFILE 4 | 
				
			||||
 | 
				
			||||
char menu_getchar(uint8_t col) | 
				
			||||
{ | 
				
			||||
    PGM_P ptr = get_string(&menu_current_p->text[field_row]); | 
				
			||||
 | 
				
			||||
    if (!ptr) | 
				
			||||
	return '\0'; | 
				
			||||
 | 
				
			||||
    if (col > 20) | 
				
			||||
	return '\0'; | 
				
			||||
 | 
				
			||||
    return pgm_read_byte(ptr + col); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
field_t fields[] = { | 
				
			||||
    {NULL /* ASCII */, 127-32, 1}, | 
				
			||||
    {enum_digits, ARRAY_SIZE(enum_digits), 1}, | 
				
			||||
    {enum_time_units, ARRAY_SIZE(enum_time_units), 1}, | 
				
			||||
    {enum_units, ARRAY_SIZE(enum_units), 3}, | 
				
			||||
    {enum_boolean, ARRAY_SIZE(enum_boolean), 3}, | 
				
			||||
    {enum_thermocouple_types, ARRAY_SIZE(enum_thermocouple_types), 1}, | 
				
			||||
    {enum_frequency, ARRAY_SIZE(enum_frequency), 4}, | 
				
			||||
    {enum_posneg, ARRAY_SIZE(enum_posneg), 1}, | 
				
			||||
    {enum_status, ARRAY_SIZE(enum_status), 6}, | 
				
			||||
    {enum_faultstatus, ARRAY_SIZE(enum_faultstatus), 21} | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
static void display_field(char c, uint8_t col) | 
				
			||||
{ | 
				
			||||
    uint8_t i; | 
				
			||||
 | 
				
			||||
    if (field_is_text(c)) { | 
				
			||||
	if ((field_values[col] >= 32) && (field_values[col] < 32+field_display_entries(c))) | 
				
			||||
	    display_putchar(field_values[col]); | 
				
			||||
	else | 
				
			||||
	    display_putchar('?'); | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (field_values[col] >= field_display_entries(c)) { | 
				
			||||
	for (i = 0; i < field_length(c); i++) | 
				
			||||
	    display_putchar('?'); | 
				
			||||
    } else { | 
				
			||||
	PGM_P str = field_text(c, field_values[col]); | 
				
			||||
	i = 0; | 
				
			||||
	if (field_length(c) > 1) { | 
				
			||||
	    i = field_length(c) - strlen_P(str); | 
				
			||||
	    if (menu_getchar(col+1) == '>') | 
				
			||||
		for (; i; i--) | 
				
			||||
		    display_putchar(' '); | 
				
			||||
	} | 
				
			||||
	display_putstr_P(str); | 
				
			||||
	for (; i; i--) | 
				
			||||
	    display_putchar(' '); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#define menu_draw_current_row() menu_draw_row(menu_row) | 
				
			||||
 | 
				
			||||
static void menu_set_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    menu_t *menu = menu_current_p; | 
				
			||||
    field_row = row; | 
				
			||||
    if ((!menu_editing) && menu->get_field) | 
				
			||||
	menu->get_field(row); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_draw_row(uint8_t row) | 
				
			||||
{ | 
				
			||||
    uint8_t col; | 
				
			||||
    display_settextpos(0, row); | 
				
			||||
    display_setinverse((row == menu_row) && !menu_editing); | 
				
			||||
    if (get_string(&menu_current_p->text[row])) { | 
				
			||||
	char c; | 
				
			||||
	menu_set_fields(row); | 
				
			||||
	col = 0; | 
				
			||||
	while ((c = menu_getchar(col)) != '\0') { | 
				
			||||
	    if (menu_editing) | 
				
			||||
		display_setinverse(col == menu_editing_field); | 
				
			||||
	    if (is_field(c)) { | 
				
			||||
		display_field(c, col); | 
				
			||||
		col += field_length(c); | 
				
			||||
	    } else { | 
				
			||||
		display_putchar(c); | 
				
			||||
		col++; | 
				
			||||
	    } | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
    display_clearline(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#if 1 | 
				
			||||
static void display_menu(bool all) | 
				
			||||
{ | 
				
			||||
    uint8_t row; | 
				
			||||
 | 
				
			||||
    for (row = 0; row < 8; row++) { | 
				
			||||
	field_row = row; | 
				
			||||
	if (all || (find_field(0) != 255)) | 
				
			||||
	    menu_draw_row(row); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void set_menu(uint8_t new_menu) | 
				
			||||
{ | 
				
			||||
    if (new_menu == menu_current) | 
				
			||||
	return; | 
				
			||||
    menu_current = new_menu; | 
				
			||||
    menu_current_p = &menus[new_menu]; | 
				
			||||
    menu_redraw = TRUE; | 
				
			||||
//    memcpy_P(&menu_row, &menu_current_p->first_line, sizeof(menu_row));
 | 
				
			||||
    menu_row = 0; | 
				
			||||
    menu_editing = FALSE; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void menu_init(void) | 
				
			||||
{ | 
				
			||||
    /* Make sure set_menu does its work */ | 
				
			||||
    menu_current = MENU_MAIN+1; | 
				
			||||
    set_menu(MENU_MAIN); | 
				
			||||
    display_init(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#define BUTTON_UP 1 | 
				
			||||
#define BUTTON_DOWN 2 | 
				
			||||
#define BUTTON_LEFT 4 | 
				
			||||
#define BUTTON_RIGHT 3 | 
				
			||||
 | 
				
			||||
static void menu_end_edit(void) | 
				
			||||
{ | 
				
			||||
    menu_t *menu = menu_current_p; | 
				
			||||
    menu_editing = FALSE; | 
				
			||||
    if (menu->put_field) | 
				
			||||
	menu->put_field(menu_row); | 
				
			||||
    menu_redraw = TRUE; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_edit_new_field(uint8_t field, bool left) | 
				
			||||
{ | 
				
			||||
    field = find_editable_field(field, left); | 
				
			||||
    menu_editing_field = field; | 
				
			||||
    menu_editing = TRUE; | 
				
			||||
    edit_dirty = FALSE; | 
				
			||||
    if (field == 255) | 
				
			||||
	menu_end_edit(); | 
				
			||||
    menu_draw_row(menu_row); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void increment_value(int8_t inc) | 
				
			||||
{ | 
				
			||||
    uint8_t val = field_values[menu_editing_field]; | 
				
			||||
    uint8_t c = menu_getchar(menu_editing_field); | 
				
			||||
 | 
				
			||||
    uint8_t min = 0; | 
				
			||||
    uint8_t max = field_display_entries(c) - 1; | 
				
			||||
 | 
				
			||||
    if (field_is_text(c)) { | 
				
			||||
	min += 32; | 
				
			||||
	max += 32; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (inc < 0 && val == min) | 
				
			||||
	val = max; | 
				
			||||
    else if (inc > 0 && val == max) | 
				
			||||
	val = min; | 
				
			||||
    else | 
				
			||||
	val += inc; | 
				
			||||
 | 
				
			||||
    field_values[menu_editing_field] = val; | 
				
			||||
    edit_dirty = TRUE; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_navigate_up(void) | 
				
			||||
{ | 
				
			||||
    if (menu_row == menu_current_p->first_line) | 
				
			||||
       return; | 
				
			||||
 | 
				
			||||
    menu_row--; | 
				
			||||
    menu_draw_row(menu_row+1); | 
				
			||||
    menu_draw_row(menu_row); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_navigate_down(void) | 
				
			||||
{ | 
				
			||||
    if (menu_row == menu_current_p->last_line) | 
				
			||||
       return; | 
				
			||||
 | 
				
			||||
    menu_row++; | 
				
			||||
    menu_draw_row(menu_row-1); | 
				
			||||
    menu_draw_row(menu_row); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_navigate_into(void) | 
				
			||||
{ | 
				
			||||
    uint8_t menu = menu_current; | 
				
			||||
    switch (menu) { | 
				
			||||
    case MENU_MAIN: | 
				
			||||
	switch (menu_row) { | 
				
			||||
	case 0: | 
				
			||||
	    menu = MENU_SELECT_PROFILE; | 
				
			||||
	    menu_next = MENU_RUN_PROFILE; | 
				
			||||
	    break; | 
				
			||||
	case 1: | 
				
			||||
	    menu = MENU_SELECT_PROFILE; | 
				
			||||
	    menu_next = MENU_EDIT_PROFILE; | 
				
			||||
	    break; | 
				
			||||
	case 2: | 
				
			||||
	    menu = MENU_CONFIGURATION; | 
				
			||||
	    break; | 
				
			||||
	} | 
				
			||||
	break; | 
				
			||||
 | 
				
			||||
    case MENU_SELECT_PROFILE: | 
				
			||||
	menu = menu_next; | 
				
			||||
	profile_select(menu_row); | 
				
			||||
	break; | 
				
			||||
 | 
				
			||||
    case MENU_RUN_PROFILE: | 
				
			||||
	control_start(); | 
				
			||||
	run_status = RUN_STATUS_RUNNING; | 
				
			||||
	break; | 
				
			||||
 | 
				
			||||
    default: | 
				
			||||
	menu_edit_new_field(21, TRUE); | 
				
			||||
	break; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    set_menu(menu); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_navigate_back(void) | 
				
			||||
{ | 
				
			||||
    uint8_t menu = menu_current; | 
				
			||||
    switch (menu) { | 
				
			||||
    case MENU_EDIT_PROFILE: | 
				
			||||
	profile_save(); | 
				
			||||
    case MENU_RUN_PROFILE: | 
				
			||||
	menu = MENU_SELECT_PROFILE; | 
				
			||||
	break; | 
				
			||||
    default: | 
				
			||||
	menu = MENU_MAIN; | 
				
			||||
	break; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    set_menu(menu); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_process_leftright(bool left) | 
				
			||||
{ | 
				
			||||
    int8_t increment = left?(-1):1; | 
				
			||||
 | 
				
			||||
    if (menu_editing) | 
				
			||||
	menu_edit_new_field(menu_editing_field+increment, left); | 
				
			||||
    else { | 
				
			||||
	if (left) | 
				
			||||
	    menu_navigate_back(); | 
				
			||||
	else | 
				
			||||
	    menu_navigate_into(); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void menu_process_updown(bool up) | 
				
			||||
{ | 
				
			||||
    int8_t increment = up?1:(-1); | 
				
			||||
 | 
				
			||||
    if (menu_editing) { | 
				
			||||
	increment_value(increment); | 
				
			||||
	menu_draw_row(menu_row); | 
				
			||||
    } else { | 
				
			||||
	if (up) | 
				
			||||
	    menu_navigate_up(); | 
				
			||||
	else | 
				
			||||
	    menu_navigate_down(); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#define GRAPH_POINTS 128 | 
				
			||||
//#define GRAPH_POINTS 128
 | 
				
			||||
//#define GRAPH_POINTS 12
 | 
				
			||||
#define GRAPH_START_X 0 | 
				
			||||
 | 
				
			||||
#define GRAPH_HEIGHT (GRAPH_PAGES * 8) | 
				
			||||
#define GRAPH_PAGES 7 | 
				
			||||
#define GRAPH_START_Y 1 | 
				
			||||
 | 
				
			||||
//uint8_t graph_desired[GRAPH_POINTS+1];
 | 
				
			||||
//uint8_t graph_actual[GRAPH_POINTS+1];
 | 
				
			||||
uint8_t graph_actual[4]; | 
				
			||||
 | 
				
			||||
static void minmax_mark(uint8_t *min_p, uint8_t *max_p, uint8_t mid) | 
				
			||||
{ | 
				
			||||
    uint8_t min = *min_p; | 
				
			||||
    uint8_t max = *max_p; | 
				
			||||
 | 
				
			||||
    if (mid == 255) { | 
				
			||||
	*min_p = 255; | 
				
			||||
	return; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (min == 255) | 
				
			||||
	min = mid; | 
				
			||||
    if (max == 255) | 
				
			||||
	max = mid; | 
				
			||||
    if (max < min) { | 
				
			||||
	uint8_t tmp = min; | 
				
			||||
	min = max; | 
				
			||||
	max = tmp; | 
				
			||||
    } | 
				
			||||
    *min_p = mid - (mid-min)/2; | 
				
			||||
    *max_p = (max-mid)/2 + mid; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t graph_temp_min, graph_temp_range; | 
				
			||||
uint32_t time_per_unit; | 
				
			||||
uint8_t graph_cursor; | 
				
			||||
 | 
				
			||||
static uint8_t graph_value(temp_t temp); | 
				
			||||
 | 
				
			||||
static uint8_t graph_at(uint8_t col) | 
				
			||||
{ | 
				
			||||
    return graph_value(temperature_at_time(col*time_per_unit)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void graphics_draw_column(uint8_t col) | 
				
			||||
{ | 
				
			||||
    uint8_t data; | 
				
			||||
    uint8_t pr_mid, pr_min, pr_max; | 
				
			||||
    uint8_t act_mid, act_min, act_max; | 
				
			||||
    uint8_t i; | 
				
			||||
    uint8_t page; | 
				
			||||
    uint8_t act_col; | 
				
			||||
 | 
				
			||||
    if (col >= GRAPH_POINTS) | 
				
			||||
	return; | 
				
			||||
 | 
				
			||||
    if (col == graph_cursor) | 
				
			||||
	act_col = 1; | 
				
			||||
    else | 
				
			||||
	act_col = 0; | 
				
			||||
 | 
				
			||||
    pr_mid = graph_at(col); | 
				
			||||
    pr_min = graph_at(col-1); | 
				
			||||
    pr_max = graph_at(col+1); | 
				
			||||
 | 
				
			||||
    minmax_mark(&pr_min, &pr_max, pr_mid); | 
				
			||||
 | 
				
			||||
    act_min = graph_actual[act_col]; | 
				
			||||
    act_mid = graph_actual[act_col+1]; | 
				
			||||
    act_max = graph_actual[act_col+2]; | 
				
			||||
 | 
				
			||||
    minmax_mark(&act_min, &act_max, act_mid); | 
				
			||||
 | 
				
			||||
    if (col == 0) { | 
				
			||||
	pr_min = 0; | 
				
			||||
	pr_max = GRAPH_HEIGHT-1; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    page = GRAPH_PAGES-1; | 
				
			||||
    for (i = 0; i < GRAPH_HEIGHT; i++) { | 
				
			||||
	data <<= 1; | 
				
			||||
	if ((i >= pr_min && i <= pr_max) || | 
				
			||||
	    (i >= act_min && i <= act_max) || | 
				
			||||
	    (i == 0)) { | 
				
			||||
		data |= 1; | 
				
			||||
	} | 
				
			||||
	if ((i & 0x7) == 7) { | 
				
			||||
	    if (graph_cursor == col) | 
				
			||||
		data |= 0x55; | 
				
			||||
	    display_setposition(col, GRAPH_START_Y + page); | 
				
			||||
	    display_data(1, &data); | 
				
			||||
	    page--; | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void graphics_draw_screen(void) | 
				
			||||
{ | 
				
			||||
    int i; | 
				
			||||
 | 
				
			||||
    /* initialise profile */ | 
				
			||||
 | 
				
			||||
    for (i = 0; i < GRAPH_POINTS; i++) | 
				
			||||
	graphics_draw_column(i); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static uint8_t __attribute__ ((noinline)) graph_value(temp_t temp) | 
				
			||||
{ | 
				
			||||
    int32_t v; | 
				
			||||
 | 
				
			||||
    if (temp == INVALID_TEMPERATURE) | 
				
			||||
	v = 255; | 
				
			||||
    else | 
				
			||||
	v = ((int32_t)temp - graph_temp_min) * GRAPH_HEIGHT / graph_temp_range; | 
				
			||||
 | 
				
			||||
    return v; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void graphics_init(void) | 
				
			||||
{ | 
				
			||||
    uint8_t i; | 
				
			||||
    uint32_t total_time; | 
				
			||||
    temp_t temp_min, temp_max; | 
				
			||||
    uint8_t *graph_actual_p; | 
				
			||||
 | 
				
			||||
    run_status = RUN_STATUS_READY; | 
				
			||||
 | 
				
			||||
    temp_min = temp_max = get_profile_temperature(1); | 
				
			||||
 | 
				
			||||
    total_time = 0; | 
				
			||||
 | 
				
			||||
    for (i = 0; i < 6; i++) { | 
				
			||||
	temp_t temp = get_profile_temperature(i+2); | 
				
			||||
	uint32_t time = profile_time(i); | 
				
			||||
	if (time == 0) | 
				
			||||
	    continue; | 
				
			||||
	total_time += time; | 
				
			||||
	if (temp > temp_max) | 
				
			||||
	    temp_max = temp; | 
				
			||||
	if (temp < temp_min) | 
				
			||||
	    temp_min = temp; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    time_per_unit = (total_time + GRAPH_POINTS - 1) / GRAPH_POINTS; | 
				
			||||
 | 
				
			||||
    temp_min -= 10 * 10; | 
				
			||||
    temp_max += 10 * 10; | 
				
			||||
    graph_temp_range = temp_max - temp_min; | 
				
			||||
    graph_temp_min = temp_min; | 
				
			||||
 | 
				
			||||
    graph_cursor = 255; | 
				
			||||
 | 
				
			||||
    graph_actual_p = &graph_actual[0]; | 
				
			||||
 | 
				
			||||
    for (i = 0; i < 4; i++) | 
				
			||||
	*(graph_actual_p++) = 255; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void graphics_poll(void) | 
				
			||||
{ | 
				
			||||
    if (menu_redraw) { | 
				
			||||
	graphics_init(); | 
				
			||||
	graphics_draw_screen(); | 
				
			||||
    } | 
				
			||||
    if (run_status == RUN_STATUS_RUNNING) { | 
				
			||||
	uint8_t col = control_now() / time_per_unit; | 
				
			||||
	if (col <= GRAPH_POINTS) { | 
				
			||||
	    if (col != graph_cursor) { | 
				
			||||
		graph_actual[0] = graph_actual[1]; | 
				
			||||
		graph_actual[1] = graph_actual[2]; | 
				
			||||
	    } | 
				
			||||
	    graph_actual[2] = graph_value(therm_temp()); | 
				
			||||
	} else { | 
				
			||||
	    run_status = RUN_STATUS_FINISHED; | 
				
			||||
	} | 
				
			||||
	graph_cursor = col; | 
				
			||||
	graphics_draw_column(col-1); | 
				
			||||
	graphics_draw_column(col); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void menu_poll(void) | 
				
			||||
{ | 
				
			||||
    uint8_t button_pressed; | 
				
			||||
    
 | 
				
			||||
    ATOMIC_BLOCK(ATOMIC_FORCEON) { | 
				
			||||
	button_pressed = button; | 
				
			||||
	button = 0; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (button_pressed == BUTTON_LEFT || button_pressed == BUTTON_RIGHT) | 
				
			||||
	menu_process_leftright(button_pressed == BUTTON_LEFT); | 
				
			||||
    if (button_pressed == BUTTON_UP || button_pressed == BUTTON_DOWN) | 
				
			||||
	menu_process_updown(button_pressed == BUTTON_UP); | 
				
			||||
 | 
				
			||||
    if (menu_redraw || (menu_current == MENU_RUN_PROFILE) || (menu_current == MENU_MAIN)) { | 
				
			||||
	display_menu(menu_redraw); | 
				
			||||
	menu_set_fields(menu_row); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (menu_current == MENU_RUN_PROFILE) { | 
				
			||||
	graphics_poll(); | 
				
			||||
    } | 
				
			||||
    menu_redraw = FALSE; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,30 @@
					 | 
				
			||||
/* menu.h */ | 
				
			||||
 | 
				
			||||
#ifndef _MENU_H_ | 
				
			||||
#define _MENU_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void menu_init(void); | 
				
			||||
void menu_poll(void); | 
				
			||||
void menu_new_data(void); | 
				
			||||
 | 
				
			||||
/* This stuff is for fields.c only, for now */ | 
				
			||||
#include "fields.h" | 
				
			||||
extern field_t fields[]; | 
				
			||||
char menu_getchar(uint8_t col); | 
				
			||||
typedef void (*field_handler)(uint8_t row); | 
				
			||||
typedef struct menu { | 
				
			||||
    PGM_P const *text; | 
				
			||||
    uint8_t first_line; | 
				
			||||
    uint8_t last_line; | 
				
			||||
    field_handler get_field; | 
				
			||||
    field_handler put_field; | 
				
			||||
} menu_t; | 
				
			||||
extern menu_t *menu_current_p; | 
				
			||||
int32_t temperature_from_kelvin(temp_t source); | 
				
			||||
temp_t temperature_to_kelvin(int32_t source); | 
				
			||||
extern uint8_t field_row; | 
				
			||||
extern uint8_t current_profile; | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,130 @@
					 | 
				
			||||
/* profile.c */ | 
				
			||||
 | 
				
			||||
#include <stddef.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/pgmspace.h> | 
				
			||||
#include <util/atomic.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "profile.h" | 
				
			||||
#include "menu.h" | 
				
			||||
#include "config.h" | 
				
			||||
#include "fields.h" | 
				
			||||
#include "therm.h" | 
				
			||||
 | 
				
			||||
const profile_t profiles[2] PROGMEM = { | 
				
			||||
//  { "123456789012345678901", 250,
 | 
				
			||||
//    { "Lead free solder     ", 250 + 2732, {
 | 
				
			||||
    { "NoPb  ", 250 + 2732, { | 
				
			||||
        { 1500 + 2732, 100, 0 }, | 
				
			||||
        { 2000 + 2732, 100, 0 }, | 
				
			||||
        { 2500 + 2732,  40, 0 }, | 
				
			||||
        { 2500 + 2732,  30, 0 }, | 
				
			||||
        { 1000 + 2732,  30, 0 }, | 
				
			||||
        {    0 + 2732,   0, 0 } | 
				
			||||
    } }, | 
				
			||||
//    { "Leaded solder        ", 250 + 2732, {
 | 
				
			||||
    { "Pb    ", 250 + 2732, { | 
				
			||||
        { 1000 + 2732, 100, 0 }, | 
				
			||||
        { 1500 + 2732, 100, 0 }, | 
				
			||||
        { 2350 + 2732,  40, 0 }, | 
				
			||||
        { 2350 + 2732,  30, 0 }, | 
				
			||||
        { 1000 + 2732,  30, 0 }, | 
				
			||||
        {    0 + 2732,   0, 0 } | 
				
			||||
    } } | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
profile_t profile; | 
				
			||||
profile_t *profile_p = &profile; | 
				
			||||
uint8_t current_profile; | 
				
			||||
 | 
				
			||||
void profile_select(uint8_t n) | 
				
			||||
{ | 
				
			||||
    current_profile = n; | 
				
			||||
    memcpy_P(&profile, &profiles[n], sizeof(profile_t)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void profile_save(void) | 
				
			||||
{ | 
				
			||||
//    memcpy(&profiles[current_profile], profile_p, sizeof(profile_t));
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
#if 0 | 
				
			||||
static void select_profile_fields(uint8_t row) | 
				
			||||
{ | 
				
			||||
    memcpy(field_values, profiles[row].name, PROFILE_NAME_LENGTH); | 
				
			||||
} | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
void set_profile_temperature(temp_t temp) | 
				
			||||
{ | 
				
			||||
    uint8_t row = field_row; | 
				
			||||
 | 
				
			||||
    if (row > 1) | 
				
			||||
	profile_p->lines[row-2].temperature = temp; | 
				
			||||
    else | 
				
			||||
	profile_p->start_temp = temp; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t get_profile_temperature(uint8_t row) | 
				
			||||
{ | 
				
			||||
    if (row > 1) | 
				
			||||
	return profile_p->lines[row-2].temperature; | 
				
			||||
    else | 
				
			||||
	return profile_p->start_temp; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void set_profile_time(uint8_t row, uint16_t time, uint8_t time_units) | 
				
			||||
{ | 
				
			||||
    profile_p->lines[row].time = time; | 
				
			||||
    profile_p->lines[row].time_units = time_units; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint16_t profile_get_time(uint8_t line) | 
				
			||||
{ | 
				
			||||
    return profile_p->lines[line].time; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t profile_get_time_units(uint8_t row) | 
				
			||||
{ | 
				
			||||
    return profile_p->lines[row].time_units; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint32_t profile_time(uint8_t line) | 
				
			||||
{ | 
				
			||||
    uint32_t time = profile_get_time(line); | 
				
			||||
    uint8_t time_units = profile_get_time_units(line); | 
				
			||||
    while (time_units--) | 
				
			||||
	time = time * 60; | 
				
			||||
 | 
				
			||||
    return time; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t temperature_at_time(uint32_t time) { | 
				
			||||
    uint32_t time0, time1; | 
				
			||||
    temp_t temp0, temp1; | 
				
			||||
    uint8_t i; | 
				
			||||
 | 
				
			||||
    temp1 = get_profile_temperature(1); | 
				
			||||
    time1 = 0; | 
				
			||||
 | 
				
			||||
    for (i = 0; i < 6; i++) { | 
				
			||||
	uint32_t ptime; | 
				
			||||
	temp0 = temp1; | 
				
			||||
	time0 = time1; | 
				
			||||
	ptime = profile_time(i); | 
				
			||||
	if (ptime > 0) { | 
				
			||||
	    temp1 = get_profile_temperature(2+i); | 
				
			||||
	    time1 += ptime; | 
				
			||||
	} | 
				
			||||
	if (time <= time1) | 
				
			||||
	    break; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    if (time > time1) | 
				
			||||
	return INVALID_TEMPERATURE; | 
				
			||||
 | 
				
			||||
    return temp0 + ((int32_t)temp1 - (int32_t)temp0) * (int32_t)(time - time0) / (int32_t)(time1 - time0); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,34 @@
					 | 
				
			||||
/* profile.h */ | 
				
			||||
 | 
				
			||||
#ifndef _PROFILE_H_ | 
				
			||||
#define _PROFILE_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void set_profile_temperature(temp_t temp); | 
				
			||||
temp_t get_profile_temperature(uint8_t row); | 
				
			||||
uint32_t profile_time(uint8_t line); | 
				
			||||
temp_t temperature_at_time(uint32_t time); | 
				
			||||
void set_profile_time(uint8_t row, uint16_t time, uint8_t time_units); | 
				
			||||
uint16_t profile_get_time(uint8_t row); | 
				
			||||
uint8_t profile_get_time_units(uint8_t row); | 
				
			||||
void profile_select(uint8_t n); | 
				
			||||
void profile_save(void); | 
				
			||||
 | 
				
			||||
typedef struct profile_line { | 
				
			||||
    uint16_t temperature; /* store degrees C * 10 reference to absolute zero */ | 
				
			||||
    unsigned int time:14; | 
				
			||||
    unsigned int time_units:2; | 
				
			||||
} profile_line_t; | 
				
			||||
 | 
				
			||||
#define PROFILE_NAME_LENGTH 6 | 
				
			||||
 | 
				
			||||
typedef struct profile { | 
				
			||||
    char name[PROFILE_NAME_LENGTH]; | 
				
			||||
    uint16_t start_temp; | 
				
			||||
    profile_line_t lines[6]; | 
				
			||||
} profile_t; | 
				
			||||
 | 
				
			||||
extern profile_t *profile_p; | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,36 @@
					 | 
				
			||||
/* reflow.c */ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "therm.h" | 
				
			||||
#include "display.h" | 
				
			||||
#include "menu.h" | 
				
			||||
#include "beep.h" | 
				
			||||
#include "timer.h" | 
				
			||||
#include "control.h" | 
				
			||||
 | 
				
			||||
/*
 | 
				
			||||
 * Reflow controller firmware | 
				
			||||
 * coolfactor.org | 
				
			||||
 */ | 
				
			||||
 | 
				
			||||
int main(void) | 
				
			||||
{ | 
				
			||||
    uint32_t last_seconds = 0; | 
				
			||||
    uint32_t this_seconds; | 
				
			||||
    /* init code here */ | 
				
			||||
 | 
				
			||||
    therm_init(); | 
				
			||||
    timer_init(); | 
				
			||||
    menu_init(); | 
				
			||||
 | 
				
			||||
    while (1) { | 
				
			||||
	therm_read(); | 
				
			||||
//	menu_new_data();
 | 
				
			||||
	this_seconds = seconds; | 
				
			||||
	if (this_seconds != last_seconds) { | 
				
			||||
	    control_poll(); | 
				
			||||
	    last_seconds = this_seconds; | 
				
			||||
	} | 
				
			||||
      menu_poll(); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,54 @@
					 | 
				
			||||
/* spi.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
#define DDR_SPI DDRB | 
				
			||||
#define PORT_SPI PORTB | 
				
			||||
 | 
				
			||||
#define PIN_MOSI 3 | 
				
			||||
#define PIN_SCK 5 | 
				
			||||
#define PIN_SS 2 | 
				
			||||
 | 
				
			||||
void spi_init(void) | 
				
			||||
{ | 
				
			||||
    /* Set MOSI, SCK and SS output */ | 
				
			||||
    PORT_SPI |= (1<<PIN_SS); | 
				
			||||
    DDR_SPI |= (1<<PIN_MOSI) | (1<<PIN_SCK) | (1<<PIN_SS); | 
				
			||||
 | 
				
			||||
    /* Enable SPI, Master, set clock rate fck/16 */ | 
				
			||||
//    SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
 | 
				
			||||
 | 
				
			||||
//    SPCR = (1<<SPE) | (1<<MSTR) | /*(1<<SPR1) |*/ (1<<SPR0) | (1<<CPHA) | (1<<CPOL);
 | 
				
			||||
    SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0) | (1<<CPHA) | (1<<CPOL); | 
				
			||||
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void spi_start(void) | 
				
			||||
{ | 
				
			||||
    PORT_SPI &= ~(1<<PIN_SS); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void spi_stop(void) | 
				
			||||
{ | 
				
			||||
    PORT_SPI |= (1<<PIN_SS); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
char spi_txrxbyte(char data) | 
				
			||||
{ | 
				
			||||
    /* Start transmission */ | 
				
			||||
    SPDR = data; | 
				
			||||
 | 
				
			||||
    /* Wait for transmission complete */ | 
				
			||||
    while (!(SPSR & (1<<SPIF))) | 
				
			||||
	; | 
				
			||||
 | 
				
			||||
    /* Return byte received */ | 
				
			||||
    return SPDR; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
char spi_rxbyte(void) | 
				
			||||
{ | 
				
			||||
    return spi_txrxbyte(0); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,15 @@
					 | 
				
			||||
/* spi.h */ | 
				
			||||
 | 
				
			||||
#ifndef _SPI_H_ | 
				
			||||
#define _SPI_H_ | 
				
			||||
 | 
				
			||||
void spi_init(void); | 
				
			||||
char spi_txrxbyte(char data); | 
				
			||||
char spi_rxbyte(void); | 
				
			||||
void spi_start(void); | 
				
			||||
void spi_stop(void); | 
				
			||||
 | 
				
			||||
#define spi_txbyte(data) (void)spi_txrxbyte(data) | 
				
			||||
//#define spi_rxbyte() spi_txrxbyte(0)
 | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,23 @@
					 | 
				
			||||
# Makefile
 | 
				
			||||
 | 
				
			||||
VPATH = ..
 | 
				
			||||
 | 
				
			||||
OBJECTS.test-menu     = test-menu.o test-display.o menu.o test-therm.o config.o
 | 
				
			||||
OBJECTS.test-graphics = test-graphics.o menu.o test-i2c.o display.o test-therm.o config.o font.o fields.o profile.o control.o
 | 
				
			||||
 | 
				
			||||
CFLAGS = -Wall -Werror -Os -I. -I..
 | 
				
			||||
LDLIBS = -lncurses
 | 
				
			||||
 | 
				
			||||
CFLAGS+=`pkg-config --cflags sdl SDL_gfx`
 | 
				
			||||
LDLIBS+=`pkg-config --libs sdl SDL_gfx`
 | 
				
			||||
LDLIBS+=-lm
 | 
				
			||||
 | 
				
			||||
#all:	test-menu test-graphics
 | 
				
			||||
all:	test-graphics | 
				
			||||
 | 
				
			||||
test-menu: $(OBJECTS.test-menu) | 
				
			||||
 | 
				
			||||
test-graphics: $(OBJECTS.test-graphics) | 
				
			||||
 | 
				
			||||
clean: | 
				
			||||
	rm -f test-menu $(OBJECTS.test-menu) $(OBJECTS.test-graphics)
 | 
				
			||||
@ -0,0 +1,13 @@
					 | 
				
			||||
#ifndef _PGMSPACE_H_ | 
				
			||||
#define _PGMSPACE_H_ | 
				
			||||
 | 
				
			||||
#include <string.h> | 
				
			||||
 | 
				
			||||
#define PROGMEM | 
				
			||||
#define PGM_P const char * | 
				
			||||
#define pgm_read_byte(x) (*(char *)(x)) | 
				
			||||
#define pgm_read_ptr(x) (*(char **)(x)) | 
				
			||||
#define strlen_P strlen | 
				
			||||
#define memcpy_P memcpy | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,68 @@
					 | 
				
			||||
/* test-display.c */ | 
				
			||||
 | 
				
			||||
#include <stdlib.h> | 
				
			||||
#include <ncurses.h> | 
				
			||||
 | 
				
			||||
#include "types.h" | 
				
			||||
 | 
				
			||||
unsigned int curpos; | 
				
			||||
 | 
				
			||||
void display_cleanup(void) | 
				
			||||
{ | 
				
			||||
    endwin(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_data(uint8_t len, uint8_t *data) | 
				
			||||
{ | 
				
			||||
    /* can't do anything with this graphics */ | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_init(void) | 
				
			||||
{ | 
				
			||||
    initscr(); | 
				
			||||
    noecho(); | 
				
			||||
    atexit(display_cleanup); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_setposition(char col, char row) | 
				
			||||
{ | 
				
			||||
    move(row, col/6); | 
				
			||||
    curpos = col/6; | 
				
			||||
    //printf("Position set to row %d, col %d\n", row, col/6);
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_setinverse(bool on) | 
				
			||||
{ | 
				
			||||
    if (on) | 
				
			||||
	attron(A_REVERSE); | 
				
			||||
    else | 
				
			||||
	attroff(A_REVERSE); | 
				
			||||
    //printf("Inverse set to %s\n", on?"on":"off");
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putchar(char c) | 
				
			||||
{ | 
				
			||||
    printw("%c", c); | 
				
			||||
    refresh(); | 
				
			||||
    curpos++; | 
				
			||||
    //printf("Put character %c\n", c);
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putstr(char *s) | 
				
			||||
{ | 
				
			||||
    while (*s) | 
				
			||||
	display_putchar(*(s++)); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_putstr_P(const char *s) | 
				
			||||
{ | 
				
			||||
    display_putstr((char *)s); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void display_clearline(void) | 
				
			||||
{ | 
				
			||||
    while (curpos < 21) { | 
				
			||||
	display_putchar(' '); | 
				
			||||
    } | 
				
			||||
    //printf("Cleared line\n");
 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,70 @@
					 | 
				
			||||
/* test-graphics.c */ | 
				
			||||
 | 
				
			||||
#include "SDL.h" | 
				
			||||
#include "SDL_gfxPrimitives.h" | 
				
			||||
#include "menu.h" | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
SDL_Surface *screen; | 
				
			||||
 | 
				
			||||
SDL_Event event; | 
				
			||||
 | 
				
			||||
#define WHITE 0xffffffff | 
				
			||||
 | 
				
			||||
int button = 0; | 
				
			||||
 | 
				
			||||
uint32_t seconds; | 
				
			||||
uint8_t output0; | 
				
			||||
 | 
				
			||||
void beep_off(void) | 
				
			||||
{ | 
				
			||||
} | 
				
			||||
 | 
				
			||||
int main(int argc, char *argv[]) { | 
				
			||||
    SDL_Init(SDL_INIT_VIDEO); | 
				
			||||
 | 
				
			||||
    screen = SDL_SetVideoMode(128, 64, 16, SDL_SWSURFACE); | 
				
			||||
 | 
				
			||||
    SDL_WM_SetCaption("Reflow oven controller", "Reflow oven controller"); | 
				
			||||
 | 
				
			||||
    bool done=FALSE; | 
				
			||||
 | 
				
			||||
    menu_init(); | 
				
			||||
 | 
				
			||||
    while(!done) { | 
				
			||||
	while(SDL_PollEvent(&event)) { | 
				
			||||
	    if (event.type == SDL_QUIT) { | 
				
			||||
		done=TRUE; | 
				
			||||
	    } | 
				
			||||
	    if (event.type == SDL_KEYDOWN) { | 
				
			||||
                switch(event.key.keysym.sym) { | 
				
			||||
		case SDLK_UP: | 
				
			||||
		    button = 1; | 
				
			||||
		    break; | 
				
			||||
		case SDLK_DOWN: | 
				
			||||
		    button = 2; | 
				
			||||
		    break; | 
				
			||||
		case SDLK_LEFT: | 
				
			||||
		    button = 4; | 
				
			||||
		    break; | 
				
			||||
		case SDLK_RIGHT: | 
				
			||||
		    button = 3; | 
				
			||||
		    break; | 
				
			||||
		case SDLK_q: | 
				
			||||
		    done = TRUE; | 
				
			||||
		    break; | 
				
			||||
		default: | 
				
			||||
		    break; | 
				
			||||
		} | 
				
			||||
	    } | 
				
			||||
	    menu_poll(); | 
				
			||||
	    SDL_Flip(screen); | 
				
			||||
	} | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    SDL_Quit(); | 
				
			||||
 | 
				
			||||
    return 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,62 @@
					 | 
				
			||||
/* i2c.c */ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "SDL.h" | 
				
			||||
#include "SDL_gfxPrimitives.h" | 
				
			||||
 | 
				
			||||
 | 
				
			||||
#define WHITE 0xffffffff | 
				
			||||
#define BLACK 0x000000ff | 
				
			||||
 | 
				
			||||
uint8_t i2c_xor; | 
				
			||||
uint8_t page; | 
				
			||||
uint8_t col; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
extern SDL_Surface *screen; | 
				
			||||
 | 
				
			||||
void i2c_init(void) | 
				
			||||
{ | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void i2c_set_xor(char xor) | 
				
			||||
{ | 
				
			||||
    i2c_xor = xor; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
bool i2c_write(char address, char prefix, char bytes, char *data) | 
				
			||||
{ | 
				
			||||
    uint8_t i, j; | 
				
			||||
 | 
				
			||||
    switch ((uint8_t) prefix) { | 
				
			||||
    case 0x80: | 
				
			||||
	switch (data[0] & 0xf0) { | 
				
			||||
	case 0xb0: | 
				
			||||
	    page = data[0] & 0xf; | 
				
			||||
	    break; | 
				
			||||
	case 0x00: | 
				
			||||
	    col = (col & ~0xf) + (data[0] & 0xf); | 
				
			||||
	    break; | 
				
			||||
	case 0x10: | 
				
			||||
	    col = (col & 0xf) + ((data[0] & 0xf) << 4); | 
				
			||||
	    break; | 
				
			||||
	} | 
				
			||||
	break; | 
				
			||||
    case 0x40: | 
				
			||||
	for (i = 0; i < bytes; i++) { | 
				
			||||
	    uint8_t byte = data[i] ^ i2c_xor; | 
				
			||||
	    for (j = 0; j < 8; j++) { | 
				
			||||
		pixelColor(screen, col, page*8 + j, (byte & 1)?WHITE:BLACK); | 
				
			||||
		byte = byte >> 1; | 
				
			||||
	    } | 
				
			||||
	    col++; | 
				
			||||
	    if (col > 127) | 
				
			||||
		col = 0; | 
				
			||||
	} | 
				
			||||
	break; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    return TRUE; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,46 @@
					 | 
				
			||||
/* test-menu.c */ | 
				
			||||
 | 
				
			||||
#include <stdio.h> | 
				
			||||
#include <stdlib.h> | 
				
			||||
#include <ncurses.h> | 
				
			||||
#include "display.h" | 
				
			||||
#include "menu.h" | 
				
			||||
 | 
				
			||||
int button = 0; | 
				
			||||
 | 
				
			||||
int main(void) | 
				
			||||
{ | 
				
			||||
/*
 | 
				
			||||
    display_init(); | 
				
			||||
    display_putstr("Hello, world!"); | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
    menu_init(); | 
				
			||||
    keypad(stdscr, TRUE); | 
				
			||||
    menu_poll(); | 
				
			||||
 | 
				
			||||
    while (1) { | 
				
			||||
	int c = getch(); | 
				
			||||
	switch (c) { | 
				
			||||
	case KEY_UP: | 
				
			||||
	    button = 1; | 
				
			||||
	    break; | 
				
			||||
	case KEY_DOWN: | 
				
			||||
	    button = 2; | 
				
			||||
	    break; | 
				
			||||
	case KEY_LEFT: | 
				
			||||
	    button = 3; | 
				
			||||
	    break; | 
				
			||||
	case KEY_RIGHT: | 
				
			||||
	    button = 4; | 
				
			||||
	    break; | 
				
			||||
	case 'q': | 
				
			||||
	case 'Q': | 
				
			||||
	    exit(0); | 
				
			||||
	} | 
				
			||||
	menu_poll(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    return 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,49 @@
					 | 
				
			||||
/* test-therm.c */ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "therm.h" | 
				
			||||
 | 
				
			||||
int32_t extend24(int32_t n) | 
				
			||||
{ | 
				
			||||
    if (n & 0x00800000) { | 
				
			||||
	return n | (~0 & 0xff000000); | 
				
			||||
    } | 
				
			||||
    return n; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_temp(void) | 
				
			||||
{ | 
				
			||||
//    return extend24(0xf06000);
 | 
				
			||||
    return 3932; | 
				
			||||
#if 0 | 
				
			||||
    return 0x078000; | 
				
			||||
    return 0x064f00; | 
				
			||||
    return 0x52d000; | 
				
			||||
#endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_coldtemp(void) | 
				
			||||
{ | 
				
			||||
    return 2987; | 
				
			||||
#if 0 | 
				
			||||
    return 0x19800; | 
				
			||||
#endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t therm_fault(void) | 
				
			||||
{ | 
				
			||||
    return 0; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_reduce(int32_t temp) | 
				
			||||
{ | 
				
			||||
    temp = (temp * 10) / 4096; | 
				
			||||
    temp += 2732; | 
				
			||||
    if (temp < 0) | 
				
			||||
        temp = 0; | 
				
			||||
    if (temp > 65535) | 
				
			||||
        temp = 65535; | 
				
			||||
 | 
				
			||||
    return (temp_t) temp; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,23 @@
					 | 
				
			||||
/* types.h */ | 
				
			||||
 | 
				
			||||
#ifndef _TYPES_H_ | 
				
			||||
#define _TYPES_H_ | 
				
			||||
 | 
				
			||||
/* Probably right for a PC */ | 
				
			||||
 | 
				
			||||
typedef unsigned char uint8; | 
				
			||||
typedef signed   char int8; | 
				
			||||
 | 
				
			||||
typedef unsigned short uint16; | 
				
			||||
typedef signed   short int16; | 
				
			||||
 | 
				
			||||
typedef unsigned int uint32; | 
				
			||||
typedef signed   int int32; | 
				
			||||
 | 
				
			||||
#ifndef TRUE | 
				
			||||
typedef unsigned char bool; | 
				
			||||
#define TRUE 1 | 
				
			||||
#define FALSE 0 | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,68 @@
					 | 
				
			||||
/* therm.c */ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "therm.h" | 
				
			||||
 | 
				
			||||
#include "spi.h" | 
				
			||||
 | 
				
			||||
uint8_t therm_cjth; | 
				
			||||
uint8_t therm_cjtl; | 
				
			||||
uint8_t therm_ltcbh; | 
				
			||||
uint8_t therm_ltcbm; | 
				
			||||
uint8_t therm_ltcbl; | 
				
			||||
uint8_t therm_sr; | 
				
			||||
 | 
				
			||||
void therm_init(void) | 
				
			||||
{ | 
				
			||||
    spi_init(); | 
				
			||||
 | 
				
			||||
    spi_start(); | 
				
			||||
    spi_txbyte(0x80); | 
				
			||||
    spi_txbyte(0x91); /* Automatic conversion, open detection on, 50Hz */ | 
				
			||||
    spi_txbyte(0x03); /* No averaging, K-type */ | 
				
			||||
    spi_txbyte(0x00); /* No masking. ~FAULT asserted for most faults. */ | 
				
			||||
    spi_stop(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void therm_read(void) | 
				
			||||
{ | 
				
			||||
    spi_start(); | 
				
			||||
    spi_txbyte(0x0a); | 
				
			||||
    therm_cjth  = spi_rxbyte(); | 
				
			||||
    therm_cjtl  = spi_rxbyte(); | 
				
			||||
    therm_ltcbh = spi_rxbyte(); | 
				
			||||
    therm_ltcbm = spi_rxbyte(); | 
				
			||||
    therm_ltcbl = spi_rxbyte(); | 
				
			||||
    therm_sr    = spi_rxbyte(); | 
				
			||||
    spi_stop(); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_reduce(int32_t temp) | 
				
			||||
{ | 
				
			||||
    temp = (temp * 10) / 4096; | 
				
			||||
    temp += 2732; | 
				
			||||
    if (temp < 0) | 
				
			||||
        temp = 0; | 
				
			||||
    if (temp > 65535) | 
				
			||||
        temp = 65535; | 
				
			||||
 | 
				
			||||
    return (temp_t) temp; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_coldtemp(void) | 
				
			||||
{ | 
				
			||||
    int16_t temp = (int16_t) (((int16_t)therm_cjth << 8) | therm_cjtl); | 
				
			||||
    return therm_reduce((int32_t)temp * 16); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
temp_t therm_temp(void) | 
				
			||||
{ | 
				
			||||
    int32_t temp = ((int32_t)therm_ltcbh << 16) | ((int32_t)therm_ltcbm << 8) | therm_ltcbl; | 
				
			||||
 | 
				
			||||
    return therm_reduce(temp); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
uint8_t therm_fault(void) | 
				
			||||
{ | 
				
			||||
    return therm_sr; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,17 @@
					 | 
				
			||||
/* therm.h */ | 
				
			||||
 | 
				
			||||
#ifndef _THERM_H_ | 
				
			||||
#define _THERM_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
void therm_init(void); | 
				
			||||
void therm_read(void); | 
				
			||||
temp_t therm_coldtemp(void); | 
				
			||||
temp_t therm_temp(void); | 
				
			||||
uint8_t therm_fault(void); | 
				
			||||
temp_t therm_reduce(int32_t); | 
				
			||||
 | 
				
			||||
#define INVALID_TEMPERATURE 0xffff | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,87 @@
					 | 
				
			||||
/* timer.c */ | 
				
			||||
 | 
				
			||||
#include <avr/io.h> | 
				
			||||
#include <avr/interrupt.h> | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
#include "timer.h" | 
				
			||||
 | 
				
			||||
uint8_t bounce_timer; | 
				
			||||
uint8_t button; | 
				
			||||
uint8_t button_port; | 
				
			||||
 | 
				
			||||
#define BOUNCE_TIMER_RESET 10 | 
				
			||||
 | 
				
			||||
#define PIN_OUTPUT0 0 | 
				
			||||
#define PIN_OUTPUT1 1 | 
				
			||||
 | 
				
			||||
volatile uint8_t output0; | 
				
			||||
volatile uint8_t output1; | 
				
			||||
uint8_t tick; | 
				
			||||
volatile uint32_t seconds; | 
				
			||||
 | 
				
			||||
void timer_init(void) | 
				
			||||
{ | 
				
			||||
    bounce_timer = 0; | 
				
			||||
    button = 0; | 
				
			||||
    button_port = 0; | 
				
			||||
    tick = TICKS_PER_SECOND-1; | 
				
			||||
    seconds = 0; | 
				
			||||
 | 
				
			||||
    TCCR0A = (1<<CTC0) | (1<<CS02); /* clk/256 */ | 
				
			||||
    OCR0A = TIMER_MAX; | 
				
			||||
    TIMSK0 = (1<<OCIE0A); | 
				
			||||
 | 
				
			||||
    output0 = 0; | 
				
			||||
    output1 = 0; | 
				
			||||
 | 
				
			||||
    PORTD &= ~((1<<PIN_OUTPUT0) | (1<<PIN_OUTPUT1)); | 
				
			||||
    DDRD |= (1<<PIN_OUTPUT0) | (1<<PIN_OUTPUT1); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
ISR(TIMER0_COMPA_vect) | 
				
			||||
{ | 
				
			||||
    uint8_t port; | 
				
			||||
    uint8_t old_button; | 
				
			||||
    uint8_t button_pressed; | 
				
			||||
 | 
				
			||||
    if (tick == 0) { | 
				
			||||
	tick = TICKS_PER_SECOND-1; | 
				
			||||
	seconds++; | 
				
			||||
    } else | 
				
			||||
	tick--; | 
				
			||||
 | 
				
			||||
    old_button = button_port; | 
				
			||||
 | 
				
			||||
    port = PIND; | 
				
			||||
    port = (~port >> 4); | 
				
			||||
/*
 | 
				
			||||
    if (bounce_timer) | 
				
			||||
	bounce_timer--; | 
				
			||||
    else | 
				
			||||
*/ | 
				
			||||
	button_port = port; | 
				
			||||
 | 
				
			||||
    if (button_port != old_button) | 
				
			||||
	bounce_timer = BOUNCE_TIMER_RESET; | 
				
			||||
 | 
				
			||||
    button_pressed = button_port & ~old_button; | 
				
			||||
    if (button_pressed & 8) | 
				
			||||
	button = 4; | 
				
			||||
    if (button_pressed & 4) | 
				
			||||
	button = 3; | 
				
			||||
    if (button_pressed & 2) | 
				
			||||
	button = 2; | 
				
			||||
    if (button_pressed & 1) | 
				
			||||
	button = 1; | 
				
			||||
 | 
				
			||||
    if ((TICKS_PER_SECOND-1 - tick) < output0) | 
				
			||||
	PORTD |= (1<<PIN_OUTPUT0); | 
				
			||||
    else | 
				
			||||
	PORTD &= ~(1<<PIN_OUTPUT0); | 
				
			||||
 | 
				
			||||
    if ((TICKS_PER_SECOND-1 - tick) < output1) | 
				
			||||
	PORTD |= (1<<PIN_OUTPUT1); | 
				
			||||
    else | 
				
			||||
	PORTD &= ~(1<<PIN_OUTPUT1); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,24 @@
					 | 
				
			||||
/* timer.h */ | 
				
			||||
 | 
				
			||||
#ifndef _TIMER_H_ | 
				
			||||
#define _TIMER_H_ | 
				
			||||
 | 
				
			||||
#include "common.h" | 
				
			||||
 | 
				
			||||
#define CLOCK_FREQ 4000000 | 
				
			||||
#define PRESCALER 256 | 
				
			||||
#define TIMER_MAX 125 | 
				
			||||
 | 
				
			||||
/* Careful to make this an integer */ | 
				
			||||
#define TICKS_PER_SECOND (CLOCK_FREQ / (TIMER_MAX * PRESCALER)) | 
				
			||||
 | 
				
			||||
extern uint8_t button; | 
				
			||||
 | 
				
			||||
extern volatile uint8_t output0; | 
				
			||||
extern volatile uint8_t output1; | 
				
			||||
 | 
				
			||||
extern volatile uint32_t seconds; | 
				
			||||
 | 
				
			||||
void timer_init(void); | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
@ -0,0 +1,25 @@
					 | 
				
			||||
/* types.h */ | 
				
			||||
 | 
				
			||||
#ifndef _TYPES_H_ | 
				
			||||
#define _TYPES_H_ | 
				
			||||
 | 
				
			||||
/* These are correct for AVR. Change accordingly if you port. */ | 
				
			||||
 | 
				
			||||
typedef unsigned char uint8; | 
				
			||||
typedef signed   char int8; | 
				
			||||
 | 
				
			||||
typedef unsigned int uint16; | 
				
			||||
typedef signed   int int16; | 
				
			||||
 | 
				
			||||
typedef unsigned long uint32; | 
				
			||||
typedef signed   long int32; | 
				
			||||
 | 
				
			||||
#ifndef TRUE | 
				
			||||
typedef unsigned char bool; | 
				
			||||
#define TRUE 1 | 
				
			||||
#define FALSE 0 | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
typedef uint16_t temp_t; | 
				
			||||
 | 
				
			||||
#endif | 
				
			||||
					Loading…
					
					
				
		Reference in new issue