lw35-upgrade/printer/avr/main.c

817 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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
/* 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;
}
ISR (TIMER1_COMPA_vect)
{
uint8_t i;
for (i = 0; i < TIMS; ++i) {
if (timer[i] > 0) {
timer[i]--;
}
}
return;
}
/* Look-up-tables */
/* Daisy wheel */
#define PRINTER_CONTROL_CHAR 56
#define PRINTER_NO_CHAR 128
const uint8_t ascii_translation_table[128 + 12] 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_tx(char c)
{
while (!(UCSRA & (1 << UDRE)));
UDR = c;
}
void uart_write(char *c)
{
while (*c) {
uart_tx(*c);
c++;
}
}
unsigned int uart_printf(char *format,...)
{
va_list args;
unsigned int i;
char printbuffer[64];
va_start (args, format);
i = vsprintf (printbuffer, format, args);
va_end (args);
uart_write(printbuffer);
return i;
}
/* 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
};
/* 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
};
/* 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') uart_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') uart_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') uart_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;
uart_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;
uart_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.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) {
uart_printf("WRAP- %u\r\n", target);
target += cfg->wraparound;
}
while (target >= cfg->wraparound) {
uart_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 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;
uart_write("[car] Moving carriage to far left...\r\n");
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;
uart_printf("[car] Carriage left after %u steps.\r\n", cnt);
}
void align_daisy_wheel()
{
int i;
uart_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;
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;
uart_write("[whl] Alignment completed.\r\n");
}
void reset_printhead()
{
uart_write("[hmr] Resetting printhead...\r\n");
DCMOTOR_EN;
_delay_ms(200);
DCMOTOR_STOP;
uart_write("[hmr] Printhead reset completed.\r\n");
}
int system_test_auto()
{
char c;
int do_it = 0;
int print_stat = 0;
uart_write("[sys] Entering system test mode\r\n");
uart_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);
uart_printf("[whl] New wheel: %d\r\n", stepper_status_WHEEL.target_pos);
break;
case ',':
SET_TARGET_DELTA(WHEEL, 2);
uart_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;
uart_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;
uart_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;
ptr = buf;
uart_write(">");
state++;
break;
case 1:
if (UCSRA & (1 << RXC)) {
//stepper_status_WHEEL.target_pos += 2;
*ptr = UDR;
uart_tx(*ptr);
if (*ptr == '\r') {
uart_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) {
//uart_printf("Prepare: %x (%c) -> %d", *rdptr, *rdptr, translated);
SET_TARGET(WHEEL, translated * 2);
state++;
} else {
//uart_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)) {
//uart_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()
{
uart_write("[tmr] System timer test\r\n");
while (1) {
if (block_for(0, 1000*4)) {
uart_write("hello");
}
}
}
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 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);
/* Set up SysTick Timer */
TCCR1B = (1 << WGM12) | (1 << CS11); // f_tim = 8 MHz / 8
OCR1A = 1000 / TIMESCALE;
TIMSK = (1 << OCIE1A);
/* Init system */
uart_write("\n\n\r[sys] STARTING IO CONTROLLER...\r\n");
uart_write("[sys] Enabling interrupts.\r\n");
sei();
/* Align printer */
move_carriage_to_far_left(1);
align_daisy_wheel();
reset_printhead();
uart_write("[sys] Startup completed.\r\n");
/* Run system */
printer_test();
system_test_auto();
systick_test();
uart_write("[sys] REACHED END OF MAIN. HALTING.\r\n");
while (1);
}