/**
 * Copyright (c) 2015 - 2021, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, 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.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 "sdk_config.h"
#include "nrf_drv_spis.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "app_error.h"
#include <string.h>
#include "nrf_delay.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#define SPIS_INSTANCE 1 /**< SPIS instance index. */
static const nrf_drv_spis_t spis = NRF_DRV_SPIS_INSTANCE(SPIS_INSTANCE);/**< SPIS instance. */

#define TEST_STRING "Nordic"
static uint8_t       m_tx_buf[] = TEST_STRING;           /**< TX buffer. */
static uint8_t       m_rx_buf[sizeof(TEST_STRING) + 1];    /**< RX buffer. */
static const uint8_t m_length = sizeof(m_tx_buf);        /**< Transfer length. */

static volatile bool spis_xfer_done; /**< Flag used to indicate that SPIS instance completed the transfer. */

/**
 * @brief SPIS user event handler.
 *
 * @param event
 */
void spis_event_handler(nrf_drv_spis_event_t event)
{
    if (event.evt_type == NRF_DRV_SPIS_XFER_DONE)
    {
        spis_xfer_done = true;
        NRF_LOG_INFO(" Transfer completed. Received: %s",(uint32_t)m_rx_buf);
    }
}

#define GPIOTE_CH_IN 0
#define GPIOTE_CH_OUT 1
#define PPI_CH_0 0
#define PPI_CH_1 1
#define COUNT_PULSE_PIN APP_SPIS_SCK_PIN
#define COUNT_LIMIT 56 //7 bytes, 8 clock cycles per byte

void ppi_init(void)
{
    NRF_LOG_INFO("PPI init");

    NRF_GPIOTE->CONFIG[GPIOTE_CH_IN]    = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos |              // Event Mode (Input pin)
                                          GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |     // Check for falling edges.
                                          COUNT_PULSE_PIN << GPIOTE_CONFIG_PSEL_Pos;// |                    // Pin number. Can be same as SPI CLK
                                          //GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;        // Not used in Event mode.

    NRF_GPIOTE->CONFIG[GPIOTE_CH_OUT]   = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos |               // Task mode (Output pin)
                                          //GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos |   // Use this to toggle the pin.
                                          GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos |     // Use this to set the pin to low (need to set high manually)
                                          16 /*LED4*/ << GPIOTE_CONFIG_PSEL_Pos |                           // Pin number. Using LED for demonstration purposes.
                                          GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_MODE_Pos;             // Initial value. (High -> LED off).

    NRF_TIMER3->BITMODE                 = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos;         // Using 32 bit counter
    NRF_TIMER3->PRESCALER               = 0;                                                                // Prescaler 0
    NRF_TIMER3->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << 0;                         // Clear the timer counter when timer reaches CC[0].
    NRF_TIMER3->MODE                    = TIMER_MODE_MODE_Counter << TIMER_MODE_MODE_Pos;                   // Timer used in counter mode. 

    NRF_TIMER3->CC[0] = COUNT_LIMIT;    // Set the counter limit (the amount of clock cycles you count before you toggle the pin.

    NRF_TIMER3->TASKS_START = 1;        // Turn on the timer3


    NRF_PPI->CH[PPI_CH_0].EEP           = (uint32_t)&NRF_GPIOTE->EVENTS_IN[GPIOTE_CH_IN];                   // PPI channel 0 event
    NRF_PPI->CH[PPI_CH_0].TEP           = (uint32_t)&NRF_TIMER3->TASKS_COUNT;                               // PPI channel 0 task

    NRF_PPI->CH[PPI_CH_1].EEP           = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[0];                         // PPI channel 1 event
    NRF_PPI->CH[PPI_CH_1].TEP           = (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_OUT];                  // PPI channel 1 task

    NRF_PPI->CHENSET |= (1 << PPI_CH_0);                                                                    // Enable PPI Channel 0
    NRF_PPI->CHENSET |= (1 << PPI_CH_1);                                                                    // Enable PPI Channel 1
}

int main(void)
{
    // Enable the constant latency sub power mode to minimize the time it takes
    // for the SPIS peripheral to become active after the CSN line is asserted
    // (when the CPU is in sleep mode).
    NRF_POWER->TASKS_CONSTLAT = 1;
    uint32_t counter=0;
    uint32_t previous = 0;

    bsp_board_init(BSP_INIT_LEDS);

    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("SPIS example");
		NRF_LOG_FLUSH();

    nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
    spis_config.csn_pin               = APP_SPIS_CS_PIN;		// P0.31
    spis_config.miso_pin              = APP_SPIS_MISO_PIN;              // P0.30
    spis_config.mosi_pin              = APP_SPIS_MOSI_PIN;              // P0.29
    spis_config.sck_pin               = APP_SPIS_SCK_PIN;		// P0.26

    APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));

    ppi_init();


    while (1)
    {
        memset(m_rx_buf, 0, m_length);
        spis_xfer_done = false;

        APP_ERROR_CHECK(nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf, m_length));
        

        while (!spis_xfer_done)
        {
            __WFE();
        }
        nrf_delay_ms(200);                              // Adding delay for visibility on LED.
        //k_sleep(K_MSEC(200);
        NRF_GPIOTE->TASKS_SET[GPIOTE_CH_OUT] = 1;       // Reset the GPIO to high.

        // TODO: Uncomment the lines below for debugging purposes. 
        //If this is to make sense, you need to disable TIMER3->SHORTS (comment out that line) and clear the timer manually after logging.
        //NRF_TIMER3->TASKS_CAPTURE[1] = 1;
        //previous = counter;
        //counter = NRF_TIMER3->CC[1];
        //NRF_LOG_INFO("counter %d, diff %d", counter, counter-previous);
        //NRF_TIMER3->TASKS_CLEAR = 1;
        


        NRF_LOG_FLUSH();

        bsp_board_led_invert(BSP_BOARD_LED_0);
    }
}
