// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdint.h>

#define _XTAL_FREQ 16000000  // 16 MHz oscillator frequency

#define SINE_TABLE_SIZE 384

uint8_t phaseA_index = 0;
uint8_t phaseB_index = SINE_TABLE_SIZE / 3;
uint8_t phaseC_index = (2 * SINE_TABLE_SIZE) / 3;

volatile uint8_t tmr0_reload_value = 20;  // Initial TMR0 reload value

volatile uint16_t dutyA = 0;
volatile uint16_t dutyB = 0;
volatile uint16_t dutyC = 0;

volatile uint16_t amplitude_scaling = 768;  // Initialized to maximum amplitude

const uint16_t sine_table[SINE_TABLE_SIZE] = {
    384, 390, 397, 403, 409, 415, 422, 428, 434, 440, 447, 453, 459, 465, 471, 477, 483, 489, 495, 501, 507, 513, 519, 525, 531, 537, 542, 548, 554, 559, 565, 571, 576, 581, 587, 592, 597, 603, 608, 613, 618, 623, 628, 632, 637, 642, 646, 651, 656, 660, 664, 669, 673, 677, 681, 685, 689, 692, 696, 700, 703, 707, 710, 713,
717, 720, 723, 726, 728, 731, 734, 736, 739, 741, 743, 746, 748, 750, 751, 753, 755, 756, 758, 759, 761, 762, 763, 764, 765, 765, 766, 767, 767, 768, 768, 768, 768, 768, 768, 768, 767, 767, 766, 765, 765, 764, 763, 762, 761, 759, 758, 756, 755, 753, 751, 750, 748, 746, 743, 741, 739, 736, 734, 731, 728, 726, 723, 720,
717, 713, 710, 707, 703, 700, 696, 692, 689, 685, 681, 677, 673, 669, 664, 660, 656, 651, 646, 642, 637, 632, 628, 623, 618, 613, 608, 603, 597, 592, 587, 581, 576, 571, 565, 559, 554, 548, 542, 537, 531, 525, 519, 513, 507, 501, 495, 489, 483, 477, 471, 465, 459, 453, 447, 440, 434, 428, 422, 415, 409, 403, 397, 390,
384, 378, 371, 365, 359, 353, 346, 340, 334, 328, 321, 315, 309, 303, 297, 291, 285, 279, 273, 267, 261, 255, 249, 243, 237, 231, 226, 220, 214, 209, 203, 197, 192, 187, 181, 176, 171, 165, 160, 155, 150, 145, 140, 136, 131, 126, 122, 117, 112, 108, 104, 99, 95, 91, 87, 83, 79, 76, 72, 68, 65, 61, 58, 55,
51, 48, 45, 42, 40, 37, 34, 32, 29, 27, 25, 22, 20, 18, 17, 15, 13, 12, 10, 9, 7, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 13, 15, 17, 18, 20, 22, 25, 27, 29, 32, 34, 37, 40, 42, 45, 48,
51, 55, 58, 61, 65, 68, 72, 76, 79, 83, 87, 91, 95, 99, 104, 108, 112, 117, 122, 126, 131, 136, 140, 145, 150, 155, 160, 165, 171, 176, 181, 187, 192, 197, 203, 209, 214, 220, 226, 231, 237, 243, 249, 255, 261, 267, 273, 279, 285, 291, 297, 303, 309, 315, 321, 328, 334, 340, 346, 353, 359, 365, 371, 378,};

void update_pwm(void) 
{
    // Set PWM duty cycles (assuming 10-bit resolution)
    PWM1DCH = (dutyA >> 2) & 0xFF;
    PWM1DCL = (dutyA & 0x0003) << 6;

    PWM2DCH = (dutyB >> 2) & 0xFF;
    PWM2DCL = (dutyB & 0x0003) << 6;

    PWM3DCH = (dutyC >> 2) & 0xFF;
    PWM3DCL = (dutyC & 0x0003) << 6;

    // Increment phase indexes
    phaseA_index = (phaseA_index + 1) % SINE_TABLE_SIZE;
    phaseB_index = (phaseB_index + 1) % SINE_TABLE_SIZE;
    phaseC_index = (phaseC_index + 1) % SINE_TABLE_SIZE;
}

void init_adc(void)
{
    // Configure ADC
    ADCON1bits.ADFM = 1;       // Right justified
    ADCON1bits.ADCS = 0b110;   // FOSC/64
    ADCON1bits.ADPREF = 0;     // VREF+ = VDD
    ADCON0bits.CHS = 0b00000;  // Select AN0
    ADCON0bits.ADON = 1;       // Enable ADC
    __delay_us(10);            // Acquisition time
}

void main(void) {
    // Oscillator configuration
    OSCCONbits.IRCF = 0b1111; // 16 MHz internal oscillator
    OSCCONbits.SCS = 0b10;    // Internal oscillator

    // Configure Timer2 for PWM
    T2CONbits.T2CKPS = 0b00;  // Prescaler = 1
    PR2 = 199;                // PR2 value for 20 kHz PWM frequency (adjust as needed)
    T2CONbits.TMR2ON = 1;     // Enable Timer2

    // Configure PWM Modules
    PWM1CON = 0xC0;  // Enable PWM1, active-high
    PWM2CON = 0xC0;  // Enable PWM2, active-high
    PWM3CON = 0xC0;  // Enable PWM3, active-high

    // Set PWM pins as outputs
    TRISCbits.TRISC5 = 0;  // PWM1 output
    TRISCbits.TRISC3 = 0;  // PWM2 output
    TRISAbits.TRISA2 = 0;  // PWM3 output

    // Configure Timer0 for updates
    OPTION_REGbits.T0CS = 0;    // Timer0 Clock Source: Internal
    OPTION_REGbits.PSA = 0;     // Prescaler assigned to Timer0
    OPTION_REGbits.PS = 0b100;  // Prescaler = 1:32

    TMR0 = 256 - tmr0_reload_value;  // Preload TMR0

    // Enable Timer0 interrupt
    INTCONbits.TMR0IE = 1;      // Enable Timer0 interrupt
    INTCONbits.PEIE = 1;        // Enable Peripheral Interrupts
    INTCONbits.GIE = 1;         // Enable Global Interrupts

    // Initialize ADC
    init_adc();

    while (1) {
        // *** Frequency Control via AN0 ***
        // Read AN0 for Timer0 reload value (frequency control)
        ADCON0bits.CHS = 0b00000;  // Select AN0
        __delay_us(10);            // Acquisition time
        ADCON0bits.GO_nDONE = 1;   
        while (ADCON0bits.GO_nDONE); // Wait for conversion to complete
        uint16_t adc_value_tmr = ((uint16_t)ADRESH << 8) | ADRESL;

        // Map AN0 ADC value to TMR0 reload value (adjust as needed)
        tmr0_reload_value = 16 + ((uint32_t)adc_value_tmr * 33) / 1023;
        if (tmr0_reload_value < 16) tmr0_reload_value = 16;
        if (tmr0_reload_value > 49) tmr0_reload_value = 49;

        // *** Amplitude Control via AN1 ***
        // Read AN1 for Amplitude Control
        ADCON0bits.CHS = 0b00001;  // Select AN1
        __delay_us(10);            // Acquisition time
        ADCON0bits.GO_nDONE = 1;   
        while (ADCON0bits.GO_nDONE); // Wait for conversion to complete
        uint16_t adc_value_amp = ((uint16_t)ADRESH << 8) | ADRESL;

        // Set amplitude scaling factor (0 - 1023)
        amplitude_scaling = adc_value_amp;
        if (amplitude_scaling > 768) amplitude_scaling = 768;

        // *** Compute Scaled Duty Cycles ***
        // Get the raw sine values
        uint16_t raw_dutyA = sine_table[phaseA_index];
        uint16_t raw_dutyB = sine_table[phaseB_index];
        uint16_t raw_dutyC = sine_table[phaseC_index];

        // Scale the amplitude with 32-bit arithmetic
        dutyA = (uint16_t)(((uint32_t)raw_dutyA * amplitude_scaling) / 768);
        dutyB = (uint16_t)(((uint32_t)raw_dutyB * amplitude_scaling) / 768);
        dutyC = (uint16_t)(((uint32_t)raw_dutyC * amplitude_scaling) / 768);

        // Ensure duty cycles do not exceed maximum PWM value
        if (dutyA > 1023) dutyA = 768;
        if (dutyB > 1023) dutyB = 768;
        if (dutyC > 1023) dutyC = 768;
    }
}

void __interrupt() ISR(void) {
    if (INTCONbits.TMR0IF) {
        INTCONbits.TMR0IF = 0;  // Clear Timer0 interrupt flag

        // Reload TMR0
        TMR0 = 256 - tmr0_reload_value;

        // Update PWM outputs with precomputed values
        update_pwm();
    }
}