/* 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.
 */
#ifndef HC12DRIVERS_LED7SEG_H_
#define HC12DRIVERS_LED7SEG_H_

#include <stdint.h>

#define PRESCALE_SETTING 5
#define PRESCALE_FACTOR (1 << PRESCALE_SETTING)

/* The period between calls to the interrupt handler. 
 * To handle PWM brightness modulation, the interrupt handler
 * needs to be called much more frequently than even for a normal
 * multiplexed set of four 7-segment displays.  */
#define LED7SEG_UPDATE_PERIOD_US 150

/* Number of timer ticks per refresh cycle.  */
/* ms * sec/ms * cycles/sec * ticks/cycle = ticks */
#define LED7SEG_UPDATE_PERIOD_TICKS \
	(F_CPU / PRESCALE_FACTOR * LED7SEG_UPDATE_PERIOD_US / 1000000ULL)
#if (LED7SEG_UPDATE_PERIOD_TICKS) < 2ULL
# error Too small LED7SEG_UPDATE_PERIOD_TICKS
#endif
#if !(LED7SEG_UPDATE_PERIOD_TICKS < 65530ULL)
# error Too large LED7SEG_UPDATE_PERIOD_TICKS for 16-bit timer
#endif

/** The number of 7-segment digits available. */
#define LED7SEG_DIGITS 4

/* Bit patterns for the digits 1, 2, 3, 4.  */
extern volatile uint8_t led7seg_bits[];

/* Brightness values for the digits. 
 * Pulse width modulation is used by this driver to adjust the
 * amount of time that the segments of the given digit are illuminated
 * during the refresh cycles (the duty cycle determines the apparent
 * brightness).  */
extern volatile uint8_t led7seg_brightness[];

/**
 * Initialize the 7-segment LED display.
 * The interrupt handler is installed and interrupts are globally
 * enabled.  The interrupt handler uses timer/counter channel 4 as an
 * output compare channel, and sets the timer prescale value.  If your
 * program needs to use a different prescale value, you should define it
 * before including this header file.  The interrupt handler is called 
 * repeatedly at a high frequency, several thousand times per second.
 *
 * The led7seg_bits array is cleared so that the display
 * will be blank (no segments illuminated) and the brightness for each
 * digit is set the 255, the maximum brightness.
 *
 * The application can modify the led7seg_bits and led7seg_brightness
 * arrays in order to change the displayed digits.  This should be done
 * in a critical section, which is to say that interrupts should be
 * disabled while the arrays are modified and re-enabled immediately 
 * afterward.  Leaving interrupts disabled for more than 100 us or so
 * may cause visible glitches in the display, since LED digit refresh
 * will be suspended during that time.
 */
void led7seg_init(void);

/** Look up the bit pattern for a character. */
uint8_t led7seg_lookup_char(char c);

#endif /* HC12DRIVERS_LED7SEG_H_ */
