/* plx_hal.c  (custom minimal HAL: GPIO + 3x PWM for PLECS model) */

#include "plx_hal.h"

#include "driverlib/device.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/epwm.h"

/* Globals live in your SSC app (led_feedback.c) */
extern volatile uint16_t g_led_input;
extern volatile uint16_t g_fb_input;
extern volatile uint16_t g_controller_fb;

/* ----------------- DIO mapping -----------------
   handle 0 -> GPIO12
   handle 1 -> GPIO24
   handle 2 -> GPIO25
   handle 3 -> GPIO13
   (This matches what the PLECS-generated HAL was doing with 4 outputs) :contentReference[oaicite:1]{index=1}
*/
static uint32_t handle_to_gpio(uint16_t h)
{
    switch (h)
    {
        case 0: return 12;
        case 1: return 24;
        case 2: return 25;
        case 3: return 13;
        default: return 12;
    }
}

bool PLXHAL_DIO_get(uint16_t handle)
{
    return (GPIO_readPin(handle_to_gpio(handle)) != 0U);
}

void PLXHAL_DIO_set(uint16_t handle, bool val)
{
    GPIO_writePin(handle_to_gpio(handle), val ? 1U : 0U);
}

/* -------- Read/Write Global hooks (must match generated names) -------- */
void PLXHAL_CUSTOM_f8(uint16_t *out)         { out[0] = g_led_input; }
void PLXHAL_CUSTOM_f12(uint16_t *out)        { out[0] = g_fb_input; }
void PLXHAL_CUSTOM_f13(uint16_t const *in)   { g_controller_fb = in[0]; }

/* ----------------- PWM mapping -----------------
   channel 0 -> ePWM1A on GPIO0
   channel 1 -> ePWM2A on GPIO2
   channel 2 -> ePWM3A on GPIO4
*/
static uint32_t pwm_base(uint16_t ch)
{
    switch (ch)
    {
        case 0: return EPWM1_BASE;
        case 1: return EPWM2_BASE;
        case 2: return EPWM3_BASE;
        default: return EPWM1_BASE;
    }
}

#define SYSCLK_HZ        (200000000UL)   /* if your project uses different SYSCLK, change this */
#define PWM_FREQ_HZ      (10000UL)       /* 10 kHz */
#define TBPRD_VALUE      (SYSCLK_HZ / (2UL * PWM_FREQ_HZ))  /* up-down mode => /2 */

static void pwm_init_one(uint32_t base)
{
    /* Time base */
    EPWM_setTimeBaseCounterMode(base, EPWM_COUNTER_MODE_UP_DOWN);
    EPWM_setTimeBasePeriod(base, (uint16_t)TBPRD_VALUE);
    EPWM_setTimeBaseCounter(base, 0U);

    EPWM_setClockPrescaler(base, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);

    /* Compare A starts at 0% duty */
    EPWM_setCounterCompareValue(base, EPWM_COUNTER_COMPARE_A, 0U);

    /* Action qualifier: set high on up-compare, clear low on down-compare */
    EPWM_setActionQualifierAction(base, EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_HIGH,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);

    EPWM_setActionQualifierAction(base, EPWM_AQ_OUTPUT_A,
                                  EPWM_AQ_OUTPUT_LOW,
                                  EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);

    /* Disable deadband / trip for now (simple test) */
    EPWM_disableDeadBand(base);
}

void PLXHAL_PWM_setDuty(uint16_t channel, float duty)
{
    /* clamp 0..0.95 */
    if (duty < 0.0f)  duty = 0.0f;
    if (duty > 0.95f) duty = 0.95f;

    uint32_t base = pwm_base(channel);

    /* CMPA in up-down mode: duty * TBPRD */
    uint16_t cmpa = (uint16_t)(duty * (float)TBPRD_VALUE);
    EPWM_setCounterCompareValue(base, EPWM_COUNTER_COMPARE_A, cmpa);
}

void PLXHAL_PWM_enableAllOutputs(void)
{
    /* nothing needed for this simple test */
}

/* Called once by led_feedback_c2000_block_initialize() :contentReference[oaicite:2]{index=2} */
void led_feedback_c2000_block_initHal(void)
{
    /* ---- Digital outputs ---- */
    GPIO_setPinConfig(GPIO_12_GPIO12);
    GPIO_setPinConfig(GPIO_13_GPIO13);
    GPIO_setPinConfig(GPIO_24_GPIO24);
    GPIO_setPinConfig(GPIO_25_GPIO25);

    GPIO_setDirectionMode(12, GPIO_DIR_MODE_OUT);
    GPIO_setDirectionMode(13, GPIO_DIR_MODE_OUT);
    GPIO_setDirectionMode(24, GPIO_DIR_MODE_OUT);
    GPIO_setDirectionMode(25, GPIO_DIR_MODE_OUT);

    /* initial states */
    GPIO_writePin(12, 1U);
    GPIO_writePin(13, 1U);
    GPIO_writePin(24, 0U);
    GPIO_writePin(25, 0U);

    /* ---- PWM pins (A outputs only) ---- */
    GPIO_setPinConfig(GPIO_0_EPWM1_A);
    GPIO_setPinConfig(GPIO_2_EPWM2_A);
    GPIO_setPinConfig(GPIO_4_EPWM3_A);

    /* Init ePWM modules */
    pwm_init_one(EPWM1_BASE);
    pwm_init_one(EPWM2_BASE);
    pwm_init_one(EPWM3_BASE);
}
