/*
 * DRO_GLCD.c <- This is the old name of the project, but a GLCD was to bad to read
 * So I decided to use a 7-segment display
 * Created: 10.11.2015 16:09:31
 *  Author: Peter Menges
 */ 
/***************************
 Be aware that in this application only the first 16 bits of the value the calipers sending are used.
 This is because the calipers used for my lathe are only 200 mm long.
 So 16 bits are sufficient to show values up to 655.35 mm in length.
 
****************************/

#include <avr/io.h>

#include "portdefs.h"


extern void Init_Timer(void);

extern volatile uint8_t key_press;

volatile uint8_t z_axis[CAL_LENGTH];				// 24 bits for the values
volatile uint8_t x_axis[CAL_LENGTH];				// sure, these are bytes, but faster access in the ISR!
// Test 2.54 mm = 0011_0111_1001_0000_0000_1000 MINUS!

		
extern volatile uint8_t cal_sig;			

/* Display Buffer holding the BCD-Values */

/* First 5 bytes are for the upper segments, the remaining for the lower segs*/
/* The sign and the double (bit) information are stored in the first byte upper nibble*/

volatile uint8_t disp_data[DISP_BUF_MAX];		

uint16_t convtab[4]={10,100,1000,10000};

/* Do not call this routine when interrupts are enabled! */
/* remove leading zeros until first location before the decimal pointer
*/
	
void clear_leading_zero(void)
{
	
	uint8_t i;
	uint8_t volatile *p;
	

	p = &disp_data[0];
	for(i=0; i < 2;i++){
		if (((*p & 0x0f) == 0)){
			*p |= 0x0f;
		}
		else
		break;
		p++;
	}
	p = &disp_data[5];
	for(i=5; i < 7;i++){
		if (((*p & 0x0f) == 0)){
			*p |= 0x0f;
		}
		else
		break;
		p++;
	}
	
}
/* Params: *p pointer to the beginning of the disp_data buffer
           val = unsigned value to convert.
*/

void tobcd(volatile uint8_t *p, uint_fast16_t val){
	
	uint_fast8_t i,res;
	volatile uint8_t *bp;
	
	register uint8_t sreg;
	
	sreg = SREG;
	asm("cli");
	
	// first clear old values
	// keep signs and double informations
	bp = p;
	for(i=0; i < (DISP_BUF_MAX/2); i++)
			*bp++ &= 0xf0;
	
		
	i= ((DISP_BUF_MAX/2) - 1);		// Number of digits minus 1
	
	do {
		i--;
		
		for(res=0; val >= convtab[i]; val -= convtab[i]){
			res++;
		}
		*p |= res;
		p++;
		
		
	} while(i);
	*p |= val;
	
	clear_leading_zero();
	SREG = sreg;
}
/* The parameter 'which' gives the offset to the disp_data buffer
   and selects the axis buffer.
*/
uint16_t cal_to_int(uint8_t which)
{
		static volatile uint8_t *bp;
		static uint8_t i;
		uint16_t val;
		register uint8_t sreg;
	
		sreg = SREG;
		asm("cli");
		
		val = 0;
		
		if ( which == CAL_NEW_X){
			
			if (x_axis[CAL_SIGN]==1)
			DISP_BUF_X |= SIGN_POS_MINUS;
			else
			DISP_BUF_X &= ~(SIGN_POS_MINUS);
			
			bp = &x_axis[CAL_BIT16];
		}
			else {
				
			if (z_axis[CAL_SIGN]==1)
			DISP_BUF_Z |= SIGN_POS_MINUS;
			else
			DISP_BUF_Z &= ~(SIGN_POS_MINUS);
			
			bp = &z_axis[CAL_BIT16];
			}
		for(i=0; i < CAL_BIT16; i++){
			
			val |= *bp--;
			if ( i < (CAL_BIT16 - 1))
				val <<= 1;
		}

			
		SREG = sreg;
		return(val);
		
}									

void Init_Display(void)
{
	
		DISP_DATA_DDR |= DISP_DATA_DDR_MSK;
		DISP_CNTRL_DDR |= DISP_DATA_DDR_MSK;
		DISP_DATA = 0x00;	/* all off */
		DISP_CNTRL = (KEY_X_MASK|KEY_Z_MASK); /* Pull up of the inputs on */
}
void Init_IO(void){
	
		DDRD = 0x83; // Only PD7, PD1 and PD0 as output

		
}


int main(void)
{

	uint16_t x_val, z_val;
	volatile uint8_t *bp;
	
			
	Init_Display();
	Init_IO();
	Init_Timer();
	cal_sig = 0;		// Initial value

	
	
    do{

			/********** Z Axis ************/
			if ( cal_sig & CAL_NEW_Z){
					z_val = cal_to_int(CAL_NEW_Z);
					if ( DISP_BUF_Z & SIGN_POS_DOUBLE)
							z_val <<= 1;
							
					tobcd(&DISP_BUF_Z,z_val);	
					cal_sig &= ~(CAL_NEW_Z);
			}
			if ( cal_sig & CAL_INVALID_Z){ // Error condition Z!
			
					bp =&DISP_BUF_Z;
					for(uint8_t i=0; i< (DISP_BUF_MAX/2);i++)
							*bp++ = 0x0c;
							
					cal_sig &= ~(CAL_INVALID_Z);	
			}
			/********* X Axis *************/
			if ( cal_sig & CAL_NEW_X){
				x_val = cal_to_int(CAL_NEW_X);
					if ( DISP_BUF_X & SIGN_POS_DOUBLE)
							x_val <<= 1;
					
				tobcd(&DISP_BUF_X,x_val);
				cal_sig &= ~(CAL_NEW_X);
			}	
			if ( cal_sig & CAL_INVALID_X){ // Error condition X!
				
				bp = &DISP_BUF_X;
				for(uint8_t i=0; i< (DISP_BUF_MAX/2);i++)
						*bp++ = 0x0c;
						
				cal_sig &= ~(CAL_INVALID_X);
			}	
			/**** KEY X Axis *********************/							
			if ( key_press & KEY_X_MASK){
						DISP_BUF_X ^= SIGN_POS_DOUBLE; // Toggle this bit
						key_press &= ~(KEY_X_MASK);
			}
			/**** KEY Z Axis *********************/	
			if ( key_press & KEY_Z_MASK){
				DISP_BUF_Z ^= SIGN_POS_DOUBLE;			// Toggle this bit too
				key_press &= ~(KEY_Z_MASK);
			}
			
			
	}while(1);
      
   
}