lw35-upgrade/printer/avr/main.c

1237 lines
26 KiB
C
Raw Normal View History

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#define BAUDRATE 9600
#define UBRR 51 // (F_CPU / BAUDRATE) / 32
inline void spi_process_data();
/* System functions */
/* Timers */
enum TIMERS{TIM_CARRIAGE,
TIM_WHEEL,
TIM_LINEFEED,
TIMS};
#define TIMESCALE 4
#define sleep_ms(channel, time_ms) sleep(channel, time_ms / TIMESCALE)
volatile uint16_t timer[TIMS];
#define set_timer(channel, time_systicks) {timer[channel] = time_systicks;}
uint8_t block_for(uint8_t channel, uint16_t time_systicks)
{
if (timer[channel] == 0) {
timer[channel] = time_systicks;
return 1;
}
return 0;
}
uint8_t system_status; // CAUTION: Must not be updated from interrupts!
ISR (TIMER1_COMPA_vect)
{
uint8_t i;
for (i = 0; i < TIMS; ++i) {
if (timer[i] > 0) {
timer[i]--;
}
}
if (SPSR & (1<<SPIF)) // We need to check and jump here, otherwise we'll be too slow.
spi_process_data();
}
/* Look-up-tables */
/* Daisy wheel */
#define PRINTER_CONTROL_CHAR 56
#define PRINTER_NO_CHAR 128
#define ASCII_TRANSLATION_TABLE_SIZE 128 + 12
const uint8_t ascii_translation_table[ASCII_TRANSLATION_TABLE_SIZE] PROGMEM = {
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR, // ASCII 15
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR,
PRINTER_CONTROL_CHAR, // ASCII 31
PRINTER_NO_CHAR, // Space
52, // !
66, // "
44, // #
40, // $
29, // %
74, // &
58, // '
88, // (
86, // )
82, // *
84, // +
0, // ,
68, // -
1, // .
28, // /
38, // 0
30, // 1
32, // 2
31, // 3
33, // 4
34, // 5
35, // 6
36, // 7
37, // 8
39, // 9
94, // :
92, // ;
PRINTER_NO_CHAR, // <
71, // =
PRINTER_NO_CHAR, // >
64, // ?
PRINTER_NO_CHAR, // @
76, // A
72, // B
67, // C
65, // D
70, // E
69, // F
63, // G
87, // H
90, // I
62, // J
85, // K
78, // L
95, // M
91, // N
61, // O
80, // P
59, // Q
57, // R
79, // S
77, // T
89, // U
73, // V
93, // W
83, // X
75, // Y
81, // Z
PRINTER_NO_CHAR, // [
PRINTER_NO_CHAR, // BACKSLASH
PRINTER_NO_CHAR, // ]
PRINTER_NO_CHAR, // ^
60, // _
54, // `
5, // a
15, // b
8, // c
21, // d
2, // e
16, // f
17, // g
12, // h
6, // i
26, // j
19, // k
11, // l
10, // m
14, // n
7, // o
13, // p
24, // q
3, // r
4, // s
9, // t
18, // u
20, // v
27, // w
25, // x
22, // y
23, // z
PRINTER_NO_CHAR, // {
48, // |
PRINTER_NO_CHAR, // }
PRINTER_NO_CHAR, // ~
PRINTER_CONTROL_CHAR, // DEL
45, // ³
47, // ²
50, // ´
51, // §
56, // °
41, // ö
42, // ü
43, // ä
46, // Ö
49, // ß
53, // Ü
55, // Ä
};
/* Hardware functions */
void uart_putc(char c)
{
while (!(UCSRA & (1 << UDRE)));
UDR = c;
}
#define ENABLE_SERIAL_DEBUG
#ifdef ENABLE_SERIAL_DEBUG
void debug_write(char *c)
{
while (*c) {
uart_putc(*c);
c++;
}
}
unsigned int debug_printf(char *format,...)
{
va_list args;
unsigned int i;
char printbuffer[64];
va_start (args, format);
i = vsprintf (printbuffer, format, args);
va_end (args);
debug_write(printbuffer);
return i;
}
#else
void debug_write(char *c) {};
unsigned int debug_printf(char *format,...) {return 0;};
#endif
/* DC drive */
#define MOTLIM(t) t##C
#define PIN_DCMOTOR (1 << 4)
#define PIN_LIMITSWITCH (1 << 5)
#define SOLENOID(t) t##D
#define PIN_CORRECTION (1 << 2)
#define PIN_ARMHAMMER (1 << 3)
/* Stepper drive */
/* Drive pattern:
* -A, +C
* -B, +D
* +A, -C
* +B, -D
*/
struct stepper_config {
char port;
uint8_t timch;
uint8_t pin_a;
uint8_t pin_b;
uint8_t pin_c;
uint8_t pin_d;
uint8_t delay;
uint16_t wraparound;
};
struct stepper_status {
uint8_t step;
int8_t dir;
int8_t ldir;
uint8_t lpstate;
uint16_t pos; /* Absolute position */
uint16_t target_pos;
};
/* Stepper configs */
#define REVERSAL_MULTIPLIER 32
#define STEPPER_CFG_HW_CARRIAGE(t) t##C
const struct stepper_config STEPPER_CFG_CARRIAGE = {
.port = 'C',
.timch = TIM_CARRIAGE,
.pin_a = (1 << 3),
.pin_b = (1 << 1),
.pin_c = (1 << 0),
.pin_d = (1 << 2),
.delay = 3.5 * TIMESCALE,
.wraparound = 0 /* No wraparonud */
};
#define STEPPER_CFG_HW_WHEEL(t) t##B
const struct stepper_config STEPPER_CFG_WHEEL = {
.port = 'B',
.timch = TIM_WHEEL,
.pin_a = (1 << 7),
.pin_b = (1 << 0),
.pin_c = (1 << 1),
.pin_d = (1 << 6),
.delay = 3.5 * TIMESCALE,
.wraparound = 96 * 2
};
#define STEPPER_CFG_HW_LINEFEED(t) t##D
const struct stepper_config STEPPER_CFG_LINEFEED= {
.port = 'D',
.timch = TIM_LINEFEED,
.pin_a = (1 << 4),
.pin_b = (1 << 7),
.pin_c = (1 << 6),
.pin_d = (1 << 5),
.delay = 6 * TIMESCALE,
.wraparound = 0 /* No wraparound */
};
/* Stepper helpers */
#define STEPPER_CFG(NAME) STEPPER_CFG_##NAME
#define STEPPER_NEXT(NAME, DIR) stepper_next_f(&stepper_status_##NAME, \
&STEPPER_CFG_##NAME, DIR)
#define STEPPER_SET_IO(NAME) STEPPER_CFG_HW_##NAME(DDR) = \
stepper_calc_ioc(STEPPER_CFG_HW_##NAME(DDR), \
&STEPPER_CFG_##NAME)
#define STEPPER_STOP(NAME) {STEPPER_NEXT(NAME, 0);}
/* Stepper vars */
struct stepper_status stepper_status_WHEEL = {
.step = 0,
.dir = -1,
.lpstate = 0
};
struct stepper_status stepper_status_CARRIAGE = {
.step = 0,
.dir = -1,
.lpstate = 0
};
struct stepper_status stepper_status_LINEFEED = {
.step = 0,
.dir = -1,
.lpstate = 0
};
/* Stepper functions */
uint8_t stepper_calc_ioc(uint8_t pstate, const struct stepper_config *cfg)
{
pstate |= cfg->pin_a | cfg->pin_b | cfg->pin_c | cfg->pin_d;
return pstate;
}
void stepper_next_f(struct stepper_status *stat,
const struct stepper_config *cfg,
int8_t dir)
{
uint8_t pstate = 0;
/* Get current state of port */
switch (cfg->port) {
case 'B':
pstate = PORTB;
break;
case 'C':
pstate = PORTC;
break;
case 'D':
pstate = PORTD;
break;
}
/* Calculate next stepper state */
if (stat->dir != 0) { /* Do not step for the recovery step */
if (dir == 1 && stat->step == 3)
stat->step = 0;
else if (dir == -1 && stat->step == 0)
stat->step = 3;
else
(stat->step) += dir;
}
/* Apply current state */
if (dir == 0) {
stat->lpstate = pstate;
//if (cfg->port == 'B') debug_printf("[%d] stop at: %x\r\n", stat->step, pstate & 0xF);
pstate &= ~(cfg->pin_a | cfg->pin_b | cfg->pin_c | cfg->pin_d);
} else {
if (stat->dir == 0) { /* If this is the first step */
pstate |= stat->lpstate & (cfg->pin_a | cfg->pin_b | cfg->pin_c | cfg->pin_d);
//if (cfg->port == 'B') debug_printf("[%d] restore to : %x\r\n", stat->step, pstate & 0xF);
} else {
switch (stat->step) {
case 0:
pstate &= ~cfg->pin_a;
pstate |= cfg->pin_c;
break;
case 1:
pstate &= ~cfg->pin_b;
pstate |= cfg->pin_d;
break;
case 2:
pstate |= cfg->pin_a;
pstate &= ~cfg->pin_c;
break;
case 3:
pstate |= cfg->pin_b;
pstate &= ~cfg->pin_d;
break;
default:
break;
}
//if (cfg->port == 'B') debug_printf("[%d] set to : %x\r\n", stat->step, pstate & 0xF);
}
}
/* Update status information */
if (stat->dir) { // Ignore recovery step
if (stat->ldir == dir) { // Ignore reversal step
stat->pos += dir;
if (cfg->wraparound) {
if (dir == 1) {
if (stat->pos >= cfg->wraparound) {
stat->pos = 0;
}
} else {
if (stat->pos == 65535) { // Underflow
stat->pos = cfg->wraparound - 1;
}
}
}
}
}
if (stat->dir)
stat->ldir = stat->dir;
stat->dir = dir;
/* Set new state of port */
switch (cfg->port) {
case 'B':
PORTB = pstate;
break;
case 'C':
PORTC = pstate;
break;
case 'D':
PORTD = pstate;
break;
}
}
int8_t stepper_required_direction(struct stepper_status *stat,
const struct stepper_config *cfg)
{
int8_t dir = 0;
uint16_t half;
if (stat->pos == stat->target_pos) {
return 0;
} else if (stat->pos > stat->target_pos) {
dir = -1;
} else {
dir = 1;
}
if (cfg->wraparound) {
half = cfg->wraparound / 2;
if ((stat->pos > half && stat->target_pos <= half) ||
(stat->pos <= half && stat->target_pos > half)){
dir *= -1;
}
}
return dir;
}
uint8_t stepper_perform_movement(struct stepper_status *stat,
const struct stepper_config *cfg)
{
int8_t dir;
/* Check whether we are in an active movement state,
or whether we need to go into one.*/
if (stat->dir != 0 || stat->pos != stat->target_pos) {
/* Set up direction */
if (block_for(cfg->timch, cfg->delay)) {
dir = stepper_required_direction(stat, cfg);
if ((dir == -1) && (stat->dir == 1)) { /* Reversal */
dir = 0;
//debug_write("[stp] Hard reversal detected. Pausing. -->|\r\n");
set_timer(cfg->timch, cfg->delay * REVERSAL_MULTIPLIER);
} else if ((dir == 1) && (stat->dir == -1)) { /* Reversal */
dir = 0;
//debug_write("[stp] Hard reversal detected. Pausing. |<--\r\n");
set_timer(cfg->timch, cfg->delay * REVERSAL_MULTIPLIER);
}
stepper_next_f(stat, cfg, dir);
}
}
return 0;
}
#define POSITION_REACHED(NAME) ((stepper_status_##NAME.dir == 0) && (stepper_status_##NAME.pos == stepper_status_##NAME.target_pos))
#define SET_TARGET(NAME, target) stepper_set_target(&stepper_status_##NAME, \
&STEPPER_CFG_##NAME,\
target)
#define SET_TARGET_DELTA(NAME, delta) SET_TARGET(NAME, \
stepper_status_##NAME.target_pos - (delta))
void stepper_set_target(struct stepper_status *stat,
const struct stepper_config *cfg,
uint16_t target)
{
if (cfg->wraparound) {
while (target >= -cfg->wraparound) {
debug_printf("WRAP- %u\r\n", target);
target += cfg->wraparound;
}
while (target >= cfg->wraparound) {
debug_printf("WRAP+ %u\r\n", target);
target -= cfg->wraparound;
}
}
stat->target_pos = target;
}
/* DC functions */
void arm_hammer()
{
SOLENOID(PORT) |= PIN_ARMHAMMER;
_delay_ms(30);
SOLENOID(PORT) &= ~(PIN_ARMHAMMER);
}
#define DCMOTOR_EN MOTLIM(PORT) |= PIN_DCMOTOR
#define DCMOTOR_STOP MOTLIM(PORT) &= ~PIN_DCMOTOR
#define DCMOTOR_ISACTIVE (MOTLIM(PORT) & PIN_DCMOTOR)
#define LIMITSWITCH (!(MOTLIM(PIN) & PIN_LIMITSWITCH))
/* Main program code */
void move_carriage_to_far_left(uint8_t reset)
{
uint16_t cnt = 0;
/* Init stepper controller */
if (reset) {
stepper_status_CARRIAGE.step = 0;
}
stepper_status_CARRIAGE.dir = -1;
if (LIMITSWITCH) {
debug_write("[car] Clearing switch area...\r\n");
while (LIMITSWITCH) {
if (block_for(TIM_CARRIAGE, STEPPER_CFG_CARRIAGE.delay)) {
STEPPER_NEXT(CARRIAGE, 1);
}
}
for (cnt = 0; cnt < 16; cnt++) {
STEPPER_NEXT(CARRIAGE, 1);
}
STEPPER_STOP(CARRIAGE);
_delay_ms(100);
}
debug_write("[car] Moving carriage to far left...\r\n");
cnt = 0;
while (!LIMITSWITCH) {
if (block_for(TIM_CARRIAGE, STEPPER_CFG_CARRIAGE.delay)) {
cnt++;
STEPPER_NEXT(CARRIAGE, -1);
}
}
STEPPER_STOP(CARRIAGE);
stepper_status_CARRIAGE.pos = 0;
stepper_status_CARRIAGE.target_pos = 0;
debug_printf("[car] Carriage left after %u steps.\r\n", cnt);
}
void align_daisy_wheel()
{
int i;
debug_write("[whl] Aligning wheel...\r\n");
stepper_status_WHEEL.dir = 0;
stepper_status_WHEEL.lpstate = 0;
stepper_status_WHEEL.pos = 0;
stepper_status_WHEEL.step = 0;
stepper_status_WHEEL.target_pos = 0;
stepper_status_WHEEL.ldir = 0;
/* Rotate right for one revolution -> lock daisy wheel to assembly */
for (i = 0; i < (96 + 1) * 2; ) {
if (block_for(TIM_WHEEL, STEPPER_CFG_WHEEL.delay)) {
STEPPER_NEXT(WHEEL, 1);
i++;
}
}
/* Rotate left for one revolution -> align daisy wheel assembly */
for (i = 0; i < (96 + 1) * 2; ) {
if (block_for(TIM_WHEEL, STEPPER_CFG_WHEEL.delay)) {
STEPPER_NEXT(WHEEL, -1);
i++;
}
}
STEPPER_STOP(WHEEL);
_delay_ms(STEPPER_CFG_WHEEL.delay * REVERSAL_MULTIPLIER);
for (i = 0; i < 4;) {
if (block_for(TIM_WHEEL, STEPPER_CFG_WHEEL.delay)) {
STEPPER_NEXT(WHEEL, 1);
i++;
}
}
STEPPER_STOP(WHEEL);
stepper_status_WHEEL.ldir = 1;
stepper_status_WHEEL.target_pos = 0;
stepper_status_WHEEL.pos = 4;
debug_write("[whl] Alignment completed.\r\n");
}
void reset_printhead()
{
debug_write("[hmr] Resetting printhead...\r\n");
DCMOTOR_EN;
_delay_ms(200);
DCMOTOR_STOP;
debug_write("[hmr] Printhead reset completed.\r\n");
}
void initialize_paperfeed()
{
int i;
debug_write("[lfd] Initializing paperfeed...\r\n");
for (i = 0; i < 10;) {
if (block_for(TIM_LINEFEED, STEPPER_CFG_LINEFEED.delay)) {
STEPPER_NEXT(LINEFEED, 1);
i++;
}
}
STEPPER_STOP(LINEFEED);
stepper_status_LINEFEED.dir = 0;
stepper_status_LINEFEED.lpstate = 0;
stepper_status_LINEFEED.pos = 32768;
stepper_status_LINEFEED.step = 0;
stepper_status_LINEFEED.target_pos = 32768;
stepper_status_LINEFEED.ldir = 0;
debug_write("[lfd] Initialization completed.\r\n");
}
//int system_test_auto()
//{
// char c;
// int do_it = 0;
// int print_stat = 0;
// debug_write("[sys] Entering system test mode\r\n");
// debug_write(">");
// while(1) {
// if (UCSRA & (1 << RXC)) {
// print_stat = 1;
// c = UDR;
// if (c >= '0' && c <= '9') {
// stepper_status_CARRIAGE.target_pos = 100 * (c - '0');
// } else {
// switch (c) {
// case ' ':
// stepper_status_CARRIAGE.target_pos += 10;
// break;
// case 'z':
// if (stepper_status_CARRIAGE.target_pos >= 10)
// stepper_status_CARRIAGE.target_pos -= 10;
// break;
// case 'r':
// move_carriage_to_far_left(0);
// break;
// case 'h':
// DCMOTOR_EN;
// arm_hammer();
// _delay_ms(100); /* Note, this also locks the carriage movement -> important! */
// DCMOTOR_STOP;
// break;
// default:
// break;
// }
// switch (c) {
// case '\'':
// //if (stepper_status_WHEEL.target_pos >= 2)
// SET_TARGET_DELTA(WHEEL, -2);
// debug_printf("[whl] New wheel: %d\r\n", stepper_status_WHEEL.target_pos);
// break;
// case ',':
// SET_TARGET_DELTA(WHEEL, 2);
// debug_printf("[whl] New wheel: %d\r\n", stepper_status_WHEEL.target_pos);
// break;
// case 'w':
// align_daisy_wheel();
// break;
// case 't':
// stepper_status_WHEEL.target_pos += 2;
// stepper_status_CARRIAGE.target_pos += 10;
// do_it = 1;
// break;
// default:
// break;
// }
// }
// }
// stepper_perform_movement(&stepper_status_CARRIAGE, &STEPPER_CFG_CARRIAGE);
// stepper_perform_movement(&stepper_status_WHEEL, &STEPPER_CFG_WHEEL);
// if (POSITION_REACHED(WHEEL) && POSITION_REACHED(CARRIAGE) && print_stat) {
// print_stat = 0;
// debug_printf("[pos] CAR: %u\r\n[pos] WHL: %u\r\n",
// stepper_status_CARRIAGE.pos,
// stepper_status_WHEEL.pos);
// }
// if (do_it) {
// if (stepper_status_CARRIAGE.pos == stepper_status_CARRIAGE.target_pos) {
// do_it = 0;
// DCMOTOR_EN;
// arm_hammer();
// _delay_ms(100);
// DCMOTOR_STOP;
// }
// }
// }
//}
//void printer_test()
//{
// uint8_t buf[80] = {0};
// uint8_t *ptr = buf;
// uint8_t *rdptr = buf;
// uint8_t translated = 0;
// int state = 0;
// debug_write("[sys] Entering printer test mode\r\n");
// while(1) {
// switch (state) {
// case 0:
// _delay_ms(100); // Motor turnoff delay
// DCMOTOR_STOP;
// stepper_status_CARRIAGE.target_pos = 80;
// stepper_status_WHEEL.target_pos = 0;stat->lpstate = pstate;
// ptr = buf;
// debug_write(">");
// state++;
// break;
// case 1:
// if (UCSRA & (1 << RXC)) {
// //stepper_status_WHEEL.target_pos += 2;
// *ptr = UDR;
// uart_putc(*ptr);
// if (*ptr == '\r') {
// debug_write("\r\nOK.\r\n");
// DCMOTOR_EN;
// _delay_ms(100); // Let motor get up to speed
// state++;
// stepper_status_CARRIAGE.target_pos = 80;
// rdptr = buf;
// } else {
// ptr++;
// }
// }
// break;
// case 2:
// stepper_status_CARRIAGE.target_pos += 10;
// translated = pgm_read_byte(&ascii_translation_table[*rdptr]);
// if (translated != PRINTER_NO_CHAR) {
// //debug_printf("Prepare: %x (%c) -> %d", *rdptr, *rdptr, translated);
// SET_TARGET(WHEEL, translated * 2);
// state++;
// } else {
// //debug_printf("Skip: %x (%c) -> %d\r\n", *rdptr, *rdptr, translated);
// rdptr++;
// if (rdptr == ptr) {
// state = 0;
// }
// }
// break;
// case 3:
// if (POSITION_REACHED(CARRIAGE) && POSITION_REACHED(WHEEL)) {
// //debug_write("!!!\r\n");
// //DCMOTOR_EN;
// arm_hammer();
// _delay_ms(50);
// //DCMOTOR_STOP;
// rdptr++;
// if (rdptr == ptr) {
// state = 0;
// } else {
// state--;
// }
// }
// break;
// default:
// break;
// }
// stepper_perform_movement(&stepper_status_CARRIAGE, &STEPPER_CFG_CARRIAGE);
// stepper_perform_movement(&stepper_status_WHEEL, &STEPPER_CFG_WHEEL);
// }
//}
//void systick_test()
//{
// debug_write("[tmr] System timer test\r\n");
// while (1) {
// if (block_for(0, 1000*4)) {
// debug_write("hello");
// }
// }
//}
void hardfault() {
debug_write("HARDFAULT!\r\n");
STEPPER_STOP(WHEEL);
STEPPER_STOP(CARRIAGE);
STEPPER_STOP(LINEFEED);
while(1);
}
/* SPI */
#define PRINT_BUFFER_SIZE 256 // Important: must be 256 atm, when changed, you need to change code handling print_buffer_*.
volatile char print_buffer[PRINT_BUFFER_SIZE];
volatile uint8_t print_buffer_wr = 0;
volatile uint8_t print_buffer_rd = 0;
#define SPI_COMMAND_NONE 0x00
#define SPI_COMMAND_PRINTER_WRITE 0x01
#define SPI_COMMAND_PRINTER_RESET 0x02
#define SPI_COMMAND_PRINTER_SETMODE 0x03
#define SPI_COMMAND_PRINTER_MANUAL 0x04
#define SPI_COMMAND_PRINTER_PAUSE 0x05
#define SPI_COMMAND_PRINTER_RESUME 0x06
#define SPI_COMMAND_PRINTER_CLEARBUF 0x07
#define SPI_COMMAND_KEYBOARD_GETKEY 0x20
#define SPI_COMMAND_KEYBOARD_CLEARBUF 0x21
#define SPI_COMMAND_SYSTEM_BACKLIGHT 0x40
#define UART_CMD_ACK_KEYPRESS 'A'
#define SPI_GET_CS (!(PINB & (1 << 2)))
#define UART_BYTE_READY (UCSRA & (1 << RXC))
volatile uint8_t spi_command = SPI_COMMAND_NONE;
volatile uint8_t keys_to_be_acked = 0;
/* This function needs to be fast AF! */
inline void spi_process_data()
{
switch (spi_command) {
case SPI_COMMAND_KEYBOARD_GETKEY: /* Needs to be first, quick reply expected. */
if (UART_BYTE_READY) {
SPDR = UDR;
keys_to_be_acked++;
} else {
SPDR = 0x00;
}
break;
/* !!! Only no-reply commands below !!! */
case SPI_COMMAND_NONE: /* This is the start of the transaction */
spi_command = SPDR;
break;
case SPI_COMMAND_PRINTER_WRITE:
print_buffer[print_buffer_wr++] = SPDR; // WARNING: No overflow protection
// Will auto wrap at 256; print_buffer_wr = print_buffer_wr % PRINT_BUFFER_SIZE;
break;
default:
//SPDR = 0x0;
break;
}
}
ISR(SPI_STC_vect)
{
spi_process_data();
}
void ioc_process()
{
if (!SPI_GET_CS) {
/* Cancel the current command */
spi_command = SPI_COMMAND_NONE;
SPDR = system_status; // TODO: status 1
}
if (keys_to_be_acked && (UCSRA & (1 << UDRE))) {
UDR = UART_CMD_ACK_KEYPRESS;
keys_to_be_acked--;
}
}
#define PRINT_CHARACTER_WIDTH 10
#define PRINT_LINE_HEIGHT 34
#define PRINTER_STOP_RIGHT 1100
enum printer_mode_t {MANUAL, INTELLIGENT};
#define GET_PRINT_CODE(c) (pgm_read_byte(&ascii_translation_table[c]))
uint16_t print_margin_left = 100;
uint16_t print_margin_right = PRINTER_STOP_RIGHT - 100;
int8_t move_one_character(uint8_t backward)
{
if (backward) {
if (stepper_status_CARRIAGE.target_pos < print_margin_left + PRINT_CHARACTER_WIDTH) {
return 1;
}
stepper_status_CARRIAGE.target_pos -= PRINT_CHARACTER_WIDTH;
} else {
if (stepper_status_CARRIAGE.target_pos >= print_margin_right - PRINT_CHARACTER_WIDTH) {
return 1;
}
stepper_status_CARRIAGE.target_pos += PRINT_CHARACTER_WIDTH;
}
return 0;
}
void move_carriage_to_left_margin()
{
stepper_status_CARRIAGE.target_pos = print_margin_left;
debug_putc('\r');
debug_putc('{');
}
/* Simulates the print of one line and returns the end position of the printhead */
/* It's only called once, so inline is good. */
inline int8_t simulate_print_run(uint8_t *start_end_index, uint8_t *recovery_index)
{
char current_char;
uint8_t index;
uint16_t carriage_position;
uint8_t translated;
uint8_t status = 0;
uint8_t line_matters = 0;
index = *start_end_index;
carriage_position = print_margin_left;
while (!status) {
current_char = print_buffer[index];
if (index == print_buffer_wr) { /* END OF BUFFER - Can't determine length */
status = 3;
line_matters = 1;
carriage_position = print_margin_right;
break;
}
if (current_char == '\n') {
index--;
status = 1;
break;
} else if (current_char < ASCII_TRANSLATION_TABLE_SIZE) {
translated = GET_PRINT_CODE(current_char);
if (translated == PRINTER_CONTROL_CHAR) {
// ignore
} else {
if (carriage_position >= print_margin_right - PRINT_CHARACTER_WIDTH) {
status = 2;
carriage_position += PRINT_CHARACTER_WIDTH;
//(*recovery_index) = index + 1;
break; /* Don't consume character */
} else {
carriage_position += PRINT_CHARACTER_WIDTH;
if (translated != PRINTER_NO_CHAR) {
line_matters = 1;
}
}
}
}
index++; /* Consume character */
(*recovery_index) = index + 1;
}
carriage_position -= PRINT_CHARACTER_WIDTH;
*start_end_index = index;
//debug_printf("$ restore to: %d M=%d S=%d\n", print_buffer[*recovery_index], line_matters, status);
//debug_printf("CMP %d <= %d\n", ((carriage_position - print_margin_left) / 2) , (stepper_status_CARRIAGE.target_pos - print_margin_left));
if (!line_matters) {
return 0;
} else if (((carriage_position - print_margin_left) / 2) < (stepper_status_CARRIAGE.target_pos - print_margin_left)) {
//debug_printf("next char: %c\n", print_buffer[index]);
//_delay_ms(5000);
stepper_status_CARRIAGE.target_pos = carriage_position;
return -1;
} else {
return 1;
}
}
enum printer_state {INIT, IDLE, HAMMER, PAUSED};
void printer_process()
{
static enum printer_state state = INIT;
static uint8_t backward = 0;
static uint8_t print_buffer_backward = 0;
static uint8_t print_buffer_recovery = 0;
static uint8_t current_is_linebreak = 0;
char current_char;
uint8_t translated;
uint8_t end_of_next_line = 0;
int8_t temp;
static char debug_character = 0;
switch (state) {
case IDLE:
if (print_buffer_rd != print_buffer_wr) { // maybe add "|| current_is_linebreak"
if (!DCMOTOR_ISACTIVE) {
DCMOTOR_EN;
_delay_ms(100); /* Let the motor get up to speed */
}
/* Fetch new character from buffer */
if (!current_is_linebreak) {
if (backward) {
current_char = print_buffer[print_buffer_backward];
print_buffer_backward--;
} else {
current_char = print_buffer[print_buffer_rd];
print_buffer_rd++;
}
} else {
current_char = '\0';
}
/* Check whether the current character is a command or something to print */
current_is_linebreak += (current_char == '\n');
if (current_is_linebreak) {
if (backward) {
uart_putc('=');
print_buffer_rd = print_buffer_recovery;
}
backward = 0; // Re-evaluate backward. Default is forward.
stepper_status_LINEFEED.target_pos -= PRINT_LINE_HEIGHT;
uart_putc('\n');
current_is_linebreak--;
//_delay_ms(1000);
/* Decide whether we move the carriage back to the left, or
* whether print the new line in reverse. */
end_of_next_line = print_buffer_rd;
temp = simulate_print_run(&end_of_next_line, &print_buffer_recovery);
if (temp == -1) {
//uart_putc('\r');
uart_putc('[');
backward = 1;
print_buffer_backward = end_of_next_line;
} else if (temp == 0) {
// do nothing
uart_putc('#');
} else {
move_carriage_to_left_margin();
}
} else if (current_char < ASCII_TRANSLATION_TABLE_SIZE) {
translated = GET_PRINT_CODE(current_char);
debug_character = current_char;
switch (translated) {
case PRINTER_CONTROL_CHAR:
break;
case PRINTER_NO_CHAR:
current_is_linebreak += move_one_character(backward);
if (!current_is_linebreak)
uart_putc(' ');
break;
default: /* It's a printable character */
SET_TARGET(WHEEL, translated * 2);
state = HAMMER;
break;
}
} else {
// TODO: Handle special commands
}
} else {
DCMOTOR_STOP;
}
break;
case HAMMER:
if (POSITION_REACHED(WHEEL) && POSITION_REACHED(CARRIAGE) && POSITION_REACHED(LINEFEED)) {
arm_hammer();
_delay_ms(50); // TODO: replace with timer
uart_putc(debug_character);
current_is_linebreak += move_one_character(backward);
state = IDLE;
}
break;
case INIT:
move_carriage_to_left_margin();
state = IDLE;
}
// if (print_buffer_rd != print_buffer_wr) {
// if (UCSRA & (1 << UDRE)) {
// UDR = print_buffer[print_buffer_rd++];
// }
// }
// // debug only: load the CPU with some stepper movements
// pgm_read_byte(&ascii_translation_table[123]);
// stepper_status_CARRIAGE.target_pos += 10;
// stepper_status_WHEEL.target_pos += 10;
// // -----------
if (stepper_status_CARRIAGE.target_pos > PRINTER_STOP_RIGHT) { // Right margin safety
hardfault();
}
stepper_perform_movement(&stepper_status_LINEFEED, &STEPPER_CFG_LINEFEED);
stepper_perform_movement(&stepper_status_CARRIAGE, &STEPPER_CFG_CARRIAGE);
stepper_perform_movement(&stepper_status_WHEEL, &STEPPER_CFG_WHEEL);
}
void mainloop()
{
while (1) {
printer_process();
ioc_process();
}
}
int main()
{
/* Pre-init I/O */
DDRB = 0;
DDRC = 0;
DDRD = 0;
PORTB = 0;
PORTC = 0;
PORTD = 0;
/* Set up UART */
UCSRB = (1 << TXEN) | (1 << RXEN);
UBRRH = (UBRR >> 8) & 0xFF;
UBRRL = UBRR & 0xFF;
/* Set up SPI */
DDRB |= (1 << PB4);
SPCR = (1 << SPE) | (1 << CPOL) | (1 << CPHA) | (1 << SPIE);
/* Set up DC components */
MOTLIM(DDR) |= PIN_DCMOTOR;
MOTLIM(DDR) &= ~(PIN_LIMITSWITCH);
MOTLIM(PORT) |= PIN_LIMITSWITCH; /* Pullup for limit switch */
SOLENOID(DDR) |= PIN_ARMHAMMER;
SOLENOID(DDR) |= PIN_CORRECTION;
/* Set up steppers */
STEPPER_SET_IO(CARRIAGE);
STEPPER_SET_IO(WHEEL);
STEPPER_SET_IO(LINEFEED);
/* Set up SysTick Timer */
TCCR1B = (1 << WGM12) | (1 << CS11); // f_tim = 8 MHz / 8
OCR1A = 1000 / TIMESCALE;
TIMSK = (1 << OCIE1A);
/* Init system */
system_status = 0;
debug_write("\n\n\r[sys] STARTING IO CONTROLLER...\r\n");
debug_write("[sys] Enabling interrupts.\r\n");
sei();
/* Align printer */
initialize_paperfeed();
move_carriage_to_far_left(1);
align_daisy_wheel();
reset_printhead();
debug_write("[sys] Startup completed.\r\n");
/* Run system */
mainloop();
// printer_test();
// system_test_auto();
// systick_test();
debug_write("[sys] REACHED END OF MAIN. HALTING.\r\n");
while (1);
}