/*!
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2026-02-25
 *
 * @attention
 *
 *  Copyright (C) 2026 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
 *
 *  The program is used for demonstration purpose only.
 *
 *  You should get a copy of the license file which is along with
 *  this program.
 */

/* Includes */
#include "Board.h"
#include "stdio.h"
#include "string.h"
#include "inttypes.h"
#include "user_tmr_config.h"

/** @addtogroup Examples
  @{
*/

/** @addtogroup TMR_Examples
@{
*/

/** @addtogroup TMR_InputCapture_SENT
  @{
*/

/** @defgroup TMR_InputCapture_SENT_Macros Macros
  @{
*/

/* printf function configs to USART1*/
#define DEBUG_USART  USART1

/**@} end of group TMR_InputCapture_SENT_Macros */

/** @defgroup TMR_InputCapture_SENT_Functions Functions
  @{
  */

void Tmr_SentInit(void);
void Tmr_DelayMs(uint32_t Ms);
void Tmr_DelayInit(void);
uint8_t Tmr_SentCalculateCrc(const uint8_t *Nibbles, uint8_t Len);
void Tmr_SentReceiveNibble(uint32_t PulseWidthUs);
void Tmr_SentValidateFrame(void);
void Tmr_SentSendFrame(const uint8_t *Data, uint8_t Len);
void Tmr_SentConfigDma(const uint32_t Buf);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
int main(void)
{
    /* The data to be sent */
    const uint8_t send_data[3] = {0x12, 0x34, 0x56};

    /* Initialization of the SENT protocol */
    Board_ComInit(COM1);
    Board_LedInit(LED2);
    
    Tmr_SentInit();
    Tmr_DelayInit();

    while (1)
    {
        /* Detected a valid frame */
        if (0u != sent_typ.frame_valid)
        {
            printf("SENT Data: 0x%02X 0x%02X 0x%02X | CRC: %d\r\n",
                   sent_typ.data_buf[0], sent_typ.data_buf[1], sent_typ.data_buf[2],
                   sent_typ.crc_calculated);
            
            Board_LedToggle(LED2);
            
            sent_typ.frame_valid = 0;
        }
        else
        {
            /* nothing */
        }
        
        /* Send one frame of SENT data */
        Tmr_SentSendFrame(send_data, 3);
        Tmr_DelayMs(1000);
    }
}

/*!
 * @brief       Tmr_DelayInit
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Tmr_DelayInit(void)
{    
    Rcm_EnableApb2PeriphClock(RCM_APB2_PERIPH_TMR1);
    Tmr_ConfigTimeBase(TMR1, &g_tmrDelayConfig);
    Tmr_EnableInterrupt(TMR1, (uint16_t)TMR_INT_UPDATE);
    Nvic_EnableIrqRequest(TMR1_BRK_UP_TRG_COM_IRQn, 0);
    Tmr_Enable(TMR1);
}

/*!
 * @brief       Tmr_DelayMs
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Tmr_DelayMs(uint32_t Ms)
{
    g_tick = 0;

    while (g_tick < Ms)
    {
        /* nothing */
    }
}

/*!
 * @brief       Tmr_SentInit
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Tmr_SentInit(void)
{
    sent_typ.state = SENT_STATE_IDLE;
    sent_typ.tick_us = 1;

    /* GPIO configuration */
    Rcm_EnableAhbPeriphClock(RCM_AHB_PERIPH_GPIOA);
    Gpio_Config(SENT_RX_GPIO_PORT, &g_gpioSentRxConfig);
    Gpio_Config(SENT_TX_GPIO_PORT, &g_gpioSentTxConfig);
    Gpio_ConfigPinAF(SENT_RX_GPIO_PORT, GPIO_PIN_SOURCE_0, SENT_RX_GPIO_AF);
    Gpio_ConfigPinAF(SENT_TX_GPIO_PORT, GPIO_PIN_SOURCE_6, SENT_TX_GPIO_AF);

    /* Timer configuration */
    Rcm_EnableApb1PeriphClock(RCM_APB1_PERIPH_TMR3);
    Rcm_EnableApb1PeriphClock(RCM_APB1_PERIPH_TMR2);
    
    Tmr_ConfigTimeBase(TMR3, &g_tmr3SentBaseConfig);
    Tmr_ConfigTimeBase(TMR2, &g_tmr2SentBaseConfig);
    
    g_tmrSentIcConfig.channel = TMR_CHANNEL_1;
    Tmr_ICConfig(TMR2, &g_tmrSentIcConfig);
    
    Tmr_OC1Config(TMR3, &g_tmrSentOcConfig);
    Tmr_OC1PreloadConfig(TMR3, TMR_OC_PRELOAD_ENABLE);
    Tmr_EnableAUTOReload(TMR3);
    
    Tmr_EnableDMASoure(TMR3, (uint16_t)TMR_DMA_CH1);    
    Tmr_ConfigDMA(TMR3, TMR_DMABASE_AUTORLD, TMR_DMA_BURSTLENGHT_3TRANSFERS);
    
    Tmr_EnableInterrupt(TMR2, (uint16_t)TMR_INT_CH1);
    Tmr_ClearIntFlag(TMR2, (uint16_t)TMR_INT_CH1);
    
    Tmr_Enable(TMR3);
    Tmr_Enable(TMR2);

    /* NVIC configuration */
    Nvic_EnableIrqRequest(TMR2_IRQn, 0x00);
    NVIC_ClearPendingIRQ(TMR2_IRQn);
}

/*!
 * @brief       DMA Init
 *
 * @param       Buf : PWM duty cycle
 *
 * @retval      None
 *
 * @note
 */
void Tmr_SentConfigDma(const uint32_t Buf)
{
    Rcm_EnableAhbPeriphClock(RCM_AHB_PERIPH_DMA1);
    Dma_Disable(DMA1_CHANNEL_4);
    
    g_dmaSentConfig.memoryAddress = Buf;
    g_dmaSentConfig.peripheralAddress = (uint32_t)&TMR3->DMADDR_R.DMADDR;
    
    Dma_Config(DMA1_CHANNEL_4, &g_dmaSentConfig);
}

/*!
 * @brief       Tmr_SentCalculateCrc
 *
 * @param       nibbles: Data half-byte array
 *
 * @retval      len: Array length
 *
 * @note
 */
uint8_t Tmr_SentCalculateCrc(const uint8_t *Nibbles, uint8_t Len)
{
    uint8_t crc = 0;
    uint8_t i = 0;
    uint8_t j = 0;

    for (i = 0; i < Len; i++)
    {
        crc ^= Nibbles[i];
        for (j = 0; j < 4u; j++)
        {
            if (0u != (crc & (uint8_t)0x01))
            {
                crc = (crc >> 1) ^ (uint8_t)SENT_CRC_POLY;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    /* Only keep the lowest 4 bits */
    return crc & (uint8_t)0x0F; 
}

/*!
 * @brief       Tmr_SentReceiveNibble
 *
 * @param       pulse_width_us: Pulse Width (us)
 *
 * @retval      None
 *
 * @note
 */
void Tmr_SentReceiveNibble(uint32_t PulseWidthUs)
{
    uint16_t ticks = 0;
    float f32_ticks_calc = (float)0;
    f32_ticks_calc = (float)PulseWidthUs / (float)SENT_TICK_US;
    ticks = (uint16_t)f32_ticks_calc;
    
    switch (sent_typ.state)
    {
        case SENT_STATE_IDLE:
            /* Detect synchronous pulse */
            if ((ticks >= (SENT_SYNC_TICKS - 2u)) && (ticks <= (SENT_SYNC_TICKS + 2u)))
            {
                sent_typ.state = SENT_STATE_DATA;
                sent_typ.nibble_idx = 0;
                memset(sent_typ.nibble_buf, 0, sizeof(sent_typ.nibble_buf));
            }
            else
            {
                /* nothing */
            }
            break;
            
        case SENT_STATE_DATA:
            /* Receive half-byte of data */
            if ((ticks >= SENT_MIN_DATA_TICKS) && (ticks <= SENT_MAX_TICKS))
            {
                sent_typ.nibble_buf[sent_typ.nibble_idx] = (uint8_t)(ticks - SENT_MIN_DATA_TICKS);
                sent_typ.nibble_idx = sent_typ.nibble_idx + 1u;
                
                /* Data half-byte reception is complete. Switch to CRC reception */
                if (sent_typ.nibble_idx >= SENT_DATA_NUM)
                {
                    sent_typ.state = SENT_STATE_CRC;
                }
                else
                {
                    /* nothing */
                }
            }
            else
            {
                /* Invalid data, reset status */
                sent_typ.state = SENT_STATE_IDLE;
            }
            break;
            
        case SENT_STATE_CRC:
            /* Receive half-byte of CRC */
            if ((ticks >= SENT_MIN_DATA_TICKS) && (ticks <= SENT_MAX_TICKS))
            {
                sent_typ.crc_received = (uint8_t)(ticks - SENT_MIN_DATA_TICKS);
                sent_typ.state = SENT_STATE_VALIDATE;
                Tmr_SentValidateFrame();
            }
            else
            {
                sent_typ.state = SENT_STATE_IDLE;
            }
            break;
            
        default:
            sent_typ.state = SENT_STATE_IDLE;
            break;
    }
}

/*!
 * @brief       Tmr_SentValidateFrame
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Tmr_SentValidateFrame(void)
{
    uint8_t i = 0;
    /* Calculate CRC */
    sent_typ.crc_calculated = Tmr_SentCalculateCrc(sent_typ.nibble_buf, SENT_DATA_NUM);
    
    /* Verify CRC */
    if (sent_typ.crc_received == sent_typ.crc_calculated)
    {
        /* CRC is correct, data is assembled */
        for (i = 0; i < (SENT_DATA_NUM / 2u); i++)
        {
            sent_typ.data_buf[i] = (sent_typ.nibble_buf[2u * i] << 4u) | sent_typ.nibble_buf[(2u * i) + 1u];
        }
        sent_typ.frame_valid = 1;
    }
    else
    {
        sent_typ.frame_valid = 0;
    }
    
    /* Reset the state machine */
    sent_typ.state = SENT_STATE_IDLE;
}

/*!
 * @brief       Tmr_SentSendFrame
 *
 * @param       data: Data
 *
 * @retval      len: Array length
 *
 * @note
 */
void Tmr_SentSendFrame(const uint8_t *Data, uint8_t Len)
{
    uint8_t nibbles[8] = {0};
    uint8_t crc;
    uint8_t i = 0;
    uint16_t ticks = 0;
    float f32_ticks_tmp = 0.0f;
    
    /* Clear the cache area */
    memset(sent_typ.send_DMA_buf, 0, sizeof(sent_typ.send_DMA_buf));
    
    /* Calculate the corresponding count value for the synchronization pulse */
    f32_ticks_tmp = (float)SENT_SYNC_TICKS * (float)SENT_TICK_US / (float)sent_typ.tick_us;
    ticks = (uint16_t)f32_ticks_tmp;

    sent_typ.send_DMA_buf[0] = ticks;
    sent_typ.send_DMA_buf[1] = 0;
    sent_typ.send_DMA_buf[2] = 5;
    
    /* Convert byte data to half-byte */
    for (i = 0; (i < Len) && (i < 3u); i++)
    {
        nibbles[2u * i] = (Data[i] >> 4) & (uint8_t)0x0F;
        nibbles[(2u * i) + 1u] = Data[i] & (uint8_t)0x0F;
    }
    
    /* Calculate the corresponding count value for the data */
    for (i = 0; i < 6u; i++)
    {
        if (nibbles[i] > 15u) 
        {
            nibbles[i] = 15u;
        }  
        else
        {
            /* nothing */
        } 

        f32_ticks_tmp = ((float)nibbles[i] + (float)SENT_MIN_DATA_TICKS) * 
                        (float)SENT_TICK_US / (float)sent_typ.tick_us;
        ticks = (uint16_t)f32_ticks_tmp;
        
        sent_typ.send_DMA_buf[(3u * i) + 3u] = ticks;
        sent_typ.send_DMA_buf[(3u * i) + 4u] = 0;
        sent_typ.send_DMA_buf[(3u * i) + 5u] = 5;
    }
    
    /* Calculate CRC */
    crc = Tmr_SentCalculateCrc(nibbles, SENT_DATA_NUM);
    f32_ticks_tmp = ((float)crc + (float)SENT_MIN_DATA_TICKS) * 
                    (float)SENT_TICK_US / (float)sent_typ.tick_us;
    ticks = (uint16_t)f32_ticks_tmp;

    sent_typ.send_DMA_buf[21] = ticks;
    sent_typ.send_DMA_buf[22] = 0;
    sent_typ.send_DMA_buf[23] = 5;

    /* Send pause position */
    sent_typ.send_DMA_buf[24] = 200;
    sent_typ.send_DMA_buf[25] = 0;
    sent_typ.send_DMA_buf[26] = 5;
    
    /* Restore high level */
    sent_typ.send_DMA_buf[27] = 200;
    sent_typ.send_DMA_buf[28] = 0;
    sent_typ.send_DMA_buf[29] = 0;
    
    Dma_Reset(DMA1_CHANNEL_4);
    Tmr_SentConfigDma((uint32_t)&sent_typ.send_DMA_buf[0]);
    Dma_Enable(DMA1_CHANNEL_4);
    
    Tmr_GenerateEvent(TMR3, (uint16_t)TMR_EVENT_UPDATE);
    Tmr_DelayMs(1); 
}

/*!
 * @brief       Tmr_SentReceive
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Tmr_SentReceive(void)
{
    uint32_t pulse_width_us = 0;
    /* Calculate pulse width */
    uint32_t pulse_width = 0;
    
    if (Tmr_ReadIntFlag(TMR2, TMR_INT_FLAG_CH1) == (uint16_t)SET)
    {
        GPIOC->ODATA_R.ODATA = GPIOC->ODATA_R.ODATA ^ (uint32_t)GPIO_PIN_11;
        /* Read the captured value */
        sent_typ.capture_curr = Tmr_ReadCaputer1(TMR2);
        
        if (sent_typ.capture_curr >= sent_typ.capture_prev)
        {
            pulse_width = sent_typ.capture_curr - sent_typ.capture_prev;
        }
        else
        {
            pulse_width = (0xFFFFu - sent_typ.capture_prev) + sent_typ.capture_curr + 1u;
        }
        
        /* Convert to microseconds */
        pulse_width_us = pulse_width * sent_typ.tick_us;
        
        /* Parse half-byte */
        Tmr_SentReceiveNibble(pulse_width_us);
        
        /* Update the last captured value */
        sent_typ.capture_prev = sent_typ.capture_curr;
        
        /* Clear interrupt flags */
        Tmr_ClearIntFlag(TMR2, (uint16_t)TMR_FLAG_CH1);
        GPIOC->ODATA_R.ODATA = GPIOC->ODATA_R.ODATA ^ (uint32_t)GPIO_PIN_11;
    }
    else
    {
        /* nothing */
    }
}

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    Usart_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == (uint8_t)RESET)
    {
        /* nothing */
    }

    return (ch);
}

#elif defined (__GNUC__)

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @retval      The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
    /* send a byte of data to the serial port */
    Usart_TxData(DEBUG_USART, ch);

    /* wait for the data to be send  */
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
    {
        /* nothing */
    }

    return ch;
}

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       file:  Meaningless in this function.
*
* @param       *ptr:  Buffer pointer for data to be sent.
*
* @param       len:  Length of data to be sent.
*
* @retval      The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        __io_putchar(*ptr++);
    }

    return len;
}

#else
#pragma message("Not supported compiler type")
#endif

/**@} end of group TMR_InputCapture_SENT_Functions */
/**@} end of group TMR_InputCapture_SENT */
/**@} end of group TMR_Examples */
/**@} end of group Examples */
