/*
        MPL115A1 SPI Digital Barometer Test Code
        Created on: April 20, 2010
        By: Jim Lindblom - jim at sparkfun.com
        
        This is a simple test program for the MPL115A1 Pressure Sensor (SPI version).
        UBRR0 is set to 51 and U2X0 is not set, so the baud rate will be 9600@8MHz and 19200@16MHz.
        Press the spacebar to get the pressure measurement.
        
        Hardware: ATmega328 (I used the Arduino Pro platform)
        Powered at 3.3V, running at 8MHz.
        The sensor is 5V tolerant, and this code should also work on 5V/16MHz Arduinos.
        
        MPL115A1 Breakout ------------- Arduino
        -----------------                               -------
                        SDN     ------------------- D9 (PB1)
                        CSN     ------------------- D8 (PB0)
                        SDO     ------------------- D12 (PB4)
                        SDI     ------------------- D11 (PB3)
                        SCK     ------------------- D13 (PB5)
                        GND     --------------------- GND
                        VDD     ------------------- VCC (3.3V)
                        
        License: CCAv3.0 Attribution-ShareAlike (http://creativecommons.org/licenses/by-sa/3.0/)
        You're free to use this code for any venture, but I'd love to hear about what you do with it,
        and any awesome changes you make to it. Attribution is greatly appreciated.
*/

//======================//
//              Includes                //
//======================//
#include <avr/io.h>
#include <stdio.h>
#include "MPL115A1.h"

//======================//
//               Macros                 //
//======================//
#define sbi(var, mask)   ((var) |= (uint8_t)(1 << mask))
#define cbi(var, mask)   ((var) &= (uint8_t)~(1 << mask))

//======================//
//       MPL115A1 Defines       //
//======================//
#define CS 0 //pin for chip select
#define SDN 1   // pin for interrupt

//======================//
//      General Functions       //
//======================//
void ioinit(void);
void delay_ms(uint16_t x);
void delay_us(uint16_t x);
static int uart_putchar(char c, FILE *stream);
uint8_t uart_getchar(void);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

//======================//
//      MPL115A1 Functions      //
//======================//
void init_SPI(void);
void txdata(char data);
char rxdata(void);
char read(uint8_t address);
void write(uint8_t address, char data);
float calculatePressure(void);

//======================//
//       Global Variables       //
//======================//
signed char sia0MSB, sia0LSB;
signed char sib1MSB, sib1LSB;
signed char sib2MSB, sib2LSB;
signed char sic12MSB, sic12LSB;
signed char sic11MSB, sic11LSB;
signed char sic22MSB, sic22LSB;
signed int sia0, sib1, sib2, sic12, sic11, sic22, siPcomp;
float decPcomp;
signed long lt1, lt2, lt3, si_c11x1, si_a11, si_c12x2;
signed long si_a1, si_c22x2, si_a2, si_a1x1, si_y1, si_a2x2;
unsigned int uiPadc, uiTadc;
unsigned char uiPH, uiPL, uiTH, uiTL;


int main(void)
{
        ioinit();
        
        init_SPI();
        sbi(PORTB,CS);  // CS Idle High
        sbi(PORTB,SDN); // SDN high turns sensor on
        
        printf("\n***************MPR115A1 Test****************\n\n");
        
        // main program loop
        while(1){
                printf("\nPress space to print pressure\n");
                while( !(UCSR0A & (1<<RXC0)) )
                        ;
                if (UDR0 == ' ')
                        printf("Pressure = %f\n", calculatePressure());
        }
}

float calculatePressure()
{
        write(0x24, 0x00);      // Start Both Conversions
        // write(0x20, 0x00);   // Start Pressure Conversion
        // write(0x22, 0x00);   // Start temperature conversion
        delay_ms(10);   // Typical wait time is 3ms
        
        // Read pressure
        uiPH = read(PRESH);
        uiPL = read(PRESL);
        uiTH = read(TEMPH);
        uiTL = read(TEMPL);
        
        uiPadc = (unsigned int) uiPH << 8;
        uiPadc += (unsigned int) uiPL & 0x00FF;
        uiTadc = (unsigned int) uiTH << 8;
        uiTadc += (unsigned int) uiTL & 0x00FF;
        
        // Placing Coefficients into 16-bit Variables
        // a0
        sia0MSB = read(A0MSB);
        sia0LSB = read(A0LSB);
        sia0 = (signed int) sia0MSB << 8;
        sia0 += (signed int) sia0LSB & 0x00FF;
        
        // b1
        sib1MSB = read(B1MSB);
        sib1LSB = read(B1LSB);
        sib1 = (signed int) sib1MSB << 8;
        sib1 += (signed int) sib1LSB & 0x00FF;
        
        // b2
        sib2MSB = read(B2MSB);
        sib2LSB = read(B2LSB);
        sib2 = (signed int) sib2MSB << 8;
        sib2 += (signed int) sib2LSB & 0x00FF;
        
        // c12
        sic12MSB = read(C12MSB);
        sic12LSB = read(C12LSB);
        sic12 = (signed int) sic12MSB << 8;
        sic12 += (signed int) sic12LSB & 0x00FF;
        
        // c11
        sic11MSB = read(C11MSB);
        sic11LSB = read(C11LSB);
        sic11 = (signed int) sic11MSB << 8;
        sic11 += (signed int) sic11LSB & 0x00FF;
        
        // c22
        sic22MSB = read(C22MSB);
        sic22LSB = read(C22LSB);
        sic22 = (signed int) sic22MSB << 8;
        sic22 += (signed int) sic22LSB & 0x00FF;
        
        // Coefficient 9 equation compensation
        uiPadc = uiPadc >> 6;
        uiTadc = uiTadc >> 6;
        
        // Step 1 c11x1 = c11 * Padc
        lt1 = (signed long) sic11;
        lt2 = (signed long) uiPadc;
        lt3 = lt1*lt2;
        si_c11x1 = (signed long) lt3;
        
        // Step 2 a11 = b1 + c11x1
        lt1 = ((signed long)sib1)<<14;
        lt2 = (signed long) si_c11x1;
        lt3 = lt1 + lt2;
        si_a11 = (signed long)(lt3>>14);
        
        // Step 3 c12x2 = c12 * Tadc
        lt1 = (signed long) sic12;
        lt2 = (signed long) uiTadc;
        lt3 = lt1*lt2;
        si_c12x2 = (signed long)lt3;
        
        // Step 4 a1 = a11 + c12x2
        lt1 = ((signed long)si_a11<<11);
        lt2 = (signed long)si_c12x2;
        lt3 = lt1 + lt2;
        si_a1 = (signed long) lt3>>11;
        
        // Step 5 c22x2 = c22*Tadc
        lt1 = (signed long)sic22;
        lt2 = (signed long)uiTadc;
        lt3 = lt1 * lt2;
        si_c22x2 = (signed long)(lt3);
        
        // Step 6 a2 = b2 + c22x2
        lt1 = ((signed long)sib2<<15);
        lt2 = ((signed long)si_c22x2>1);
        lt3 = lt1+lt2;
        si_a2 = ((signed long)lt3>>16);
        
        // Step 7 a1x1 = a1 * Padc
        lt1 = (signed long)si_a1;
        lt2 = (signed long)uiPadc;
        lt3 = lt1*lt2;
        si_a1x1 = (signed long)(lt3);
        
        // Step 8 y1 = a0 + a1x1
        lt1 = ((signed long)sia0<<10);
        lt2 = (signed long)si_a1x1;
        lt3 = lt1+lt2;
        si_y1 = ((signed long)lt3>>10);
        
        // Step 9 a2x2 = a2 * Tadc
        lt1 = (signed long)si_a2;
        lt2 = (signed long)uiTadc;
        lt3 = lt1*lt2;
        si_a2x2 = (signed long)(lt3);
        
        // Step 10 pComp = y1 + a2x2
        lt1 = ((signed long)si_y1<<10);
        lt2 = (signed long)si_a2x2;
        lt3 = lt1+lt2;
        
        // Fixed point result with rounding
        //siPcomp = ((signed int)lt3>>13);
        siPcomp = lt3/8192;
        
        // decPcomp is defined as a floating point number
        // Conversion to decimal value from 1023 ADC count value
        // ADC counts are 0 to 1023, pressure is 50 to 115kPa respectively
        decPcomp = ((65.0/1023.0)*siPcomp)+50;
        
        return decPcomp;
}

void write(uint8_t address, char data)
{
        //write any data byte to any single address
        //adds a 0 to the MSB of the address byte (WRITE mode)

        address &= 0x7F;        

        //printf("\nWriting 0x%x to 0x%x\n", data, address);

        cbi(PORTB,CS);
        delay_ms(1);
        txdata(address);
        delay_ms(1);
        txdata(data);
        delay_ms(1);
        sbi(PORTB,CS);
}

char read(uint8_t address)
{
        //returns the contents of any 1 byte register from any address
        //sets the MSB for every address byte (READ mode)

        char byte;

        address |= 0x80;

        cbi(PORTB,CS);
        txdata(address);
        byte = rxdata();
        sbi(PORTB,CS);

        return byte;
}

char rxdata(void)
{
        SPDR = 0x55;
        
        while((SPSR&0x80) == 0x00)
                ;

        return SPDR;
}

void txdata(char data)
{
        SPDR = data;
        while((SPSR&0x80) == 0x00)
                ;
}

void init_SPI(void)
{
        sbi(SPCR,MSTR); //make SPI master
        cbi(SPCR,CPOL); cbi(SPCR,CPHA); //SCL idle low, sample data on rising edge
        cbi(SPCR,SPR1); cbi(SPCR,SPR0); sbi(SPSR,SPI2X); //Fosc/4 is SPI frequency
        sbi(SPCR,SPE); //enable SPI
}

static int uart_putchar(char c, FILE *stream)
{
    if (c == '\n') uart_putchar('\r', stream);

    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;

    return 0;
}

uint8_t uart_getchar(void)
{
    while( !(UCSR0A & (1<<RXC0)) )
                ;
    return(UDR0);
}


void ioinit (void)
{
        int MYUBRR = 51;        // Results in 9600bps@8MHz or 19200bps@16MHz
        
    //1 = output, 0 = input
    //DDRA = 0b00000000; //ADC inputs
    DDRB = 0b11101111; //MISO input
    DDRC = 0b11111111; //All outputs
    DDRD = 0b11111110; //PORTD (RX on PD0)
        stdout = &mystdout; //Required for printf init

        UBRR0H = (MYUBRR) >> 8;
        UBRR0L = MYUBRR;
        UCSR0B = (1<<RXEN0)|(1<<TXEN0);
        UCSR0C = (3<<UCSZ00);

        TCCR2B = (1<<CS21);
}

//General short delays
void delay_ms(uint16_t x)
{
    for (; x > 0 ; x--)
        delay_us(1000);
}

//General short delays
void delay_us(uint16_t x)
{
    while(x > 256)
    {
        TIFR2 = (1<<TOV2); //Clear any interrupt flags on Timer2
        TCNT2 = 0; //256 - 125 = 131 : Preload timer 2 for x clicks. Should be 1us per click
        while( (TIFR2 & (1<<TOV2)) == 0);

        x -= 256;
    }

    TIFR2 = (1<<TOV2); //Clear any interrupt flags on Timer2
    TCNT2= 256 - x; //256 - 125 = 131 : Preload timer 2 for x clicks. Should be 1us per click
    while( (TIFR2 & (1<<TOV2)) == 0);
}