#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <htc.h>
#include "usart.h"

#define BUFSIZE 16
#define CR  0x0D
#define BS  0x08
#define EXLITE RB0     // Fluorometer excitation light
#define EXLITEBIT TRISB0
#define ON  1
#define OFF 0
#define TRUE 1
#define FALSE 0

void init_timers(void);
void measure(unsigned char);
void timeset(void);
void interrupt isr(void);

/* Control program for the Sedimentation Event Sensor Fluorometer
 *
 * Revision history
 *
 * v 1.2.1      19Aug11 Added error checking to getch()
 *
 * v 1.2.0      16Aug11 Fixed bug in timer1 counting routine,
 *                      added dark measurement command
 * v 1.1.0      6Jun11  Changed PIC from 16F876A to 16F628,
 *                      changed excitation light bit from RC3 to RB0
 * v 1.0.0      3Apr11  Initial release */

__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & LVP_OFF & CPD_OFF);

volatile unsigned char done;        // measurement done flag
volatile unsigned int msCounter;    // number of elapsed millisecs
volatile unsigned int integTime;    // number of millisecs to integrate
volatile unsigned int chlaCount;    // count from chlorophyll A PMT
volatile unsigned int phycoCount;   // count from phycoerythrin PMT

void main(void){
    char cmdbuf[BUFSIZE];
    unsigned char i, c;

    di();               // disable all interrupts
    init_timers();      // set up all three timers
    init_comms();	// set up the USART - settings defined in usart.h
    EXLITEBIT = 0;      // set up exitation light bit as output
    EXLITE = OFF;       // turn light off
    ei();               // enable all interrupts
    integTime = 1000;   // integrate for 1 sec

    printf("\r\nSES Fluorometer v1.2.1\r\n");
    while(1) {              // main loop
        printf("\r\n> ");   // print prompt
        i = 0;
        do {
            c = getch();
            if(c == BS) {
                if(i > 0) {
                    i--;
                    putch(c);
                    putch(' ');
                    putch(c);
                } // end if
            } else {
                cmdbuf[i++] = c;
                putch(c);
            } // end if
        } while(c != CR && i < BUFSIZE); // until CR or buffer full
        cmdbuf[i-1] = NULL; // get rid of CR
        // parse command
        if(!cmdbuf[0]);     // empty command, do nothing
        else if(!strcmp(cmdbuf, "measure")) measure(TRUE);
        else if(!strcmp(cmdbuf, "dark")) measure(FALSE);
        else if(!strcmp(cmdbuf, "timeset")) timeset();
        else if(!strcmp(cmdbuf, "lighton")) {
            EXLITE = ON;
            printf("\r\nlight is ON");
        } // end else if
        else if(!strcmp(cmdbuf, "lightoff")) {
            EXLITE = OFF;
            printf("\r\nlight is OFF");
        } // end else if
        else if(!strcmp(cmdbuf, "help") || !strcmp(cmdbuf, "?")) {
            printf("\r\nValid commands are:\r\n");
            printf("measure\t\ttake a measurement\r\n");
            printf("dark\t\tmeasure dark count\r\n");
            printf("timeset\t\tset integration time\r\n");
            printf("lighton\t\tturn light on\r\n");
            printf("lightoff\tturn light off\r\n");
            printf("help or ?\tprint command list");
        } // end if
        else printf("\r\nInvalid command!");
    } // end while
} // end main

void init_timers(void) {

    // Timer0 initialization. Timer0 accumulates the phycoerythrin count.
    // Note: commands that are commented out aren't necessary as they are the
    //  default condition of the registers
    // T0CS = 1;           // clock source is pin RA4
    TRISA4 = 1;         // make RA4 an input
    T0SE = 0;           // increment on rising edge
    // TMR0IE = 0;         // don't enable interrupt yet

    // Timer1 initialization. Timer1 accumulates the chlorophyll A count.
    // TMR1ON = 0;         // disable timer for now
    TMR1CS = 1;         // clock source is external pin RB6
    TRISB6 = 1;         // make RB6 an input
    nT1SYNC = 1;        // don't synchronize clock input
    // T1OSCEN = 0;        // turn off oscillator
    // T1CONbits..T1CKPS = 0b00;   // prescalar divides by 1
    TMR1IE = 0;         // don't enable interrupt yet

    // Timer2 initialization. Timer2 times the integration interval.
    T2CONbits.T2CKPS = 0b01;      // prescaler divides 1 us clock by 4
    // T2CONbits.TOUTPS = 0b0000;    // postscaler divides by 1
    PR2 = 249;          // reset every 250 x 4 us = 1 ms
    TMR2ON = 1;         // turn on timer2
    TMR2IE = 0;         // don't enable interrupt yet

    PEIE = 1;           // enable peripheral interrupts
} // end init_timers()

void measure(unsigned char light) {
    printf("\r\ntaking measurement...");
    done = FALSE;   // measurement in progress
    phycoCount = 0; // clear counters
    chlaCount = 0;
    msCounter = 0;
    // turn light on here
    if(light) {
        EXLITE = ON;    // turn light on if not dark measurement
    } // end if

    TMR2 = 0;       // clear timer2 count
    TMR2IF = 0;     // clear timer2 event flag
    TMR2IE = 1;     // enable timer2 interrupt

    TMR0 = 0;       // clear timer0 count, it's already counting
    TMR0IF = 0;     // clear timer0 event flag
    TMR0IE = 1;     // enable timer0 interrupt

    TMR1 = 0;       // clear timer1 count
    TMR1ON = 1;     // start timer1 counting
    TMR1IE = 1;     // enable timer1 interrupt

    while(!done) {  // wait until measurement done
      // the interrupt service routine will turn off the light
    } // end while
    printf("\r\n%u %u %u", integTime, chlaCount, phycoCount);
} // end measure()


void timeset(void) {
    unsigned int input;
    char inbuf[10];

    printf("\r\nEnter integration time in milliseconds (1 - 10000): ");
    gets(inbuf);
    input = atoi(inbuf);
    if(input > 0 && input <= 10000) {
        integTime = input;
        printf("\r\nIntegration time set to %d ms", integTime);
    } else {
        printf("\r\nInvalid time!");
    } // end if
} // end timeset()

void interrupt isr(void){

    // Timer2 code
    if((TMR2IE)&&(TMR2IF)) {     // another ms has passed
        if(msCounter++ >= integTime) {
            TMR1ON = 0;         // stop timer1 count
            if(TMR0IE) {        // if not overflow
                phycoCount += TMR0; // capture the timer0 count while still counting
            } // end if
            if(TMR1IE) {        // if not overflow
                chlaCount = TMR1;   // capture the timer1 count
            } // end if
            // turn light off here
            EXLITE = OFF;	// turn light off
            TMR2IE = 0;         // disable all timer interrupts
            TMR0IE = 0;
            TMR1IE = 0;
            done = TRUE;        // notify that measurement is done
        } // end if
        TMR2IF = 0;             // clear event flag
    } /* end if */

    // Timer0 code
    if((TMR0IE)&&(TMR0IF)) {    // timer0 has rolled over
       phycoCount += 256;       // add to phyco count
       if(phycoCount >> 8 == 0) {   // if accumulator overflow
           phycoCount = 65535;  // set to max value
           TMR0IE = 0;          // disable any more interrupts
       } // end if
       TMR0IF = 0;              // clear event flag
    } // end if

    // Timer1 code
    if((TMR1IE)&&(TMR1IF)) {    // timer1 has rolled over
        chlaCount = 65535;      // set to max value
        TMR1IE = 0;             // disable any more interrupts
        TMR1IF = 0;             // clear event flag
    } // end if

} // end isr()
