From ffb91b51c308258cf62f03d05aba1d6eb986b4ad Mon Sep 17 00:00:00 2001 From: Markus Koch Date: Sat, 12 May 2018 20:29:50 +0200 Subject: [PATCH] printer: Add preliminary printer controller --- printer/avr/.gitignore | 5 + printer/avr/Makefile | 26 ++ printer/avr/main.c | 816 ++++++++++++++++++++++++++++++++++++++++ printer/avr/main.obj | Bin 0 -> 23676 bytes printer/avr/printer.pro | 9 + 5 files changed, 856 insertions(+) create mode 100644 printer/avr/.gitignore create mode 100644 printer/avr/Makefile create mode 100644 printer/avr/main.c create mode 100755 printer/avr/main.obj create mode 100644 printer/avr/printer.pro 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 0000000000000000000000000000000000000000..d1e169da57d0e8d2bae07675535be89a13cbc39b GIT binary patch literal 23676 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 literal 0 HcmV?d00001 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