/* Correction du TP 9 (partie 2, FreeRTOS) -- charge du processeur.
 *
 * Configuration CubeMX : FREERTOS CMSIS_V2 ; Config parameters :
 * USE_TRACE_FACILITY, GENERATE_RUN_TIME_STATS et
 * USE_STATS_FORMATTING_FUNCTIONS actives ; SYS Timebase Source = TIM6 ;
 * UART5 et printf comme au TP 5 de la partie 1 ; LED_1..LED_4 en sortie,
 * SW1 et SW2 en entree sans tirage ; taches blinkTask (StartBlink),
 * loadTask (StartLoad), statsTask (StartStats), osPriorityNormal, pile 512.
 * Ajouter #include <stdio.h> et #include "task.h" (USER CODE Includes).
 */

/* ===== USER CODE BEGIN PV =================================================== */
static volatile uint32_t g_load_ms = 10u;
/* USER CODE END PV */

/* ===== USER CODE BEGIN 0 ==================================================== */
int __io_putchar(int ch)
{
    uint8_t c = (uint8_t)ch;

    HAL_UART_Transmit(&huart5, &c, 1u, 10u);
    return ch;
}

/* Calcul simule : ne compte que le temps ou la tache a tourne (TP 4). */
static void busy_ms(uint32_t ms)
{
    uint32_t last = HAL_GetTick();

    while (ms > 0u)
    {
        uint32_t now = HAL_GetTick();

        if (now != last) { last = now; ms--; }
    }
}
/* USER CODE END 0 */

/* Etape 1 : fonctions generees vides par CubeMX (freertos.c ou main.c). */
void configureTimerForRunTimeStats(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0u;
    DWT->CTRL  |= DWT_CTRL_CYCCNTENA_Msk;
}

unsigned long getRunTimeCounterValue(void)
{
    return DWT->CYCCNT;
}

void StartBlink(void *argument)
{
    (void)argument;
    for (;;)
    {
        HAL_GPIO_TogglePin(LED_1_GPIO_Port, LED_1_Pin);
        osDelay(100);
    }
}

/* Etape 2 : load ms de calcul toutes les 100 ms ; SW1 / SW2 par pas de 10. */
void StartLoad(void *argument)
{
    uint32_t next = osKernelGetTickCount();
    uint8_t  prev1 = 0u, prev2 = 0u;

    (void)argument;
    for (;;)
    {
        uint8_t sw1 = (HAL_GPIO_ReadPin(SW1_GPIO_Port, SW1_Pin) == GPIO_PIN_SET) ? 1u : 0u;
        uint8_t sw2 = (HAL_GPIO_ReadPin(SW2_GPIO_Port, SW2_Pin) == GPIO_PIN_SET) ? 1u : 0u;

        /* lecture toutes les 100 ms : plus longue que les rebonds */
        if (sw1 && !prev1 && (g_load_ms < 90u)) { g_load_ms += 10u; }
        if (sw2 && !prev2 && (g_load_ms > 0u))  { g_load_ms -= 10u; }
        prev1 = sw1;
        prev2 = sw2;

        busy_ms(g_load_ms);
        next += 100u;
        osDelayUntil(next);
    }
}

/* Etapes 2 et 3 : tableau cumule, puis charge sur 5 s. */
void StartStats(void *argument)
{
    static char buf[512];
    uint32_t prev_total = getRunTimeCounterValue();
    uint32_t prev_idle  = ulTaskGetIdleRunTimeCounter();

    (void)argument;
    for (;;)
    {
        uint32_t total, idle, d_total, d_idle, load_pct;

        osDelay(5000);

        vTaskGetRunTimeStats(buf);
        printf("%s\r\n", buf);

        total   = getRunTimeCounterValue();
        idle    = ulTaskGetIdleRunTimeCounter();
        d_total = total - prev_total;          /* juste meme au passage par 0 */
        d_idle  = idle - prev_idle;
        prev_total = total;
        prev_idle  = idle;

        load_pct = (d_total > 0u)
                 ? 100u - (uint32_t)(((uint64_t)d_idle * 100u) / d_total) : 0u;
        printf("consigne %lu ms / 100 ms  ->  charge %lu %%\r\n",
               (unsigned long)g_load_ms, (unsigned long)load_pct);
        HAL_GPIO_WritePin(LED_4_GPIO_Port, LED_4_Pin,
                          (load_pct > 80u) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    }
}

/* Reponses attendues aux questions :
 *  1. Nom de la tache, temps cumule (en unites du compteur, ici des cycles),
 *     part du temps total en %. On y trouve defaultTask s'il est garde,
 *     les taches creees, IDLE et Tmr Svc.
 *  2. Environ 10 %, puis 50 % : load ms de calcul toutes les 100 ms. Le
 *     tableau cumule ne suit qu'avec retard (voir etape 3).
 *  3. 512 octets sur une pile de 256 mots (1 Kio), avec printf par-dessus :
 *     debordement de pile. static met le tampon en memoire globale.
 *  4. Oui, a quelques % pres : charge = load % + le peu que coutent les
 *     autres taches et le noyau.
 *  5. 2^32 / 64 MHz = 67 s. Les ecarts en entiers non signes restent justes
 *     tant que la fenetre est plus courte que 67 s. Le tableau cumule, lui,
 *     devient faux apres 67 s : les compteurs de taches et le total
 *     debordent a des moments differents. Pour une mesure longue, il faut un
 *     compteur plus lent (un timer a 10 ou 100 kHz) ou de 64 bits.
 *  6. Sa ligne dans le tableau : surtout le temps de printf, qui attend
 *     l'UART octet par octet (plusieurs dizaines de ms par tableau).
 */
