/*
	FreeRTOS V3.2.3 - Copyright (C) 2003-2005 Richard Barry.

	This file is part of the FreeRTOS distribution.

	FreeRTOS is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	FreeRTOS is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FreeRTOS; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	A special exception to the GPL can be applied should you wish to distribute
	a combined work that includes FreeRTOS, without being obliged to provide
	the source code for any proprietary components.  See the licensing section 
	of http://www.FreeRTOS.org for full details of how and when the exception
	can be applied.

	***************************************************************************
	See http://www.FreeRTOS.org for documentation, latest information, license 
	and contact details.  Please ensure to read the configuration and relevant 
	port sections of the online documentation.
	***************************************************************************
*/

//This was ported from the lpc2106 GCC demo. 


// Standard includes. 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// Scheduler includes. 
#include "FreeRTOS.h"
#include "task.h"
#include "mainfunctions.h"

// Demo application includes.
#include "helpers.h" 
#include "serial.h"
#include "rtc.h"
#include "pwm.h"
#include "gpio.h"
#include "adc.h"
#include "vic.h"
#include "wdt.h"



#define TWENTY_MS 1166000


/*-----------------------------------------------------------*/
extern void ( vtimer_ISR )( void );


void vApplicationIdleHook( void );


/*----------------------idle hook----------------------------*/
void vApplicationIdleHook( void ) {
	//PCON  = 0x1; //set the CPU into idil mode (note: cant use JTAG interface while in idle mode). an interupt must occure for it to come out of idle mode
}

static portTASK_FUNCTION_PROTO( vTaskTest, pvParameters );
void vStartTestTasks( unsigned portBASE_TYPE uxPriority);

void vOSSDemoTask( void *pvParameters);

void vExternalInteruptTask( void *pvParameters );
void vPWMTask( void *pvParameters );
void vGPIOTask( void *pvParameters );
void vADCTask( void *pvParameters );
void vADCLEDBlinkTask( void *pvParameters );
void vWDTTask( void *pvParameters );
//void vTimerInteruptTask( void *pvParameters );


void vStartTestTasks( unsigned portBASE_TYPE uxPriority)
{
	xTaskCreate( vTaskTest, ( const signed portCHAR * const ) "Test", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	
	//xTaskCreate( vTimerInteruptTask, ( const signed portCHAR * const ) "TestTimerINT", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	
	xTaskCreate( vGPIOTask, ( const signed portCHAR * const ) "TestGPIO", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	//xTaskCreate( vPWMTask, ( const signed portCHAR * const ) "TestPWM", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	xTaskCreate( vADCTask, ( const signed portCHAR * const ) "TestADC", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	xTaskCreate( vADCLEDBlinkTask, ( const signed portCHAR * const ) "TestADCLEDBlink", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	//xTaskCreate( vWDTTask, ( const signed portCHAR * const ) "TestWDT", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );		
	
	
	//xTaskCreate( vOSSDemoTask, ( const signed portCHAR * const ) "OSSDemo", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
	
	//xTaskCreate( vExternalInteruptTask, ( const signed portCHAR * const ) "vExternalInteruptTask", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
}

static portTASK_FUNCTION_PROTO( vTaskTest, pvParameters ) 
{
	( void ) pvParameters;
	
	initGPIOPin(PORT0, 10, FALSE); //setup P0.10 as an output pin (this is the LED on the olimex board)
    
    for(;;)	{
    	setGPIOPin(PORT0, 10, FALSE);
		vTaskDelay(500);
		setGPIOPin(PORT0, 10, TRUE);
		vTaskDelay(500);		
		//vSerialPutString(0,"Hello World\n\r",0);
        	//vSerialPutString(1,"Hello World\n\r",0);
		xWaitRTC_Tick(5000);
	}
}


/*
void vTimerInteruptTask( void *pvParameters )
{
	( void ) pvParameters;
   
    initGPIOPin(PORT0, 11, FALSE); //setup the GPIO pin as output
   
    
    T1MR1 = 500000000; //set the match register value
    T1IR |= (1<<1); //enable the interupt
    
    T1MCR |= (1<<3); //enable interupt for MR1
    T1MCR |= (1<<4); //reset timer on match
    
    T1TCR |= (1<<0); //enable timer
    
    VICIntSelect &= ~(1<<5); //timer1, assigned to IRQ category not FIQ category
    
    VICVectAddr6 = ( portLONG ) vtimer_ISR;
    
    VICVectCntl6 = 5 | (1<<5); //
    
    VICIntEnable |= (1<<5); //enable VIC for timer 0
     
    for(;;)	{
	    //showVar3("T0TC", T0TC, 0);
	    
	    showVar3("T1CCR", T1CCR, 0);vTaskDelay(5);
	    showVar3("VICIRQStatus", VICIRQStatus, 0);vTaskDelay(5);
	    showVar3("VICIntSelect", VICIntSelect, 0);vTaskDelay(5);
	    showVar3("VICIntEnClr", VICIntEnClr, 0);vTaskDelay(5);
	    showVar3("VICIntEnable", VICIntEnable, 0);vTaskDelay(5);
	    showVar3("VICVectCntl6", VICVectCntl6, 0);vTaskDelay(5);
	    showVar3("VICVectAddr6", VICVectAddr6, 0);vTaskDelay(5);
	    showVar3("VICVectAddr", VICVectAddr, 0);vTaskDelay(5);
	    showVar3("T1CTCR", T1IR, 0);vTaskDelay(5);
	    showVar3("T1IR", T1IR, 0);vTaskDelay(5);
	    showVar3("T1MCR", T1MCR, 0);vTaskDelay(5);
	    showVar3("T1TCR ", T1TCR, 0);vTaskDelay(5);
	    
	    showVar3("T1TC ", T1TC, 0);vTaskDelay(5);
	    showVar3("T1MR1", T1MR1, 0);vTaskDelay(5);
	    
	    
	    
	    //showVar3("tbit ", toggleBit, 0);vTaskDelay(5);
		vTaskDelay(400);	
	}
}
*/










/*
 * This function was used during the OSS end of course course presentation. A servo was 
 * on PWM2, an LED on GPIO 0.29, and a button on GPIO 0.28 to switch directions. 
 */
void vOSSDemoTask( void *pvParameters )
{
	( void ) pvParameters;

    portBASE_TYPE dutyCycleLow = (portBASE_TYPE) (TWENTY_MS * 0.04);
    portBASE_TYPE dutyCycleHigh = (portBASE_TYPE) (TWENTY_MS * 0.15);
    portBASE_TYPE dutyCycle = dutyCycleLow;
    
    portBASE_TYPE stepsize = (dutyCycleHigh - dutyCycleLow) / 255;
    enum BOOL up = TRUE;
    portBASE_TYPE changeCounter = 0;
    
    for(;;)	{
    	if( up ) {  
			dutyCycle += stepsize;
    	} else {
			dutyCycle -= stepsize;
		}
		
		if( dutyCycle >= dutyCycleHigh ) {
			dutyCycle = dutyCycleLow;	
		} else if( dutyCycle < dutyCycleLow ) {
			dutyCycle = dutyCycleHigh;	
		}

		enum BOOL v  = readGPIOPin(PORT0, 28);
		
		if( v && changeCounter > 15 ) {
			changeCounter = 0;
			if( up ) {
				up = FALSE;
				setGPIOPin(PORT0, 29, FALSE);
			} else {
				up = TRUE;
				setGPIOPin(PORT0, 29, TRUE);	
			}	
		}
		
		if( changeCounter < 30 ) {
			changeCounter++;	
		}

		vTaskDelay(50);
		
		setPWMDutyCycle(PWM2, dutyCycle);
	}
}


/*
 * This is an example of using the watchdog timer. 
 */
void vWDTTask( void *pvParameters )
{
	( void ) pvParameters;
	
	vSerialPutString(0,"INITIALIZING WDT...\n\r",0);
	
	initWDT(DEFAULT_WDT_DURATION); //turn on the WDT and set the duration
	feedWDT(); //do an initial feeding to get the WDT working.
    
    for(;;)	{
    	if (WDTV < 0x0FFFFFF){
		    feedWDT();
			vSerialPutString(0,"reset WDT...\n\r",0);     
		}
    	
    	showVar("WDTV", WDTV);
		
		vTaskDelay(150);
	}
}


extern int counter;
void vExternalInteruptTask( void *pvParameters )
{
	( void ) pvParameters;
	
	//initGPIOPin(PORT0, 15, TRUE);
	//initGPIOPin(PORT0, 16, TRUE);
	
	vSetupVIC();
	
    for(;;)	{
			vSerialPutString(0,"running ExtIntLoop\n\r",0); 
    	showVar("counter", counter);
			vTaskDelay(1000);	
		}
}



/*
 * This task demonstrates the use of the PWM channels. On the olimex board, it's output pin
 * is SSEL0/P0.7. It generates a PWM signal with about a 20ms period and 1ms to 2ms duty cycle.
 * You may want to feed this into a servo (buffered) to see somthing neet happen. If you look
 * at it on an o-scope you'll notice it continually changing.
 */
void vPWMTask( void *pvParameters )
{
	( void ) pvParameters;

	PWMinit(0, TWENTY_MS); //setup the PWM period and pre-scalar
	//setupPWMChannel(PWM1, 0x200);
	setupPWMChannel(PWM2, 0xF00); //pin P0.7 / SSEL0
	//setupPWMChannel(PWM3, 0x200);
	//setupPWMChannel(PWM4, 0x200);
	//setupPWMChannel(PWM5, 0x200);
	//setupPWMChannel(PWM6, 0x200);

    portBASE_TYPE dutyCycleLow = (portBASE_TYPE) (TWENTY_MS * 0.04);
    portBASE_TYPE dutyCycleHigh = (portBASE_TYPE) (TWENTY_MS * 0.15);
    portBASE_TYPE dutyCycle = dutyCycleLow;
    
    portBASE_TYPE stepsize = (dutyCycleHigh - dutyCycleLow) / 255;    
    
    for(;;)	{
    	dutyCycle += stepsize;
    	
		if( dutyCycle >= dutyCycleHigh ) {
			dutyCycle = dutyCycleLow;	
		}
		vTaskDelay(15);
		setPWMDutyCycle(PWM2, dutyCycle);
	}
}



/*
 * This task demonstarates the use of basic GPIO input/output.
 * When you press B1 on the olimex board, P0.29 will go low and the BUZZAR will make noise, 
 * when button is not pressed, it will stay high.
 */
void vGPIOTask( void *pvParameters )
{
	( void ) pvParameters;
   
   initGPIOPin(PORT0, 15, TRUE); //setup the GPIO pin as input. This is B1 on the olimex board
   initGPIOPin(PORT0, 29, FALSE); //setup the GPIO pin as output
   
   initGPIOPin(PORT0, 12, FALSE); //setup the GPIO pin as output
   initGPIOPin(PORT0, 13, FALSE); //setup the GPIO pin as output
       
   
    for(;;)	{
    	//Read P0.15, the B1 button on the olimex board. Note, P0.15 is pulled up normally.
    	//Pressing the button will take the input low.
   		enum BOOL v  = readGPIOPin(PORT0, 15);
		
		
		//What ever was on P0.28, cary it thru to P0.29
		if( v  ) {
			setGPIOPin(PORT0, 29, TRUE);
		} else {
			setGPIOPin(PORT0, 29, FALSE);		
		}
	
	
		if( ! v ) {
			//Make some annoying noise with the buzzer
			//to change the pitch, twiddle the vTaskDelay() values
			setGPIOPin(PORT0, 12, TRUE);
			setGPIOPin(PORT0, 13, FALSE);	
			vTaskDelay(8);//vTaskDelay(8);
			setGPIOPin(PORT0, 12, FALSE);
			setGPIOPin(PORT0, 13, TRUE);
			vTaskDelay(7);//vTaskDelay(7);
		} else {
			vTaskDelay(50);	
		}
	}
}


/*
 * This is an example of using the ADC for conversion. By adjusting the POT on the olimex board,
 * you can change the speed at which the LED blinks. It will also output it's value on UART0. 
 */
void vADCTask( void *pvParameters )
{
	( void ) pvParameters;

	configureADCPort(AD0_3);
	
	//commented this out, no LED in this portion
	//initGPIOPin(PORT0, 11, FALSE); //setup P0.11 as an output pin (this is the LED on the olimex board)
	
//Why were these init to 50??  weird.
    portLONG blinkDelay = 50;
    portLONG temp = 50;
    
    portCHAR buff[200];
    int i;
    
    for(;;)	{
    	temp = cADC_Result(AD0_3);
    	if( temp >= 0 && temp <= 1024 ) 
    		blinkDelay = 0 + temp;//this used to be 15 + temp

    	for(i=0; i < sizeof(buff); i++ ) {
    		buff[i] = '\0';	
    	}
    	vSerialPutString(0,toBinaryString(blinkDelay, buff),0);
    	vSerialPutString(0,"   ",0);
   	
    	for(i=0; i < sizeof(buff); i++ ) {
    		buff[i] = '\0';	
    	}
    	
    	toString(blinkDelay, buff, sizeof(buff));
    	vSerialPutString(0,buff,0);

    	vSerialPutString(0,"\n\r",0);
    	
	//vSerialPutString(0,"Yo Mamma!\n\r",0);
    	
    	//trying to keep the LED delay from slowing down input
    	//setGPIOPin(PORT0, 11, FALSE);
    	//vTaskDelay(blinkDelay);
    	
		//setGPIOPin(PORT0, 11, TRUE);
		//vTaskDelay(blinkDelay);

	//padding delay to not hose the serial port
	vTaskDelay(5);
	}
}


//Attempting to run a separate parallel task that just reads the ports...
//can we make it work?
void vADCLEDBlinkTask( void *pvParameters )
{
	( void ) pvParameters;

	//I'm sorta creeped out knowing this thing means that main starts by configing the ADC twice...
	configureADCPort(AD0_3);
	
	initGPIOPin(PORT0, 11, FALSE); //setup P0.11 as an output pin (this is the LED on the olimex board)
	

    portLONG blinkDelay = 50;
    portLONG temp = 50;
    
    //portCHAR buff[200];
    int i;
    
    for(;;)	{
    	temp = cADC_Result(AD0_3);
    	if( temp >= 0 && temp <= 1024 ) 
    		blinkDelay = 15 + temp;
    	
    	//for(i=0; i < sizeof(buff); i++ ) {
    	//	buff[i] = '\0';	
    	//}
    	
    	//toString(blinkDelay, buff, sizeof(buff));
    	//vSerialPutString(0,buff,0);
    	
    	//vSerialPutString(0,"   ",0);
    	//for(i=0; i < sizeof(buff); i++ ) {
    	//	buff[i] = '\0';	
    	//}
    	//vSerialPutString(0,toBinaryString(blinkDelay, buff),0);
    	//vSerialPutString(0,"\n\r",0);
    	
    	
    	//the LED is all that's left in this one!
    	setGPIOPin(PORT0, 11, FALSE);
    	vTaskDelay(blinkDelay);
    	
	setGPIOPin(PORT0, 11, TRUE);
	vTaskDelay(blinkDelay);

	
	}
}


//Starts all the other tasks, then starts the scheduler. 
int main( void )
{
	// Setup the hardware for use with the Olimex demo board.
	prvSetupHardware();
	enableSerial0();
	enableSerial1();
	InitRTC();


	xSerialPortInitMinimal(0, 115200, 250 );
	xSerialPortInitMinimal(1, 115200, 250 );   	

	vSerialPutString(0,"En Taro Adun!\n\r",0);

	vStartTestTasks( tskIDLE_PRIORITY + 1 );


	//NOTE : Tasks run in system mode and the scheduler runs in Supervisor mode.
	//The processor MUST be in supervisor mode when vTaskStartScheduler is 
	//called.  The demo applications included in the FreeRTOS.org download switch
	//to supervisor mode prior to main being called.  If you are not using one of
	//these demo application projects then ensure Supervisor mode is used here. 
	
	vTaskStartScheduler();

	return 0;
}
/*-----------------------------------------------------------*/


