/* led7seg.h - Seven segment LED driver for Dragon12-Plus
 * Copyright 2008 Colin D. Bennett <colin@gibibit.com>.
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *    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 3 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, see <http://www.gnu.org/licenses/>.
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * This driver controls the four 7-segment LED displays on the Dragon12-Plus
 * board.  These displays are on PP0:3 and PB0:7.
 */

#include "hc12drivers/led7seg.h"
#include "hc12/hcs12.h"
#include "hc12/interrupt.h"
#include "hc12/vectors12.h"

volatile uint8_t led7seg_bits[4];
volatile uint8_t led7seg_brightness[4];

static uint8_t digitbit;
static uint8_t digitindex;
static uint8_t cycle;

DECLARE_ISR(led7seg_update_isr);

void led7seg_init(void)
{
	digitindex = 0;
	digitbit = 1;
	cycle = 0;

	for (uint8_t i = 0; i < 4; i++)
	{
		led7seg_bits[i] = 0;
		led7seg_brightness[i] = 255;
	}

	/*** Configure 7-segment display. ***/
	/*
	 * The common cathodes of the digits 3, 2, 1, and 0 are driven by 
	 * PP0, PP1, PP2, and PP3, respectively.
	 *
	 * The anodes are separate for each of the 7 segments:
	 *       
	 *       --0--           Each of these numbers is the port B pin
	 *    5 |     | 1        number that controls the segment.  Pin 7
	 *      |     |          controls the decimal point, which is actually
	 *       --6--           in the upper left for the last 2 digits.
	 *      |     |          (This allows a colon to be displayed between
	 *    4 |     | 2        the 2nd and 3rd digits.)
	 *       --3--     . 7 
	 *
	 */
	DDRB = 0xFF;   // Set port B to output for LEDs (PB7:0 are LED anodes).
	DDRP = 0xFF;   // Set port P to output (PP0:3 are common cathodes)

	/*** Set up timer interrupt. ***/
	disable_interrupts();

	/* Set prescale value, clear overflow interrupt enable bit (TOI). */
	TSCR2 = PRESCALE_SETTING;

	TIOS |= TIOS_IOS4;   /* Set channel 4 as output compare. */

	/* Disconnect timer from output pin logic. */
	TCTL1 &= ~(1 << 4);   /* Set OM4=0 ... */
	TCTL2 &= ~(1 << 4);   /* ... and set OL4=0. */

	TIE |= (1 << 4);      /* Set C4I to enable OC4 interrupt. */
	TSCR |= TEN;   /* Set bit 7 of TSCR, timer enable (TEN). */

	/* Set the interrupt vector to point to our interrupt service routine. */
	SET_INTERRUPT_VECTOR(UserTimerCh4, &led7seg_update_isr);

	enable_interrupts();
}

ISR(led7seg_update_isr)
{
	if (cycle <= (led7seg_brightness[digitindex] >> 4))
	{
		/* Set only one cathode to low, all others to high. */
		PTP = ~digitbit;
		PORTB = led7seg_bits[digitindex];    /* Write segment bit pattern. */
	}

	/* Advance to next digit. */
	if (++digitindex < 4)
	{
		digitbit <<= 1;        /* Select the next digit. */
	}
	else
	{
		/* Start over at first digit. */
		digitindex = 0;
		digitbit = 1;

		/* Next cycle (0..15). */
		cycle = (cycle + 1) & 0x0F;
	}

	TC4 = TCNT + LED7SEG_UPDATE_PERIOD_TICKS;
	TFLG1 |= C4F;   /* Clear OC4 interrupt flag. */
}

uint8_t led7seg_lookup_char(char c)
{
	const int A = 1 << 0;
	const int B = 1 << 1;
	const int C = 1 << 2;
	const int D = 1 << 3;
	const int E = 1 << 4;
	const int F = 1 << 5;
	const int G = 1 << 6;
	/*const int DP = 1 << 7;*/

	switch (c) 
	{
		case '0': return A|B|C|D|E|F;
		case '1': return B|C;
		case '2': return A|B|D|E|G;
		case '3': return A|B|C|D|G;
		case '4': return B|C|F|G;
		case '5': return A|F|G|C|D;
		case '6': return A|F|E|D|C|G;
		case '7': return A|B|C;
		case '8': return A|B|C|D|E|F|G;
		case '9': return A|B|C|D|F|G;
		case 'A': return A|B|C|E|F|G;   /* Uppercase A. */
		case 'B': return F|E|D|C|G;     /* Lowercase b. */
		case 'C': return A|F|E|D;       /* Uppercase C. */
		case 'D': return B|C|D|E|G;     /* Lowercase d. */
		default: return G;   /* Return "-" on error. */
	}
}

