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