/********************************************************************************
    FileName:		IO_Board_Interrupts.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:
 
********************************************************************************/

#include <XC.h>
#include <math.h>
#include "IO_Board.h"
#include "audio_sample.h"

float samp_rate = 44100;
uint16_t sine_table[1024];
float step_size;
float index_ = 0;
uint8_t waveMode = 0;
uint16_t frequency = 1000;
uint16_t video_line = 1;
uint16_t level = 0x0;
uint16_t width = 50;
uint16_t horiz = 80;
uint16_t vert = 0;
uint8_t new_data = 0;

void IO_Board_Interrupts_Init(void)
{
    /*************************************
     * PORTD
     *************************************/    
    //Configure PORTD.13 as an input
    TRISDbits.TRISD13 = 1;
    
    //Disable Analog Mode for PORTD.13
    ANSELDbits.ANSELD13 = 0;
    
    //Enable change notification event
    CNCOND |= 1 << 15;
    
    //Edge style (detects edge transitions)
    CNCONDbits.CNSTYLE = 1;
    
    //Rising edge trigger
    CNEN0Dbits.CNEN0D13 = 0;
    
    //Falling edge trigger
    CNEN1Dbits.CNEN1D13 = 1;

    //Interrupt priority
    IPC18bits.CNDIP = 4;
    
    //enable CN Interrupt on Port D
    IEC4bits.CNDIE = 1;
    
    //clear the Interrupt Flag
    IFS4bits.CNDIF = 0;
    
    /*************************************
     * PORTE
     *************************************/
    //PORTE.3
    // /INT SRAM out
    TRISEbits.TRISE3 = 1;
    PORTEbits.RE3 = 1;
    
    CNPUEbits.CNPUE5 = 0;
    CNPDEbits.CNPDE5 = 0;
    
    //Configure RE1 as an input
    TRISEbits.TRISE1 = 1;
    
    //Disable Analog Mode for RE1
    ANSELEbits.ANSELE1 = 0;
    
    //Enable change notification event
    CNCONE |= 1 << 15;
    
    //Edge style (detects edge transitions)
    CNCONEbits.CNSTYLE = 1;
    
    //Rising edge trigger
    CNEN0Ebits.CNEN0E1 = 0;
    
    //Falling edge trigger
    CNEN1Ebits.CNEN1E1 = 1;
    //CNEN1Ebits.CNEN1E5 = 0;
    
    //Interrupt priority
    IPC19bits.CNEIP = 4;
    
    //enable CN Interrupt on Port D
    IEC4bits.CNEIE = 1;
    
    //clear the Interrupt Flag
    IFS4bits.CNEIF = 0;
    
    step_size = ((float)frequency * 1024) / samp_rate;
    
    //Create a Sine table for the DAC sine wave function
    for (int i = 0; i < 1024; i++)
    {
        sine_table[i] = (uint16_t)(2048 + 2047 * sin(2 * M_PI * i / 1024));
    }    
    
    //Enable Global interrupts
    INTCON2bits.GIE = 1;
    INTCON1bits.NSTDIS = 0; 
}

void __attribute__((__interrupt__,no_auto_psv)) _CNDInterrupt(void)
{   
    //Check to see if the trigger was PORTD.13
    if(CNFD & (1 << 13))
    {
	//Indicate the trigger
	sram_busy = 1;
	
	//we must wait for the entire time /BUSY is active
	while (!(PORTD & (1 << 13)));
	
	//Clear the Flag
	CNFD &= ~(1 << 13); 
    }
        
    //Clear the Flag
    IFS4 &= ~(1 << 11);
}

void __attribute__((__interrupt__,no_auto_psv)) _CNEInterrupt(void)
{   
    int myWord = 0;
		
    //Check to see if the trigger was PORTE.1
    //RE1 is the Board Select Signal - when this goes low it triggers
    //a pin change interrupt
    if(CNFE & (1 << 1))
    {
//	//Check to see if RE1 is low
	//Get the command
	IO_Board_70V05_RD(0);

	//Show the command on the LED port
	MyLEDPort.LED_Port_value = m_data;
	SetLEDPort(MyLEDPort);

	switch(m_data)
	{
	case 0x65:
	    //Command 0 - read SRAM address 1 
	    //and show the data on the LED port
	    IO_Board_70V05_RD(2);

	    MyLEDPort.LED_Port_value = m_data;
	    SetLEDPort(MyLEDPort);

	    break;
	case 0x66:
	    //Command 1
	    break;
	case 0x67:
	    //Set DAC
	    DACCTRL1Lbits.DACON = 1;
	    //hi-byte
	    IO_Board_70V05_RD(3);
	    DAC1DATH = m_data << 8;

	    //lo-byte
	    IO_Board_70V05_RD(2);
	    DAC1DATL = m_data;

	    break;
	case 0x68:
	    //Set PWM
	    //Period
	    //hi-byte
	    IO_Board_70V05_RD(3);
	    myWord = m_data;
	    myWord = myWord << 8;

	    //lo-byte
	    IO_Board_70V05_RD(2);
	    myWord = myWord + m_data;

	    if(myWord >= 0xffff)
	    {
		myWord = 0xffff;
	    }

	    if(myWord <= 0x01)
	    {
		myWord = 0x01;
	    }

	    PG4PER = myWord;

	    PG4STATbits.UPDREQ = 1;
	    while(!PG4STATbits.UPDATE);

	    //Duty Cycle
	    //hi-byte
	    IO_Board_70V05_RD(5);
	    myWord = m_data;
	    myWord = myWord << 8;

	    //lo-byte
	    IO_Board_70V05_RD(4);
	    myWord = myWord + m_data;

	    PG4DC = myWord; 
	    PG4STATbits.UPDREQ = 1;
	    while(!PG4STATbits.UPDATE);

	    break;
	case 0x69:
	    //DAC - Waveforms

	    IO_Board_70V05_RD(6);

	    if(m_data == 0x0)
	    {
		DACCTRL1Lbits.DACON = 0;
		T1CONbits.TON = 0;
		waveMode = 0x0;
		SLP1CONHbits.TWME = 0;
	    }

	    //Sine Wave
	    if(m_data == 0x1)
	    {
		DACCTRL1Lbits.CLKDIV = 0;
		DACCTRL1Lbits.DACON = 1;
		//Timer 1
		PR1 = ((OSC_FREQ * 3) / samp_rate) - 1; // 44.1 kHz
		T1CONbits.TON = 1;
		waveMode = 0x76;
		SLP1CONHbits.TWME = 0;		    
	    }

	    //Audio Wave
	    if(m_data == 0x2)
	    {
		DACCTRL1Lbits.DACON = 1;
		PR1 = ((OSC_FREQ * 3) / samp_rate) - 1; // 44.1 kHz
		T1CONbits.TON = 1;
		waveMode = 0x48;
		SLP1CONHbits.TWME = 0;
	    }

	    //Triangle Wave
	    if(m_data == 0x4)
	    {
		DACCTRL1Lbits.DACON = 1;
		T1CONbits.TON = 0;
		waveMode = 0x0;
		SLP1CONHbits.TWME = 1;
	    }

	    //Composite video
	    if(m_data == 0x8)
	    {
		//get horizontal
		IO_Board_70V05_RD(2);
		horiz = m_data;
		IO_Board_70V05_RD(5);
		vert = m_data;		    
		DACCTRL1Lbits.CLKDIV = 0;
		DACCTRL1Lbits.DACON = 1;
		//Timer 1
		PR1 = 6250;
		T1CONbits.TON = 1;
		waveMode = 0x23;
		SLP1CONHbits.TWME = 0;		    
	    }
	    break;
	case 0x6a:
	
	    //Timer 1
	    PR1 = 6250;
	    IO_Board_70V05_RD(2);
	    if(!m_data)
	    {
		T1CONbits.TON = 0;
	    }
	    else
	    {
		T1CONbits.TON = 1;
	    }
	    waveMode = 0x89;
	    
	    for(int i=0;i<1000;i++)
	    {
		IO_Board_70V05_WR(0xa + (i * 2), result[i] >> 8);
		IO_Board_70V05_WR(0xb + (i * 2), result[i+1] & 0xff);
	    }
	    break;
	default:
	    //default
//		    DACCTRL1Lbits.DACON = 0;
//		    T1CONbits.TON = 0;
//		    waveMode = 0x0;
//		    SLP1CONHbits.TWME = 0;
	    break;
	}

	// /ACK
	PORTEbits.RE5 = 0;

	//wait for MainBrain to release Board Select
	while(!(PORTE & (1 << 1)));
	
	//now release the ACK
	PORTEbits.RE5 = 1;
	
	//Clear the Flag
	CNFE &= ~(1 << 1); 
    }
        
    //Clear the Flag
    IFS4 &= ~(1 << 12);
}

int i = 0;
int cv = 10;

void __attribute__((__interrupt__,no_auto_psv)) _T1Interrupt(void)
{   
    //Sine wave
    if(waveMode == 0x76)
    {
	// Write 12-bit value to DAC registers
	DAC1DATH = sine_table[(int)index_];  

	index_ += step_size;  
	if (index_ >= 1024) index_ -= 1024;

	TMR1 = 0;
    }
    
    //Audio Waveform
    if(waveMode == 0x48)
    {
	// Send one sample to the DAC
	DAC1DATH = audio_data[i] << 4; // Scale from 8-bit to 12-bit

	// Move to the next sample
	i++;

	// Loop playback when reaching the end
	if (i >= 31744)
	{	    
	    i = 0; // Restart from beginning
	}
    }

    //Composite video
    if(waveMode == 0x23)
    {
	//Pre-equalizing 
	if(video_line <= 3)
	{
	    level = 0x200;
	    width = 35;
	    
	    DAC1DATH = 0;
	    for (int i = 0; i < width; i++);
	    DAC1DATH = level;
	    for (int i = 0; i < 205; i++);
	    DAC1DATH = 0;
	    for (int i = 0; i < 35; i++);
	    DAC1DATH = level;	    
	}
	
	//vertical sync
	if((video_line > 3) && (video_line <= 6))
	{
	    //level = 0x300;
	    //width = 180;
	    
	    DAC1DATH = 0;
	    for (int i = 0; i < 180; i++);
	    DAC1DATH = level;
	    for (int i = 0; i < 35; i++);
	    DAC1DATH = 0;
	    for (int i = 0; i < 220; i++);
	    DAC1DATH = level;

	}
	
	//Post-equalizing 
	if((video_line > 6) && (video_line <= 9))
	{	
	    //level = 0x300;
	    //width = 55;
	    
	    DAC1DATH = 0;
	    for (int i = 0; i < width; i++);
	    DAC1DATH = level;
	    for (int i = 0; i < 205; i++);
	    DAC1DATH = 0;
	    for (int i = 0; i < 35; i++);
	    DAC1DATH = level;	    
	}
	
	//color field
	if((video_line > 9) && (video_line <= 20))
	{	
	    //level = 0x300;
	    //width = 55;
	    
	    DAC1DATH = 0;
	    for (int i = 0; i < width; i++);
	    DAC1DATH = level;
	}
	
	//picture information
	if(video_line > 20)
	{
	    DAC1DATH = level;
	
	    //front porch
	    //DAC1DATH = 0x0a0;
	    for (int i = 0; i < 19; i++);

	    // Sync pulse (~4.7 us)
	    DAC1DATH = 0;
	    for (int i = 0; i < width; i++);
	    DAC1DATH = level;
	    
	    
//	    for(int i=0;i<5;i++)
//	    {			    
//		DAC1DATH = 500 + (i * 100);	    
//		for (int i = 0; i < 30; i++);
//	    }

	    //Back porch
//	    for (int i = 0; i < 35; i++);
//	    DAC1DATH = level;
	    
	    //Display a rectangle
	    if((video_line > (50 + vert))&&(video_line <= (60 + vert)))
	    {
		for (int i = 0; i < horiz; i++);//200
		DAC1DATH = 0x800;
		for (int i = 0; i < 80; i++);
		DAC1DATH = level;	
	    }
	}
	
	//next line
	video_line++;
	
	//number of lines
	if(video_line > 280)
	{
	    video_line = 1;
	}

    }
    
    if(waveMode == 0x89)
    {
	//get ADC
	GetADC();
	
	currentArrayPosition++;
	
	if(currentArrayPosition > 1000)
	{
	    currentArrayPosition = 0;	    
	}	
    }
     
    IFS0bits.T1IF = 0;    
}