//
// This file is part of the GNU ARM Eclipse distribution.
// Copyright (c) 2014 Liviu Ionescu.
//

// ----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include "diag/Trace.h"

#include "Timer.h"
#include "sinetable.h"
#include "freqtable.h"

// ----------------------------------------------------------------------------
//
// Standalone STM32F4 led blink sample (trace via ITM).
//
// In debug configurations, demonstrate how to print a greeting message
// on the trace device. In release configurations the message is
// simply discarded.
//
// Then demonstrates how to blink a led with 1 Hz, using a
// continuous loop and SysTick delays.
//
// Trace support is enabled by adding the TRACE macro definition.
// By default the trace messages are forwarded to the ITM output,
// but can be rerouted to any device or completely suppressed, by
// changing the definitions required in system/src/diag/trace_impl.c
// (currently OS_USE_TRACE_ITM, OS_USE_TRACE_SEMIHOSTING_DEBUG/_STDOUT).
//


// ----- main() ---------------------------------------------------------------

// Sample pragmas to cope with warnings. Please note the related line at
// the end of this function, used to pop the compiler diagnostics status.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#pragma GCC diagnostic ignored "-Wreturn-type"

int32_t time, button_time;
TIM_HandleTypeDef TimHandle;
ADC_HandleTypeDef AdcHandle;
// DAC and Timer 6 for DAC clock
TIM_HandleTypeDef    Tim6Handle;
DAC_HandleTypeDef    DacHandle;

uint16_t dacBuffer[32];	// 32 integer buffer for callback

/* Variable used to get converted value */
__IO uint16_t adcBuffer[8];
uint16_t dacBuffer[32];
float inBuffer[32], outBuffer[32]; // interleaved - LRLRLRLRLRLRLRLRLRLRLR - inBuffer[frame << 1] inBuffer[(frame << 1) + 1]
float millisecs;
int32_t ledselected, adccount;
uint32_t seed;
float phase1, phase2;

// for ADC smoothing
uint32_t smoothadc[8], avgtotal[8];
uint32_t adc0average[64];
uint32_t adc1average[64];
int16_t avgindex;

void
led_init()
{
  // Enable GPIO Peripheral clock
  __HAL_RCC_GPIOD_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStructure;

  // Configure pin in output push/pull mode
  GPIO_InitStructure.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
  GPIO_InitStructure.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);

}

void
switch_init()
{
  // Enable GPIO Peripheral clock
  __HAL_RCC_GPIOA_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStructure;

  // Configure pin for polling
  GPIO_InitStructure.Pin = GPIO_PIN_0;
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
  GPIO_InitStructure.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);


  // Configure for interrupt
  /* Configure PA0 pin as input floating */
/*  GPIO_InitStructure.Pin = GPIO_PIN_0;
  GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStructure.Pull = GPIO_NOPULL;
  GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);*/

  /* Enable and set EXTI Line0 Interrupt to the lowest priority */
//  HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
//  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

}

void
timer3_init()
{
	  __HAL_RCC_TIM3_CLK_ENABLE();

	  TimHandle.Instance = TIM3;
	  TimHandle.Init.Period = 8399; // Clock = 168000000/2 = 84000000/(8399+1) = 10000
	  TimHandle.Init.Prescaler = 0;
	  TimHandle.Init.ClockDivision = 0;
	  TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
	  HAL_TIM_Base_Init(&TimHandle) ;
	  HAL_TIM_Base_Start_IT(&TimHandle);

	  /*##-2- Configure the NVIC for TIMx ########################################*/
	  /* Set Interrupt Group Priority */
	  HAL_NVIC_SetPriority(TIM3_IRQn, 4, 0);
	  HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

void timer6_init(void)
{
  static TIM_HandleTypeDef  htim;
  TIM_MasterConfigTypeDef sMasterConfig;

  /*##-1- Configure the TIM peripheral #######################################*/
  /* Time base configuration */
  __HAL_RCC_TIM6_CLK_ENABLE();
  htim.Instance = TIM6;

  // 216MHz -> 108MHz Peripheral Clock -> 108,000,000/48,000 = 2250 -> 2249 count
  htim.Init.Period = 2249;	// 48000 clock - suitable for 1 channel 48000
  htim.Init.Prescaler = 0;
  htim.Init.ClockDivision = 0;
  htim.Init.CounterMode = TIM_COUNTERMODE_UP;
  HAL_TIM_Base_Init(&htim);

  /* TIM6 TRGO selection */
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

  HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);

  /*##-2- Enable TIM peripheral counter ######################################*/
  HAL_TIM_Base_Start(&htim);
}

void dac_init(void)
{
  static DAC_ChannelConfTypeDef sConfig;
  DacHandle.Instance = DAC;

  HAL_DAC_Init(&DacHandle);
  sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

  HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DAC_CHANNEL_1);

  /*##-2- Enable DAC Channel1 and associated DMA #############################*/
  HAL_DAC_Start_DMA(&DacHandle, DAC_CHANNEL_1, (uint32_t*)dacBuffer, 32, DAC_ALIGN_12B_R);

}


int
main(int argc, char* argv[])
{
	timer_start();

	led_init();
	switch_init();
	timer3_init();
	timer6_init();
	dac_init();

	// initialize globals
	time = 240;
	button_time = 0;
	millisecs = 0.0f;
	ledselected = 0;

	// Infinite loop
	while (1)
	{
		timer_sleep(100);
		if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0))
			time = 80;
		else
			time = 240;
	}
  // Infinite loop, never return.
}

//---------------Audio Function
void audioBlock(float *input, float *output, int32_t frames)
{
	int32_t iphase, i, sample, frame;
	float phaseinc1, phaseinc2, phase2mod, mix;

	phaseinc1 = sineinc[2048];
	phaseinc2 = phaseinc1 * 1.5f;

	for(frame = 0, sample = 0; frame < frames; frame++, sample+=2)
	{
		// phasor~ 1
		phase1 += phaseinc1;
		iphase = (int32_t)(phase1 * 8192.0f);
		mix = (phase1 * 8192.0f) - iphase;
		output[sample] = sinetable[iphase & 0x00001fff] * (1.0f - mix)
				+ sinetable[(iphase + 1) & 0x00001fff] * mix;

		// phasor~ 2
		phase2 += phaseinc2;
		iphase = (int32_t)(phase2 * 8192.0f);
		mix = (phase2 * 8192.0f) - iphase;
		output[sample + 1] = sinetable[iphase & 0x00001fff] * (1.0f - mix)
				+ sinetable[(iphase + 1) & 0x00001fff] * mix;
	}

	while(phase1 >= 1.0f) phase1 -= 1.0f;
	while(phase1 < 0.0f) phase1 += 1.0f;

	while(phase2 >= 1.0f) phase2 -= 1.0f;
	while(phase2 < 0.0f) phase2 += 1.0f;
}
//----------------DAC functions---------------------
// interrupt from DMA 1 - Stream 5 (DAC Ch 1)
void DMA1_Stream5_IRQHandler(void)
{
  HAL_DMA_IRQHandler(DacHandle.DMA_Handle1);
}

void DACx_DMA_IRQHandler2(void)
{
  HAL_DMA_IRQHandler(DacHandle.DMA_Handle2);
}

// half buffer data shuffling routines...
// callback from DMA interrupt function HAL_DMA_IRQHandler()

void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* DacHandle)
{
	int i, j;
	float outmono;

	// process 16 left and 16 right sample - interleaved
	audioBlock(inBuffer, outBuffer, 16);
	// only one dac - mix left and right values (+/- 2 > 0 to 4 > 0 to 4095)
	for(i = 0, j = 0; i < 16; i++, j+=2)
	{
		outmono = (outBuffer[j] + outBuffer[j+1]) * 0.5f;
		dacBuffer[i+16] = (uint16_t)((outmono + 1.0f) * 2047.5f);
	}
}

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* DacHandle)
{
	int i,j;
	float outmono;

	audioBlock(inBuffer, outBuffer, 16);
	for(i = 0, j = 0; i < 16; i++, j+=2)
	{
		outmono = (outBuffer[j] + outBuffer[j+1]) * 0.5f;
		dacBuffer[i] = (uint16_t)((outmono + 1.0f) * 2047.5f);
	}
}

// interrupt from switch - pin A0
void EXTI0_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

// callback from switch interrupt function HAL_GPIO_EXTI_IRQHandler()
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// debouncing
	int32_t time_elapsed;
	time_elapsed = HAL_GetTick() - button_time;
	if(time_elapsed < 1)
		return;
//	time = time_elapsed;
	button_time = HAL_GetTick();
    if(time > 100)
    	time = 80;
    else
    	time = 240;
}

// interrupt from timer 3
void TIM3_IRQHandler(void)
{
  HAL_TIM_IRQHandler(&TimHandle);
}

// callback from timer interrupt function HAL_TIM_IRQHandler()
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//	HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
	adccount = 0;
	millisecs += 0.1f;
	if(millisecs > time)
	{
		millisecs = 0.0f;
		switch(ledselected)
		{
		case 0:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
			break;
		case 1:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
			break;
		case 2:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
			break;
		case 3:
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
			break;
		}
		ledselected += 1;
		ledselected &= 3;
	}
}

#pragma GCC diagnostic pop

// ----------------------------------------------------------------------------
