Big LED Screen/ledtest.c
Jump to navigation
Jump to search
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdint.h> /* serial output */ #define MOSI_PINB PB3 /* serial clock */ #define SCK_PINB PB5 /* slave select, needs to be output but is otherwise just ued for * debugging */ #define SS_PINB PB2 /* pin on PORTB that's hooked up to the latch output from the buffer board */ #define LATCH_IN_PINB PB1 /* our output latch port to the daughterboard */ #define LATCH_OUT_PINB PB0 /* pin on portd that's hooked up to one of the power pins so we can * synchronize which power pin we're using */ #define POWER_PIND PD7 /* output pin that's 1 when we are on our first power output */ #define DEBUG_FIRST_POWER_PIND PD6 void ioinit(void) { cli(); // set output pins for output DDRB = _BV(MOSI_PINB) | _BV(SCK_PINB) | _BV(SS_PINB) | _BV(LATCH_OUT_PINB); // enable internal pull-up on power pin so it doesn't cause issues if unconnected // PORTD = _BV(POWER_PIND); // debugging output DDRD = _BV(DEBUG_FIRST_POWER_PIND); // enable PCI0 interrupt interrupt when latch pin changes PCMSK0 = _BV(PCINT1); // enable PCI2 interrupt when power pin changes (PD7) PCMSK2 = _BV(PCINT23); // enable PCI0 and PCI2 interrupts PCICR = _BV(PCIE0) | _BV(PCIE2); // put MOSI up when we're not clocking out PORTB |= _BV(MOSI_PINB); sei (); } static void spistart(void) { // start SPI in master mode, at SPI clock // running at CPU frequency / 8 SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); SPSR = _BV(SPI2X); PORTB &= ~_BV(SS_PINB); // lower slave select (for debugging) } static void spistop(void) { SPCR = 0; PORTB |= _BV(SS_PINB); // raise slave select (for debugging) } unsigned volatile char can_write_out; unsigned char pow_select; // 0..15 depending on sequence of latching unsigned char pow_inverse; unsigned char display_count; unsigned char inverse; unsigned char hilite_byte; // clock out serial data static void writeout(void) { unsigned char this_one_inverse; unsigned char this_pow_select; unsigned char byte_count; this_pow_select = pow_select; pow_select++; if (pow_select > 15) pow_select = 0; sei(); if (this_pow_select == 0) PORTD |= _BV(DEBUG_FIRST_POWER_PIND); if (this_pow_select == pow_inverse) { display_count++; if (display_count > (pow_inverse ? 120 : 30)) { if (inverse) { inverse = 0; } else { pow_inverse++; if (pow_inverse > 15) { pow_inverse = 0; hilite_byte++; if (hilite_byte > (200 / 8)) { hilite_byte = 0; } } inverse = 1; } display_count = 0; } this_one_inverse = inverse; } else { this_one_inverse = 0; } // turn off the latch to the daughterboards PORTB &= ~_BV(LATCH_OUT_PINB); // and actually clock out spistart(); SPDR = 0b01010101; for (byte_count = 0; byte_count < (200/8); byte_count++) { unsigned register char to_write; to_write = this_one_inverse ? ~byte_count : byte_count; to_write = (byte_count == hilite_byte) ? ~to_write : to_write; /* SPIF drops low when the SPI output is finished, so we want to start * the next byte as soon as possible. * * This requires any work that needs to be done to gather the value to * write (such as loading it in a register) to be done before we wait * for SPIF to drop */ __asm__( ".spi_not_ready:" "\n\t" "in __tmp_reg__,%[spsr]" "\n\t" "sbrs __tmp_reg__, %[spif]" "\n\t" "rjmp .spi_not_ready" "\n\t" "out %[spdr], %[towrite]" "\n\t" :: [spsr] "I" (_SFR_IO_ADDR(SPSR)), [spif] "I" (SPIF), [spdr] "I" (_SFR_IO_ADDR(SPDR)), [towrite] "r" (to_write) ); } spistop(); PORTD &= ~_BV(DEBUG_FIRST_POWER_PIND); } ISR(PCINT0_vect) /* "PCI0" interrupts */ { // latch pin changed. write out serial if we're not in the middle of it already if (!can_write_out) return; // output latch pin to activate the new values we shifted in last time: PORTB |= _BV(LATCH_OUT_PINB); // (it's turned off within "writeout") // mark writing out as busy, and then enable interrupts so we can go on with life can_write_out = 0; // now, emit our data writeout(); // we're done! re-enable writing out can_write_out = 1; } // PCINT23 toggled ISR(PCINT2_vect) { if (PIND & _BV(POWER_PIND)) { // low->high transition at the beginning of the power signal. // we get a latch spike at approximately this time, so we have to figure out whether // we got that or not in order to avoid the race condition: if (can_write_out == 0) { // we're busy writing out, hopefully this is frame 0. reset to frame 1. pow_select = 1; } else { // haven't started to write out frame 0 yet; next frame is 0 pow_select = 0; } } } int main(void) { can_write_out = 1; ioinit(); for (;;) { can_write_out = 2; _delay_ms(100); cli(); if (can_write_out == 2) { // we didn't receive any input on the latch pin; write out every 100 ms // for debugging purposes writeout(); } sei(); } }