/**
 * @file main.c
 * @brief Demo LPM
 *
 * In this example ADC conversion results are buffered and every 256 samples
 * are averaged. Main loop waits in LPM until 256 samples are acquired and then
 * performs averaging
 *
 * @date 2019
 * @author Strahinja Jankovic (jankovics@etf.bg.ac.rs)
 * @author Marija Bezulj (meja@etf.bg.ac.rs)
 *
 * @version [2.0 @ 05/2021] Hardware change
 * @version [1.1 @ 03/2019] Code refactor.
 * @version [1.0 @ 03/2017] Initial version
 */
#include <msp430.h>
#include <stdint.h>
#include "function.h"

/**
 * @brief Timer period for ADC12 conversion triggering
 *
 * Timer is clocked by ACLK (32768Hz).
 * A 8x divider is set.
 * We want 128Hz, so use 256 for CCR0
 */
#define CONV_PERIOD        (256)  /* 128Hz */
/**
 * @brief Display mux period
 *
 * ACLK is used
 */
#define MUX_PERIOD        (163)  /* ~5ms (4.97ms)  */

/** buffer 1 used for double-buffering */
uint8_t buf1[256] = {0};
/** buffer 2 used for double-buffering */
uint8_t buf2[256] = {0};
/** pointer to buffer that is currently used for acquisition */
volatile uint8_t *curr_acq;
/** pointer to buffer that is currently used for averaging */
volatile uint8_t *curr_avg;
/** counter for acquisition */
volatile uint16_t acq_cnt = 0;

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

/**
 * @brief Function that populates digit array with hex digits
 */
void display(const uint8_t number)
{
    uint8_t nr = number;
    uint8_t tmp;
    for (tmp = 0; tmp < 2; tmp++)
    {
        digits[tmp] = nr % 16;
        nr /= 16;
    }
}

/**
 * @brief Main function
 *
 * Demo averages every 256 samples acquired by ADC
 */
int main(void)
{
    uint16_t avg_cnt = 0;
    volatile uint16_t avg = 0;

    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 |= 0x48;              // configure P2.3 and P2.6 as out
    P3DIR |= BIT7;              // configure P3.7 as out
    P4DIR |= 0x09;              // configure P4.0 and P4.3 as out
    P8DIR |= 0x06;              // configure P8.1 and P8.2 as out

    // init TA1 as compare in up mode
    TA1CCR0 = MUX_PERIOD;       // set timer period in CCR0 register
    TA1CCTL0 = CCIE;            // enable interrupt for TA1CCR0
    TA1CTL = TASSEL__ACLK | MC__UP; //clock select and up mode

    /* ADC12_A channel A0 init */
    P6SEL |= BIT0;              // input pin P6.0 - pot2
                                // set pin P6.0 as alternate function - A0 analog

    ADC12CTL0 &= ~ADC12ENC;     // disable ADC before configuring
    ADC12CTL0 |= ADC12ON;       // ADC on
    ADC12CTL1 |= ADC12SHS_2 + ADC12CONSEQ_2;
                                      // start address ADC12MEM0, timer B0 OUT0 starts conv
                                      // single channel continuous conversion, MODOSC clock
    ADC12CTL2 &= ~ADC12RES_2;   // 8 bit conversion result
    ADC12MCTL0 |= ADC12INCH_0;  //reference AVCC and AVSS, channel A0
    ADC12CTL0 |= ADC12ENC;      // enable ADC12
    ADC12IE |= ADC12IE0;        // enable interrupt when MEM0 is written


    /* initialize Timer B0 for ADC12 trigger */
    // CBOUT CCR0 TB0 -> ADC12SHS2 this is internal, ports and pins not used (P5.6)
    TB0CCR0 = (CONV_PERIOD/2  - 1);// f_OUT = 128 Hz, f_ACLK = 32768 Hz
                                   // TOGGLE outmod => T_OUT = 2 * T_CCR0 =>
                                   // T_CCR0 = T_OUT/2 = 128 (TA0CCR0 = T_CCR0 - 1)
    TB0CCTL0 |= OUTMOD_4;    // toggle mode
    TB0CTL |= TBSSEL__ACLK + MC__UP;

    // initialize debug LED
    P2DIR |= BIT4;           // init LED1 P2.4 as out

    // initialize buffers
    curr_acq = buf1;
    curr_avg = buf2;

    __enable_interrupt();                   // GIE

    while (1)
    {
        // enter LPM3 mode
        __bis_SR_register(LPM3_bits);

        // we are awake, so 256 samples are ready
        // dbg only, turn on LED 1
        P2OUT |= BIT4;

        // prepare avg
        avg = 0;
        for (avg_cnt = 0; avg_cnt < 256; avg_cnt++)
        {
            avg += curr_avg[avg_cnt];
        }
        // divide by 256
        avg >>= 8;

        display(avg);

        // debug only, turn off LED1
        P2OUT &= ~BIT4;
    }
}

/**
 * ADC ISR
 *
 * Read conversion result and store it to buffer
 */
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12ISR (void)
{
    if (ADC12IV == ADC12IV_ADC12IFG0)
    {
        if (acq_cnt < 256)
        {
            // store value in array
            curr_acq[acq_cnt++] = ADC12MEM0;
        }
        else
        {
            // if we have 256 samples, change buffer
            volatile uint8_t *tmp = curr_acq;
            curr_acq = curr_avg;
            curr_avg = tmp;

            // reset counter
            acq_cnt = 0;

            // leave LPM3
            __bic_SR_register_on_exit(LPM3_bits);
        }
    }
}

/**
 * @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;
}

