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