/**
 * @file main.c
 * @brief Demo Timer multiplex
 *
 * In this example number 12 is displayed on multiplexed 7 segment LED display
 *
 * @date 2021
 * @author  Marija Bezulj [meja@etf.bg.ac.rs]
 *
 * @version [1.0 - 04/2021] Initial version
 */

#include <msp430.h> 
#include <stdint.h>
#include "function.h"

/**
 * Choose whether to use
 * 0 - DIVMOD
 * 1 - Double Dabble (shift and add 3)
 * 2 - MSP430 DADD - decimal add
 */
#define BIN2BCD     2

/**
 * @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        (163)  /* ~5ms (4.97ms)  */

/**
 * @brief Number to be displayed
 */
#define NUMBER          (67)

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

/**
 * @brief Function that populates digit array
 * @author Strahinja Jankovic
 */
void display(const uint16_t number)
{
    uint16_t nr = number;
    uint8_t tmp;
#if (BIN2BCD == 0)
    for (tmp = 0; tmp < 2; tmp++)
    {
        digits[tmp] = nr % 10;
        nr /= 10;
    }
#elif (BIN2BCD == 1)
    uint8_t data[2] = {0};
    int8_t count;

    for (count = 7; count >= 0; count--)
    {
        uint8_t next = (nr & BIT7) ? 1 : 0;
        nr <<= 1;
        for (tmp = 0; tmp < 2; tmp++)
        {
            uint8_t nibble = data[tmp];
            nibble += (nibble >= 5) ? 3 : 0;
            nibble <<= 1;
            nibble |= next;
            next = (nibble & BIT4) ? 1 : 0;
            data[tmp] = nibble & 0xf;
        }
    }
    for (tmp = 0; tmp < 2; tmp++)
    {
        digits[tmp] = data[tmp];
    }
#else
    uint16_t converted = 0;
    for (tmp = 8; tmp > 0; tmp--) {
        converted = __bcd_add_short(converted, converted);
        if ((nr & BIT7) != 0) {
            converted = __bcd_add_short(converted, 1);
        }
        nr <<= 1;
    }
    for (tmp = 0; tmp < 2; tmp++)
    {
        digits[tmp] = (converted & (0xf << tmp*4)) >> tmp*4;
    }
#endif
}

/**
 * @brief Main function
 * Initialize the 7seg display and timer in compare mode.
 * ISR will multiplex the display
 */
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

    // init TA0 as compare in up mode
    TA0CCR0 = TIMER_PERIOD;     // set timer period in CCR0 register
    TA0CCTL0 = CCIE;            // enable interrupt for TA10CCR0
    TA0CTL = TASSEL__ACLK | MC__UP; //clock select and up mode

    // create BCD digits
    display(NUMBER);

    __enable_interrupt();

    while(1);
}

/**
 * @brief TA0CCR0 ISR
 *
 * Multiplex the 7seg display. Each ISR activates one digit.
 */
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) CCR0ISR (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
        // DAT AIN'T OPTIMAL:
        WriteLed(digits[current_digit]);    // define seg a..g
        P7OUT &= ~BIT0;         // turn on SEL1
    }
    else if (current_digit == 0)
    {
        P7OUT |= BIT0;
        // DAT AIN'T OPTIMAL:
        WriteLed(digits[current_digit]);
        P6OUT &= ~BIT4;
    }
    current_digit = (current_digit + 1) & 0x01;

    return;
}
