//********************************************************************************************************************************************************************************** // Fichier C_BUS.C // // Contient toutes les fonctions liées à la gestion du C_Bus //**********************************************************************************************************************************************************************************/ #define __DEBUG_C_BUS 0 // Passer cette ligne à 0: aucun message de debug de C_bus, 1: les messages de debug de C_Bus sur l'écran, 2: sur fichier (cf NOM_FICHIER_DEBUG_BUS dans 'debug.h') ou 3: sur l'écran et fichier. // Includes : #include "main.h" // Prototypes : int8_t init_c_bus(void); void gestion_c_bus(void); int8_t gestion_protocole_rx_c_bus(void); void gestion_protocole_tx_c_bus(void); int8_t c_bus_next_write_buffer_cdes(uint8_t priorite); // Variables globales : struct Struct_c_bus c_bus; /********************************************************************************************************************************************************************************** Fonction INIT_C_BUS Fonction d'initialisation du C-Bus **********************************************************************************************************************************************************************************/ int8_t init_c_bus(void) { //------------------------------------------------------------------------------------------------------ // Init des variables générales : c_bus.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_INIT; c_bus.sequenceur.compteur = C_BUS_TEMPO_INIT; c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_INIT; c_bus.protocole_rx.sequenceur.compteur = C_BUS_PROTOCOLE_RX_TEMPO_INIT; c_bus.protocole_rx.flag_dle = FALSE; c_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_INIT; c_bus.protocole_tx.sequenceur.compteur = C_BUS_PROTOCOLE_TX_TEMPO_INIT; // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation du C-BUS\t\t: OK", TYPE_MESSAGE_DEBUG_OK); return(0); } /********************************************************************************************************************************************************************************** Fonction GESTION_C_BUS Fonction de plus haut niveau de gestion du C-Bus **********************************************************************************************************************************************************************************/ void gestion_c_bus(void) { char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; switch(c_bus.sequenceur.etat) { //-------------------------------------------------------- case ETAT_SEQUENCEUR_C_BUS_INIT : { if (c_bus.sequenceur.compteur == 0) { //......................................................... // Vidage du buffer de réception de tous les caractères reçus pendant la phase d'init : serial1.ptr_debut_buffer_rx = 0; serial1.ptr_fin_buffer_rx = 0; //......................................................... // Mise à jour des séquenceurs pour démarrage des envois : c_bus.protocole_rx.sequenceur.compteur = 0; c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS; c_bus.protocole_tx.sequenceur.compteur = 0; c_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_REPOS; c_bus.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_REPOS; } break; } //-------------------------------------------------------- case ETAT_SEQUENCEUR_C_BUS_REPOS : { //================================= // Gestion du C-Bus en émission : gestion_protocole_tx_c_bus(); // Vérifie si une réponse (Up) ou une commande (Down) est en attente. Si oui, elle la traite et l'émet. //================================= // Gestion du C-Bus en réception : if (gestion_protocole_rx_c_bus() == 1) // Vérifie si une commande (Up) ou une réponse (Down) a été reçue. { #ifdef __DEBUG_C_BUS c_bus.down.reponse.reponse[c_bus.down.reponse.lg_reponse] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. affichage_debug_bus(TYPE_BUS_DEBUG_C_BUS, "Rx", (char *) c_bus.down.reponse.reponse, __DEBUG_C_BUS); #endif if (c_bus.protocole_rx.sens == 'U') // Réception d'une commande (Up) : { c_bus_up_recherche_commande(); // Recherche et traitement de la commande reçue. La réponse est préparée et son 'flag_ready' est positionné afin d'être traité par gestion_protocole_tx_c_bus() lors de la prochaine boucle. } else // Réception d'une réponse (Down) : { if (c_bus_down_recherche_reponse() != -1) // Comme la réponse a été reçue sans erreur, on libère le Protocole_Tx : { c_bus.down.flag_attente_reponse = FALSE; c_bus.protocole_tx.repetitions = 0; c_bus.protocole_tx.sequenceur.compteur_time_out = 0; } else c_bus.protocole_tx.sequenceur.compteur_time_out = 0; // En cas de réponse reçue avec erreur, on simule un déclenchement de time-out pour faire de suite le test du nombre de répétitions (cela permet juste de ne pas attandre l'expiration du time-out) } } break; } //-------------------------------------------------------- default : { sprintf(tmp, "L'état du séquenceur C_bus est inconnu [%i]. Il est repositionné au REPOS", c_bus.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); c_bus.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_REPOS; break; } } } /***************************************************************************************************************************************************************** Fonction : Appelée en tâche de fond, cette fonction lit le buffer de réception de l'UART 1 et traite les trames reçues. Soit une commande du Compagnon (Up), soit une réponse à une commande du CM3+ (Down) Reçoit : Rien Renvoit : 0 si RAS, 1 si une nouvelle commande complète a été reçue ******************************************************************************************************************************************************************/ int8_t gestion_protocole_rx_c_bus(void) { uint8_t c; bool flag_controle; bool flag_cde_recue; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; flag_cde_recue = FALSE; // Traitement de chaque caractère reçu : while (serial1.ptr_debut_buffer_rx != serial1.ptr_fin_buffer_rx) { // Calcul de la prochaine position à lire (avec bouclage en fin de buffer tournant) : if (++ serial1.ptr_debut_buffer_rx == MAX_TAILLE_RX_BUFFER_UART) serial1.ptr_debut_buffer_rx = 0; // Lecture du caractère dans le buffer : flag_controle = FALSE; // Permettra de marquer le caractère en cours de lecture c = serial1.buffer_rx[serial1.ptr_debut_buffer_rx]; // Lecture du caractère à traiter // Traitement des caractères DLE : if (c_bus.protocole_rx.flag_dle == TRUE) // Si le caractère précédent était un DLE { switch(c) { case ASCII_DLE : break; // C'est un DLE qui a fort justement été doublé, on va donc traiter le second comme un octet de data. case ASCII_SOH : // Caractères de contrôle échappés case ASCII_STX : case ASCII_ETX : { flag_controle = TRUE; // On marque donc 'x' comme un caractère de contrôle et non une data break; } default: // erreur de "DLE isolé" { flag_controle = TRUE; // On marque donc 'x' comme un caractère de contrôle et non une data c = ASCII_NULL; // On change volontairement la valeur à NULL afin que la trame soit rejetée et qu'en fonction de l'état du séquenceur, un NACK soit retourné ou non... break; } } c_bus.protocole_rx.flag_dle = FALSE; // Dans tous les cas précédent, le premier DLE a été traité (y compris dans le cas d'un double DLE) } else // Si le caractère précédent n'était PAS un DLE : { if (c == ASCII_DLE) c_bus.protocole_rx.flag_dle = TRUE; // On positionne le flag si c'est un DLE } // Traitement du caractère reçu : if (c_bus.protocole_rx.flag_dle == FALSE) // Sauf si on est sur le "premier" DLE... { switch(c_bus.protocole_rx.sequenceur.etat) { //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_INIT : { // NB : Le caractère lu est jeté tant que la phase d'init n'est pas terminée... // NB 2 : le passage à l'état REPOS se fait par le séquenceur C-Bus, indépendemment de la réception ou non de caractères. break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS : { if ((flag_controle == TRUE) && (c == ASCII_SOH)) // Attente de réception du SOH de début de trame { c_bus.protocole_rx.cs = (uint16_t) c; // Premier chargement du CS c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_SENS; } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_SENS : { c_bus.protocole_rx.cs += (uint16_t) c; // Rechargement du CS if ((c == 'D') || (c =='U')) { c_bus.protocole_rx.sens = c; c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_STX; } else { #ifdef __DEBUG_C_BUS affichage_debug(__FILE__, __LINE__, "Etat séquenceur C-Bus = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_SENS", TYPE_MESSAGE_DEBUG_WARNING); #endif c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR; // Erreur de CS } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_STX : { c_bus.protocole_rx.cs += (uint16_t) c; // Rechargement du CS if ((flag_controle == TRUE) && (c == ASCII_STX)) { if (c_bus.protocole_rx.sens == 'U') c_bus.up.cde.lg_cde = 0; // On reçoit une commande Up... else c_bus.down.reponse.lg_reponse = 0; // ... ou une réponse à une commande Down c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_DATA; } else { #ifdef __DEBUG_C_BUS affichage_debug(__FILE__, __LINE__, "Etat séquenceur C-Bus = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_STX", TYPE_MESSAGE_DEBUG_WARNING); #endif c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR; // Erreur de CS } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_DATA : { c_bus.protocole_rx.cs += (uint16_t) c; // Rechargement du CS if (flag_controle == FALSE) { if (c_bus.protocole_rx.sens == 'U') c_bus.up.cde.cde[c_bus.up.cde.lg_cde ++] = c; else c_bus.down.reponse.reponse[c_bus.down.reponse.lg_reponse ++] = c; } else { if (c == ASCII_ETX) { c_bus.protocole_rx.cs_recu = 0; c_bus.protocole_rx.ptr_cs_recu = 0; c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_CS; } else { #ifdef __DEBUG_C_BUS affichage_debug(__FILE__, __LINE__, "Etat séquenceur C-Bus = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_DATA", TYPE_MESSAGE_DEBUG_WARNING); #endif c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR; // Erreur de CS } } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_CS : { if (c_bus.protocole_rx.ptr_cs_recu == 0) // Premier caractère de CS reçu, à archviver en LSB : { c_bus.protocole_rx.cs_recu = ((uint16_t) c) & 0x00FF; c_bus.protocole_rx.ptr_cs_recu ++; } else // Second caractère de CS reçu, à archiver en MSB puis comparer avec la valeur calculée : { c_bus.protocole_rx.cs_recu |= (((uint16_t) c) & 0x00FF) << 8; if (c_bus.protocole_rx.cs == c_bus.protocole_rx.cs_recu) { flag_cde_recue = TRUE; // Flag pour remonter qu'une commande a été reçue et sans erreur c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS; // Retour du séquenceur au repos } else { #ifdef __DEBUG_C_BUS affichage_debug(__FILE__, __LINE__, "Etat séquenceur C-Bus = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_CS", TYPE_MESSAGE_DEBUG_WARNING); #endif c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR; // Erreur de CS } } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR : { // !!!! Voir si retour d'un NACK.... !!!! affichage_debug(__FILE__, __LINE__, "Erreur de protocole Rx sur C-Bus", TYPE_MESSAGE_DEBUG_ERREUR); c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS; break; } //------------------------------------------------------------------------------------ default : { sprintf(tmp, "L'état du séquenceur C-Bus.Protocole_Rx est inconnu [%i]. Il est repositionné au REPOS", c_bus.protocole_rx.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); c_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS; break; } } } if (flag_cde_recue) break; } if (flag_cde_recue) return(1); else return(0); } /***************************************************************************************************************************************************************** Fonction : Appelée en tâche de fond, cette fonction vérifie si une réponse est à émettre vers le Compagnon (Up) ou si une commande est prête à être émise dans le buffer de commandes Down. Si oui, elle gère via son séquenceur le formattage de la trame à émettre. Reçoit : Rien Renvoit : Rien *****************************************************************************************************************************************************************/ void gestion_protocole_tx_c_bus(void) { uint16_t i; uint8_t c; uint8_t sens; uint16_t cs; uint8_t tx_buffer[C_BUS_MAX_TAILLE_BUFFER_TX]; uint16_t lg_tx_buffer; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; switch(c_bus.protocole_tx.sequenceur.etat) { //-------------------------------------------------------- case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_INIT : { // NB : le passage à l'état REPOS se fait par le séquenceur C-Bus, indépendemment de la présence de commande à émettre break; } //-------------------------------------------------------- case ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_REPOS : { //.................................. // Recherche d'une trame à émettre : if (c_bus.up.reponse.flag_ready) // Si une réponse est à émettre (en mode Up donc), elle est émise en priorité : { c_bus.up.reponse.flag_ready = FALSE; // RAZ du flag sens = 'U'; // Mise à jour du sens avant préparation de la trame et envoi } else // Sinon, on regarde si une action est à faire en mode Down : { sens = 'D'; // Pour ne pas l'oublier.... if (c_bus.down.flag_attente_reponse) // Cas où une commande Down a déjà été émise et qu'on est en attente de réponse : { if (c_bus.protocole_tx.sequenceur.compteur_time_out == 0) // On vérifie alors si le time-out a expiré : { c_bus.down.flag_attente_reponse = FALSE; // RAZ du flag (qui sera éventuellement remis à 1 si la répétition a lieu) c_bus.protocole_tx.repetitions ++; // Incrémentation du nombre de répétitions if (c_bus.protocole_tx.repetitions > C_BUS_PROTOCOLE_TX_MAX_REPETITIONS) // Si le nombre maxi de répétitions a été dépassé, on laisse tomber la commande { c_bus.protocole_tx.repetitions = 0; // RAZ du compteur pour utilisation ultérieure break; // Sortie du séquenceur } } else break; // Tant qu'on est en attente d'une réponse et que le time-out n'est pas expiré, on ne traite aucun envoi en mode Down } else // Sinon, recherche d'une nouvelle commande à émettre en mode Down : { if (c_bus.down.ptr_debut_buffer_cdes != c_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission... { // ATTENTION : gérer les priorités ...! if (++ c_bus.down.ptr_debut_buffer_cdes == C_BUS_MAX_TAILLE_BUFFER_CDES) c_bus.down.ptr_debut_buffer_cdes = 0; #ifdef __DEBUG_C_BUS c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde[c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].lg_cde] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. affichage_debug_bus(TYPE_BUS_DEBUG_C_BUS, "Tx", (char *) c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_C_BUS); #endif } else break; // Il n'y a pas de nouvelle commande en attente d'émission } } //..................................... // Préparation de la trame d'émission : lg_tx_buffer = 0; tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = ASCII_SOH; cs = ASCII_SOH; // Initialisation du CS calculé tx_buffer[lg_tx_buffer ++] = sens; cs += sens; // Rechargement du CS tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = ASCII_STX; cs += ASCII_STX; // Rechargement du CS if (sens == 'D') { for (i=0; (i < c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < C_BUS_MAX_TAILLE_BUFFER_TX); i++) { c = c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde[i]; if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = c; cs += c; // Rechargement du CS } } else { for (i=0; (i < c_bus.up.reponse.lg_reponse) && (lg_tx_buffer < C_BUS_MAX_TAILLE_BUFFER_TX); i++) { c = c_bus.up.reponse.reponse[i]; if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = c; cs += c; // Rechargement du CS } } tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = ASCII_ETX; cs += ASCII_ETX; // Rechargement du CS c = (uint8_t)(cs & 0x00FF); // LSB du CS if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = c; c = (uint8_t)(cs >> 8); // MSB du CS if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; tx_buffer[lg_tx_buffer ++] = c; //............................. // Envoi au driver d'émission : write(config_serial1.filestream, tx_buffer, lg_tx_buffer); //.......................................................... // Mise à jour des paramètres après l'émission de la trame : if (sens == 'D') // S'il s'agissait d'une commande émise en mode Down, on lance un time-out et on indique qu'on attend la réponse : { c_bus.down.flag_attente_reponse = TRUE; c_bus.protocole_tx.sequenceur.compteur_time_out = C_BUS_TIME_OUT_REPONSE_TX; } else ; // S'il s'agissait d'une réponse à une commande reçue en mode Up, on revient immédiatement au repos (la gestion des répétitions des commandes est de la responsabilité du maitre) break; } //-------------------------------------------------------- default : { sprintf(tmp, "L'état du séquenceur C-Bus.Protocole_Tx est inconnu [%i]. Il est repositionné au REPOS", c_bus.protocole_tx.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); c_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_REPOS; break; } } } /***************************************************************************************************************************************************************** Fonction : Incrémente le pointeur du buffer de commandes en attente d'émission vers C-Bus La fonction gère : - le rebouclage en fin de buffer (buffer circulaire) - le dépassement (quand la fin rattrape le début) en émettant un message de debug et en choissant : . soit d'écraser la commande la plus ancienne si elle est de priorité inférieure (strictement) . soit refuser l'ajout en laissant le pointeur de fin du buffer à sa position et en renvoyant une erreur. Reçoit : La priorité de la commande à ajouter (permet d'arbitrer dans la cas où le buffer est plein) Renvoit : 0 si une position disponible a été trouvée, -1 si aucune position n'est disponible. ******************************************************************************************************************************************************************/ int8_t c_bus_next_write_buffer_cdes(uint8_t priorite) { int8_t retour = 0; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; // Incrémentation : if (++ c_bus.down.ptr_fin_buffer_cdes == C_BUS_MAX_TAILLE_BUFFER_CDES) c_bus.down.ptr_fin_buffer_cdes = 0; // Vérification si buffer plein : if (c_bus.down.ptr_fin_buffer_cdes == c_bus.down.ptr_debut_buffer_cdes) { if (c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes].priorite >= priorite) { // Décrémentation du pointeur puisqu'on ne va pas accepter la nouvelle commande qui n'a pas une priorité suffisante : if (c_bus.down.ptr_fin_buffer_cdes == 0) c_bus.down.ptr_fin_buffer_cdes = (C_BUS_MAX_TAILLE_BUFFER_CDES - 1); else c_bus.down.ptr_fin_buffer_cdes --; // Message de débug : sprintf(tmp, "Buffer de commandes C-Bus plein. La demande de stockage d'une nouvelle commande a été rejetée..."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_BUS_BUFFER_COMMANDES_PLEIN, (uint32_t) 'C', 0, NULL); retour = -1; } else // Dans ce cas, la nouvelle commande est plus prioritaire (strictement) à la commande existante dans le buffer et elle va la remplacer. { // Message de débug : sprintf(tmp, "Buffer de commandes C-Bus plein. La demande de stockage d'une nouvelle commande a écrasée la commande la plus ancienne qui était de priorité inférieure..."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); } } return (retour); }