#include #include #include #include #include #include /**************************************\ * Laser projector. * 32x16 display. 16x16 active pixels. * Use 8MHz clock. * * Ports: * B0: out: Laser * B1: out: Motor * B2: in: Fork reader (active low) (B2=INT2) * D7: out: Servo control (D7=OC2) \*************************************/ //#include "video/x.h" //#include "test_pattern.h" #include "video_alf.h" /* Calibration */ const char offsetx[16] = {4,10,13,13, 12,12,14,11, 8,8,9,9, 13,9,9,3}; const uint8_t suboffsetx[16] = {8,9,8,15, 6,3,5,2, 1,9,7,5, 13,6,8,8}; /* Variables */ const uint8_t frame_div = 4; /* Number of times to show each frame */ volatile uint8_t frame_div_cnt; /* Counter for frame slowdown */ volatile uint16_t pixel_pos; /* Rotor position in pixels (0 to 511) */ volatile uint16_t rot_ticks; /* Number of counter1 ticks per rotation */ volatile uint16_t frame; /* Current video frame */ volatile uint16_t frame_inc; /* Frames forward per rotation */ volatile uint8_t the_end; /* Has the movie reached the end? */ volatile uint8_t pixelclock; /* Pixel clock (counter0 ticks per pixel) */ volatile uint8_t pixelclock_frac; /* 64*(pixelclock fraction) */ volatile uint8_t pixelclock_frac_running; /* Incremented by pixelclock_frac once per pixel */ void ioinit(void) { /* Set port direction (0=in, 1=out) */ DDRA = 0x00; DDRB = 0x03; DDRC = 0x00; DDRD = 0x80; /* Set internal pull-up (0=off, 1=on) */ PORTA = 0xff; PORTB = 0xf8; PORTC = 0xff; PORTD = 0x7f; /* 8-bit counter (counter0) - Pixel clock */ #define TCCR0_DIV_OFF ( 0) #define TCCR0_DIV_1 ( BV(CS00)) #define TCCR0_DIV_8 ( BV(CS01) ) #define TCCR0_DIV_64 ( BV(CS01)|BV(CS00)) #define TCCR0_DIV_256 (BV(CS02) ) #define TCCR0_DIV_1024 (BV(CS02) |BV(CS00)) outp(BV(WGM01)|TCCR0_DIV_8, TCCR0); /* Reset counter on compare, set clock div */ /* 8-bit counter (counter2) - PWM generator */ outp(BV(WGM21)|BV(WGM20)|BV(COM21)|7, TCCR2); OCR2=10; /* Range: 3-18 */ /* 16-bit counter (counter1) - Used to measure rotation time */ #define TCCR1B_DIV_OFF ( 0) #define TCCR1B_DIV_1 ( BV(CS10)) #define TCCR1B_DIV_8 ( BV(CS11) ) #define TCCR1B_DIV_64 ( BV(CS11)|BV(CS10)) #define TCCR1B_DIV_256 (BV(CS12) ) #define TCCR1B_DIV_1024 (BV(CS12) |BV(CS10)) outp(0, TCCR1A); outp(0, TCCR1B); /* External interrupt INT2 - "Reading fork", triggers once per revolution */ cbi(MCUCSR, ISC2); /* Trigger on falling edge */ sbi(GICR, INT2); /* Enable */ /* Enable interrupts */ sei(); } /* 8-bit counter (counter0) - Pixel clock */ SIGNAL(SIG_OUTPUT_COMPARE0) { int8_t x,y,i; pixel_pos++; x = pixel_pos & 0x1f; y = pixel_pos >> 5; /* Sub-x-offset */ i = 0; if (x == 0) i = (pixelclock * (uint16_t)suboffsetx[y]) >> 4; if (x == 30) { pixel_pos++; i = pixelclock - ((pixelclock * (uint16_t)suboffsetx[y]) >> 4); } /* Pixel clock fractions / Sub-x-offset */ pixelclock_frac_running += pixelclock_frac; if (pixelclock_frac_running >= 0x40) { pixelclock_frac_running -= 0x40; OCR0 = pixelclock + i + 1; } else OCR0 = pixelclock + i; /* Output pixel */ x = x - offsetx[(int)y]; if ((x >= 0) && (x < 16)) { if (pgm_read_byte_near(video + (frame<<5) + (y<<1) + (x>>3)) & BV(7-(x & 0x07))) sbi(PORTB,0); else cbi(PORTB,0); } else cbi(PORTB,0); /* Turn off counter0 (this one), so it won't disturb INT2 interrupt */ if (pixel_pos > 498) { timer_enable_int(0); cbi(PORTB,0); } } /* External interrupt INT2 */ SIGNAL(SIG_INTERRUPT2) { const uint16_t hysteresis = 4; static uint16_t i; pixel_pos = 0; /* Read rotation time from 16-bit counter */ outp(TCCR1B_DIV_OFF, TCCR1B); /* Stop counting */ if (TCNT1 > rot_ticks + hysteresis) rot_ticks = TCNT1 - hysteresis; if (TCNT1 < rot_ticks - hysteresis) rot_ticks = TCNT1 + hysteresis; i = rot_ticks >> 6; /* rot_ticks * 64 / 512 / 8 */ if (i > 126) i = 126; pixelclock = i; /* Typical: 61 */ OCR0 = pixelclock; pixelclock_frac = rot_ticks & 0x3f; pixelclock_frac_running = 0; TCNT1 = 0; outp(TCCR1B_DIV_64, TCCR1B); /* Start counting again */ /* Re-enable 8-bit counter */ timer_enable_int(BV(OCIE0)); /* Output Compare Interrupt Enable */ TCNT0 = 0; /* Reset counter */ TIFR = 0xff; /* Clear pending counter interrupts. FIXME: Clears all interrupts! */ /* Video */ if (++frame_div_cnt >= frame_div) { frame_div_cnt = 0; frame += frame_inc; if (frame >= video_frames) { frame = video_frames-1; the_end = 1; } } } int main(void) { uint8_t i; uint32_t j; frame = 0; frame_inc = 0; ioinit(); for (;;) { /* Startup */ frame = 0; frame_inc = 0; sei(); /* Enable interrupts */ OCR2 = 3; /* Raise screen */ sbi(PORTB, 1); /* Turn on motor */ /* Play video */ for (j=0; j < 32000000; j++); for (i=0; i<1; i++) { frame = 0; frame_div_cnt = 0; the_end = 0; for (j=0; j < 32000000; j++); frame_inc = 1; while (!the_end); frame_inc = 0; for (j=0; j < 32000000; j++); } /* Shutdown */ cli(); /* Disable interrupts */ cbi(PORTB, 0); /* Turn off laser */ cbi(PORTB, 1); /* Turn off motor */ for (j=0; j < 128000000; j++); OCR2 = 10; /* Lower screen */ /* Wait */ for (j=0; j < 256000000; j++); } for(;;); return 0; }