Gavan Fantom
9 years ago
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