diff --git a/printer/avr/.gitignore b/printer/avr/.gitignore new file mode 100644 index 0000000..93cb814 --- /dev/null +++ b/printer/avr/.gitignore @@ -0,0 +1,5 @@ +*.hex +*.o +*.pro.user +*.autosave +*.old* diff --git a/printer/avr/Makefile b/printer/avr/Makefile new file mode 100644 index 0000000..f88f849 --- /dev/null +++ b/printer/avr/Makefile @@ -0,0 +1,26 @@ +MCU=atmega8 +CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) -Os -DF_CPU=8000000 +LDFLAGS=-Wl,-gc-sections -Wl,-relax +CC=avr-gcc +TARGET=main +OBJECT_FILES=main.o + +all: $(TARGET).hex + +clean: + rm -f *.o *.hex *.obj *.hex + +%.hex: %.obj + avr-objcopy -R .eeprom -O ihex $< $@ + +%.obj: $(OBJECT_FILES) + $(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@ + +program: $(TARGET).obj + avrdude -p $(MCU) -c usbasp -U flash:w:$(TARGET).hex + #avrdude -p $(MCU) -P /dev/ttyUSB1 -c arduino -b 57600 -U flash:w:$(TARGET).hex + +fuse: + avrdude -p m8 -c usbasp -U lfuse:w:0xe4:m -U hfuse:w:0xd9:m + +.phony: program fuse diff --git a/printer/avr/main.c b/printer/avr/main.c new file mode 100644 index 0000000..0079e1c --- /dev/null +++ b/printer/avr/main.c @@ -0,0 +1,816 @@ +#include +#include +#include +#include +#include + +#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); +} diff --git a/printer/avr/main.obj b/printer/avr/main.obj new file mode 100755 index 0000000..d1e169d Binary files /dev/null and b/printer/avr/main.obj differ diff --git a/printer/avr/printer.pro b/printer/avr/printer.pro new file mode 100644 index 0000000..4e0df99 --- /dev/null +++ b/printer/avr/printer.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt + +INCLUDEPATH += /usr/avr/include + +SOURCES += \ + main.c