/**
 * @file main.c
 * @brief Timer A0 capture mode for frequency measurement.
 * Signal is generated by Timer B0. 7seg display multiplex
 * is done using Timer A1.
 *
 */
#include <msp430.h> 
#include <stdint.h>
#include "function.h"

/**
 * @brief Timer period
 *
 * Timer is clocked by ACLK (32768Hz)
 * It takes 32768 cycles to count to 1s.
 * If we need a period of X ms, then number of cycles
 * that is written to the CCR0 register is
 * 32768/1000 * X
 */
#define TIMER_PERIOD        (65)  /* ~2ms (1.98ms) */

/**
 * @brief Generated period
 *
 * Generate PWM with 50 Hz frequency
 */
#define GENERATED_PERIOD    (655)  /* ~20ms (19.98ms)  */

/** number of samples before processing starts */
#define SAMPLING_WINDOW     (64)

/** Timer A0 frequency */
#define TIMER_A0_FREQ       (1048576UL)

/**
 * @brief array of digits used for display
 */
static volatile uint8_t digits[2] = {0};

/**
 * @brief Global accumulator for mesured periods
 *
 * ISR stores measured delta between SAMPLING_WINDOW consecutive periods
 */
static volatile uint32_t measured_sum = 0;

/** synchronization flag */
static volatile uint8_t flag = 0;

/**
 * @brief Function that populates digit array
 * @author Strahinja Jankovic
 */
void display(const uint16_t number)
{
    uint16_t nr = number;
    uint8_t tmp;
    for (tmp = 0; tmp < 2; tmp++)
    {
        digits[tmp] = nr % 10;
        nr /= 10;
    }
}
/**
 * @ brief Main function
 * Initialize 7seg LED, Timer A in capture mode
 * Timer B in Compare OUT mode.
 *
*/
int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer

    // sevenseg 1
    P7DIR |= BIT0;              // set P7.0 as out (SEL1)
    P7OUT |= BIT0;             // disable display 1
    // sevenseg 2
    P6DIR |= BIT4;              // set P6.4 as out (SEL2)
    P6OUT |= BIT4;              // disable display 2

    // a,b,c,d,e,f,g
    P2DIR |= BIT6 | BIT3;              // configure P2.3 and P2.6 as out
    P3DIR |= BIT7;                     // configure P3.7 as out
    P4DIR |= BIT3 | BIT0;              // configure P4.0 and P4.3 as out
    P8DIR |= BIT2 | BIT1;              // configure P8.1 and P8.2 as out

    /* initialize Timer A1 for multiplexing*/
    TA1CCR0 = TIMER_PERIOD;
    TA1CCTL0 = CCIE;
    TA1CTL = TASSEL__ACLK | MC__UP;

    /* use timer B for signal source */
    P7SEL |= BIT4;              // alternate function on P7.4
    P7DIR |= BIT4;              // TB0.2 OUT
    TBCCR2 = GENERATED_PERIOD >> 2; // quarter of the period is pulse
    TBCCTL2 = OUTMOD_7;         // reset/set mode for signal
    TBCCR0 = GENERATED_PERIOD;
    TBCTL = TBSSEL__ACLK | MC__UP;       // set TB clock source

    /* init timerA0 with input CCI2A P1.3 pin */
    P1SEL |= BIT3;              // alternate function
    P1DIR &= ~BIT3;             // input CCI2A

    /* Capture compare 2 is in capture mode */
    TA0CCTL2 = CM0 | SCS | CAP | CCIE;  // capture on rising
                                        // sync capture source
                                        // capture mode
                                        // enable interrupt for CCR2
    TA0CTL = TASSEL__SMCLK | MC__CONTINUOUS; //free running counter wtih 1MHz clk

    __enable_interrupt();

    while(1)
    {
        while(!flag);
        /* do processing */
        measured_sum /= SAMPLING_WINDOW;
        display(TIMER_A0_FREQ / measured_sum);
        flag = 0;
    }
}

/**
 * @brief TA1CCR0 ISR
 *
 * Multiplex the 7seg display. Each ISR activates one digit.
 */
void __attribute__ ((interrupt(TIMER1_A0_VECTOR))) TA1CCR0ISR (void)
{
    static uint8_t current_digit = 0;

    /* algorithm:
     * - turn off previous display (SEL signal)
     * - set a..g for current display
     * - activate current display
     */
    if (current_digit == 1)
    {
        P6OUT |= BIT4;          // turn off SEL2
        WriteLed(digits[current_digit]);    // define seg a..g
        P7OUT &= ~BIT0;         // turn on SEL1
    }
    else if (current_digit == 0)
    {
        P7OUT |= BIT0;
        WriteLed(digits[current_digit]);
        P6OUT &= ~BIT4;
    }
    current_digit = (current_digit + 1) & 0x01;

    return;
}

/**
 * @brief TIMERA0 Interrupt service routine
 *
 * ISR measures time between rising edges
 */
void __attribute__ ((interrupt(TIMER0_A1_VECTOR))) TA0CCR2ISR (void)
{
    static uint16_t last = 0;
    static uint8_t cnt = 0;
    static uint32_t sum = 0;

    if (TA0IV == TA0IV_TACCR2)      // check if the highest priority is CCR2 interrupt
    {
        uint16_t curr = TA0R;
        uint16_t delta;
        if (curr > last)
        {
            delta = curr - last;
        }
        else
        {
            delta = (0xffff - last) + curr + 1;
        }
        sum += delta;
        last = curr;

        cnt++;
        if (cnt >= SAMPLING_WINDOW)
        {
            flag = 1;
            measured_sum = sum;
            sum = 0;
            cnt = 0;
        }

    }
}
