/*******************************************************************************
  Main Source File

  Company:
    Microchip Technology Inc.

  File Name:
    main.c

  Summary:
    This file contains the "main" function for a project.

  Description:
    This file contains the "main" function for a project.  The
    "main" function calls the "SYS_Initialize" function to initialize the state
    machines of all modules in the system
 *******************************************************************************/

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************

#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                    // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "definitions.h"                // SYS function prototypes
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

// TODO 17.01
#include "Microchip_Logo.h"
#include "Alphabet_Fonts.h" 

// TODO 17.02
#define LCM_WIDTH 128
#define LCM_HEIGHT 64
#define FONT_WIDTH  7
#define FONT_HEIGHT 8
uint8_t LogoX, LogoY;
unsigned char LCM_Blank[1024];
char Alphabet[95] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^&()[]{}_\\|;:,.`'\"<>+-*/=? ";

const uint8_t LCM_InitCMD[] = {
    0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40, 0x8D, 0x14, 0x20, 0x02, 0xA1,
    0xC8, 0xDA, 0x12, 0x81, 0xCF, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0xAF
};

uint32_t i = 0;
uint8_t USARTRTxBuffer[100];
uint8_t USART5_ReceiveData[1];
volatile uint8_t USART5_IsReceived = 0;
volatile uint8_t USART5_IsTransmitted = 1;

#define SYS_CONSOLE_PRINT_BUFFER_SIZE	200
static char consolePrintBuffer[SYS_CONSOLE_PRINT_BUFFER_SIZE];
volatile uint8_t TC3_HasExpired = 0;
volatile uint8_t TC4_HasExpired = 0;

uint16_t ADC_Result[2];
volatile uint8_t ADC_IsCompleted = 0;
volatile int8_t  ADC_ChannelIdx = 0;

uint8_t Duty = 50;
int8_t DutyDistance = 2;

void myprintf(const char *format, ...)
{
    size_t len = 0;
    va_list args = {0};
    va_start(args, format);
    len = vsnprintf(consolePrintBuffer, SYS_CONSOLE_PRINT_BUFFER_SIZE, format, args);
    va_end(args);

    if ((len > 0) && (len < SYS_CONSOLE_PRINT_BUFFER_SIZE))
    {
        consolePrintBuffer[len] = '\0';
        SERCOM5_USART_Write(consolePrintBuffer, len);
        while (SERCOM5_USART_WriteIsBusy());
    }
}

// TODO 17.03
void LCM_SetCursor( uint8_t x, uint8_t y )
{
    uint8_t OUT[3] = { 0x0F & x, 0x10 + (x >> 4), 0xB0 + y };
    SSD1306_RS_Clear();
    SERCOM4_SPI_Write(( void* )OUT, 3);
}

void LCM_DrawBitmap( uint8_t sx, uint8_t sy, uint8_t width, uint8_t height, const unsigned char *byte )
{
    for( int y = 0; y < (height/8); y++ )
    {
        LCM_SetCursor(sx, sy + y);
        SSD1306_RS_Set();
        SERCOM4_SPI_Write(( void* )byte, (width - sx));
        byte += width;
    }
}

void LCM_DrawFont( uint8_t sx, uint8_t row, char ch )
{
    int chIdx = 0;

    while( ch != Alphabet[chIdx] ) { chIdx++; }	
    LCM_SetCursor(sx, row);
    SSD1306_RS_Set();
    SERCOM4_SPI_Write( ( void* )(Alphabet_Fonts+(chIdx*FONT_WIDTH)), FONT_WIDTH );
}

void LCM_DrawString( uint8_t sx, uint8_t row, char *str )
{
    for( int x=0 ; x<strlen(str); x++ )
    {
        LCM_DrawFont( sx+(x*FONT_WIDTH), row, str[x] );
    }
}

void USART5_Transmit( uintptr_t context )
{
    USART5_IsTransmitted = 1;
} 

void USART5_Receive( uintptr_t context )
{
    USART5_IsReceived = 1;
}

void TC3_TimerExpired(TC_TIMER_STATUS status, uintptr_t context) 
{
    if( status & TC_INTFLAG_OVF_Msk)
    {
        TC3_HasExpired = 1;
    }
}

void TC4_TimerExpired(TC_TIMER_STATUS status, uintptr_t context) 
{
    if( status & TC_INTFLAG_OVF_Msk)
    {
        TC4_HasExpired = 1;
    }
}

void ADC_Complete( ADC_STATUS status, uintptr_t context )
{
    if( status & ADC_INTFLAG_RESRDY_Msk )
    {
        ADC_Result[ADC_ChannelIdx] = ADC_ConversionResultGet();
        ADC_ChannelIdx = ( ADC_ChannelIdx + 1 ) % 2;
        if( ADC_ChannelIdx == 0 )
        {
            ADC_IsCompleted = 1;
        }
    }
}

// *****************************************************************************
// *****************************************************************************
// Section: Main Entry Point
// *****************************************************************************
// *****************************************************************************

int main ( void )
{
    /* Initialize all modules */
    SYS_Initialize ( NULL );
    
    TC3_TimerCallbackRegister( TC3_TimerExpired, (uintptr_t )NULL );
    TC3_TimerStart();
    TC4_TimerCallbackRegister( TC4_TimerExpired, (uintptr_t )NULL );
    TC4_TimerStart();
    
    SERCOM5_USART_ReadCallbackRegister( USART5_Receive, (uintptr_t )NULL );
    SERCOM5_USART_WriteCallbackRegister( USART5_Transmit, (uintptr_t )NULL );
    SERCOM5_USART_Read( USART5_ReceiveData, 1 );
    
    ADC_CallbackRegister( ADC_Complete, (uintptr_t )NULL );
    ADC_Enable();
    
    myprintf("\033[2J");
    myprintf("\033[?25l");
    
    TCC2_PWMStart();
    
    // TODO 17.04
    SYSTICK_TimerStart();
    SSD1306_CS_Clear();
    SSD1306_RS_Clear();
    SERCOM4_SPI_Write(( void* )LCM_InitCMD, sizeof (LCM_InitCMD));
    memset(LCM_Blank, 0, 1024);
    LCM_DrawBitmap(0, 0, LCM_WIDTH, LCM_HEIGHT, LCM_Blank);
    LogoX = 127;
    LogoY = 0;
    for( i = 0; i < 128; i += 2 )
    {
        LCM_DrawBitmap(LogoX - i, LogoY, LCM_WIDTH, LCM_HEIGHT, Microchip_Logo);
        SYSTICK_DelayMs((i * i) / 400);
    }
    SYSTICK_DelayMs(1000);
    memset(LCM_Blank, 0, 1024);
    LCM_DrawBitmap(0, 0, LCM_WIDTH, LCM_HEIGHT, LCM_Blank);
    sprintf( (char *)USARTRTxBuffer, "SysClock %ld.%-ldMHz", SYSTICK_TimerFrequencyGet()/1000000, (SYSTICK_TimerFrequencyGet()%1000000)/1000 );
    LCM_DrawString( 0, 0, (char *)USARTRTxBuffer );			
    sprintf( (char *)USARTRTxBuffer, "TC3 period %ldms", (TC3_Timer16bitPeriodGet()+1)*1000/TC3_TimerFrequencyGet() );
    LCM_DrawString( 0, 1, (char *)USARTRTxBuffer );			
    sprintf( (char *)USARTRTxBuffer, "TC4 period %ldms", (TC4_Timer16bitPeriodGet()+1)*1000/TC4_TimerFrequencyGet() );
    LCM_DrawString( 0, 2, (char *)USARTRTxBuffer );			
    sprintf( (char *)USARTRTxBuffer, "Received Data :" );
    LCM_DrawString( 0, 7, (char *)USARTRTxBuffer );

    while ( true )
    {
        /* Maintain state machines of all polled MPLAB Harmony modules. */
        SYS_Tasks ( );
        
        if (TC3_HasExpired)
        {
            TC3_HasExpired = 0;
            LED1_Toggle();
            myprintf( "\033[1;1HHello World!" );
            
            // TODO 17.05
            sprintf( (char *)USARTRTxBuffer, "Hello World!");
            LCM_DrawString( 0, 3, (char *)USARTRTxBuffer );
        }
        
        if (TC4_HasExpired)
        {
            TC4_HasExpired = 0;
            LED2_Toggle();
            ADC_ConversionStart();
            
            Duty += DutyDistance;
            if (Duty >= 100 || Duty <= 0)
                DutyDistance = -DutyDistance;
            
            TCC2_PWM16bitDutySet( 1, ((uint32_t)Duty * (TCC2_PWM16bitPeriodGet() + 1)) / 100 );
            
            // TODO 17.06
            sprintf( (char *)USARTRTxBuffer, "PWM Duty : %3d%%", Duty);
            LCM_DrawString( 0, 4, (char *)USARTRTxBuffer );
        }
        
        if( USART5_IsReceived )
        {
            USART5_IsReceived = 0;
            myprintf("\033[6;1HReceived Data : %1c", USART5_ReceiveData[0] );
            SERCOM5_USART_Read( USART5_ReceiveData, 1 );
            
            // TODO 17.07
            sprintf( (char *)USARTRTxBuffer, "Received Data : %1c", USART5_ReceiveData[0] );
            LCM_DrawString( 0, 7, (char *)USARTRTxBuffer );
        }
        
        if( ADC_IsCompleted )
        {
            ADC_IsCompleted = 0;

            myprintf( "\033[2;1HVR1 Value : %4d", ADC_Result[0] );
            myprintf( "\033[3;1HTemperature Value : %4d", ADC_Result[1] );
            
            // TODO 17.08
            sprintf( (char *)USARTRTxBuffer, "VR1  : %4d", ADC_Result[0]);
            LCM_DrawString( 0, 5, (char *)USARTRTxBuffer );
            sprintf( (char *)USARTRTxBuffer, "Temp : %4d", ADC_Result[1]);
            LCM_DrawString( 0, 6, (char *)USARTRTxBuffer );
        }
    }

    /* Execution should not come here during normal operation */

    return ( EXIT_FAILURE );
}


/*******************************************************************************
 End of File
*/

