Browse Source

First commit of partly-working reflow oven controller.

master
Gavan Fantom 9 years ago
commit
aaaec8ff40
  1. 4
      .mtn-ignore
  2. 67
      Makefile
  3. 24
      beep.c
  4. 9
      beep.h
  5. 33
      button.c
  6. 8
      button.h
  7. 11
      common.h
  8. 15
      config.c
  9. 25
      config.h
  10. 122
      control.c
  11. 12
      control.h
  12. 140
      display.c
  13. 18
      display.h
  14. 268
      fields.c
  15. 53
      fields.h
  16. 123
      font.c
  17. 8
      font.h
  18. 3
      humancontrol/Makefile
  19. 115
      humancontrol/soldertimer.c
  20. 95
      i2c.c
  21. 12
      i2c.h
  22. 970
      menu.c
  23. 30
      menu.h
  24. 130
      profile.c
  25. 34
      profile.h
  26. 36
      reflow.c
  27. 54
      spi.c
  28. 15
      spi.h
  29. 23
      test/Makefile
  30. 0
      test/avr/io.h
  31. 13
      test/avr/pgmspace.h
  32. 68
      test/test-display.c
  33. 70
      test/test-graphics.c
  34. 62
      test/test-i2c.c
  35. 46
      test/test-menu.c
  36. 49
      test/test-therm.c
  37. 23
      test/types.h
  38. 1
      test/util/atomic.h
  39. 68
      therm.c
  40. 17
      therm.h
  41. 87
      timer.c
  42. 24
      timer.h
  43. 25
      types.h

4
.mtn-ignore

@ -0,0 +1,4 @@
\.o$
\.hex$
\.elf$
\.elf.map$

67
Makefile

@ -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

24
beep.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);
}

9
beep.h

@ -0,0 +1,9 @@
/* beep.h */
#ifndef _BEEP_H_
#define _BEEP_H_
void beep_on(void);
void beep_off(void);
#endif

33
button.c

@ -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;
}

8
button.h

@ -0,0 +1,8 @@
/* button.h */
#ifndef _BUTTON_H_
#define _BUTTON_H_
extern uint8 button;
#endif

11
common.h

@ -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

15
config.c

@ -0,0 +1,15 @@
/* config.c */
#include "config.h"
config_t config = {
127,
1, /* beep */
3, /* thermocouple */
0, /* frequency */
0, /* units */
0x080,
0x040,
0xfc0
};

25
config.h

@ -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

122
control.c

@ -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);
}

12
control.h

@ -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

140
display.c

@ -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
}

18
display.h

@ -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

268
fields.c

@ -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);
}

53
fields.h

@ -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

123
font.c

@ -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;
}

8
font.h

@ -0,0 +1,8 @@
/* font.h */
#ifndef _FONT_H_
#define _FONT_H_
uint8_t *font_getchar(char c);
#endif

3
humancontrol/Makefile

@ -0,0 +1,3 @@
# Makefile
soldertimer: soldertimer.c

115
humancontrol/soldertimer.c

@ -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");
}

95
i2c.c

@ -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;
}

12
i2c.h

@ -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

970
menu.c

@ -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;
}

30
menu.h

@ -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

130
profile.c

@ -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);
}

34
profile.h

@ -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

36
reflow.c

@ -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();
}
}

54
spi.c

@ -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);
}

15
spi.h

@ -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

23
test/Makefile

@ -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
test/avr/io.h

13
test/avr/pgmspace.h

@ -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

68
test/test-display.c

@ -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");
}

70
test/test-graphics.c

@ -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;
}

62
test/test-i2c.c

@ -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;
}

46
test/test-menu.c

@ -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;
}

49
test/test-therm.c

@ -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;
}

23
test/types.h

@ -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

1
test/util/atomic.h

@ -0,0 +1 @@
#define ATOMIC_BLOCK(x)

68
therm.c

@ -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;
}

17
therm.h

@ -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

87
timer.c

@ -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);
}

24
timer.h

@ -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

25
types.h

@ -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…
Cancel
Save