APP4 S5

Dependencies:   mbed-rtos mbed

radio.cpp

Committer:
Cheroukee
Date:
2017-10-22
Revision:
13:b44c1f678aff
Parent:
12:e21604b50719

File content as of revision 13:b44c1f678aff:

/*
*   radio.cpp - Code de radio manchester - Jean-Philippe Fournier (fouj1807) - Jean-Pascal McGee (mcgj2701)
*/

#include "radio.h"
#include "quick_queue.h"

#include "mbed.h"
#include "rtos.h"

#include "crc.h"
// Vitesse de sortie des message manchester
#define MANCHESTER_SPEED_OUT 4    

// Pins de reception et d'entree
#define INPUT_RADIO p18
#define OUTPUT_RADIO p6

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

// Private API enumerations
// Receiver states
typedef enum {
    in_attente = 0,
    in_preambule,
    in_data    
} receive_state_t;

// Emitter states
typedef enum {
    out_preambule = 0,
    out_start,
    out_options,
    out_length,
    out_data,
    out_crc,
    out_end,
    out_idle
} emitter_state_t;

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Fonctions privee de l'API 

// Setup radio output
void setup_radio_out();
// Setup radio input
void setup_radio_in();
// Fonction periodique d'envoi de messages
void radio_out(void const *args);
// Fonction appellee lors de l'interruption sur la pin d'entree
void radio_in();
// Fonction content la state machine de reception de trame
bool receive_frame(byte read_byte);
// Fonction de timeout de la reception de d ata manchester
void stop_frame(void const *n);
// Fonction du thread d'affichage dans le terminal
void thread_putc();

// Fonction qui cree un message et le mets dans un message
void add_new_message();

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

// TODO HOW WE DO THIS
Mail<radio_message_t, MESSAGE_BUFFER_SIZE> out_mail;
Mail<radio_message_t, MESSAGE_BUFFER_SIZE> in_mail;

// Utilise pour les interruptions et la lecture de la valeur d'entree de la pin de reception
InterruptIn input(INPUT_RADIO);
// Pin de sortie de la radio, permet l'envoi de code binaire, dans ce cas, manchester
DigitalOut output(OUTPUT_RADIO);

// Definition des LED de debug de la radio
#ifndef LED
DigitalOut in_debug_led4(LED4);
DigitalOut out_debug_led3(LED3);

DigitalOut frame_out_end_led2(LED2);
DigitalOut frame_in_end_led1(LED1);
#endif

// Thread d'output de debugging
Thread thread;

int start_speed = MANCHESTER_SPEED_OUT;

byte current_state = in_attente;
byte current_byte_progress = 0;

// Timer d'evoi periodic de donnes vers la radio
RtosTimer out_timer(radio_out, osTimerPeriodic, NULL);

// Timer qui mesure le temps et cree un timer sur la reception de data, permet le timeout du
// data qui arrive de la reception. Termine la reception manchester
RtosTimer ticker_watch(stop_frame, osTimerOnce, NULL);

// Char used by the debugging serial 
volatile int debug_char_output = 0;

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

// Setup radio input
void setup_radio_in()
{
    //thread.start(callback(thread_putc));
    
    input.rise(&radio_in);
    input.fall(&radio_in);       
}

// Setup radio output
void setup_radio_out()
{
    out_debug_led3 = 0;    
    frame_out_end_led2 = 0;
    output = 0;
    
    //add_new_message();
    
    wait(1);
    out_timer.start(start_speed);       
}

void add_new_message(){
//////////////////////////////////////////////////////
    // Creation d'un message et insertion dans le buffer
    radio_message_t* message = get_new_out_message();
    if (message != NULL)
    {        
        message->preambule = HEADER_DELIMITER;
        message->start = HEADER_START;
        message->options = HEADER_DELIMITER;
        message->length = 0x3;
        
        message->data[0] = 0xC0;
        message->data[1] = 0xFF;    
        message->data[2] = 0xEE;    
        
        // Ajouter calcul
        message->control = 0xCE;
        
        message->end = FOOTER_END;
        // On avance dans le buffer;
        new_out_message_ready();
    }
    //////////////////////////////////////////////////////   
}

#define SET_VAL_BIT_MASK(val) next_value = 0x1 & val; 
#define SET_VAL_SHIFT(val, shift) SET_VAL_BIT_MASK(val >> (7 - shift))
#define CHECK_NEXT_STATE if (current_byte_progress > 7)     \
                         {                                  \
                                out_current_state++;            \
                                current_byte_progress = 0;  \
                         }

// Fonction appellee periodiquement pour l'envoi du message courant
void radio_out(void const *args)
{
    static byte current_byte_progress = 0;    
    static byte current_byte = 0;    
    static byte out_current_state = out_preambule;    
    static bool IsBitTransition = false;
    static byte next_value = 0;
    static radio_message_t* message = NULL;

    out_debug_led3 = !out_debug_led3;
    // Si on est a la fin du packet, on retourne au debut et on reenvoye les donnees
    if (out_current_state > out_idle)
    {
        // Message termine
        message = NULL;
        last_out_message_read();
        frame_out_end_led2 = 0;
        
        // Reinitialisation des var de messages
        current_byte = 0;
        current_byte_progress = 0;
        out_current_state = out_preambule;

        out_timer.stop(); 
        out_timer.start(start_speed);   
    }

    if (message == NULL)
    {
        message = get_last_out_message();
    }

    if (message != NULL)
    {        
        if (!IsBitTransition)
        {            
            // Dependant du state, on progresse dans l'envoi du message
            switch (out_current_state) 
            {
                case out_preambule: // preambule
                {
                    frame_out_end_led2 = 1;
                    SET_VAL_SHIFT(message->preambule, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break;
                }        
                case out_start: // start
                {
                    SET_VAL_SHIFT(message->start, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break;
                }   
                case out_options: // entete options
                {
                    SET_VAL_SHIFT(message->options, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break;
                }             
                case out_length: // entete lenght
                {
                    SET_VAL_SHIFT(message->length, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break;
                }        
                case out_data: // charge utile
                {
                    SET_VAL_SHIFT(message->data[current_byte], current_byte_progress++)
                    if (current_byte_progress > 7)
                    {
                        current_byte++;
                        current_byte_progress = 0;                 
                        if (current_byte >= message->length)
                        {
                            current_byte = 0;
                            out_current_state++;
                        }                 
                    }
                    break;
                }        
                case out_crc: // controle
                {
                    SET_VAL_SHIFT(message->control, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break;
                }        
                case out_end: // end
                {
                    SET_VAL_SHIFT(message->end, current_byte_progress++);
                    CHECK_NEXT_STATE
                    break; 
                }
                case out_idle:
                {
                    /*out_debug_led3 = !out_debug_led3;
                    message = NULL;
                    last_out_message_read();
                    current_byte = 0;
                    current_byte_progress = 0;*/
                    break;
                }                                         
            }
            
            // Changement d'etat pour permettre de faire la bonne transition de valeur
            if (next_value != output && out_current_state <= out_idle)
            {
                output = !output;
            }
        }
        // Si on est pas dans une transitipon 
        else if (out_current_state != out_idle + 1)
        {
            output = !output;
            if (out_current_state == out_idle)
            {
                out_current_state++;
            }
        }
        IsBitTransition = !IsBitTransition;    
    }
}

// Fonction appellee lors de l'interruption sur la pin d'entree
void radio_in()
{    
    // Timer utilise pour le calcul de la periode de l'horloge du code manchester
    static Timer timer;   
    // Valeur de la demi periode 
    static int half_period = 0;
    // Valeur de la periode de l'horloge
    static int calculated_period = 0;
    // 
    static byte current_byte = 0;
    
    in_debug_led4 = !in_debug_led4;   
    
    
    switch (current_state)
    {
        case in_attente:
        {     
            frame_in_end_led1 = 0;
            if (input == 1)
            {
                timer.start();
                current_state = in_preambule;   
                current_byte_progress = 1;
                frame_in_end_led1 = 1;
            }
            break;
        }
        case in_preambule:
        {
            current_byte_progress++;
            calculated_period = timer.read_ms();           
            timer.reset();
            if (current_byte_progress > 7)
            {             
                half_period = calculated_period / 2;
                ticker_watch.start(calculated_period + half_period);
                current_byte_progress = 0;
                current_byte = 0;
                current_state = in_data;    
                debug_char_output = calculated_period;
               // thread.signal_set(0x1);
            }
            break;
        }
        case in_data:
        {        
            // Si ca fait plus longtemps que la periode usuelle de reception de donne
            // Cela veut dire que l'on doit recommencer la reception
            if(timer.read_ms() > calculated_period)
            {
                frame_in_end_led1 = 0;
                timer.stop();      
                timer.reset();    
                if (input == 1)
                {
                    timer.start();
                    current_state = in_preambule;   
                    current_byte_progress = 1;
                }
                else
                {
                    current_state = in_attente;   
                    current_byte_progress = 0;
                }  
            }
            // Si le temps de la demi periode est passe, donc on a une donne reele
            else if (timer.read_ms() > half_period)
            {               
                current_byte = (!input << (7 - current_byte_progress)) | current_byte;
                current_byte_progress++ ;                
                ticker_watch.start(calculated_period + half_period);
                timer.reset();      
                
                if (current_byte_progress > 7)
                {                    
                    debug_char_output = current_byte;
                    if (receive_frame(current_byte))
                    {
                        current_state = in_attente;
                    }
                    current_byte_progress = 0;
                    current_byte = 0;
                }
            }
            break;
        }
    }
}

// Fonction content la state machine de reception de trame
bool receive_frame(byte read_byte)
{
    static radio_message_t* current_message;
    static byte receive_current_state = out_start;
    static int data_index = 0;

    if (current_message == NULL)
    {
        current_message = get_new_in_message();
        receive_current_state = out_start;
        if (current_message == NULL){
            return true;
        }
    }

    if (current_message != NULL)
    {
        switch (receive_current_state)
        {
            case out_start:
            {
                current_message->start = read_byte;
                receive_current_state++;
                break;
            }
            case out_options:
            {
                current_message->options = read_byte;
                receive_current_state++;
                break;
            }
            case out_length:
            {
                current_message->length = read_byte;
                receive_current_state++;
                break;
            }
            case out_data:
            {
                current_message->data[data_index++] = read_byte;
                if (data_index >= current_message->length)
                {
                    data_index = 0;
                    receive_current_state++;
                }
                break;
            }
            case out_crc:
            {
                current_message->control = read_byte;
                receive_current_state++;
                break;
            }
            case out_end:
            {
                current_message->end = read_byte;
                new_in_message_ready();
                receive_current_state = out_start;
                current_message = NULL;
                return true;
                break;
            }

        }
    }
    return false;
}

// Fonction de timeout de la reception de data manchester
void stop_frame(void const *n)
{
    frame_in_end_led1 = 0;
    current_state = in_attente;
    current_byte_progress = 0;
    ticker_watch.stop();
}   
    
// Fonction du thread d'affichage dans le terminal
/*
void thread_putc()
{
    while(true)
    {
        Thread::signal_wait(0x1);
        debug_output.printf("0x%c\n\r", debug_char_output);    
    }   
}*/

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

/*
* Public API function
*/

/*
*   Inits the radio system and begins receiving manchester coded messages
*   Sends buffered messages
*/
void init_radio_system()
{
   init_crc_module();
   setup_radio_in();   
   setup_radio_out(); 
}



bool send_message(char* buffer, int length)
{
    //////////////////////////////////////////////////////
    // Creation d'un message et insertion dans le buffer
    radio_message_t* message = get_new_out_message();
    if (length <= MAX_MESSAGE_LENGTH && message != NULL)
    {        
        message->preambule = HEADER_DELIMITER;
        message->start = HEADER_START;
        message->options = HEADER_DELIMITER;
        message->length = length;
        
        for (int i = 0; i < length; i++){
            message->data[i] = buffer[i];
        }         
        
        // Ajouter calcul du CRC
        message->control = get_crc_value(message->data, message->length);//0xCE;
        
        message->end = FOOTER_END;
        // On avance dans le buffer;
        return new_out_message_ready();
    }
    return false;
    //////////////////////////////////////////////////////   
}

bool get_message(radio_message_t* message)
{
    radio_message_t* received_message;
    // Si un message a ete recu par la radio
    if((received_message = get_last_in_message()) != NULL)
    {
        // Si la valeur de CRC est valide par rapport a ce qui est calcule
        if (get_crc_value(received_message->data, received_message->length) == received_message->control)
        {
            message->preambule = received_message->preambule;
            message->start = received_message->start;
            message->options = received_message->options;
            message->length = received_message->length;
            // memcopy
            memcpy(message->data, received_message->data, received_message->length);
            
            message->control = received_message->control;
            message->end = received_message->end;
            
            last_in_message_read();
            return true;
        }
    }
    return false;
}

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////