/********************************************************************************
    FileName:		IO_Board.c
    Dependencies:	See #includes
    Processor:		dsPIC33CK512MP608 
    Hardware:		
    Complier:		XC16, XC-DSC 3.21
    Author:		Larry Knight 2025

    Software License Agreement:
        This software is licensed under the Apache License Agreement

    Description:
        
    File Description:

    Change History:
 
********************************************************************************/

// FOSCSEL
// Oscillator Source Selection
#pragma config FNOSC = FRC          

//2 speed start up
#pragma config IESO = OFF            

// FOSC
// Primary Oscillator Mode Select bits (EC (External Clock) Mode)
#pragma config POSCMD = EC        
// OSC2 Pin Function bit (OSC2 is I/O)
#pragma config OSCIOFNC = ON         
// Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
#pragma config FCKSM = CSECMD        
// PLL Lock Status Control
#pragma config PLLKEN = ON           
// Watchdog Timer Enable bit (WDT enabled in hardware)
#pragma config FWDTEN = ON_SW        

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

LEDPortUnion MyLEDPort;

uint8_t m_data;
uint16_t m_address;
float system_freq;
volatile uint8_t sram_busy = 0;
uint16_t result[1000] = {0};
uint16_t currentArrayPosition = 0;

int main(void)
{
    PLLDIVbits.VCODIV = 3;
    
    // Oscillator configuration for ~103 MHz using FRC PLL
    CLKDIVbits.PLLPRE = 1;		//(N1 = 1)
    PLLFBDbits.PLLFBDIV = 66;		//(M = 14)
    PLLDIVbits.POST1DIV = 4;		//(N2 = 1)
    PLLDIVbits.POST2DIV = 1;		//(N3 = 1)
    
    //Initiate Clock Switch from FRC to PRIPLL
    __builtin_write_OSCCONH(0x03);   // Start clock switch to PLL
    __builtin_write_OSCCONL(OSCCON | 0x01);  // Initiate clock switch

    //Wait for Clock switch to occur
    while (OSCCONbits.OSWEN != 0);
    
    //Calculate system frequency 
    //FPLLO = FPLLI * M/(N1 * N2 * N3);
    //396 MHz
    system_freq = OSC_FREQ * PLLFBDbits.PLLFBDIV / (CLKDIVbits.PLLPRE * PLLDIVbits.POST1DIV * PLLDIVbits.POST2DIV);
    
    // LED0 Pin Setup (TRISD12 as output)
    TRISDbits.TRISD12 = 0; 

    // Wait for PLL to lock
   while (OSCCONbits.LOCK != 1)
   {
       LATD = (1 << 12); 
   }
    
    //LED1
    TRISEbits.TRISE6 = 0;
    
    //LED2
    ANSELDbits.ANSELD10 = 0;
    TRISDbits.TRISD10 = 0; 
    
    //LED3
    TRISEbits.TRISE7 = 0;
    
    //LED4
    TRISEbits.TRISE15 = 0;
    
    //LED5
    TRISEbits.TRISE14 = 0;
    
    //LED6
    TRISEbits.TRISE13 = 0;    
    
    //LED7
    ANSELEbits.ANSELE2 = 0;
    TRISEbits.TRISE2 = 0;

    // /CE1
    TRISDbits.TRISD14 = 0;
    PORTDbits.RD14 = 1;
    
    // /BUSY
    TRISDbits.TRISD13 = 1;
    
    // /SEM
    TRISEbits.TRISE4 = 0;
    PORTEbits.RE4 = 1;
    
    // /MINT
    TRISCbits.TRISC0 = 1;
    
    //prototype data direction bit 0 = read, 1 = write
    TRISEbits.TRISE9 = 0;
    PORTEbits.RE9 = 0;
    
    //R/W
    TRISCbits.TRISC5 = 0;
    PORTCbits.RC5 = 1;
    
    // Board Select
    TRISEbits.TRISE1 = 1;
    
    // /ACK    
    TRISEbits.TRISE5 = 0;
    PORTEbits.RE5 = 1;
    
    // /HALT
    
    //Heartbeat Test Point
    TRISCbits.TRISC9 = 0;
    
    TRISBbits.TRISB3 = 0;
    ANSELBbits.ANSELB3 = 0;

    //unlock PPS
    __builtin_write_RPCON(0x0000);
    
    //map CMP1 to pin 43
    RPOR1bits.RP35R = 23;
    
    //lock PPS
    __builtin_write_RPCON(0x0800);

    Timer1_Init();
    PWM4_Init();
    PMP_Init();
    DAC_Init();
    IO_Board_Interrupts_Init();
    IO_Board_Init();
    OPAMP1_Init();
    ADC_Init();
    
    // Main Program Loop
    while(1)
    {
        // Heartbeat - Bitwise XOR toggle
	//Pin 47 - RC9
	PORTCbits.RC9 ^= 1;	
   
	//Debug - toggles LED0
	//LATD ^= (1 << 12); // Toggle LATD12		
    }   
    
    return 0;
}

// Function definition
void SetLEDPort(LEDPortUnion data)
{
    LED0 = data.bit0;
    LED1 = data.bit1;
    LED2 = data.bit2;
    LED3 = data.bit3;
    LED4 = data.bit4;
    LED5 = data.bit5;
    LED6 = data.bit6;
    LED7 = data.bit7;
}

//Initialize the Parallel Main Port Module
void PMP_Init(void)
{
    //Make sure the PMP is off before configuring
    PMCON |= 0 << 15;
    
    //RD Line
    PMCONbits.PTRDEN = 1;
    
    //WR
    PMCONbits.PTWREN = 1;
    
    //Set Dual Buffer Mode
    PMCONHbits.DUALBUF = 1;
    
    //Set Host Mode 2
    PMMODEbits.MODE = 2;
    
    //Data Setup to Read/Write Strobe Wait States bits
    PMMODEbits.WAITB = 0;
            
    //Data Hold After Read/Write Strobe Wait States bits
    PMMODEbits.WAITM = 2;
    PMMODEbits.WAITE = 2;    
    
    //Enable addresses 0-7
    PMAEN = 0x00ff;
    
    //Turn on the module
    PMCON |= 1 << 15;
}

void IO_Board_70V05_WR(uint16_t m_address, uint8_t m_data)
{
        //PMP - set the address
	PMWADDR = m_address;

    	// /CE1
	PORTD &= ~(1 << 14);
	
	//prototype data direction bit 0 = read, 1 = write
	PORTEbits.RE9 = 1;
	
	PMDOUT1 = m_data;
	while(PMMODEbits.BUSY == 1);
	
	if(sram_busy == 1)
	{
	    PMDOUT1 = m_data;
	    while(PMMODEbits.BUSY == 1);
	}
	
	sram_busy = 0;
	
	//prototype data direction bit 0 = read, 1 = write
	PORTEbits.RE9 = 0;
	
	// /CE1
	PORTD |= (1 << 14);
}

void IO_Board_70V05_RD(uint16_t m_address)
{
        //PMP - set the address
	PMRADDR = m_address;

    	// /CE1
	PORTD &= ~(1 << 14);
	
	//prototype data direction bit 0 = read, 1 = write
	//PORTEbits.RE9 = 0;
	PORTE &= ~(1 << 9);
	
	//Dummy Read
	m_data = PMRDIN;
	while(PMMODEbits.BUSY == 1);
	
	//Read the data
	m_data = PMRDIN;
	while(PMMODEbits.BUSY == 1);

//	//prototype data direction bit 0 = read, 1 = write
//	PORTE &= ~(1 << 9);

	// /CE1
	//PORTD &= (1 << 14);
	PORTD |= (1 << 14);
}

void IO_Board_Init(void)
{
    
}

void DAC_Init(void)
{
    ANSELAbits.ANSELA3 = 0;
    
    //disable module
    DACCTRL1Lbits.DACON = 0;
    
    //disable DAC 1
    DAC1CONLbits.DACEN = 0;
    
    DACCTRL1Lbits.CLKSEL = 3;
    DACCTRL1Lbits.CLKDIV = 0;
    
    //Slope stuff
    SLP1DATbits.SLPDAT = 0x20; // Slope rate, counts per step
    SLP1CONHbits.TWME = 0;
    SLP1CONHbits.SLOPEN = 0;    

    //analog voltage is connected to the DACOUT1 pin
    DAC1CONLbits.DACOEN = 1;
    
    //This register specifies the high DACx data value, VDAC = DACDAT * (VDD)/4095.
    //Valid values are from 205 (0x0CD) to 3890 (0xF32)
    DAC1DATHbits.DACDAT = 0xF32;
    
    /*In Hysteretic mode, Slope Generator mode and Triangle mode, this register specifies the low data value
    and/or limit for the DACx module. VOUT = DACDAT * (VDD)/4095. Valid values are from 205 (0x0CD) to
    3890 (0xF32).
    */
    DAC1DATLbits.DACLOW = 0x0CD;
    
    //Comparator input select (0 or 3)
    DAC1CONLbits.INSEL = 0;
    
    //Comparator output polarity
    DAC1CONLbits.CMPPOL = 1;
    
    //enable DAC 1
    DAC1CONLbits.DACEN = 1;
        
    //enable module
    //DACCTRL1Lbits.DACON = 1;
}

void PWM4_Init(void)
{
    PG4CONL |= 0 << 15;
    
    PCLKCONbits.DIVSEL = 0;
	    
    /*PWM control register configuration*/
    PCLKCONbits.MCLKSEL = 0;
    
    //Host clock selected by the MCLKSEL[1:0] (PCLKCON[1:0]) control bits
    PG4CONLbits.CLKSEL = 1;
    
    //Independent Edge PWM mode
    PG4CONLbits.MODSEL = 0b00; //0; 
    
    //PWM Generator produces two PWM cycles after triggered
    //PG4CONLbits.TRGCNT = 1;
    
    //High Resolution mode
    PG4CONLbits.HREN = 1;
    
    //PWM Generator controls the PWM4H output pin
    PG4IOCONHbits.PENH = 1;
    
    PG4IOCONHbits.PMOD = 1;
    
    //PWM Generator broadcasts software set/clear of the UPDATE status 
    //  bit and EOC signal to other PWM Generators
    PG4CONHbits.MSTEN = 1;
    
    //PWM Generator operates in Retriggerable mode
    PG4CONHbits.TRGMOD = 0; //1;
    
    //
    PG4CONHbits.UPDMOD = 0;
    PG4CONHbits.MDCSEL = 0;
    PG4EVTLbits.UPDTRG = 1;

    //PWM Phase
    PG4PHASE = 0;
    
    //Period
    PG4PER = 256;
    
    //Duty Cycle
    PG4DC = 125;
    
    //Enable PWM
    PG4CONL |= 1 << 15;   
}

void OPAMP1_Init(void)
{
    
}

void Timer1_Init(void)
{
    T1CON = 0; 
    TMR1 = 0; 
    PR1 = (OSC_FREQ / 1000000) * 63.5; // 63.5 us
    IFS0bits.T1IF = 0;
    IPC0bits.T1IP = 4;
    IEC0bits.T1IE = 1;
    
    T1CONbits.TON = 1;
}

void ADC_Init(void)
{
    //enable analog for AN15
    ANSELCbits.ANSELC3 = 1;
    TRISCbits.TRISC3 = 1;
    
    ADCON1Lbits.ADON = 0; // Turn off ADC before configuration

    // Configure the common ADC clock. 
    ADCON3Hbits.CLKSEL = 3; 
    ADCON3Hbits.CLKDIV = 0; 

    //Select input mode: unsigned, right-justified, 12-bit
    ADMOD0Hbits.SIGN15 = 0; // Unsigned input
    ADMOD0Hbits.DIFF15 = 0; // Single-ended

    //No trigger source, use software trigger
    ADTRIG3Hbits.TRGSRC15 = 0x10;
    
    //Configure the Core
    ADCORE0Hbits.ADCS = 15; // ADC Clock Divider
    ADCORE0Hbits.RES = 3;   // 12-bit resolution
    ADCORE0Lbits.SAMC = 10; // Sample time (adjust as needed)
    
    // Interrupt enable
    ADIELbits.IE15 = 0; 
    
    // Set initialization time to maximum 
    ADCON5Hbits.WARMTIME = 15; 
    
    // Turn on ADC module 
    ADCON1Lbits.ADON = 1; 

    // Turn on analog power for shared core 
    ADCON5Lbits.SHRPWR = 1; 
    
    // Wait when the shared core is ready for operation 
    while(ADCON5Lbits.SHRRDY == 0); 
    
    // Turn on digital power to enable triggers to the shared core 
    ADCON3Hbits.SHREN = 1;    
}

void GetADC(void)
{
	// Select channel and trigger conversion
	ADCON3Lbits.CNVCHSEL = 15;     // Select AN15
	ADCON3Lbits.CNVRTCH = 1;       // Trigger conversion

	// Wait for conversion to complete
	while (!ADSTATLbits.AN15RDY);

	//Write data to array
	//Read result
	result[currentArrayPosition] = ADCBUF15;	    
}

