/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  *
  * Copyright (c) 2019 STMicroelectronics International N.V. 
  * 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. Redistribution 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. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS 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.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
#include "usb_host.h"

/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include <math.h>			// fabsf()
#include <stdlib.h>			// abs()
#include "bt_a2dp_sink.h"
#include "bt_spec_a2dp.h"	// SBC config
#include "bt_spec_avdtp.h"	// SBC config
#include "lcd_gui.h"
#include "bt_hci.h"			// bt_isAliveHCI()
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc1;

TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim7;
DMA_HandleTypeDef hdma_tim1_up;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
//=============================================================================
//
//	Audio amplifier mini 0.1
//		Created by Toyohiko Togashi 2018-2019
//
//	2019.02.16 Supported a PWM3B
//	2021.01.31 Supported a ADC LPF(SMA 2 or 3 samples)
//
//=============================================================================

/* Configuration ---------------------------------------------------------*/
#define SOUND_FRAME_SIZE	1024

									// Analog port channel#
#define ADC_CHANNEL_AGND	2		// Undefine is an automatic AND
#define ADC_CHANNEL_L1		0		//
#define ADC_CHANNEL_R1		1		//
#define ADC_CHANNEL_L2		3		//
#define ADC_CHANNEL_R2		4		//
#define ADC_CHANNEL_MAX		5		//

//#define ADC_LPF_SMA2				// Low pass filter option	ADD 2021.01.31
//#define ADC_LPF_SMA2R				// Low pass filter option	ADD 2021.01.31
//#define ADC_LPF_SMA3				// Low pass filter option	ADD 2021.01.31
//#define ADC_LPF_SMA3_252			// Low pass filter option	ADD 2021.01.31
//#define ADC_LPF_SMA3R				// Low pass filter option	ADD 2021.01.31
//#define ADC_LPF_IIR21				// Low pass filter option	ADD 2021.02.02

#define MIXER_CHANNEL_AUX1	0		// Logical numbers
#define MIXER_CHANNEL_AUX2	1		//
#define MIXER_CHANNEL_BT	2		//
#define MIXER_CHANNEL_MAX	3		//

#define MIXER_VOLUME_MAX	2.0		// 200%

// Speaker drive <PWM mode>         // Circuit      Control   STM32CubeMX Configuration TIM1 CH Polarity
//#define PWM2						// Half-bridge  2-value   CH1=High CH2=High CH3=High CH4=High
#define PWM2_FULL					// Full-bridge  2-value   CH1=High CH2=Low  CH3=High CH4=Low
//#define PWM3A						// Full-bridge  3-value   CH1=Low  CH2=Low  CH3=Low  CH4=Low
//#define PWM3B						// Full-bridge  3-value   CH1=Low  CH2=Low  CH3=Low  CH4=Low
									//                        [Temporary change -> MX_TIM1_Init() function modify]

#define NO_SIGNAL_CHECK		100		// [PWM-CCR] Stand by condition
#define STAND_BY_DELAY		8070	// x 22.7[ms] (1/44.1kHz*SOUND_FRAME_SIZE)  3[minute]
#define LEVEL_METER_SAMPLE_TIME	16	// In the sound frame
//#define LEVEL_METER_AVERAGE		// Average mode
#define LEVEL_METER_MAXIMUM			// Max mode

/* Private variables ---------------------------------------------------------*/
unsigned short	adcBuff[2][SOUND_FRAME_SIZE][ADC_CHANNEL_MAX];
int				adcBuffFlag;
unsigned short	pwmBuff[2][SOUND_FRAME_SIZE][4];	// L+, L-, R+, R-
int				pwmBuffFlag;
int				quietTim;							// No signal timing

struct audioSignal {
	int	gain;				// 128 == 100%
	int	gainSave;			//
};
struct audioChannel {
	float				volume;		// 1.0 == 100%
	float				balance;	// 0.0:(L=200%,R=0%) 1.0:(L=100%,R=100%) 2.0:(L=0%,R=200%)
	struct audioSignal	left;
	struct audioSignal	right;
};
struct audioChannel	mixerSetting[MIXER_CHANNEL_MAX] = {
									//      Vol  Bal
		{1.0, 1.0, {128}, {128}},	// AUX1 100% 100%
		{1.0, 1.0, {128}, {128}},	// AUX2 100% 100%
		{2.0, 1.0, {256}, {256}},	// BT   200% 100%
};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM3_Init(void);
static void MX_ADC1_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM7_Init(void);
static void MX_ADC2_Init(void);
static void MX_NVIC_Init(void);
void MX_USB_HOST_Process(void);

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
                                

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
#define MAX(a,b)	((a)>(b) ? (a) : (b))
#define MIN(a,b)	((a)<(b) ? (a) : (b))
#define ABS(a)		((a)<0 ?   (-a) : (a))
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
//=============================================================================
//
//	Standard subroutines
//
//=============================================================================
#ifndef DEL
//
//	DEBUG message to host machine(/dev/cu.usbmodem*)
//
int __io_putchar(int ch) {
	uint8_t		b[2];
	uint16_t	l;

	b[0] = ch;
	l    = 1;
	if (ch == 0x0a) {
		b[0] = 0x0d;
		b[1] = 0x0a;
		l    = 2;
	}
	HAL_UART_Transmit(&huart2, b, l, 0xFFFF);
	return ch;
}
int _write(int file, char *ptr, int len){
	int DataIdx;

	for (DataIdx = 0; DataIdx < len; DataIdx++){
		__io_putchar(*ptr++);
	}
	return len;
}
#endif

//
//	time() implement / TIM7 version
//
volatile time_t		rtc_time;
time_t time(time_t *t) {
	if (t != NULL) {
		*t = rtc_time;
	}
	return(rtc_time);
}
int settimeofday(const struct timeval *tv, const struct timezone *tz){
	int	r;

	if (tv != NULL) {
		rtc_time = tv->tv_sec;
		__HAL_TIM_SET_COUNTER(&htim7, tv->tv_usec / 100);
		r = 0;
	} else {
		r = -1;
		errno = EFAULT;
	}
	return(r);
}
int gettimeofday(struct timeval *tp, void *tzp){
	struct timezone	*tz;

	tz = tzp;
	if (tp != NULL) {
		tp->tv_sec  = rtc_time;
		tp->tv_usec = __HAL_TIM_GET_COUNTER(&htim7) * 100;
	}
	if (tz != NULL) {
		tz->tz_minuteswest = 0;
		tz->tz_dsttime     = 0;
	}
	return(0);
}

//=============================================================================
//
//	Core tasks
//
//=============================================================================

//
//	ADC automation task (Very high priority, light weight process)
//
void adcToPwm(void) {
	int			i;
	int 		j;
	static int	k;
#ifndef ADC_CHANNEL_AGND
	static unsigned short	zeroL1;		// Center level (ADC value)
	static unsigned short	zeroR1;
	static unsigned short	zeroL2;
	static unsigned short	zeroR2;
	static int				zeroTim;	// Re examination timing
#endif

	// Initial values
	if (k == 0) {			// Maximum number
		k = htim1.Init.Period / 2 + 1;
	}
#ifndef ADC_CHANNEL_AGND
	if (zeroL1 == 0) {		// ADC Zero center level
		// Get a zero center level
		long	sumL1, sumR1;
		long	sumL2, sumR2;

		// Center level is average
		sumL1   = 0;
		sumR1   = 0;
		sumL2   = 0;
		sumR2   = 0;
		for(i = 0; i < SOUND_FRAME_SIZE; i++) {
			sumL1   += adcBuff[adcBuffFlag][i][ADC_CHANNEL_L1];
			sumR1   += adcBuff[adcBuffFlag][i][ADC_CHANNEL_R1];
			sumL2   += adcBuff[adcBuffFlag][i][ADC_CHANNEL_L2];
			sumR2   += adcBuff[adcBuffFlag][i][ADC_CHANNEL_R2];
	    }
		zeroL1   = sumL1   / SOUND_FRAME_SIZE;
		zeroR1   = sumR1   / SOUND_FRAME_SIZE;
		zeroL2   = sumL2   / SOUND_FRAME_SIZE;
		zeroR2   = sumR2   / SOUND_FRAME_SIZE;
	}
#endif

	// Find a Bluetooth buffer
	j = -1;
	if (bt_a2dp_endPoint[0].pcmReadPos < bt_a2dp_endPoint[0].pcmWritePos) {
		if ((bt_a2dp_endPoint[0].pcmWritePos - bt_a2dp_endPoint[0].pcmReadPos) >= (BT_A2DP_SINK_BUFFER_NUM-1)) {
			bt_a2dp_endPoint[0].pcmReadPos = bt_a2dp_endPoint[0].pcmWritePos - (BT_A2DP_SINK_BUFFER_NUM-1);
		}
		j = bt_a2dp_endPoint[0].pcmReadPos % BT_A2DP_SINK_BUFFER_NUM;
		(bt_a2dp_endPoint[0].pcmReadPos)++;
	}

	// Convert and mixing
    for(i = 0; i < SOUND_FRAME_SIZE; i++) {
    	int	l, r;						// PCM(Signed 16bit)
#ifdef ADC_CHANNEL_AGND
		int	agnd;
#endif
		int	l1, r1;
		int	l2, r2;
		int	lb, rb;
		int x, y;

		// Input: ADC raw data	(12bit -> 16bit)
#ifdef ADC_CHANNEL_AGND
		agnd = adcBuff[adcBuffFlag][i][ADC_CHANNEL_AGND];
		l1 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_L1] - agnd) * mixerSetting[MIXER_CHANNEL_AUX1].left.gain ) >> 3;
		r1 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_R1] - agnd) * mixerSetting[MIXER_CHANNEL_AUX1].right.gain) >> 3;
		l2 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_L2] - agnd) * mixerSetting[MIXER_CHANNEL_AUX2].left.gain ) >> 3;
		r2 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_R2] - agnd) * mixerSetting[MIXER_CHANNEL_AUX2].right.gain) >> 3;
#else
		l1 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_L1] - zeroL1) * mixerSetting[MIXER_CHANNEL_AUX1].left.gain ) >> 3;
		r1 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_R1] - zeroR1) * mixerSetting[MIXER_CHANNEL_AUX1].right.gain) >> 3;
		l2 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_L2] - zeroL2) * mixerSetting[MIXER_CHANNEL_AUX2].left.gain ) >> 3;
		r2 = ((adcBuff[adcBuffFlag][i][ADC_CHANNEL_R2] - zeroR2) * mixerSetting[MIXER_CHANNEL_AUX2].right.gain) >> 3;
#endif

		// Input: LPF(Low Pass Filter) - SMA(Simple Moving Average)		ADD START 2021.01.31
#ifdef ADC_LPF_SMA2R
		{
			static int	bl1 = 0, br1 = 0;
			static int	bl2 = 0, br2 = 0;

			l1 = (bl1 + l1) / 2;
			r1 = (br1 + r1) / 2;
			l2 = (bl2 + l2) / 2;
			r2 = (br2 + r2) / 2;
			bl1 = l1;
			br1 = r1;
			bl2 = l2;
			br2 = r2;
		}
#endif
#ifdef ADC_LPF_SMA2
		{
			static int	bl1 = 0, br1 = 0;
			static int	bl2 = 0, br2 = 0;
			int	a,b,c,d;

			a = l1;
			b = r1;
			c = l2;
			d = r2;
			l1 = (bl1 + l1) / 2;
			r1 = (br1 + r1) / 2;
			l2 = (bl2 + l2) / 2;
			r2 = (br2 + r2) / 2;
			bl1 = a;
			br1 = b;
			bl2 = c;
			br2 = d;
		}
#endif
#ifdef ADC_LPF_SMA3R
		{
			static int	bl1 = 0, br1 = 0;
			static int	bl2 = 0, br2 = 0;
			static int	bbl1 = 0, bbr1 = 0;
			static int	bbl2 = 0, bbr2 = 0;

			l1 = (bbl1 + bl1 + l1) / 3;
			r1 = (bbr1 + br1 + r1) / 3;
			l2 = (bbl2 + bl2 + l2) / 3;
			r2 = (bbr2 + br2 + r2) / 3;
			bbl1 = bl1;
			bbr1 = br1;
			bbl2 = bl2;
			bbr2 = br2;
			bl1 = l1;
			br1 = r1;
			bl2 = l2;
			br2 = r2;
		}
#endif
#ifdef ADC_LPF_SMA3
		{
			static int	bl1 = 0, br1 = 0;
			static int	bl2 = 0, br2 = 0;
			static int	bbl1 = 0, bbr1 = 0;
			static int	bbl2 = 0, bbr2 = 0;
			int	a,b,c,d;

			a = l1;
			b = r1;
			c = l2;
			d = r2;
			l1 = (bbl1 + bl1 + l1) / 3;
			r1 = (bbr1 + br1 + r1) / 3;
			l2 = (bbl2 + bl2 + l2) / 3;
			r2 = (bbr2 + br2 + r2) / 3;
			bbl1 = bl1;
			bbr1 = br1;
			bbl2 = bl2;
			bbr2 = br2;
			bl1 = a;
			br1 = b;
			bl2 = c;
			br2 = d;
		}
#endif
#ifdef ADC_LPF_SMA3_252
		{
			static int	bl1 = 0, br1 = 0;
			static int	bl2 = 0, br2 = 0;
			static int	bbl1 = 0, bbr1 = 0;
			static int	bbl2 = 0, bbr2 = 0;
			int	a,b,c,d;

			a = l1;
			b = r1;
			c = l2;
			d = r2;
			l1 = ((bbl1 * 25) + (bl1 * 50) + (l1 * 25)) / 100;
			r1 = ((bbr1 * 25) + (br1 * 50) + (r1 * 25)) / 100;
			l2 = ((bbl2 * 25) + (bl2 * 50) + (l2 * 25)) / 100;
			r2 = ((bbr2 * 25) + (br2 * 50) + (r2 * 25)) / 100;
			bbl1 = bl1;
			bbr1 = br1;
			bbl2 = bl2;
			bbr2 = br2;
			bl1 = a;
			br1 = b;
			bl2 = c;
			br2 = d;
		}
#endif
																		// ADD END 2021.01.31
																		// ADD START 2021.02.02
#ifdef ADC_LPF_IIR21
		{
//			const float k = 6.01517676405421194e-03;	// 44.1kHz, 18kHz-22kHz, 0.5dB-35dB
//			const float b1 = 1.51579374786455667e+00;
//			const float b2 = 6.12036576089424078e-01;
//			const float a0 = 1.29997440085444481e+02;
//			const float a1 = 2.59994880170888962e+02;
//			const float a2 = 1.29997440085444481e+02;
			const float k = 0.0351466201730500361;		// 44.1kHz, 13kHz-20kHz, 1dB-20dB
			const float b1 = 0.696264761136165311;
			const float b2 = 0.258610683904965888;
			const float a0 = 13.9051453270327823;
			const float a1 = 27.8102906540655646;
			const float a2 = 13.9051453270327823;

			{
				float z0;
				static float z1;
				static float z2;

				z0 = (l1 * k ) - (z1 * b1) - (z2 * b2);
				l1 = (z0 * a0) + (z1 * a1) + (z2 * a2);
				z2  = z1;
				z1  = z0;
			}
			{
				float z0;
				static float z1;
				static float z2;

				z0 = (r1 * k ) - (z1 * b1) - (z2 * b2);
				r1 = (z0 * a0) + (z1 * a1) + (z2 * a2);
				z2  = z1;
				z1  = z0;
			}
			{
				float z0;
				static float z1;
				static float z2;

				z0 = (l2 * k ) - (z1 * b1) - (z2 * b2);
				l2 = (z0 * a0) + (z1 * a1) + (z2 * a2);
				z2  = z1;
				z1  = z0;
			}
			{
				float z0;
				static float z1;
				static float z2;

				z0 = (r2 * k ) - (z1 * b1) - (z2 * b2);
				r2 = (z0 * a0) + (z1 * a1) + (z2 * a2);
				z2  = z1;
				z1  = z0;
			}
		}
#endif
																		// ADD END 2021.02.02

		// Input: Bluetooth PCM data
		if ((j >= 0) && (i < bt_a2dp_endPoint[0].pcmLen[j])) {
			lb = (bt_a2dp_endPoint[0].pcmBuffer[j][i][0] * mixerSetting[MIXER_CHANNEL_BT].left.gain ) >> 7;
			rb = (bt_a2dp_endPoint[0].pcmBuffer[j][i][1] * mixerSetting[MIXER_CHANNEL_BT].right.gain) >> 7;
		} else {
			lb = 0;
			rb = 0;
		}

		// Mixing
		l = l1 + l2 + lb;
		r = r1 + r2 + rb;
		l = MAX(-32768, MIN(32767, l));		// Peak cut on PCM16
		r = MAX(-32768, MIN(32767, r));		//

#if defined(PWM3A)
		// Output: Speaker PWM3: full bridge and 3 phase drive
		x = (l * k) >> 14;
		y = (r * k) >> 14;
		if (x > 0) {
			pwmBuff[pwmBuffFlag][i][0] = x;
			pwmBuff[pwmBuffFlag][i][1] = 0;
		} else {
			pwmBuff[pwmBuffFlag][i][0] = 0;
			pwmBuff[pwmBuffFlag][i][1] = -x;
		}
		if (y > 0) {
			pwmBuff[pwmBuffFlag][i][2] = y;
			pwmBuff[pwmBuffFlag][i][3] = 0;
		} else {
			pwmBuff[pwmBuffFlag][i][2] = 0;
			pwmBuff[pwmBuffFlag][i][3] = -y;
		}
#elif defined(PWM3B)
		x = (l * k) >> 15;
		y = (r * k) >> 15;
		pwmBuff[pwmBuffFlag][i][0] = k + x;
		pwmBuff[pwmBuffFlag][i][1] = k - x;
		pwmBuff[pwmBuffFlag][i][2] = k + y;
		pwmBuff[pwmBuffFlag][i][3] = k - y;
#elif defined(PWM2_FULL)
		// Output: Speaker PWM2: full bridge and normal drive
		x = ((l * k) >> 15) + k;
		y = ((r * k) >> 15) + k;
		pwmBuff[pwmBuffFlag][i][0] = x;
		pwmBuff[pwmBuffFlag][i][1] = x;
		pwmBuff[pwmBuffFlag][i][2] = y;
		pwmBuff[pwmBuffFlag][i][3] = y;
#else
		// Output: Speaker PWM2: half bridge and normal drive
		x = ((l * k) >> 15) + k;
		y = ((r * k) >> 15) + k;
		pwmBuff[pwmBuffFlag][i][0] = x;
		pwmBuff[pwmBuffFlag][i][1] = 0;
		pwmBuff[pwmBuffFlag][i][2] = y;
		pwmBuff[pwmBuffFlag][i][3] = 0;
#endif

	}

#ifdef NO_SIGNAL_CHECK
    {
    	static int b;
    	int	a;

		a = pwmBuff[pwmBuffFlag][0][0];
    	if (ABS(b - a) < NO_SIGNAL_CHECK) {
    		quietTim++;
    	} else {
    		quietTim = 0;
    	}
    	b = a;
    }
#endif

#ifndef ADC_CHANNEL_AGND
    // Re examination request
	if (--zeroTim <= 0) {
		zeroTim = 1000;		// About 23sec
		zeroL1  = 0;
	}
#endif

	return;
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *handle) {
	adcBuffFlag = 1;
	adcToPwm();
	return;
}
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *handle) {
	adcBuffFlag = 0;
	adcToPwm();
	return;
}

//
//	PWM automation task
//
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	if (htim == &htim1) {
		pwmBuffFlag = 1;
	}
	return;
}
void HAL_TIM_PeriodHalfElapsedCallback(TIM_HandleTypeDef *htim){
	if (htim == &htim1) {
		pwmBuffFlag = 0;
	}
	return;
}
void TIM_DMAPeriodElapsedCplt2(DMA_HandleTypeDef *hdma) {
  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
  htim->State= HAL_TIM_STATE_READY;
  HAL_TIM_PeriodElapsedCallback(htim);
}
void TIM_DMAPeriodHalfElapsedCplt2(DMA_HandleTypeDef *hdma) {
  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
  htim->State= HAL_TIM_STATE_READY;
  HAL_TIM_PeriodHalfElapsedCallback(htim);
}
void TIM_DMATriggerCplt2(DMA_HandleTypeDef *hdma) {
  TIM_HandleTypeDef *htim = (TIM_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
  htim->State= HAL_TIM_STATE_READY;
  HAL_TIM_TriggerCallback(htim);
}
// Custom HAL_TIM_DMABurst_WriteStart()
HAL_StatusTypeDef HAL_TIM_DMABurst_WriteStart2(TIM_HandleTypeDef *htim, uint32_t BurstBaseAddress, uint32_t BurstRequestSrc, uint32_t* BurstBuffer, uint32_t  BurstLength, uint32_t  BufferLength) {
	if((htim->State == HAL_TIM_STATE_BUSY)) {
		return HAL_BUSY;
	} else if((htim->State == HAL_TIM_STATE_READY)) {
		if((BurstBuffer == 0U) && (BurstLength > 0U)) {
			return HAL_ERROR;
		} else {
			htim->State = HAL_TIM_STATE_BUSY;
		}
	}
	switch(BurstRequestSrc) {
	case TIM_DMA_UPDATE:
		htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback     = TIM_DMAPeriodElapsedCplt2;
		htim->hdma[TIM_DMA_ID_UPDATE]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_CC1:
		htim->hdma[TIM_DMA_ID_CC1]->XferCpltCallback     = TIM_DMADelayPulseCplt;
		htim->hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_CC1]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC1], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_CC2:
		htim->hdma[TIM_DMA_ID_CC2]->XferCpltCallback     = TIM_DMADelayPulseCplt;
		htim->hdma[TIM_DMA_ID_CC2]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_CC2]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC2], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_CC3:
		htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback     = TIM_DMADelayPulseCplt;
		htim->hdma[TIM_DMA_ID_CC3]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_CC4:
		htim->hdma[TIM_DMA_ID_CC4]->XferCpltCallback     = TIM_DMADelayPulseCplt;
		htim->hdma[TIM_DMA_ID_CC4]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC4], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_COM:
		htim->hdma[TIM_DMA_ID_COMMUTATION]->XferCpltCallback     = TIMEx_DMACommutationCplt;
		htim->hdma[TIM_DMA_ID_COMMUTATION]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_COMMUTATION]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_COMMUTATION], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	case TIM_DMA_TRIGGER:
		htim->hdma[TIM_DMA_ID_TRIGGER]->XferCpltCallback     = TIM_DMATriggerCplt2;
		htim->hdma[TIM_DMA_ID_TRIGGER]->XferHalfCpltCallback = TIM_DMAPeriodHalfElapsedCplt2;
		htim->hdma[TIM_DMA_ID_TRIGGER]->XferErrorCallback    = TIM_DMAError;
		HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_TRIGGER], (uint32_t)BurstBuffer, (uint32_t)&htim->Instance->DMAR, BufferLength);
		break;
	default:
		break;
	}
	htim->Instance->DCR = BurstBaseAddress | BurstLength;
	__HAL_TIM_ENABLE_DMA(htim, BurstRequestSrc);
	htim->State = HAL_TIM_STATE_READY;
	return HAL_OK;
};

//=============================================================================
//
//	Man machine interface
//
//=============================================================================
// Screen layout
// +----------------+------+
// |                |      |
// |     MIXER      |RENDER|
// |                |      |
// +----------------+------+

//
// Display elements
//
struct {
	struct lcd_guiRoot 		*root;
	struct {
		struct lcd_guiGroup	*group;
		struct {
			struct lcd_guiGroup	*group;
			struct lcd_guiText	*label;
			struct lcd_guiGage	*outputLevelLeft;
			struct lcd_guiGage	*outputLevelRight;
			char				labelName[32];
		}	spk;
	}	render;
	struct {
		struct lcd_guiGroup	*group;
		struct {
			struct lcd_guiGroup		*group;
			struct lcd_guiButton	*select;
			struct lcd_guiGage		*inputLeft;
			struct lcd_guiGage		*inputRight;
			struct lcd_guiGage		*levelLeft;
			struct lcd_guiGage		*levelRight;
			struct lcd_guiGage		*peakLeft;
			struct lcd_guiGage		*peakRight;
			struct lcd_guiBarChart	*volume;
			struct lcd_guiButton	*volumeUp;
			struct lcd_guiButton	*volumeDown;
			struct lcd_guiText		*volumeText;
			struct lcd_guiBarChart	*balance;
			struct lcd_guiButton	*balanceLeft;
			struct lcd_guiButton	*balanceRight;
			struct lcd_guiText		*balanceText;
			struct lcd_guiText		*info;
			unsigned short			volumeColor[2];
			float					volumeValue[2];
			char					volumeTextStr[16];
			unsigned short			balanceColor[2];
			float					balanceValue[2];
			char					balanceTextStr[16];
			char					infoStr[32];
		}	ch[MIXER_CHANNEL_MAX];
	}	mixer;
}	display;

//
// Touch actions
//
void volumeAndBalanceUpdate(void) {
	int	i;

	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		mixerSetting[i].left.gain  = mixerSetting[i].volume * 128 * (2.0 - mixerSetting[i].balance);
		mixerSetting[i].right.gain = mixerSetting[i].volume * 128 *        mixerSetting[i].balance;
	}
	return;
}

void displayVolumeButton(struct lcd_guiButton *seg){
	int		i;
	float	d;

	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		if (seg == display.mixer.ch[i].volumeUp) {
			d = 0.1;
			break;
		}
		if (seg == display.mixer.ch[i].volumeDown) {
			d = -0.1;
			break;
		}
	}
	if (i < MIXER_CHANNEL_MAX) {
		float	f;

		f = mixerSetting[i].volume + d;
		mixerSetting[i].volume = MIN(MAX(f, 0.0), MIXER_VOLUME_MAX);

		volumeAndBalanceUpdate();
	}
	return;
}

void displayBalanceButton(struct lcd_guiButton *seg){
	int		i;
	float	d;

	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		if (seg == display.mixer.ch[i].balanceLeft) {
			d = -0.1;
			break;
		}
		if (seg == display.mixer.ch[i].balanceRight) {
			d = 0.1;
			break;
		}
	}
	if (i < MIXER_CHANNEL_MAX) {
		float	f;

		f = mixerSetting[i].balance + d;
		mixerSetting[i].balance = MIN(MAX(f, 0.0), 2.0);

		volumeAndBalanceUpdate();
	}
	return;
}

void displaySelectButton(struct lcd_guiButton *seg){
	int		i;

	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		if (seg == display.mixer.ch[i].select) {
			break;
		}
	}
	if (i < MIXER_CHANNEL_MAX) {
		static float save[MIXER_CHANNEL_MAX];

		if (seg->attr.state) {
			seg->attr.state = 0;
			save[i] = mixerSetting[i].volume;
			mixerSetting[i].volume = 0.0;
		} else {
			seg->attr.state = 1;
			if (save[i] < 0.1) {
				save[i] = 1.0;
			}
			mixerSetting[i].volume = save[i];
		}
		volumeAndBalanceUpdate();
		lcd_guiSetDirtys(display.mixer.ch[i].select);
	}
	return;
}

void displayInit(void) {
	int i;

	lcd_drawOpen();
	display.root = lcd_guiNewRoot(LCD_COLOR_BLACK, NULL, NULL);

	display.render.group = lcd_guiNewGroup(display.root, 244, 0, 80, 240, LCD_COLOR_BLUE, NULL, NULL);
	display.render.spk.group            = lcd_guiNewGroup(display.render.group, 0,  0, 76, 240, LCD_COLOR_BLUE, NULL, NULL);
	display.render.spk.label            = lcd_guiNewText(display.render.spk.group,  1,  1, 74, 48, LCD_COLOR_WHITE, LCD_COLOR_DARKBLUE, LCD_GUI_TEXT_ALIGN_CENTER, LCD_GUI_TEXT_ALIGN_CENTER, display.render.spk.labelName, LCD_FONT_6x8);
	display.render.spk.outputLevelLeft  = lcd_guiNewGage(display.render.spk.group,  2, 51, 34,168, LCD_COLOR_LIME, LCD_COLOR_BLACK, 1, 0.0);
	display.render.spk.outputLevelRight = lcd_guiNewGage(display.render.spk.group, 38, 51, 34,168, LCD_COLOR_LIME, LCD_COLOR_BLACK, 1, 0.0);
	                                      lcd_guiNewText(display.render.spk.group,  1,221, 74, 18, LCD_COLOR_WHITE, LCD_COLOR_DARKBLUE, LCD_GUI_TEXT_ALIGN_CENTER, LCD_GUI_TEXT_ALIGN_CENTER, "Audio Amp", LCD_FONT_6x8);

	display.mixer.group = lcd_guiNewGroup(display.root, 0, 0, 240, 240, LCD_COLOR_BLUE, NULL, NULL);
	display.mixer.ch[MIXER_CHANNEL_AUX1].group = lcd_guiNewGroup(display.mixer.group, 160,  0, 80, 240, LCD_COLOR_BLUE, NULL, NULL);
	display.mixer.ch[MIXER_CHANNEL_AUX2].group = lcd_guiNewGroup(display.mixer.group,  80,  0, 80, 240, LCD_COLOR_BLUE, NULL, NULL);
	display.mixer.ch[MIXER_CHANNEL_BT  ].group = lcd_guiNewGroup(display.mixer.group,   0,  0, 80, 240, LCD_COLOR_BLUE, NULL, NULL);
	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		display.mixer.ch[i].select          = lcd_guiNewButton(  display.mixer.ch[i].group,  1,  1, 78, 48, display.mixer.ch[i].infoStr, LCD_FONT_6x8, LCD_COLOR_WHITE, LCD_COLOR_DARKBLUE, LCD_COLOR_DIMGRAY, LCD_GUI_BUTTON_MODE_ALTERNATE, 0, displaySelectButton);
		display.mixer.ch[i].inputLeft       = lcd_guiNewGage(    display.mixer.ch[i].group, 22, 51,  4,168, LCD_COLOR_YELLOW, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].inputRight      = lcd_guiNewGage(    display.mixer.ch[i].group, 27, 51,  4,168, LCD_COLOR_YELLOW, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].levelLeft       = lcd_guiNewGage(    display.mixer.ch[i].group,  8, 51, 14,168, LCD_COLOR_LIME, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].levelRight      = lcd_guiNewGage(    display.mixer.ch[i].group, 31, 51, 14,168, LCD_COLOR_LIME, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].peakLeft        = lcd_guiNewGage(    display.mixer.ch[i].group,  1, 51,  7,168, LCD_COLOR_GREEN, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].peakRight       = lcd_guiNewGage(    display.mixer.ch[i].group, 45, 51,  7,168, LCD_COLOR_GREEN, LCD_COLOR_BLACK, 1, 0.0);
		display.mixer.ch[i].volume          = lcd_guiNewBarChart(display.mixer.ch[i].group, 54, 76, 25,118, LCD_COLOR_LAMPBLACK, 1, 2, display.mixer.ch[i].volumeValue, display.mixer.ch[i].volumeColor);
		display.mixer.ch[i].volumeUp        = lcd_guiNewButton(  display.mixer.ch[i].group, 54, 50, 25, 26, "+", LCD_FONT_6x8, LCD_COLOR_WHITE, LCD_COLOR_GRAY, LCD_COLOR_RED, LCD_GUI_BUTTON_MODE_MOMENTARY, 0, displayVolumeButton);
		display.mixer.ch[i].volumeDown      = lcd_guiNewButton(  display.mixer.ch[i].group, 54,194, 25, 26, "-", LCD_FONT_6x8, LCD_COLOR_WHITE, LCD_COLOR_GRAY, LCD_COLOR_RED, LCD_GUI_BUTTON_MODE_MOMENTARY, 0, displayVolumeButton);
		display.mixer.ch[i].volumeText      = lcd_guiNewText(    display.mixer.ch[i].group, 54,220, 25, 20, LCD_COLOR_WHITE, LCD_COLOR_DARKBLUE, LCD_GUI_TEXT_ALIGN_CENTER, LCD_GUI_TEXT_ALIGN_CENTER, display.mixer.ch[i].volumeTextStr, LCD_FONT_6x8);
		display.mixer.ch[i].balance         = lcd_guiNewBarChart(display.mixer.ch[i].group,  0,220, 54,  2, LCD_COLOR_TRANSPARENT, 0, 2, display.mixer.ch[i].balanceValue, display.mixer.ch[i].balanceColor);
		display.mixer.ch[i].balanceLeft     = lcd_guiNewButton(  display.mixer.ch[i].group,  0,222, 18, 18, "<", LCD_FONT_6x8, LCD_COLOR_WHITE, LCD_COLOR_GRAY, LCD_COLOR_RED, LCD_GUI_BUTTON_MODE_MOMENTARY, 0, displayBalanceButton);
		display.mixer.ch[i].balanceText     = lcd_guiNewText(    display.mixer.ch[i].group, 18,222, 18, 18, LCD_COLOR_WHITE, LCD_COLOR_DARKBLUE, LCD_GUI_TEXT_ALIGN_CENTER, LCD_GUI_TEXT_ALIGN_CENTER, display.mixer.ch[i].balanceTextStr, LCD_FONT_6x8);
		display.mixer.ch[i].balanceRight    = lcd_guiNewButton(  display.mixer.ch[i].group, 36,222, 18, 18, ">", LCD_FONT_6x8, LCD_COLOR_WHITE, LCD_COLOR_GRAY, LCD_COLOR_RED, LCD_GUI_BUTTON_MODE_MOMENTARY, 0, displayBalanceButton);
		display.mixer.ch[i].volumeColor[0]  = LCD_COLOR_DIMGRAY;
		display.mixer.ch[i].volumeColor[1]  = LCD_COLOR_ORANGE;
		display.mixer.ch[i].balanceColor[0] = LCD_COLOR_TRANSPARENT;
		display.mixer.ch[i].balanceColor[1] = LCD_COLOR_ORANGE;
	}
	sprintf(display.mixer.ch[MIXER_CHANNEL_AUX1].infoStr, "AUX1\n\nADC/4096\n%dHz", (int)(SystemCoreClock / (htim3.Init.Period+1) / 2));
	sprintf(display.mixer.ch[MIXER_CHANNEL_AUX2].infoStr, "AUX2\n\nADC/4096\n%dHz", (int)(SystemCoreClock / (htim3.Init.Period+1) / 2));
	sprintf(display.mixer.ch[MIXER_CHANNEL_BT  ].infoStr, "Bluetooth");
	sprintf(display.render.spk.labelName, "Speaker\n\nPWM/%d\n%dHz", (int)(htim1.Init.Period+1), (int)(SystemCoreClock/(htim1.Init.Period+1)));
	return;
}

void displayUpdate(void){
	static int		aa;
	static int		bb;
	static int		bt;
	static float	volume[3];
	static float	balance[3];
	static int		btState;
	static int		btEnable = 99;
	int				i;
	int				j;
	int 			l, r;
	int				ll, rr;

	// Static information
	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		if (volume[i] != mixerSetting[i].volume) {
			volume[i]  = mixerSetting[i].volume;
			display.mixer.ch[i].volumeValue[0] = (volume[i]/MIXER_VOLUME_MAX) - 0.04;
			display.mixer.ch[i].volumeValue[1] = 0.06;
			if (display.mixer.ch[i].volumeValue[0] < 0.0) {
				display.mixer.ch[i].volumeValue[1] += display.mixer.ch[i].volumeValue[0];
				display.mixer.ch[i].volumeValue[0] = 0.0;
			}
			if (display.mixer.ch[i].volumeValue[0] > (1.0 - 0.08)) {
				display.mixer.ch[i].volumeValue[1] = 1.0 - display.mixer.ch[i].volumeValue[0];
			}
			lcd_guiSetDirtys(display.mixer.ch[i].volume);

			sprintf(display.mixer.ch[i].volumeTextStr, "%d", (int)(volume[i]*100));
			lcd_guiSetDirtys(display.mixer.ch[i].volumeText);
		}
	}
	for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
		if (balance[i] != mixerSetting[i].balance) {
			balance[i]  = mixerSetting[i].balance;
			display.mixer.ch[i].balanceValue[0] = (balance[i]/2.0) - 0.03;
			display.mixer.ch[i].balanceValue[1] = 0.06;
			if (display.mixer.ch[i].balanceValue[0] < 0.0) {
				display.mixer.ch[i].balanceValue[1] += display.mixer.ch[i].balanceValue[0];
				display.mixer.ch[i].balanceValue[0] = 0.0;
			}
			if (display.mixer.ch[i].balanceValue[0] > (1.0 - 0.06)) {
				display.mixer.ch[i].balanceValue[1] = 1.0 - display.mixer.ch[i].balanceValue[0];
			}
			lcd_guiSetDirtys(display.mixer.ch[i].balance);

//			sprintf(display.mixer.ch[i].balanceTextStr, "%d", (int)(balance[i]*100));
//			lcd_guiSetDirtys(display.mixer.ch[i].balanceText);
		}
	}
	if (btState != bt_a2dp_endPoint[0].state) {
		btState  = bt_a2dp_endPoint[0].state;

		if (btState == A2DP_STATE_PLAY) {		// Displaying the audio attributes
			struct bt_avdtp_serviceCapabilitie *param;
			int 	len;
			char	*type;
			char	*fs;
			char	*mode;
			int		block;
			int		subBand;
			char	*alloc;

			type    = "?";
			fs      = "?";
			mode    = "?";
			block   = 0;
			subBand = 0;
			alloc   = "?";
			param = (struct bt_avdtp_serviceCapabilitie *)bt_a2dp_endPoint[0].config;
			len   = bt_a2dp_endPoint[0].configLen;
			while(len > 0){
				int l;

				l = param->LOSC;
				switch (param->serviceCategory){
				case AVDTP_SERVICE_CATEGORY_MediaCodec:
				{
					union bt_a2dp_CodecSpecificInformationElements *p;

					p = (union bt_a2dp_CodecSpecificInformationElements *)&(param->mediaCodec.mediaCodecSpecific);
					if (param->mediaCodec.mediaCodecType == A2DP_AudioCodecID_SBC) {
						type = "SBC";
						if (p->SBC.info1 & A2DP_SBC_SamplingFrequency_16000Hz) {
							fs = "16kHz";
						} else if (p->SBC.info1 & A2DP_SBC_SamplingFrequency_32000Hz) {
							fs = "32kHz";
						} else if (p->SBC.info1 & A2DP_SBC_SamplingFrequency_44100Hz) {
							fs = "44.1kHz";
						} else if (p->SBC.info1 & A2DP_SBC_SamplingFrequency_48000Hz) {
							fs = "48kHz";
						}
						if (p->SBC.info1 & A2DP_SBC_ChannelMode_MONO) {
							mode = "Mono";
						} else if (p->SBC.info1 & A2DP_SBC_ChannelMode_DUAL_CHANNEL) {
							mode = "Dual";
						} else if (p->SBC.info1 & A2DP_SBC_ChannelMode_STEREO) {
							mode = "Stereo";
						} else if (p->SBC.info1 & A2DP_SBC_ChannelMode_JOINT_STEREO) {
							mode = "Joint";
						}
						if (p->SBC.info2 & A2DP_SBC_BlockLength_4) {
							block = 4;
						} else if (p->SBC.info2 & A2DP_SBC_BlockLength_8) {
							block = 8;
						} else if (p->SBC.info2 & A2DP_SBC_BlockLength_12) {
							block = 12;
						} else if (p->SBC.info2 & A2DP_SBC_BlockLength_16) {
							block = 16;
						}
						if (p->SBC.info2 & A2DP_SBC_Subbands_4) {
							subBand = 4;
						} else if (p->SBC.info2 & A2DP_SBC_Subbands_8) {
							subBand = 8;
						}
						if (p->SBC.info2 & A2DP_SBC_AllocationMethod_SNR) {
							alloc = "SNR";
						} else if (p->SBC.info2 & A2DP_SBC_AllocationMethod_Loudness) {
							alloc = "Loudness";
						}
//						DEBUGP("BitPool=%d,%d\n", p->SBC.minimumBitpoolValue, p->SBC.maximumBitpoolValue);
					}
					break;
				}
				case AVDTP_SERVICE_CATEGORY_MediaTransport:
					break;
				case AVDTP_SERVICE_CATEGORY_Reporting:
					break;
				case AVDTP_SERVICE_CATEGORY_DelayReporting:
					break;
				default:
					break;
				}
				param = (struct bt_avdtp_serviceCapabilitie *)&(param->body[l]);
				len -= (AVDTP_serviceCapabilitie_HeadSize + l);
			}
			sprintf(display.mixer.ch[MIXER_CHANNEL_BT].infoStr, "Bluetooth\n%s\n%s\n%s\n%s\n%d-%d", type, fs, mode, alloc, block, subBand);
		} else if (btState == A2DP_STATE_STOP) {
			sprintf(display.mixer.ch[MIXER_CHANNEL_BT].infoStr, "Bluetooth\n\nConnect");
		} else {
			sprintf(display.mixer.ch[MIXER_CHANNEL_BT].infoStr, "Bluetooth");
		}
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].select);
	}
	if (btEnable != bt_isAliveHCI()) {	// Bluetooth device is a dead
		btEnable  = bt_isAliveHCI();
		if (btEnable) {
			display.mixer.ch[MIXER_CHANNEL_BT].group->seg.flags.hidden = 0;
		} else {
			display.mixer.ch[MIXER_CHANNEL_BT].group->seg.flags.hidden = 1;
		}
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].group);
	}

	// Dynamic information
	if (aa != adcBuffFlag) {
		aa  = adcBuffFlag;
		i = adcBuffFlag ? 0 : 1;

		// AUX1
		ll = rr = 0;
		for(j = 0; j < SOUND_FRAME_SIZE; j += (SOUND_FRAME_SIZE/LEVEL_METER_SAMPLE_TIME)) {
#ifdef LEVEL_METER_AVERAGE
			l = abs(adcBuff[i][j][0] - adcBuff[i][j][2]);
			r = abs(adcBuff[i][j][1] - adcBuff[i][j][2]);
			ll += l;
			rr += r;
#else
			ll = MAX(ll, adcBuff[i][j][0] - adcBuff[i][j][2]);
			rr = MAX(rr, adcBuff[i][j][1] - adcBuff[i][j][2]);
#endif
		}
#ifdef LEVEL_METER_AVERAGE
		l = ll / LEVEL_METER_SAMPLE_TIME;
		r = rr / LEVEL_METER_SAMPLE_TIME;
#else
		l = ll;
		r = rr;
#endif

		display.mixer.ch[MIXER_CHANNEL_AUX1].inputLeft->value  = l / 2048.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX1].inputRight->value = r / 2048.0f;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].inputLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].inputRight);

		display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft->value  = l * 16 * mixerSetting[MIXER_CHANNEL_AUX1].left.gain  / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight->value = r * 16 * mixerSetting[MIXER_CHANNEL_AUX1].right.gain / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft->color.face  = display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft->value  > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight->color.face = display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight);

		if (display.mixer.ch[MIXER_CHANNEL_AUX1].peakLeft->value < display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft->value) {
			display.mixer.ch[MIXER_CHANNEL_AUX1].peakLeft->value = display.mixer.ch[MIXER_CHANNEL_AUX1].levelLeft->value;
//			display.mixer.ch[MIXER_CHANNEL_AUX1].peakLeft->color.face = display.mixer.ch[MIXER_CHANNEL_AUX1].peakLeft->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].peakLeft);
		}
		if (display.mixer.ch[MIXER_CHANNEL_AUX1].peakRight->value < display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight->value) {
			display.mixer.ch[MIXER_CHANNEL_AUX1].peakRight->value = display.mixer.ch[MIXER_CHANNEL_AUX1].levelRight->value;
//			display.mixer.ch[MIXER_CHANNEL_AUX1].peakRight->color.face = display.mixer.ch[MIXER_CHANNEL_AUX1].peakRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX1].peakRight);
		}

		// AUX2
		ll = rr = 0;
		for(j = 0; j < SOUND_FRAME_SIZE; j += (SOUND_FRAME_SIZE/LEVEL_METER_SAMPLE_TIME)) {
#ifdef LEVEL_METER_AVERAGE
			l = abs(adcBuff[i][j][3] - adcBuff[i][j][2]);
			r = abs(adcBuff[i][j][4] - adcBuff[i][j][2]);
			ll += l;
			rr += r;
#else
			ll = MAX(ll, abs(adcBuff[i][j][3] - adcBuff[i][j][2]));
			rr = MAX(rr, abs(adcBuff[i][j][4] - adcBuff[i][j][2]));
#endif
		}
#ifdef LEVEL_METER_AVERAGE
		l = ll / LEVEL_METER_SAMPLE_TIME;
		r = rr / LEVEL_METER_SAMPLE_TIME;
#else
		l = ll;
		r = rr;
#endif

		display.mixer.ch[MIXER_CHANNEL_AUX2].inputLeft->value  = l / 2048.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX2].inputRight->value = r / 2048.0f;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].inputLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].inputRight);

		display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft->value  = l * 16 * mixerSetting[MIXER_CHANNEL_AUX2].left.gain  / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight->value = r * 16 * mixerSetting[MIXER_CHANNEL_AUX2].right.gain / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft->color.face  = display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft->value  > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight->color.face = display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight);

		if (display.mixer.ch[MIXER_CHANNEL_AUX2].peakLeft->value < display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft->value) {
			display.mixer.ch[MIXER_CHANNEL_AUX2].peakLeft->value = display.mixer.ch[MIXER_CHANNEL_AUX2].levelLeft->value;
//			display.mixer.ch[MIXER_CHANNEL_AUX2].peakLeft->color.face = display.mixer.ch[MIXER_CHANNEL_AUX2].peakLeft->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].peakLeft);
		}
		if (display.mixer.ch[MIXER_CHANNEL_AUX2].peakRight->value < display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight->value) {
			display.mixer.ch[MIXER_CHANNEL_AUX2].peakRight->value = display.mixer.ch[MIXER_CHANNEL_AUX2].levelRight->value;
//			display.mixer.ch[MIXER_CHANNEL_AUX2].peakRight->color.face = display.mixer.ch[MIXER_CHANNEL_AUX2].peakRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_AUX2].peakRight);
		}
	}

	// Bluetooth
	if (bt != bt_a2dp_endPoint[0].pcmReadPos) {
		bt  = bt_a2dp_endPoint[0].pcmReadPos;
		ll = rr = 0;
		i = bt % BT_A2DP_SINK_BUFFER_NUM;		// Target buffer
		for(j = 0; j < SOUND_FRAME_SIZE; j += (SOUND_FRAME_SIZE/LEVEL_METER_SAMPLE_TIME)) {
#ifdef LEVEL_METER_AVERAGE
			ll += abs(bt_a2dp_endPoint[0].pcmBuffer[i][j][0]);
			rr += abs(bt_a2dp_endPoint[0].pcmBuffer[i][j][1]);
#else
			ll = MAX(ll, bt_a2dp_endPoint[0].pcmBuffer[i][j][0]);
			rr = MAX(rr, bt_a2dp_endPoint[0].pcmBuffer[i][j][1]);
#endif
		}
#ifdef LEVEL_METER_AVERAGE
		l = ll / LEVEL_METER_SAMPLE_TIME;
		r = rr / LEVEL_METER_SAMPLE_TIME;
#else
		l = ll;
		r = rr;
#endif

		display.mixer.ch[MIXER_CHANNEL_BT].inputLeft->value  = l / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_BT].inputRight->value = r / 32768.0f;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].inputLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].inputRight);

		display.mixer.ch[MIXER_CHANNEL_BT].levelLeft->value  = l * mixerSetting[MIXER_CHANNEL_BT].left.gain  / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_BT].levelRight->value = r * mixerSetting[MIXER_CHANNEL_BT].right.gain / 128 / 32768.0f;
		display.mixer.ch[MIXER_CHANNEL_BT].levelLeft->color.face  = display.mixer.ch[MIXER_CHANNEL_BT].levelLeft->value  > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		display.mixer.ch[MIXER_CHANNEL_BT].levelRight->color.face = display.mixer.ch[MIXER_CHANNEL_BT].levelRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].levelLeft);
		lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].levelRight);

		if (display.mixer.ch[MIXER_CHANNEL_BT].peakLeft->value < display.mixer.ch[MIXER_CHANNEL_BT].levelLeft->value) {
			display.mixer.ch[MIXER_CHANNEL_BT].peakLeft->value = display.mixer.ch[MIXER_CHANNEL_BT].levelLeft->value;
//			display.mixer.ch[MIXER_CHANNEL_BT].peakLeft->color.face = display.mixer.ch[MIXER_CHANNEL_BT].peakLeft->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].peakLeft);
		}
		if (display.mixer.ch[MIXER_CHANNEL_BT].peakRight->value < display.mixer.ch[MIXER_CHANNEL_BT].levelRight->value) {
			display.mixer.ch[MIXER_CHANNEL_BT].peakRight->value = display.mixer.ch[MIXER_CHANNEL_BT].levelRight->value;
//			display.mixer.ch[MIXER_CHANNEL_BT].peakRight->color.face = display.mixer.ch[MIXER_CHANNEL_BT].peakRight->value > 0.9 ? LCD_COLOR_RED : LCD_COLOR_LIME;
			lcd_guiSetDirtys(display.mixer.ch[MIXER_CHANNEL_BT].peakRight);
		}
	}

	if (bb != pwmBuffFlag) {
		bb  = pwmBuffFlag;
		i = pwmBuffFlag ? 0 : 1;
		ll = rr = 0;
		for(j = 0; j < SOUND_FRAME_SIZE; j += (SOUND_FRAME_SIZE/LEVEL_METER_SAMPLE_TIME)) {
#ifdef LEVEL_METER_AVERAGE
			l = pwmBuff[i][j][0] + pwmBuff[i][j][1];
			r = pwmBuff[i][j][2] + pwmBuff[i][j][3];
			ll += l;
			rr += r;
#else
			ll = MAX(ll, pwmBuff[i][j][0]);
			rr = MAX(rr, pwmBuff[i][j][2]);
#endif
		}
#ifdef LEVEL_METER_AVERAGE
		l = ll / LEVEL_METER_SAMPLE_TIME;
		r = rr / LEVEL_METER_SAMPLE_TIME;
#else
		l = ll;
		r = rr;
#endif
#ifdef PWM3
		display.render.spk.outputLevelLeft->value  = l / (float)htim1.Init.Period;
		display.render.spk.outputLevelRight->value = r / (float)htim1.Init.Period;
#else
		display.render.spk.outputLevelLeft->value  = (l / (float)htim1.Init.Period - 0.5) * 2.0;
		display.render.spk.outputLevelRight->value = (r / (float)htim1.Init.Period - 0.5) * 2.0;
#endif
		lcd_guiSetDirtys(display.render.spk.outputLevelLeft);
		lcd_guiSetDirtys(display.render.spk.outputLevelRight);

	}

	{	// Peak level
		int				i;
		uint32_t 		t1;
		static uint32_t	t2 = 0;

		t1 = HAL_GetTick();
		if (t1 > t2) {
			t2 = t1 + 500;		// [ms]
			for(i = 0; i < MIXER_CHANNEL_MAX; i++) {
				if (display.mixer.ch[i].peakLeft->value > 0.0) {
					display.mixer.ch[i].peakLeft->value -= 0.01;
					lcd_guiSetDirtys(display.mixer.ch[i].peakLeft);
				}
				if (display.mixer.ch[i].peakRight->value > 0.0) {
					display.mixer.ch[i].peakRight->value -= 0.01;
					lcd_guiSetDirtys(display.mixer.ch[i].peakRight);
				}
			}
		}
	}

	lcd_guiHandler(display.root);

	return;
}
/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  MX_TIM3_Init();
  MX_ADC1_Init();
  MX_TIM1_Init();
  MX_USB_HOST_Init();
  MX_TIM7_Init();
  MX_ADC2_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();

  /* USER CODE BEGIN 2 */
  printf("\nStart main()\n");

  HAL_TIM_Base_Start_IT(&htim7);

  HAL_TIM_Base_Start(&htim3);
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcBuff, 2*SOUND_FRAME_SIZE*5);

  HAL_TIM_Base_Start(&htim1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
  HAL_TIM_DMABurst_WriteStart2(&htim1, TIM_DMABASE_CCR1, TIM_DMA_UPDATE,
                    (uint32_t*)pwmBuff, TIM_DMABURSTLENGTH_4TRANSFERS, 2*SOUND_FRAME_SIZE*4);
  	  	  	  	  	  	  	  	  	  	  	// STM Document "DocID027362 Rev 2" STM32F446xC/xE Errata sheet
  PWR->CR |= ((uint32_t)PWR_CR_ADCDC1);		//   2.1.6 Internal noise impacting the ADC accuracy
											//         Option1: Set ADCDC1 bit
  displayInit();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1) {
	static int	f;
	static int	b;

	// Power control
	if (quietTim < STAND_BY_DELAY) {	// On state
		if (f) {						// Stand by -> On
			HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);		// LCD back light on
			__HAL_TIM_MOE_ENABLE(&htim1);							// Speaker on
			MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV1);	// CPU core is a full speed (180MHz)
			f = 0;
		}
		displayUpdate();
	} else {							// Stand by state
		if (!f) {						// On -> Stand by			// Power (Supply=6V)
			HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);	// -0.2W
			__HAL_TIM_MOE_DISABLE_UNCONDITIONALLY(&htim1);			// -0.4W  Volume=50%
			MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_SYSCLK_DIV8);	// -0.3W  180MHz->22.5MHz
			f = 1;
		}
//		HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);	// -0.3W NG
		if (b != bt_a2dp_endPoint[0].state) {						// Bluetooth event
			b  = bt_a2dp_endPoint[0].state;
			quietTim = 0;
		}
	}

  /* USER CODE END WHILE */
    MX_USB_HOST_Process();

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;

    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 180;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  RCC_OscInitStruct.PLL.PLLR = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

    /**Activate the Over-Drive mode 
    */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLRCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
  PeriphClkInitStruct.PLLSAI.PLLSAIM = 4;
  PeriphClkInitStruct.PLLSAI.PLLSAIN = 96;
  PeriphClkInitStruct.PLLSAI.PLLSAIQ = 2;
  PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV4;
  PeriphClkInitStruct.PLLSAIDivQ = 1;
  PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48CLKSOURCE_PLLSAIP;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/** NVIC Configuration
*/
static void MX_NVIC_Init(void)
{
  /* DMA2_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
  /* TIM7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(TIM7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(TIM7_IRQn);
}

/* ADC1 init function */
static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 5;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = 4;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_7;
  sConfig.Rank = 5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

/* ADC2 init function */
static void MX_ADC2_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
    */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DMAContinuousRequests = DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
    */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

/* TIM1 init function */
static void MX_TIM1_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 4081;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 2040;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }

  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_TIM_MspPostInit(&htim1);

}

/* TIM3 init function */
static void MX_TIM3_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 2040;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

/* TIM7 init function */
static void MX_TIM7_Init(void)
{

  TIM_MasterConfigTypeDef sMasterConfig;

  htim7.Instance = TIM7;
  htim7.Init.Prescaler = 8999;
  htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim7.Init.Period = 10000;
  if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

/* USART2 init function */
static void MX_USART2_UART_Init(void)
{

  huart2.Instance = USART2;
//huart2.Init.BaudRate = 9600;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }

}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, LCM_DB13_Pin|LCM_DB14_Pin|LCM_DB15_Pin|LCM_DB0_Pin 
                          |LCM_DB1_Pin|LCM_DB2_Pin|LCM_DB3_Pin|LCM_DB4_Pin 
                          |LCM_DB5_Pin|LCM_DB6_Pin|LCM_DB7_Pin|LCM_DB8_Pin 
                          |LCM_DB9_Pin|LCM_DB10_Pin|LCM_DB11_Pin|LCM_DB12_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LCM_YP_Pin|LCM_XP_Pin|LCM_CS_Pin|LCM_RS_Pin 
                          |LCM_WR_Pin|LCM_RD_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LCM_BL_GPIO_Port, LCM_BL_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : LCM_DB13_Pin LCM_DB14_Pin LCM_DB15_Pin LCM_DB0_Pin 
                           LCM_DB1_Pin LCM_DB2_Pin LCM_DB3_Pin LCM_DB4_Pin 
                           LCM_DB5_Pin LCM_DB6_Pin LCM_DB7_Pin LCM_DB8_Pin 
                           LCM_DB9_Pin LCM_DB10_Pin LCM_DB11_Pin LCM_DB12_Pin */
  GPIO_InitStruct.Pin = LCM_DB13_Pin|LCM_DB14_Pin|LCM_DB15_Pin|LCM_DB0_Pin 
                          |LCM_DB1_Pin|LCM_DB2_Pin|LCM_DB3_Pin|LCM_DB4_Pin 
                          |LCM_DB5_Pin|LCM_DB6_Pin|LCM_DB7_Pin|LCM_DB8_Pin 
                          |LCM_DB9_Pin|LCM_DB10_Pin|LCM_DB11_Pin|LCM_DB12_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : LCM_YP_Pin LCM_XP_Pin LCM_CS_Pin LCM_RS_Pin 
                           LCM_WR_Pin LCM_RD_Pin LCM_BL_Pin */
  GPIO_InitStruct.Pin = LCM_YP_Pin|LCM_XP_Pin|LCM_CS_Pin|LCM_RS_Pin 
                          |LCM_WR_Pin|LCM_RD_Pin|LCM_BL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler */
  /* User can add his own implementation to report the HAL error return state */
  printf("Error_Handler: STOP\n");
  while(1) 
  {
  }
  /* USER CODE END Error_Handler */ 
}

#ifdef USE_FULL_ASSERT

/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

}

#endif

/**
  * @}
  */ 

/**
  * @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
