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