/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <string.h>

#include "device/fsl_device_registers.h"
#include "clock/fsl_clock_manager.h"
#include "gpio/fsl_gpio_driver.h"
#include "fsl_port_hal.h"
#include "pit/fsl_pit_driver.h"
#include "fsl_pit_hal.h"
#include "interrupt/fsl_interrupt_manager.h"

#include "board.h"
#include "gpio_pins.h"

#include "gpio_uart.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define PIT_TIMER_INST          0

#define OSR                     5
#define OSR_DIV                 3U
#define BAUD_RATE               19200

/*******************************************************************************
 * Global Variables
 ******************************************************************************/
extern gpio_input_pin_user_config_t  gpioUartDemoRxPin[];
extern gpio_output_pin_user_config_t gpioUartDemoTxPin[];
extern gpio_output_pin_user_config_t led_pins[];

volatile unsigned char curr_val = 0, intCount = 0, rx_start = 0, char_recvd = 0;
volatile unsigned char tx_index = 0, rx_index = 0;
volatile uint8_t bitBuff[OSR * 10] = {0};
volatile char rcv_bit_index = 0;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void PIT_0_IRQHandler(void);
void io_irq_callback(void);
void send_char(char character);
char recv_char(void);

/*******************************************************************************
 * Code
 ******************************************************************************/
 
/******************************************************************************/
int main (void)
{
    // General Variables
    uint32_t busClock = 0, pit_ldval = 0, buf_index = 0, a = 0;
    
    // TX specific variables
    char sourceBuff[50] = {"************************************************\n\r"};
    
    uint8_t receiveBuff[50] = {0};
    
    // Configure the PIT init structure.  The PIT will be needed to get the 
    //  correct baud rate.  
    const pit_user_config_t pitInitStruct = {
        .isInterruptEnabled = true,// Disable timer 0 interrupt.
        .isTimerChained = false,   // Disable timer chain, no meaning if this feature is invalid.
        .periodUs = 0,             // Set timer to period to 0.
    };
    
    hardware_init();
    
    // Change the UART pins from their default functionality.  
    port_hal_mux_control(UART_TX_INSTANCE, UART_TX_PIN_NUM, kPortMuxAsGpio);
    port_hal_mux_control(UART_RX_INSTANCE, UART_RX_PIN_NUM, kPortMuxAsGpio);
        
    // Initialize the GPIO pins
    gpio_init(gpioUartDemoRxPin, gpioUartDemoTxPin);

    // Initialize PIT, enable module clock, disable run in debug.
    pit_init_module(false);

    // Initialize PIT timer channel.
    pit_init_channel(PIT_TIMER_INST, &pitInitStruct);

    // Get the bus clock frequency (PIT reference frequency
    clock_manager_get_frequency(kBusClock, &busClock);

    // Calculate the PIT load value.  This value sets the PIT interrupt 
    // period to the oversampling time that we require.  
    
    // Adjust the busClock on K64 twr
    #if (defined(CPU_MK64FN1M0VMD12))
    busClock = 60*1000*1000;
    #endif
    pit_ldval = (busClock/BAUD_RATE)/(OSR) - 1;

    // Now set the PIT LDVAL
    pit_hal_set_timer_period_count(PIT_TIMER_INST, pit_ldval);

    // Configure the interrupt vector for the interrupt handler
    interrupt_register_handler(PIT0_IRQn, &PIT_0_IRQHandler);
    
    // Install GPIO callback function
    gpio_register_isr_callback_function(gpioUartDemoRxPin[0].pinName, io_irq_callback);
    
    // Enable the IRQs
    interrupt_enable(PIT0_IRQn);
    
    // Ensure that the program is ready to receive characters
    char_recvd = 0;
    
    // Start the timer
    pit_timer_start(PIT_TIMER_INST);
    
    // Print banner
    for(buf_index = 0; buf_index < 50; buf_index++)
    {
        send_char(sourceBuff[buf_index]);
    }
    
    strcpy(sourceBuff, "* Beginning GPIO UART Emulation Demo          *\n\r");
    
    for(buf_index = 0; buf_index < 50; buf_index++)
    {
        send_char(sourceBuff[buf_index]);
    }
    
    strcpy(sourceBuff, "* Input any character to have it echoed to    *\n\r");
    
    for(buf_index = 0; buf_index < 50; buf_index++)
    {
        send_char(sourceBuff[buf_index]);
    }
    
    strcpy(sourceBuff, "* the terminal                                *\n\r");
    
    for(buf_index = 0; buf_index < 50; buf_index++)
    {
        send_char(sourceBuff[buf_index]);
    }
    
    strcpy(sourceBuff, "***********************************************\n\n\r");

    for(buf_index = 0; buf_index < 50; buf_index++)
    {
        send_char(sourceBuff[buf_index]);
    }
    
    // Done printing the banner ///////////////////////
    
    // Initialize the receive buffer.  
    for(a=0; a<30; a++)
    {
        bitBuff[a] = 0;
    }
    
    while(1)
    {
        /////////////////////////////////////////////////
        // Echo received characters
        
        // First, get character.  
        receiveBuff[0] = recv_char();
        
        // Now, put the received character in the transmit buffer
        sourceBuff[0] = receiveBuff[0];
        
        // Send the character
        send_char(sourceBuff[0]);
    } 
}
/********************************************************************/

/********************************************************************/
/*   PIT IRQ Handler                                                */
/********************************************************************/
void PIT_0_IRQHandler(void)
{
    char rxVal = 0, recvStart = rx_start;

    // Clear the interrupt flag.
    pit_hal_clear_interrupt_flag(PIT_TIMER_INST);

    intCount++;
    if( intCount > (OSR-1) )
    {
        intCount = 0;
    }

    if(rx_index > 0)
    {
        ///////////////////////////////////////
        // Receive the bits
        rxVal = gpio_read_pin_input(gpioUartDemoRxPin[0].pinName);

        // Put the received pin value in the bit buffer
        bitBuff[rcv_bit_index] = rxVal;
 
        // Update the receive bit index value.  
        rcv_bit_index++;

        // Decrement the receive index so we know where we are in the received character.  
        if( (recvStart - intCount) == 0 )
        {
            rx_index--;
        }

        // Is this the last bit?
        if(rx_index == 0)
        {
            // Reset the receive bit buffer index and signal that a character
            // has been received.
            char_recvd = 1;
            rcv_bit_index = 0;
        }
    }

    // We only want to send characters when intCount == 0.  That way the baud rate is correct.  
    if(intCount == 0)
    {
        if(tx_index != 0xff)
        {
            if(tx_index == 10)
            {
                // start
                gpio_write_pin_output(gpioUartDemoTxPin[0].pinName, 0);
            }
            else if(tx_index == 1)
            {
                // stop
                gpio_write_pin_output(gpioUartDemoTxPin[0].pinName, 1);
            }
            else if(tx_index == 0)
            {
                tx_index = 0xff;
                return;
            }
            else
            {
                gpio_write_pin_output(gpioUartDemoTxPin[0].pinName, curr_val&1);
                curr_val >>=1;
            }
            
            tx_index--;
        }
    }
}

/********************************************************************/
/*   Send character function                                        */
/********************************************************************/
void send_char(char character)
{   
    curr_val = character;
    tx_index = 10;
    while(tx_index != 0xff)
    {}
}

/********************************************************************/
/*   Receive character function                                     */
/********************************************************************/
char recv_char(void)
{
    unsigned int i = 0, j = 0;
    unsigned char return_char = 0, temp = 0;
    
    // Wait until character has been received
    while (char_recvd == 0)
    {}
    
    // Now parse the character from the receive buffer
    for(i=1; i<10; i++)
    {
        temp = 0;
        for(j=0; j<OSR; j++)
        {
            temp = temp + bitBuff[j+(i*OSR)];
        }
        
        return_char |= (temp / OSR_DIV) << (i-1);
    }
    
    // Once the character has been received, negate the received character flag
    char_recvd = 0;
    
    // Turn on the pin interrupt.  Configure to look for a falling edge. 
    port_hal_configure_pin_interrupt(UART_RX_INSTANCE, UART_RX_PIN_NUM, kPortIntFallingEdge);
    
    return return_char;
}

/********************************************************************/
/*   PORT E IRQ callback function                                   */
/********************************************************************/
void io_irq_callback(void)
{
    // Clear the interrupt flag for the UART pin
    gpio_clear_pin_interrupt_flag(gpioUartDemoRxPin[0].pinName);
    
    // Disable the port interrupt.  
    port_hal_configure_pin_interrupt(UART_RX_INSTANCE, UART_RX_PIN_NUM, kPortIntDisabled);
    
    // Set the RX index so that we receive characters
    rx_index = 10;
    rcv_bit_index = 0;
    
    // If the count is greater than two... reset the count
    if(intCount > OSR-1)
    {
        rx_start = 0;
    }
    else 
    {
        rx_start = intCount;
    }
}
/********************************************************************/



