// Timer : PWM sur 5 sorties, affiche la position du potentiomètre
// EPFL 2020, Pierre-Yves Rochat, pyr@pyr.ch

#include <msp430.h>
#include <stdint.h>
#include <HAL_Wheel.h>

// Définition des LED et des poussoirs pour la carte "blanche"

#define Led1On P1OUT|=(1<<0)
#define Led1Off P1OUT&=~(1<<0)
#define Led1Toggle P1OUT^=(1<<0)
#define InitLed1 P1DIR|=(1<<0);Led1Off

#define Led2On P8OUT|=(1<<1)
#define Led2Off P8OUT&=~(1<<1)
#define Led2Toggle P8OUT^=(1<<1)
#define InitLed2 P8DIR|=(1<<1);Led2Off

#define Led3On P8OUT|=(1<<2)
#define Led3Off P8OUT&=~(1<<2)
#define Led3Toggle P8OUT^=(1<<2)
#define InitLed3 P8DIR|=(1<<2);Led3Off

#define Led4On P1OUT|=(1<<1)
#define Led4Off P1OUT&=~(1<<1)
#define Led4Toggle P1OUT^=(1<<1)
#define InitLed4 P1DIR|=(1<<1);Led4Off

#define Led5On P1OUT|=(1<<2)
#define Led5Off P1OUT&=~(1<<2)
#define Led5Toggle P1OUT^=(1<<2)
#define InitLed5 P1DIR|=(1<<2);Led5Off

#define Led6On P1OUT|=(1<<3)
#define Led6Off P1OUT&=~(1<<3)
#define Led6Toggle P1OUT^=(1<<3)
#define InitLed6 P1DIR|=(1<<3);Led6Off

#define Led7On P1OUT|=(1<<4)
#define Led7Off P1OUT&=~(1<<4)
#define Led7Toggle P1OUT^=(1<<4)
#define InitLed7 P1DIR|=(1<<4);Led7Off

#define Led8On P1OUT|=(1<<5)
#define Led8Off P1OUT&=~(1<<5)
#define Led8Toggle P1OUT^=(1<<5)
#define InitLed8 P1DIR|=(1<<5);Led8Off

#define Pous1On (!(P1IN&(1<<7)))
#define InitPous1 P1DIR&=~(1<<7);P1REN|=(1<<7);P1OUT|=(1<<7)

#define Pous2On (!(P2IN&(1<<2)))
#define InitPous2 P2DIR&=~(1<<2);P2REN|=(1<<2);P2OUT|=(1<<2)

void InitCarteBlanche() {
  InitLed1;
  InitLed2;
  InitLed3;
  InitLed4;
  InitLed5;
  InitLed6;
  InitLed7;
  InitLed8;
  InitPous1;
  InitPous2;
}

void AfficheLedBleues(uint16_t val) {
  if (val & (1<<0)) { Led8On; } else { Led8Off; }
  if (val & (1<<1)) { Led7On; } else { Led7Off; }
  if (val & (1<<2)) { Led6On; } else { Led6Off; }
  if (val & (1<<3)) { Led5On; } else { Led5Off; }
  if (val & (1<<4)) { Led4On; } else { Led4Off; }
}

// Procédures pour passer la fréquence de 1 à 25 MHz
// (fournies par Texas Instrument !)
void SetVCoreUp (unsigned int level) {
  PMMCTL0_H = 0xA5; // Open PMM registers for write access
  // Set SVS/SVM high side new level :
  SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
  // Set SVM low side to new level :
  SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
  while ((PMMIFG & SVSMLDLYIFG) == 0) {} // Wait till SVM is settled
  PMMIFG &= ~(SVMLVLRIFG + SVMLIFG); // Clear already set flags
  PMMCTL0_L = PMMCOREV0 * level; // Set VCore to new level
  if ((PMMIFG & SVMLIFG)) { // Wait till new level reached
    while ((PMMIFG & SVMLVLRIFG) == 0);
  }
  // Set SVS/SVM low side to new level :
  SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
  PMMCTL0_H = 0x00; // Lock PMM registers for write access
}

void setupDCO(void) {
  SetVCoreUp(1u); // Power settings
  SetVCoreUp(2u); SetVCoreUp(3u);
  UCSCTL3 = SELREF__REFOCLK; // select REFO as FLL source
  UCSCTL6 = XT1OFF | XT2OFF; // turn off XT1 and XT2
  // Initialize DCO to 25.00MHz :
  __bis_SR_register(SCG0); // Disable the FLL control loop
  UCSCTL0 = 0x0000u;  // Set lowest possible DCOx, MODx
  UCSCTL1 = DCORSEL_6; // Set RSELx for DCO = 50 MHz
  UCSCTL2 = 762u;  // Set DCO Multiplier for 25MHz
                       // (N + 1) * FLLRef = Fdco, (762 + 1) * 32768 = 25.00MHz
  UCSCTL4 = SELA__REFOCLK | SELS__DCOCLK | SELM__DCOCLK;
  __bic_SR_register(SCG0); // Enable the FLL control loop

  // Worst-case settling time for the DCO when the DCO range bits have been
  // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
  // UG for optimization.
  // 32*32*25MHz/32768Hz = 781250 = MCLK cycles for DCO to settle
  __delay_cycles(781250u);

  do { // Loop until XT1,XT2 & DCO fault flag is cleared
    UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // Clear XT2,XT1,DCO fault flags
    SFRIFG1 &= ~OFIFG; // Clear fault flags
  } while (SFRIFG1&OFIFG); // Test oscillator fault flag
}

#define LIMITE_MIN_PWM 1 // limite inférieure du PWM
#define MAX_PWM 65535

uint16_t pwm4, pwm5, pwm6, pwm7, pwm8;

#define MAX_ROUE 4095 // 12 bits

volatile uint16_t debCycle;

void main(void) {
  WDTCTL = WDTPW | WDTHOLD;
  setupDCO(); // Fréquence à 25 MHz
  InitCarteBlanche();
  Wheel_init();

  // Initialisation du Timer :
  // choix de l'horloge et mode continu
  TA0CTL = TASSEL_2 | ID_0 | MC_2 | TAIE;
  // Interrupt On sur les 5 registres de comparaison :
  TA0CCTL0 = TA0CCTL1 = TA0CCTL2 = TA0CCTL3 = TA0CCTL4 = CCIE;
  __enable_interrupt(); // Active l'ensemble des interruption

  uint16_t roue;
  uint16_t roueDiv, roueSolde;

  while (1) { // boucle infinie vide
    while(debCycle==0) {} // synchronise avec le cycle du PWM
    debCycle = 0;
    roue =  Wheel_getValue();
    pwm4 = pwm5 = pwm6 = pwm7 = pwm8 = 0;
    roueDiv = roue / (MAX_ROUE/5); // quel cinquième ?
    roueSolde = ((roue % (MAX_ROUE/5))*5)<<4; // reste, reporté sur 16 bits
    switch (roueDiv) {
    case 0 : pwm8 = roueSolde; break;
    case 1 : pwm8 = MAX_PWM; pwm7 = roueSolde; break;
    case 2 : pwm8 = pwm7 = MAX_PWM; pwm6 = roueSolde; break;
    case 3 : pwm8 = pwm7 = pwm6 = MAX_PWM; pwm5 = roueSolde; break;
    case 4 : pwm8 = pwm7 = pwm6 = pwm5 = MAX_PWM; pwm4 = roueSolde; break;
    default : break;
    }
  }
}

// Timer_A0 Interrupt Vector handler
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) { // CCR0
  Led4Off;
}

// Timer_A1 Interrupt Vector (TAIV) handler
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A1(void) {
  switch(TA0IV) {
  case  2: // CCR1 :
    Led5Off;
    break;
  case  4: // CCR2 :
    Led6Off;
    break;
  case  6: // CCR3 :
   Led7Off;
   break;
  case  8: // CCR4 :
    Led8Off;
    break;
  case 14: // Overflow
    if (pwm4>=LIMITE_MIN_PWM) { TA0CCR0 = pwm4; Led4On; }
    if (pwm5>=LIMITE_MIN_PWM) { TA0CCR1 = pwm5; Led5On; }
    if (pwm6>=LIMITE_MIN_PWM) { TA0CCR2 = pwm6; Led6On; }
    if (pwm7>=LIMITE_MIN_PWM) { TA0CCR3 = pwm7; Led7On; }
    if (pwm8>=LIMITE_MIN_PWM) { TA0CCR4 = pwm8; Led8On; }
    debCycle = 1; // signale le début du cycle
    break;
  }
}

