/* Correction du TP 3 (partie 2, FreeRTOS) -- fonctions des taches.
 *
 * Configuration CubeMX : FREERTOS CMSIS_V2 ; SYS Timebase Source = TIM6 ;
 * UART5 et printf comme au TP 5 de la partie 1 ; LED_1..LED_4 en sortie ;
 * SW2..SW4 en entree sans tirage ; SW1 (PE10) en GPIO_EXTI10, front
 * montant, NVIC EXTI[15:10] active, priorite 5 ; taches buttonTask
 * (StartButton) et ledTask (StartLed) en osPriorityNormal, sw1Task
 * (StartSw1) en osPriorityAboveNormal ; file buttonQueue, 8 x uint8_t ;
 * semaphore binaire sw1Sem.
 * Ajouter #include <stdio.h> (USER CODE Includes).
 */

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

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

/* Etape 2 : l'interruption ne fait que lever le semaphore. */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == SW1_Pin)
    {
        osSemaphoreRelease(sw1SemHandle);
    }
}
/* USER CODE END 0 */

/* Etape 1 : scrutation toutes les 10 ms, anti-rebond par 3 lectures
 * identiques ; envoi du numero du bouton a chaque front montant. A l'etape
 * 2, SW1 est retire (premier = 1). */
void StartButton(void *argument)
{
    static GPIO_TypeDef *const port[4] = { SW1_GPIO_Port, SW2_GPIO_Port,
                                           SW3_GPIO_Port, SW4_GPIO_Port };
    static const uint16_t      pin[4]  = { SW1_Pin, SW2_Pin, SW3_Pin, SW4_Pin };
    const uint8_t first = 1u;            /* 0 a l'etape 1, 1 a l'etape 2 */
    uint8_t stable[4] = { 0u, 0u, 0u, 0u };
    uint8_t count[4]  = { 0u, 0u, 0u, 0u };

    (void)argument;
    for (;;)
    {
        for (uint8_t b = first; b < 4u; b++)
        {
            uint8_t now = (HAL_GPIO_ReadPin(port[b], pin[b]) == GPIO_PIN_SET) ? 1u : 0u;

            if (now == stable[b]) { count[b] = 0u; continue; }
            if (++count[b] < 3u)  { continue; }
            stable[b] = now;
            count[b]  = 0u;
            if (now)
            {
                /* delai 0 : si la file est pleine, l'appui est perdu */
                osMessageQueuePut(buttonQueueHandle, &b, 0u, 0u);
            }
        }
        osDelay(10);
    }
}

/* Etape 1 : bascule la LED du bouton recu. */
void StartLed(void *argument)
{
    static GPIO_TypeDef *const port[4] = { LED_1_GPIO_Port, LED_2_GPIO_Port,
                                           LED_3_GPIO_Port, LED_4_GPIO_Port };
    static const uint16_t      pin[4]  = { LED_1_Pin, LED_2_Pin, LED_3_Pin, LED_4_Pin };
    uint8_t b;

    (void)argument;
    for (;;)
    {
        if (osMessageQueueGet(buttonQueueHandle, &b, NULL, osWaitForever) == osOK
            && b < 4u)
        {
            HAL_GPIO_TogglePin(port[b], pin[b]);
#if 0
            osDelay(2000);                /* question 2 */
#endif
        }
    }
}

/* Etape 2 : reveillee par l'interruption. Question 5 : anti-rebond dans la
 * tache -- on ignore 50 ms, puis on vide le semaphore leve par les rebonds. */
void StartSw1(void *argument)
{
    uint32_t presses = 0u;

    (void)argument;
    for (;;)
    {
        osSemaphoreAcquire(sw1SemHandle, osWaitForever);
#if 1
        osDelay(50);
        (void)osSemaphoreAcquire(sw1SemHandle, 0u);
        if (HAL_GPIO_ReadPin(SW1_GPIO_Port, SW1_Pin) != GPIO_PIN_SET) { continue; }
#endif
        HAL_GPIO_TogglePin(LED_4_GPIO_Port, LED_4_Pin);
        presses++;
        printf("SW1 : %lu appuis\r\n", (unsigned long)presses);
    }
}

/* Reponses attendues aux questions :
 *  1. Elle est bloquee dans osMessageQueueGet : elle ne consomme rien.
 *  2. Les appuis s'accumulent dans la file et les LED basculent ensuite,
 *     une toutes les 2 s. Au-dela de 8 appuis en attente (+1 en cours de
 *     traitement), la file est pleine : osMessageQueuePut (delai 0) echoue,
 *     les appuis suivants sont perdus.
 *  3. La tache passe une premiere fois sans appui : LED_4 bascule et le
 *     compteur vaut 1 au demarrage. (CubeMX cree le semaphore binaire a 1
 *     par defaut dans certaines versions : verifier "Initial State".)
 *  4. Oui : les rebonds du contact produisent plusieurs fronts montants,
 *     donc plusieurs interruptions. Comme le semaphore est binaire, ceux
 *     qui arrivent pendant que la tache travaille se fondent en un seul,
 *     mais ceux qui arrivent apres son acquisition comptent en plus.
 *  5. Voir la variante #if 1 de StartSw1 : attendre que les rebonds soient
 *     finis, oublier le semaphore releve entre-temps, verifier l'etat.
 *  6. L'interruption doit rester tres courte : printf attend la liaison
 *     serie (plusieurs ms), bloque les autres interruptions de priorite
 *     inferieure, et n'est pas prevu pour etre appele en interruption.
 */
