Below is the file 'firm/led_controller/led_display.c' from this revision. You can also download the file.

// ### BOILERPLATE ###
// Orthographic Cube Firmware
// Copyright (C) 2006 Peter Todd <pete@petertodd.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// ### BOILERPLATE ###

// LED Display stuff

// These functions maintain the low-level led display stuff, the bitmap data and
// initialization and scan functions.


// The size of the display.
#define display_xsize 35
#define display_ysize 35

// Initialize the display.
void display_init();

// Does whatever is needed to multiplex the display. This is called from the TMR0 init.
void display_do_multiplex();

// Clears the display, sets all pixels off.
void display_clear();

// Bitmap data for the display. Each pixel is one bit to make things compact.
#define display_xmax display_xsize
#define display_ymax ((display_ysize / 8) + 1)
uint8_t bitmap0[display_xmax][display_ymax];
uint8_t bitmap1[display_xmax][display_ymax];

// The column that is being currently displayed by the multiplex code.
uint8_t current_column;

// Which of the two buffers is being displayed for writing and displaying.
uint8_t current_buffer;

// Switch buffers.
#define switch_display_buffers() do { \
					current_buffer = ~current_buffer; \
				} while (0);

// Turns pixel x,y on
#define display_pset(x,y) \
	do { \
		if (current_buffer){ \
			bitmap1[(x)][(y) / 8] |= (0x01 << ((y) % 8)); \
		} else { \
			bitmap0[(x)][(y) / 8] |= (0x01 << ((y) % 8)); \
		} \
	} while (0);

// Turns pixel x,y off
#define display_pclear(x,y) \
	do { \
		if (current_buffer){ \
			bitmap1[(x)][(y) / 8] |= ~(0x01 << ((y) % 8)); \
		} else { \
			bitmap0[(x)][(y) / 8] |= ~(0x01 << ((y) % 8)); \
		} \
	} while (0);

// Macros to set the clock and data lines of the MM5451
#define mm_clk_low()	PORTCbits.RC0 = 0;
#define mm_clk_high()	PORTCbits.RC0 = 1;
#define mm_data_low()	PORTCbits.RC1 = 0;
#define mm_data_high()	PORTCbits.RC1 = 1;
#define mm_out(x)	{PORTCbits.RC1 = x; Nop(); Nop(); mm_clk_high(); Nop(); Nop(); Nop(); Nop(); mm_clk_low(); };

void display_clear(){
	uint8_t x,y;

	for (x = 0; x < display_xmax; x++){
		for (y = 0; y < display_ymax; y++){
			if (current_buffer){
				bitmap1[x][y] = 0x00;
			} else {
				bitmap0[x][y] = 0x00;
			}
		}
	}
}

void display_init(){
	current_column = display_xsize;
	current_buffer = 0;

	// clear the display, both buffers
	display_clear();
	switch_display_buffers();
	display_clear();

	// Set tris bits to output for all the FET pins.
	TRISE = TRISD = TRISB = TRISA = TRISF = TRISG = 0;

	TRISC = 0;

	// Set the MM5451 to a default state.
	mm_clk_low();
	mm_data_low();

	// Turn brightness on
	PORTCbits.RC2 = 1;
}

uint8_t mplex_dout;

void display_do_multiplex(){
static	uint8_t d,x,i,n,y;

	// Go to the next column.
	if (!current_column)
		current_column = display_xmax;
	current_column--;




	// Load the row data into the MM5451 chip.

	// "Wakeup" the MM by sending it some zero'd out data.
	mm_data_low();
	for (i = 0; i < 5; i++){
		mm_out(0);
	}

	// Send the start bit.
	mm_out(1);

	// We are loading display_ysize bits, backed into i bytes.

// Macro to send a single bit. This has been verified to give the proper timings with the MM5451
#define mobit(i) \
		_asm \
			nop			\n \
			btfss	_mplex_dout,i	\n \
			bcf	_PORTC,1	\n \
			btfsc	_mplex_dout,i	\n \
			bsf	_PORTC,1	\n \
			nop			\n \
			nop			\n \
			bsf	_PORTC,0	\n \
			nop			\n \
			nop			\n \
			nop			\n \
			nop			\n \
			nop			\n \
			nop			\n \
			bcf	_PORTC,0	\n \
		_endasm;

	for (y = 0; y < display_ymax; y++){
		// The second byte, rows 8-15, has a problem, rows 10 and 11 are flipped, so correct that when we send it.
		if (y == 1){
			if (current_buffer){
				mplex_dout = bitmap0[current_column][y];
			} else {
				mplex_dout = bitmap1[current_column][y];
			}

			mobit(0);
			mobit(1);
			mobit(3); // swapped
			mobit(2); // swapped
			mobit(4);
			mobit(5);
			mobit(6);
			mobit(7);
		} else { // All other bytes are normal.
			if (current_buffer){
				mplex_dout = bitmap0[current_column][y];
			} else {
				mplex_dout = bitmap1[current_column][y];
			}

			mobit(0);
			mobit(1);
			mobit(2);
			mobit(3);
			mobit(4);
			mobit(5);
			mobit(6);
			mobit(7);
		}

	}

	// Some notes on the hardware... The FETs are connected directly to
	// various ports on the pics. The order of the pin connections is as
	// follows:

	// PORTE -  0-7
	// PORTD -  8-15
	// PORTB - 16-19 **4 bits** remaining pins unused
	// PORTA - 20-23 **4 bits** remaining pins unused
	// PORTF - 24-31
	// PORTG - 32-34 **3 bits** remaining pins unused

	// The MM5451 will latch the values we have given it to it's outputs on
	// the next clock pulse, so now is the time to figure out which column
	// FET to turn on.

	// Turn off the column FETs.
	PORTE = PORTD = PORTB = PORTA = PORTF = PORTG = 0;

	// Figure out which FET to turn on.

	if (current_column <= 7){
		// Column 2 and 3 are swapped.
		if (current_column == 2){
			PORTE = 1 << 3;
		}
		else if (current_column == 3){
			PORTE = 1 << 2;
		}
		else{
			PORTE = 1 << current_column;
		}
	}
	else if ((current_column >= 8) && (current_column <= 15)){
		PORTD = (1 << (current_column - 8));
	}
	else if ((current_column >= 16) && (current_column <= 19)){
		PORTB = 1 << (current_column - 16);
	}
	else if ((current_column >= 20) && (current_column <= 23)){
		PORTA = 1 << (current_column - 20);
	}
	else if ((current_column >= 24) && (current_column <= 31)){
		PORTF = 1 << (current_column - 24);
	}
	else if ((current_column >= 32) && (current_column <= 34)){
		PORTG = 1 << (current_column - 32);
	}

	// Pulse clock for an end bit.
	mm_out(0);
	mm_out(0);

}