/*
 * finalproject.c
 *
 *  Created on: May 14, 2015
 *      Author: balyrd
 */
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>

#include "peekpoke.h"	//header files available from manufacturer
#include "eeprog.h"
#include "ep93xx_adc.h"

#define DATA_PAGE 0x80840000
#define CALIB_LOC    2027          //location of calibration values
#define DOSETIME 50000		//defined value for time to keep does pump on in us

/* Prototypes */
static double read_ADC_channel(int channel);	//take analog reading
static void init_adc_channel_calibration();		//set up analog channel
int getLightLevel(double voltage);		//converts light voltage to a level 1-5


/* globals */
static unsigned long dr_page, adc_page, syscon_page, pld_page;
int stored_cal[5][2];  //stored calibration values for ADC, first dimension are
                       // 5 channel, second dimension are 2 calibration values
int virgin = TRUE;        //calibration detected?


//child process converts and prints temperature
void child(int pipe_p,int pipe_c){
	double temp,temp_v;
	while(1)
	{
	read(pipe_c,&temp_v,sizeof(&temp_v));	//read from pipe
	temp=25*(temp_v/1.65);//temp in c
	printf("temp: %f ,%.2f\n",temp_v,temp);	//print value
	usleep(2000000);
	}

}



int main(void)
{

	double light_v,temp_v;	//light and temp as voltage
	int light,float1,float2,flag=0; //levels of floats and light
	unsigned long *pbdr, *pbddr, *fptr;//register pointers
	unsigned long *input;

	int pid;
	//create pipes one probably would have worked
	int pipe_p[2],pipe_c[2];
	pipe(pipe_p);
	pipe(pipe_c);
	pid=fork();

	//setup child
	if(pid==0)	//child
	{
		close(pipe_p[0]);
		close(pipe_c[1]);
		child(pipe_p[1],pipe_c[0]);
	}



	//proper register memory mappings
	int devmem = open("/dev/mem", O_RDWR|O_SYNC);
	assert(devmem != -1);

	//for analog measurements
	dr_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
		, MAP_SHARED, devmem, DATA_PAGE);
	assert(&dr_page != MAP_FAILED);

	spistart = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, SPI_PAGE);
	assert(&spistart != MAP_FAILED);

	adc_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, ADC_PAGE);
	assert(&adc_page != MAP_FAILED);

	syscon_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
		, MAP_SHARED, devmem, SYSCON_PAGE);
	assert(&syscon_page != MAP_FAILED);

	pld_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
		MAP_SHARED, devmem, PLD_PAGE);
	assert(&pld_page != MAP_FAILED);

	//iniate analog channels
	init_adc_channel_calibration(0);
	init_adc_channel_calibration(1);

	//intiatlize digital input
	fptr=(unsigned long *)mmap(NULL,getpagesize(),PROT_READ|PROT_WRITE,MAP_SHARED,devmem,0x80840000);
	pbddr=fptr+0x14/4;
	*pbddr=0xfC;//d0-1 input rest output
	pbdr=fptr+1;
	*pbdr=*pbdr|0x1C;//set d2 and d3 high as the relay is active low

	while(1){
	temp_v=read_ADC_channel(0);	//temp voltage
	write(pipe_c[1],&temp_v,sizeof(&temp_v)); //send voltage over pipe to child

	light_v=read_ADC_channel(1);//light voltage

	light=getLightLevel(light_v);//convert light to level 1-5

	input=*pbdr & 0x03;		//mask input bits

	if((input==1)||(input==0)){//float 1 is high
		float1=1;
	}
	else{//float 1 is low
		float1=0;
		flag=1;	//tank refill will occur, flag for dosing
	}
	if((input==2)||(input==0)){ //float 2 is high
			float2=1;
		}
		else //float 2 is low
			float2=0;
	if(float2==1){
		*pbdr=*pbdr|0x04;//drive d2 high
		if(flag){
			*pbdr=*pbdr&0xF7;//dose
			flag=0;//stop dosing
			usleep(DOSETIME);//wait with pump on
		}
		else
			*pbdr=*pbdr|0x08;//stop dosing
		}
	else if(float1==0)
		*pbdr=*pbdr&0xFB;//drive d2 low

	if(light<3){
		*pbdr=*pbdr&0xEF; //turn on relay for light
	}
	else{
		*pbdr=*pbdr|0x10;
	}

	//display float and light levels
	printf("float 1: %d, float 2: %d\n",float1,float2);
	printf("light: %f, %d\n",light_v,light);



	//wait a while
	usleep(2000000);
	}
	close(devmem);

	return 0;
}

//function originally from manufacturer slightly modified for adc calibration
/************************************************************************
 *DESCRIPTION: Read all five of the EP93xx onboard ADC. Discard the first
 *two samples then save the next 10.
 ***********************************************************************/
/************************************************************************
 *DESCRIPTION: Figure out the calibration value of the board and store them
 *stored_cal global variable. In eeprom 2027->2046 are the calibration values. 2027->2036
*are the zero volt calibration values and 2037->2046 are the 2.5V calibration
*values. Each calibration value is 16 bits written using little endian
 ***********************************************************************/
static void init_adc_channel_calibration(){
    int i, j, ch, addr;
    char buffer[20];

    /* intialize the eeprom */
    POKE16(pld_page, (PEEK16(pld_page) & ~CS_MSK)); //disable CS
    POKE32((spistart + SSPCR1), 0x10);  //turn on transmit
    while ((PEEK32(spistart + 0xc) & 0x10) == 0x10); // wait for unbusy
    while ((PEEK32(spistart + 0xc) & 0x5) != 0x1);   // empty FIFO's
    POKE16(pld_page, (PEEK16(pld_page) | CS_MSK));   //enable CS

    POKE32(spistart, 0xc7);                          // set SPI mode 3, 8bit
    POKE32(spistart + 0x10, 0x2);                    // divide clk by 2
    POKE32(spistart + 0x4, 0x0);                     // stop transmit

    while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

    ee_wren(dr_page);
    ee_wrsr(dr_page, 0x0);              // make eeprom R/W

    while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

    /* Read in the calibration values */
    //printf("Calibration Values = \n[ ");

    addr = CALIB_LOC;
    for(i = 0; i < 20; i++)
    {
        ee_read_byte(dr_page, addr, &buffer[i]);

        //check if board has stored calibration values
        if(buffer[i] != 0xFF)
            virgin = FALSE;
        addr++;
    }

    //convert to 16 bit values
    j = 0;
    ch = 0;
    for(i = 0; i < 20; i = i + 2)
    {
        if(i == 10)
        {
            ch = 0;
            j = 1;
        }

        stored_cal[ch][j] = (buffer[i] | (buffer[i+1] << 8));
        //printf("(%d, %d) = 0x%x | ", ch, j, stored_cal[ch][j]);
        ch++;
    }
    //printf(" ]\n");

    //make eeprom RO
    ee_wren(dr_page);
    ee_wrsr(dr_page, 0x1c);
}

//modified function which will read one analog value at a time based on input modified from manufactuer
//which would measure and print all at once.  returns the analog reading.
double read_ADC_channel(int ch_num){
    double val, full_scale;
    int cur_ch;
    int result;

    /* intialize the eeprom */
    POKE16(pld_page, (PEEK16(pld_page) & ~CS_MSK)); //disable CS
    POKE32((spistart + SSPCR1), 0x10);  //turn on transmit
    while ((PEEK32(spistart + 0xc) & 0x10) == 0x10); // wait for unbusy
    while ((PEEK32(spistart + 0xc) & 0x5) != 0x1);   // empty FIFO's
    POKE16(pld_page, (PEEK16(pld_page) | CS_MSK));   //enable CS

    POKE32(spistart, 0xc7);                          // set SPI mode 3, 8bit
    POKE32(spistart + 0x10, 0x2);                    // divide clk by 2
    POKE32(spistart + 0x4, 0x0);                     // stop transmit

    while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

    ee_wren(dr_page);
    ee_wrsr(dr_page, 0x0);              // make eeprom R/W

    while((ee_rdsr(dr_page) & 0x1) == 0x1);//wait for unbusy

    //read channel based on input parameter
    switch(ch_num)
    {
        case 0:	
            cur_ch = ADC_CH0;
        break;
        case 1:
            cur_ch = ADC_CH1;
        break;
        case 2:
            cur_ch = ADC_CH2;
        break;
        case 3:
            cur_ch = ADC_CH3;
        break;
        case 4:
            cur_ch = ADC_CH4;
        break;
    }

   
    //discard first two samples
    read_channel(adc_page, cur_ch);
    read_channel(adc_page, cur_ch);
    usleep(10000);
    result = read_channel(adc_page, cur_ch);

	//callibration
    full_scale = (((((double)(stored_cal[ch_num][1] + 0x10000)
        - stored_cal[ch_num][0]) / 2.5 ) * 3.3 ));

    if(result < 0x7000)
        result = result + 0x10000;

    if(virgin == TRUE)  //use approximation
    {
        result = result - 0x9E58;
        val = ((double)result * 3.3) / 0xC350;
    }
    else                //use calibration values
    {
        result = result - stored_cal[ch_num][0];
        val = ((double)result * 3.3) / full_scale;
    }



    //make eeprom RO
    ee_wren(dr_page);
    ee_wrsr(dr_page, 0x1c);

    return val;
}

//function which converts voltage to an understandable level of light
int getLightLevel(double voltage){
	if(voltage<=.05)//full daylight
		return 5;
	else if(voltage<=.25)//overcast day
		return 4;
	else if(voltage<=1)//very overcast
		return 3;
	else if(voltage<=1.9)//dark room
		return 2;
	else	//extremely dark
		return 1;
}

