#include "init.h"
#include <math.h>

/* DMA буфер под DAC */
uint16_t g_dac_buffer[SINE_POINTS];

/* Базовый синус 0..1 (float), пересчитывается один раз */
static float s_sine_base[SINE_POINTS];

/* ---- Вспомогательно: получить реальную частоту тактирования TIM2 ----
   TIM2 находится на APB1. Если prescaler APB1 != 1, то таймер тактируется *2.
*/
static uint32_t TIM2_GetTimerClockHz(void)
{
    RCC_ClocksTypeDef clocks;
    uint32_t pclk1;

    RCC_GetClocksFreq(&clocks);
    pclk1 = clocks.PCLK1_Frequency;

    /* Если делитель APB1 больше 1, таймеры на APB1 получают частоту 2*PCLK1 */
    if ((RCC->CFGR & RCC_CFGR_PPRE1) != RCC_CFGR_PPRE1_DIV1)
    {
        return (pclk1 * 2u);
    }
    return pclk1;
}

/* ---------------- GPIO ----------------
   PA4 = аналог (DAC OUT1)
   PA5 = аналог (ADC IN5)
*/
void GPIO_Init_All(void)
{
    GPIO_InitTypeDef gpio;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    gpio.GPIO_Pin  = DAC_OUT_PIN | AMP_ADC_PIN;
    gpio.GPIO_Mode = GPIO_Mode_AN;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &gpio);
}

/* ---------------- DAC1 (Channel 1 -> PA4) ---------------- */
void DAC1_Init_All(void)
{
    DAC_InitTypeDef dac;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    DAC_StructInit(&dac);
    dac.DAC_Trigger      = DAC_Trigger_T2_TRGO;      /* запуск от TIM2 TRGO (как в файле) */
    dac.DAC_OutputBuffer = DAC_OutputBuffer_Enable;

    DAC_Init(DAC_Channel_1, &dac);
    DAC_Cmd(DAC_Channel_1, ENABLE);

    /* Разрешаем запросы DMA от DAC */
    DAC_DMACmd(DAC_Channel_1, ENABLE);
}

/* ---------------- DMA1 Stream5 Channel7 -> DAC1 ---------------- */
void DMA1_For_DAC1_Init(uint16_t *buf, uint32_t n)
{
    DMA_InitTypeDef dma;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Stream5);
    while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE) { }

    DMA_StructInit(&dma);

    dma.DMA_Channel            = DMA_Channel_7;
    dma.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1;
    dma.DMA_Memory0BaseAddr    = (uint32_t)buf;
    dma.DMA_DIR                = DMA_DIR_MemoryToPeripheral;
    dma.DMA_BufferSize         = n;

    dma.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    dma.DMA_MemoryInc          = DMA_MemoryInc_Enable;

    dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;

    dma.DMA_Mode               = DMA_Mode_Circular;
    dma.DMA_Priority           = DMA_Priority_High;
    dma.DMA_FIFOMode           = DMA_FIFOMode_Disable;

    DMA_Init(DMA1_Stream5, &dma);
    DMA_Cmd(DMA1_Stream5, ENABLE);
}

/* ---------------- TIM2 TRGO: частота обновления DAC ---------------- */
void TIM2_TRGO_Init(uint32_t update_hz)
{
    TIM_TimeBaseInitTypeDef tim;
    uint32_t timclk;
    uint32_t period;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructInit(&tim);

    timclk = TIM2_GetTimerClockHz();

    /* Prescaler = 0 (делим на 1), Period под update_hz */
    /* period = timclk/update_hz - 1 */
    period = (timclk / update_hz);
    if (period == 0u) { period = 1u; }
    period = period - 1u;

    tim.TIM_Prescaler     = 0u;
    tim.TIM_CounterMode   = TIM_CounterMode_Up;
    tim.TIM_ClockDivision = TIM_CKD_DIV1;
    tim.TIM_Period        = (uint32_t)period;

    TIM_TimeBaseInit(TIM2, &tim);

    /* TRGO на событие Update */
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    TIM_Cmd(TIM2, ENABLE);
}

/* ---------------- ADC1 on PA5 (Channel 5) ---------------- */
void ADC1_PA5_Init(void)
{
    ADC_InitTypeDef adc;
    ADC_CommonInitTypeDef adc_common;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    ADC_CommonStructInit(&adc_common);
    adc_common.ADC_Mode = ADC_Mode_Independent;
    adc_common.ADC_Prescaler = ADC_Prescaler_Div4;
    adc_common.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    adc_common.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&adc_common);
ADC_StructInit(&adc);
    adc.ADC_Resolution = ADC_Resolution_12b;
    adc.ADC_ScanConvMode = DISABLE;
    adc.ADC_ContinuousConvMode = DISABLE;               /* читаем по запросу */
    adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    adc.ADC_DataAlign = ADC_DataAlign_Right;
    adc.ADC_NbrOfConversion = 1;
    ADC_Init(ADC1, &adc);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_144Cycles);

    ADC_Cmd(ADC1, ENABLE);
}

/* Усреднение АЦП (чтобы меньше шумело) */
uint16_t ADC1_ReadAverage(uint8_t samples)
{
    uint32_t sum;
    uint8_t i;
    uint16_t v;

    if (samples == 0u) { samples = 1u; }

    sum = 0u;
    for (i = 0u; i < samples; i++)
    {
        ADC_SoftwareStartConv(ADC1);
        while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET) { }
        v = (uint16_t)ADC_GetConversionValue(ADC1);
        sum += (uint32_t)v;
    }

    return (uint16_t)(sum / (uint32_t)samples);
}

/* ---- Ключевое требование: НЕ пихать ADC напрямую в волну ----
   Конвертируем: ADC -> Voltage -> Amplitude 0..100
*/
uint8_t ADC_To_Amplitude_0_100(uint16_t adc_raw)
{
    float voltage;
    float a;

    voltage = ((float)adc_raw * VREF_VOLTS) / (float)ADC_MAX_12BIT;

    /* нормируем в 0..100 */
    a = (voltage / VREF_VOLTS) * 100.0f;

    if (a < 0.0f)   { a = 0.0f; }
    if (a > 100.0f) { a = 100.0f; }

    return (uint8_t)(a + 0.5f);
}

/* Предрасчёт базового синуса (0..1) */
void SineBase_Init(void)
{
    uint32_t i;
    for (i = 0u; i < SINE_POINTS; i++)
    {
        /* (sin + 1)/2 -> 0..1 */
        s_sine_base[i] = (sinf(2.0f * 3.1415926f * (float)i / (float)SINE_POINTS) + 1.0f) * 0.5f;
    }
}

/* Обновить DMA-буфер под новую амплитуду 0..100 */
void SineBuffer_Update(uint8_t amplitude_0_100)
{
    uint32_t i;
    float k;

    k = ((float)amplitude_0_100) / 100.0f;

    for (i = 0u; i < SINE_POINTS; i++)
    {
        float v;
        v = s_sine_base[i] * k; /* 0..1 * 0..1 */
        g_dac_buffer[i] = (uint16_t)(v * (float)DAC_MAX_12BIT);
    }
}
