/* Ovládání sedmisegmentového displeje
 * led_disp.h
 *
 * Created:	1.2.2014 20:00:00
 * Author:	David Müller 
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <avr/pgmspace.h>
#include "../_common/common.h"
#include "led_disp.h"

// tabulka pro převod číslic na kombinace segmentů
static const uint8_t charset[] PROGMEM = {
	0b11010111,	// 0
	0b10000001,	// 1
	0b11001110,	// 2
	0b11001011,	// 3	
	0b10011001,	// 4
	0b01011011,	// 5
	0b01011111,	// 6
	0b11000001,	// 7
	0b11011111,	// 8
	0b11011011	// 9
	};

// interní proměnné programového modulu
static volatile uint8_t dispOut[3];							// registr segmentů
static volatile uint8_t loop;								// délka multiplexní smyčky (odpovídá jasu)
static volatile uint8_t ct;									// čítač slotů
static volatile uint8_t dispBlink;							// čítač délky bliknutí

// inicializace displeje
void disp_init(const uint8_t brightness)
{
	DDRA |= 0b11100000;						// nastavení výstupů pro řízení společných anod
	DDRB = 0b11111111;						// nastavení výstupů pro segmenty
	
	PORTA &= 0b00011111;					// žádná pozice
	PORTB = 0b00000000;						// žádný segment

	disp_blink(FALSE);
	disp_off(FALSE);	
	disp_brightness(brightness);
}

// nastavení (změna) jasu displeje
void disp_brightness(const uint8_t brightness)
{
	if (!brightness)
	{
		// vypnutí zobrazování (použito při ladění, aby při break nedošlo k poškození displeje)
		loop = 0;
		return;
	}
	if (brightness < 5)
		loop = 10 + (1<<(7-brightness));
	else
		loop = 10;
}

// Zobrazení kódu stavu/chyby "E n"  	
void disp_err(const uint8_t bcd)
{
	dispOut[0] = SYMBOL_E;
	if (bcd >> 4)
		dispOut[1] = pgm_read_byte(&charset[bcd >> 4]);
	else
		dispOut[1] = SYMBOL_empty;
	dispOut[2] = pgm_read_byte(&charset[bcd & 0x0F]);
}

// Zobrazení hodnoty v deci jednotkách
void disp_10(int16_t temp)
{
	// řešení znaménka (minus bar je zapojen v obvodu segmentovky dispOut[0])
	if (temp < 0)
	{
		dispOut[0] = SYMBOL_MINUS;
		temp = -temp;
	}
	else
		dispOut[0] = SYMBOL_empty;
			
	// zobrazení abs.hodnoty
	
	if (temp < 1000)
	{		
		// zobrazení ve formátu XX.X
		uint8_t x = temp/100;
		if (x)		
			// čísla na pozici desítek pouze nenulové
			dispOut[0] |= pgm_read_byte(&charset[x]);
			
		dispOut[1] = pgm_read_byte(&charset[temp%100/10]) | SYMBOL_DP;
		dispOut[2] = pgm_read_byte(&charset[temp%10]);		
	}
	else
	{
		// zobrazení ve formátu XXX.
		
		if (temp%10 > 4)
			// korekce pro správné zaokrouhlení jednotek
			temp += 5;
			
		dispOut[0] |= pgm_read_byte(&charset[temp/1000]);
		dispOut[1] = pgm_read_byte(&charset[temp%1000/100]);
		dispOut[2] = pgm_read_byte(&charset[temp%100/10]) | SYMBOL_DP;
	}	
}

// zhasnutí displeje
void disp_off(const bool_t dotPoint)
{
	dispOut[0] = SYMBOL_empty;
	dispOut[1] = SYMBOL_empty;
	if (dotPoint)
		dispOut[2] = SYMBOL_DP;
	else
		dispOut[2] = SYMBOL_empty;
}

// zobrazení obecné
void disp_print(const uint8_t text[static 3])
{
	memcpy((void *)dispOut, text, 3);
}

// nastavení blikání displeje
void disp_blink(const bool_t blink)
{
	dispBlink = (uint8_t)blink;
}

// obsluha časování (multiplex + blikání) - voláno z hl. programu každých 100us
void disp_tick(void)
{
	if (loop)
		ct++;
	if (ct >= loop)
		ct = 0;

	if ((((ct & 1) == 1) && (ct < 6)) && ((dispBlink & 0x1000) == 0))
	{
		PORTA &= 0b00011111;				// vypnutí spol. anod
		PORTA |= (1 << ((ct >> 1) + 5 ));	// spínání spol. anod (PA5..PA7)
		PORTB = dispOut[(ct >> 1)];			// spínání segmentů
	}
	else
	{
		PORTA &= 0b00011111;				// vypnutí spol. anod
		PORTB = 0b00000000;					// vypnutí segmentů
	}
	
	if (dispBlink)
	{
		dispBlink++;
		if (dispBlink == 0x9FFF)
		dispBlink = 1;
	}
}
