/* This source file is part of the ATMEL AVR32-SoftwareFramework-AT32AP7000-1.0.0 Release */

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief SPI bus example application.
 *
 * This file gives an example of using the SPI bus driver for both slave
 * and master mode. It sends a text string to a slave in master mode, and
 * in slave mode it receives data and try to compare this data with an
 * expected string.
 *
 * LEDs on STK1000 is used to give feedback to the user. The switches on
 * STK1000 is used to change mode and send text strings.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices with a SPI module can be used.
 *                       The example is written for AP7000 and STK1000.
 * - AppNote:            AVR32105 - Master and Slave SPI Driver
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr32@atmel.com
 *
 *****************************************************************************/

/* Copyright (C) 2006-2008, Atmel Corporation 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 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. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``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 EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 */

/*! \mainpage
 * \section intro Introduction
 * This is the documentation for the data structures, functions, variables,
 * defines, enums, and typedefs in the software for application note AVR32105.
 * It also gives an example of the usage of the SPI driver for both master and
 * slave mode.
 *
 * \section files Main Files
 * - spi_at32ap7000.c: SPI driver source
 * - spi_at32ap7000.h: SPI driver header
 * - spi_example.c: SPI example
 *
 * \section compinfo Compilation Info
 * This software was written for the GNU GCC for AVR32 and IAR Systems compiler
 * for AVR32. Other compilers may or may not work.
 *
 * \section deviceinfo Device Info
 * All AVR32 devices with a SPI module can be used. This example has been tested
 * with the following setup:
 *
 * - STK1000 development kit
 * - AP7000 top board for STK1000 development kit
 *
 * \section setupinfo Setup Information
 * CPU speed: <i>20 MHz</i>
 *
 * This example uses the switches, LEDs and RGB LEDs on the STK1000 card. They
 * must be connected by the following scheme for the example to work out of the
 * box.
 *
 * - The header \b LED must be connected with a flat ribbon cable to \b J3 on \b GPIO
 * - The header \b RGB must be connected with a flat ribbon cable to \b J6 on \b GPIO
 * - The header \b SWITCH must be connected with a flat ribbon cable to \b J1 on \b GPIO
 *
 * This example uses chip select signal 0, which is shared with the external DAC
 * on the STK1000 development kit. This signal is pulled low when the
 * microprocessor is active in master mode. And the same signal is used to
 * signal the device while in slave mode.
 *
 * The SPI buss located on the <em>SPI header</em> (J27) on the STK1000 board
 * must be connected to anther target capable of SPI. For example an other
 * STK1000 board or an Atmel AVR. It is nessesary to connect \em NPCS0
 * (chip select signal) on AP7000 for the application to work in slave mode.
 *
 * \section applicationUsage Application Usage
 *
 * The device will start up in slave mode, and \em LED6 and \em LED0 on STK1000
 * board should be lit. If \em LED6 and \em LED0 is not lit there is a problem
 * with setup or the CPU is not running.
 *
 * The application is controlled using the switches on the STK1000 board. The
 * switches have the following functions:
 *
 * - \b SW7: change between master and slave mode
 * - \b SW5 (master mode): sends "Atmel AVR32 SPI test application\r\n" on SPI
 * - \b SW4 (master mode): sends "AVR32 SPI Atmel test application\r\n" on SPI
 * - \b SW3 (master mode): sends "This string is far to long to be used in Atmel AVR32 SPI test application\r\n"
 *
 * \em SW5, \em SW4 and \em SW3 does not have any function in slave mode.
 *
 * The application polls the SPI module in slave mode and will read all data
 * sent on the SPI bus if \em NPSC0 is pulled low.
 *
 * \subsection applicationUsageSlave Slave mode
 *
 * When receiving data on the SPI bus the \em LED1 will blink for a very short
 * periode and then \em LED3 will be lit while the device is comparing the text
 * string received with the expected text string. At the same time as \em LED3 is
 * lit the <em>RGB LEDs</em> will blink orange.
 *
 * After the compare the status will be displayed using the <b>RGB LEDs</b>. The
 * RGB LEDs have the following status:
 *
 * - \b Green - string received was identical to compared string
 * - \b Red - string received was not identical to compared string
 * - <b>Blinking red</b> - received data was larger than receive buffer
 *
 * The blinking red mode will usually follow by a new analyse mode since the
 * application breaks the processing and there is often still data in the SPI
 * receive register. I.e. normally there will first be blinking orange, then
 * blinking red, then blinking orange and then continously red.
 *
 * \subsection applicationUsageMaster Master mode
 *
 * When \em SW5, \em SW4 or \em SW3 switch is pressed, a text string is sent on
 * the SPI bus. While sending \em LED1 will be active on the STK1000 board.
 *
 * \section contactinfo Contact Info
 * For more info about Atmel AVR32 visit
 * <A href="http://www.atmel.com/products/AVR32/" >Atmel AVR32</A> \n
 * <A href="http://www.atmel.com/dyn/products/app_notes.asp?family_id=682">
 * AVR32 Application Notes</A>\n
 * Support mail: avr32@atmel.com
 */

#ifdef __GNUC__
#include <avr32/io.h>
#elif __ICCAVR32__
#include <avr32/ioap7000.h>
#else
#error No known compiler used
#endif
#include "spi_at32ap7000.h"
#include "gpio.h"


/* These defines should be adjusted to match the application */
/*! \brief CPU core speed in Hz */
#define CPUHZ      20000000
/*! \brief Number of bytes in the receive buffer when operating in slave mode */
#define BUFFERSIZE    64
/*! \brief A adjustable delay avoiding multiple requests on the switches */
//#define TIMEOUT 150000
#define TIMEOUT      CPUHZ/200
/*! \brief Number of bits in each SPI package*/
#define SPI_BITS    8
/*! \brief SPI slave speed in Hz */
#define SPI_SLAVE_SPEED    160000
/*! \brief SPI master speed in Hz */
#define SPI_MASTER_SPEED  125000

/* This is a work around for the IAR EWAVR32, version 2.10B */
#ifdef __ICCAVR32__
#define AVR32_SPI0_MISO_0_PIN              0
#define AVR32_SPI0_MISO_0_FUNCTION         0
#define AVR32_SPI0_MOSI_0_PIN              1
#define AVR32_SPI0_MOSI_0_FUNCTION         0
#define AVR32_SPI0_NPCS_0_PIN              3
#define AVR32_SPI0_NPCS_0_FUNCTION         0
#define AVR32_SPI0_NPCS_1_PIN              4
#define AVR32_SPI0_NPCS_1_FUNCTION         0
#define AVR32_SPI0_NPCS_2_PIN              5
#define AVR32_SPI0_NPCS_2_FUNCTION         0
#define AVR32_SPI0_NPCS_3_PIN              20
#define AVR32_SPI0_NPCS_3_FUNCTION         1
#define AVR32_SPI0_SCK_0_PIN               2
#define AVR32_SPI0_SCK_0_FUNCTION          0
#define AVR32_SPI1_MISO_0_PIN              32
#define AVR32_SPI1_MISO_0_FUNCTION         1
#define AVR32_SPI1_MOSI_0_PIN              33
#define AVR32_SPI1_MOSI_0_FUNCTION         1
#define AVR32_SPI1_NPCS_3_PIN              27
#define AVR32_SPI1_NPCS_3_FUNCTION         0
#define AVR32_SPI1_NPCS_0_PIN              34
#define AVR32_SPI1_NPCS_0_FUNCTION         1
#define AVR32_SPI1_NPCS_1_PIN              35
#define AVR32_SPI1_NPCS_1_FUNCTION         1
#define AVR32_SPI1_NPCS_2_PIN              36
#define AVR32_SPI1_NPCS_2_FUNCTION         1
#define AVR32_SPI1_SCK_0_PIN               37
#define AVR32_SPI1_SCK_0_FUNCTION          1
#endif

/*! \brief Map over PIO for setup of peripherals */
typedef unsigned char avr32_piomap_t[][2];

void init_spiMaster(volatile avr32_spi_t *spi, long cpuHz);
void init_spiSlave(volatile avr32_spi_t *spi, long cpuHz);
void rgb_setColor(volatile avr32_pio_t *pio, unsigned char color);
void rgb_blinkColor(volatile avr32_pio_t *pio, unsigned char color);
void spi_masterSend(volatile avr32_spi_t *spi, char *string);
void spi_slaveReceive_and_compare(volatile avr32_spi_t *spi, char *string);

/*! \brief initialise SPI in master mode
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param cpuHz CPU clock frequency in Hz
 */
void init_spiMaster(volatile avr32_spi_t *spi, long cpuHz)
{
  volatile avr32_pio_t *pioc = &AVR32_PIOC;
  spi_options_t spiOptions;

  spiOptions.reg = 0;
  spiOptions.baudrate = SPI_MASTER_SPEED;
  spiOptions.bits = SPI_BITS;
  spiOptions.spck_delay = 0;
  spiOptions.trans_delay = 4;
  spiOptions.stay_act = 0;
  spiOptions.spi_mode = 1;
  spiOptions.modfdis = 0;

  /* Initialize as master */
  spi_initMaster(spi, &spiOptions);

  /* Set master mode; variable_ps, pcs_decode, delay */
  spi_selectionMode(spi, 0, 0, 0);

  /* Select slave chip 0 (SPI_NPCS0) */
  spi_selectChip(spi, 0);

  spi_setupChipReg(spi, &spiOptions, cpuHz);

  spi_enable(spi);

  pioc->sodr = (1<<AVR32_PIO_P7)|(1<<AVR32_PIO_P0);
  pioc->codr = (1<<AVR32_PIO_P6)|(1<<AVR32_PIO_P1);
  rgb_setColor(pioc, 0);
}


/*! \brief initialise SPI in slave mode
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param cpuHz CPU clock frequency in Hz
 */
void init_spiSlave(volatile avr32_spi_t *spi, long cpuHz)
{
  volatile avr32_pio_t *pioc = &AVR32_PIOC;

  spi_options_t spiOptions;

  spiOptions.reg = 0;
  spiOptions.baudrate = SPI_SLAVE_SPEED;
  spiOptions.bits = SPI_BITS;
  spiOptions.spck_delay = 0;
  spiOptions.trans_delay = 4;
  spiOptions.stay_act = 0;
  spiOptions.spi_mode = 1;
  spiOptions.modfdis = 0;

  /* Initialize as slave; bits, spi_mode */
  spi_initSlave(spi, 8, 1);

  spi_setupChipReg(spi, &spiOptions, cpuHz);

  spi_enable(spi);

  pioc->sodr = (1<<AVR32_PIO_P6)|(1<<AVR32_PIO_P0);
  pioc->codr = (1<<AVR32_PIO_P7)|(1<<AVR32_PIO_P1);
  rgb_setColor(pioc, 0);
}


/*! \brief Function to set the color of the RGB LEDs
 *
 *  \param pio Pointer to an avr32_pio_t struct for RGB blinking
 *  \param color The color of the LED
 *    \arg 0 Off
 *    \arg 1 red
 *    \arg 2 green
 *    \arg 3 blue
 *    \arg 4 orange
 *    \arg 5 white
 */
void rgb_setColor(volatile avr32_pio_t *pio, unsigned char color)
{
  pio->codr = 0x00003F00;

  switch(color) {
    case 0:
      break;
    case 1:
      pio->sodr = 0x00000300;
      break;
    case 2:
      pio->sodr = 0x00000C00;
      break;
    case 3:
      pio->sodr = 0x00003000;
      break;
    case 4:
      pio->sodr = 0x00000F00;
      break;
    case 5:
      pio->sodr = 0x00003F00;
      break;
    default:
      break;
  }
}


/*! \brief Function which blinks the RGB leds in a given color
 *
 *  \param pio Pointer to an avr32_pio_t struct for RBG blinking
 *  \param color The color of the LED when active
 *    \arg 0 Off
 *    \arg 1 red
 *    \arg 2 green
 *    \arg 3 blue
 *    \arg 4 orange
 *    \arg 5 white
 */
void rgb_blinkColor(volatile avr32_pio_t *pio, unsigned char color)
{
  volatile int pauseTimer = TIMEOUT/4;
  volatile int pauseRepeat = 10;

  do{
    do{
      --pauseTimer;
    } while (pauseTimer > 0);

    pauseTimer = TIMEOUT/4;

    if ((pauseRepeat % 2) == 1) {
      rgb_setColor(pio, 0);
    } else {
      rgb_setColor(pio, color);
    }

    --pauseRepeat;
  } while (pauseRepeat > 0);
}


/*! \brief Send a text string to the SPI
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param string The text string to send on the SPI buss, terminated with a \\n
 */
void spi_masterSend(volatile avr32_spi_t *spi, char *string)
{
  volatile avr32_pio_t *pioc = &AVR32_PIOC;
  int error;
  char *textStringPtr = string;

  pioc->codr = (1<<AVR32_PIO_P0);
  pioc->sodr = (1<<AVR32_PIO_P1);

  do {
    error = spi_write(spi, (unsigned short) *textStringPtr);
  } while (*textStringPtr++ != '\n');

  pioc->codr = (1<<AVR32_PIO_P1);
  pioc->sodr = (1<<AVR32_PIO_P0);

  if (error != SPI_OK) {
  }
}


/*! \brief Receive a text and compare it to excpected text
 *
 *  \param spi Pointer to the correct avr32_spi_t struct
 *  \param string The text string which the received data is compared with.
 *        String is terminated with a \n
 */
void spi_slaveReceiveAndCompare(volatile avr32_spi_t *spi, char *string)
{
  int error = 0;
  int index = 0;
  int receivedChars = 0;
  char *textStringPtr = string;
  unsigned short receiveBuffer[BUFFERSIZE];
  volatile avr32_pio_t *pioc = &AVR32_PIOC;

  pioc->sodr = AVR32_PIO_P1_MASK|AVR32_PIO_P3_MASK;
  rgb_setColor(pioc, 4);

  do {
    int errVal = SPI_OK;

    errVal = spi_read(spi, &receiveBuffer[index]);

    if (errVal == SPI_OK) {
      ++index;
      ++receivedChars;
    }

    /* break on buffer overflow */
    if (receivedChars > BUFFERSIZE) {
      error = BUFFERSIZE + 1;
      break;
    }

  } while (receiveBuffer[index - 1] != '\n');

  index = 0;
  pioc->codr = AVR32_PIO_P1_MASK;
  rgb_blinkColor(pioc, 4);

  /* compare received buffer with expected text string */
  do {
    if ((receiveBuffer[index++] & 0x00FF)
        != ((*textStringPtr++) & 0x00FF)) {
      ++error;
    }
  } while (*textStringPtr != '\n');

  /* print result on RGB LEDs
   * error > BUFFERSIZE - buffer overflow
   * error > 0 - string mismatch
   * error = 0 - no error
   */
  if (error > BUFFERSIZE) {
    rgb_blinkColor(pioc, 1);
    rgb_setColor(pioc, 1);
  }
  else if (error > 0) {
    rgb_setColor(pioc, 1);
  } else {
    rgb_setColor(pioc, 2);
  }

  pioc->codr = (1<<AVR32_PIO_P3);
}


/*! \brief Main function, executing starts here
 *
 *  \return 0 on success
 */
int main(void)
{
  volatile avr32_pio_t *pioa = &AVR32_PIOA;
  volatile avr32_pio_t *piob = &AVR32_PIOB;
  volatile avr32_pio_t *pioc = &AVR32_PIOC;
  volatile avr32_spi_t *spi = &AVR32_SPI0;
  char masterMode = 0;

  gpio_map_t spi_piomap = {          \
    {AVR32_SPI0_SCK_0_PIN, AVR32_SPI0_SCK_0_FUNCTION},  \
    {AVR32_SPI0_MISO_0_PIN, AVR32_SPI0_MISO_0_FUNCTION},  \
    {AVR32_SPI0_MOSI_0_PIN, AVR32_SPI0_MOSI_0_FUNCTION},  \
    {AVR32_SPI0_NPCS_0_PIN, AVR32_SPI0_NPCS_0_FUNCTION},  \
    {AVR32_SPI0_NPCS_1_PIN, AVR32_SPI0_NPCS_1_FUNCTION},  \
    {AVR32_SPI0_NPCS_2_PIN, AVR32_SPI0_NPCS_2_FUNCTION},  \
    {AVR32_SPI0_NPCS_3_PIN, AVR32_SPI0_NPCS_3_FUNCTION},  \
  };

  /* Disable all interrupts on PIO */
  pioa->idr = 0xFFFFffff;
  piob->idr = 0xFFFFffff;
  pioc->idr = 0xFFFFffff;

  /* Enable PIO on PIOB and clear all bits
   * This is used for status information on LEDS and signals from switches
   *
   * LED0 - system is on and ready
   * LED1 - system is receiving/sending data on SPI
   * LED3 - system is comparing received data from SPI with expected data
   * LED6 - system is in slave mode
   * LED7 - system is in master mode
   *
   * PIOB: P0 to P7 is used for switches
   * PIOC: P0 to P7 is used for LEDs
   * PIOC: P8 to P13 is used for RGB LEDs
   */
  piob->per = 0x000000FF;
  piob->odr = 0x000000FF;
  piob->puer = 0x000000FF;

  pioc->per = 0x0000FFFF;
  pioc->oer = 0x0000FFFF;
  pioc->ower = 0x0000FFFF;
  pioc->codr = 0x0000FFFF;

  /* Init PIO */
  gpio_enable_module(spi_piomap, 7);

  pioc->codr = 0x000000FF;
  pioc->sodr = 0x000000AA;
  rgb_setColor(pioc, 1);

  int buttonTimer = 0;
  char *textString = "Atmel AVR32 SPI test application\r\n";
  char *textStringAlt = "AVR32 SPI Atmel test application\r\n";
  char *textStringToLong = "This string is far to long to be used " \
      "in Atmel AVR32 SPI test application\r\n";

  rgb_setColor(pioc, 0);
  init_spiSlave(spi, CPUHZ);

  for (;;) {
    /* SW7 is used to switch between master and slave mode */
    if ((piob->pdsr & AVR32_PIO_P7_MASK) == 0
        && buttonTimer <= 0) {
      buttonTimer = TIMEOUT;

      if (masterMode == 1) {
        init_spiSlave(spi, CPUHZ);
        masterMode = 0;
      } else {
        init_spiMaster(spi, CPUHZ);
        masterMode = 1;
      }
    }

    /* Actions for master mode
     * SW5 sends "Atmel AVR32 SPI test application\r\n" on SPI
     * SW4 sends "AVR32 SPI Atmel test application\r\n" on SPI
     * SW3 sends "This string is far to long to be used in Atmel AVR32 SPI test application\r\n"
     */
    if (masterMode == 1) {
      if ((piob->pdsr & AVR32_PIO_P5_MASK) == 0
            && buttonTimer <= 0) {
        buttonTimer = TIMEOUT;
        spi_masterSend(spi, textString);
      } else if ((piob->pdsr & AVR32_PIO_P4_MASK) == 0
          && buttonTimer <= 0) {
        buttonTimer = TIMEOUT;
        spi_masterSend(spi, textStringAlt);
      } else if ((piob->pdsr & AVR32_PIO_P3_MASK) == 0
            && buttonTimer <= 0) {
        buttonTimer = TIMEOUT;
        spi_masterSend(spi, textStringToLong);
      }

    /* Slave mode polls if there is an incoming message on SPI */
    } else {
      if (spi_readRegisterFullCheck(spi) == 1) {
        spi_slaveReceiveAndCompare(spi, textString);
      }
    }

    if (buttonTimer > 0) {
      --buttonTimer;
    }
  }
}
