/*
    Tutoriaux AvrX

    Utilisation de sémaphores pour synchroniser 2 tâches.
    
    Une tâche est utilisée pour faire clignoter une LED, l'autre pour scruter une entrée.
    La LED change d'état à chaque appui sur le poussoir connecté entre PA0 et la masse
    
    Cible : ATMega163
    Quartz : 8MHz
    Compilateur : avr-gcc (WInAVR)
    
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include "avrx.h"
#include "hardware.h"

// définition des timers utiliser

AVRX_TIMER (timer) ;    // lecture de l'entrée

/*
 Timer 0 Overflow Interrupt Handler

 Structure typique :
 . Switch en contexte kernel 
 . gestion de l'interruption
 . retour au context initial
 */

AVRX_SIGINT(SIG_OVERFLOW0)
{
    // here, you can insert specific handling stuff not related to AvrX
    
    IntProlog();                // Switch to kernel stack/context
    EndCritical();                // autorise à nouveau les interruptions
    TCNT0 = TCNT0_INIT;            // reset timer counter to its start point
    AvrXTimerHandler();         // Call Time queue manager
    Epilog();                   // back to initial context
}

// le sémaphore de communication entre les tâches
AVRX_MUTEX(sem) ;

/*
 * Définition des tâches
 * 
 * Elles sont au nombre de 2 :
 *     tâche 1 : gestion de la LED
 *  tâche 2 : gestion de l'entrée
 */
 
 /*
  * Cette tâche attend que le sémaphore soit levé  pour basculer
  * l'état de la LED
  */
AVRX_GCC_TASKDEF(task1, 8, 3) {
    while (1) {
        // reset du sémaphore et attente
        AvrXWaitSemaphore(&sem) ;
        
        // le sémaphore a été levé => bascule l'état de la LED
        LED = LED ^ 0x01;
    }
}

/*
 * Cette tâche scrute l'entrée toute les 100ms et lève le sémaphore
 * si elle détecte un front descendant (correspondant à l'appui du poussoir)
 * Pour vérifier l'appui du poussoir, on allume une deuxième LED comme témoin
 */
AVRX_GCC_TASKDEF(task2, 8, 2) {
    char is_pressed, was_pressed ;
    
    was_pressed = 0 ;                        // initialisation de la mémoire d'état
    
    while (1) {
        is_pressed = (SWITCH & 0x01) == 0 ; // lecture de l'état du poussoir
        if (is_pressed) {                    // si pressé
            if (!was_pressed) {                // et que ce n'était pas déjà le cas
                LED &= ~_BV(1) ;            // on allume la LED témoin,
                was_pressed = 1 ;            // on mémorise l'état
                AvrXSetSemaphore(&sem) ;    // et on lève le sémaphore
            }
        } else {                            // le bouton n'est pas pressé
            if (was_pressed) {                // et que ce n'était pas déjà le cas
                was_pressed = 0 ;            // on remet tout à 0
                LED |= _BV(1) ;
            }
        }
        
        AvrXDelay(&timer, 100);            // pause de 100mx avant scrutation suivante
    }
}

//--------------------------------------------------------------------------------------------

int main(void)                     // Main démarre dans le contexte du kernel AvrX
{
    AvrXSetKernelStack(0);        // définition du stack du kernel

    MCUCR = 1<<SE;                // autorise l'instruction SLEEP
    TCNT0 = TCNT0_INIT;            // initialise le timer 0 (utilisé pour la gestion des timers
    TCCR0 = TMC8_CK256;            // définit son prescaler à CLK/256
    TIMSK = 1<<TOIE0;            // active l'interruption Timer0 Overflow

    LEDDDR = 0xFF;                // configure en sortie le port des LEDs 
    LED   = 0xFF;                // initialise toutes les sortie à 1 
                                // => éteint les LEDs si celles-ci sont reliées à Vcc
                                
    SWITCHDDR = 0x00 ;            // le port des switch est configuré en entrée
    SWITCHPUP = 0xFF ;            // les pullups sont activées => appui sur le poussoir = entrée à 0

    AvrXRunTask(TCB(task1));    // active les 2 tâches
    AvrXRunTask(TCB(task2));

    Epilog();                   // bascule sur le contexte de la première tâche
                                // => active l'ensemble
                                
    while(1);                    // il n'y a plus rien à faire ici
}