//********************************************************************************************************************************************************************************** // Fichier M_BUS.C // // Contient toutes les fonctions liées à la gestion du M_Bus (liaison avec le Modem SIM7070G) //**********************************************************************************************************************************************************************************/ #define __DEBUG_M_BUS 0 // Passer cette ligne à 0: aucun message de debug de M_bus, 1: les messages de debug de M_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_m_bus(void); void gestion_m_bus(void); int8_t gestion_protocole_rx_m_bus(void); void gestion_protocole_tx_m_bus(void); int8_t m_bus_next_write_buffer_cdes(uint8_t priorite); // Variables globales : struct Struct_m_bus m_bus; /********************************************************************************************************************************************************************************** Fonction INIT_M_BUS Fonction d'initialisation du C-Bus **********************************************************************************************************************************************************************************/ int8_t init_m_bus(void) { if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) { //------------------------------------------------------------------------------------------------------ // Init des variables générales : m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_INIT; m_bus.sequenceur.compteur = M_BUS_TEMPO_INIT; m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_INIT; m_bus.protocole_rx.sequenceur.compteur = M_BUS_PROTOCOLE_RX_TEMPO_INIT; m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_INIT; m_bus.protocole_tx.sequenceur.compteur = M_BUS_PROTOCOLE_TX_TEMPO_INIT; // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation du M-BUS\t\t: OK", TYPE_MESSAGE_DEBUG_OK); } else { // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation du M-BUS\t\t: Devalidé car non valide sur cette version de ShipBase", TYPE_MESSAGE_DEBUG_WARNING); } return(0); } /********************************************************************************************************************************************************************************** Fonction GESTION_M_BUS Fonction de plus haut niveau de gestion du C-Bus **********************************************************************************************************************************************************************************/ void gestion_m_bus(void) { int8_t ret; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; if (systeme.generation_shipbase != GENERATION_SHIPBASE_V1) return; switch(m_bus.sequenceur.etat) { //-------------------------------------------------------- case ETAT_SEQUENCEUR_M_BUS_INIT : { if (m_bus.sequenceur.compteur == 0) { //......................................................... // Vidage du buffer de réception de tous les caractères reçus pendant la phase d'init : serial0.ptr_debut_buffer_rx = 0; serial0.ptr_fin_buffer_rx = 0; //......................................................... // Vidage du buffer de commandes (utile dans le cas d'un reset du modem pour overflow) : m_bus.down.ptr_debut_buffer_cdes = 0; m_bus.down.ptr_fin_buffer_cdes = 0; //......................................................... // Mise à jour des séquenceurs pour démarrage des envois : m_bus.protocole_rx.sequenceur.compteur = 0; m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; m_bus.protocole_tx.sequenceur.compteur = 0; m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_REPOS; m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_REPOS; } break; } //-------------------------------------------------------- case ETAT_SEQUENCEUR_M_BUS_REPOS : { if (modem.etat == ON) // Si le modem n'est pas ON, il est inutile d'essayer de communiquer avec lui. { //================================= // Gestion du M-Bus en émission : gestion_protocole_tx_m_bus(); // Vérifie si une commande (Down) est en attente. Si oui, elle la traite et l'émet. //================================= // Gestion du M-Bus en réception : ret = gestion_protocole_rx_m_bus(); if (ret != 0) // Vérifie si une réponse (à une commande Down) a été reçue. { #ifdef __DEBUG_M_BUS m_bus.down.reponse.reponse[m_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. if (ret == 1) affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Rx", (char *) m_bus.down.reponse.reponse, __DEBUG_M_BUS); // Cas d'une réponse normale else { sprintf(tmp, "[%li data bytes]", strlen((const char *)m_bus.down.reponse.reponse)); affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Rx", tmp, __DEBUG_M_BUS); // Cas d'une réponse en mode binaire } #endif m_bus_down_analyse_reponse_commande(); // Traitement de la réponse à la commande... } } break; } //-------------------------------------------------------- default : { sprintf(tmp, "L'état du séquenceur M-bus est inconnu [%i]. Il est repositionné au REPOS", m_bus.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_REPOS; break; } } } /***************************************************************************************************************************************************************** Fonction : Appelée en tâche de fond, cette fonction lit le buffer de réception de l'UART 0 et traite les trames reçues (réponse du modem à une commande du CM3+) Reçoit : Rien Renvoit : 0 si RAS, 1 si une réponse complète a été reçue, 2 si réponse complète en mode binaire a été reçue ******************************************************************************************************************************************************************/ int8_t gestion_protocole_rx_m_bus(void) { uint8_t c; int8_t reponse_recue; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; reponse_recue = 0; // Traitement de chaque caractère reçu : while (serial0.ptr_debut_buffer_rx != serial0.ptr_fin_buffer_rx) { // Calcul de la prochaine position à lire (avec bouclage en fin de buffer tournant) : if (++ serial0.ptr_debut_buffer_rx == MAX_TAILLE_RX_BUFFER_UART) serial0.ptr_debut_buffer_rx = 0; // Lecture du caractère dans le buffer : c = serial0.buffer_rx[serial0.ptr_debut_buffer_rx]; // Lecture du caractère à traiter // Traitement du caractère reçu : switch(m_bus.protocole_rx.sequenceur.etat) { //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_M_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 M-Bus, indépendemment de la réception ou non de caractères. break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS : { if (c == ASCII_CR) // Attente de réception du CR de début de trame { m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF; } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF : { if (c == ASCII_CR) break; // Cas d'un CR qui suit un CR. On reste en attente d'un LF à venir... if (c == ASCII_LF) { if (m_bus.down.reponse.lg_reponse != 0) reponse_recue = 1; // Si la longueur actuelle de réponse est nulle, on ne fait rien (on va juste revenir en mode data pour le prochain caractère) m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_DATA; // Dans tous les cas, après un CR/LF, on passe en mode Data car il arrive qu'un CR/LF soit juste un séparateur entre plusieurs blocs (il y a donc une fin mais pas de "début" suivant) } else { m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_DATA : { if (c != ASCII_CR) m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse ++] = c; else { m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF; } break; } //------------------------------------------------------------------------------------ case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE: { m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse ++] = c; if (m_bus.down.reponse.lg_reponse == m_bus.down.reponse.lg_reponse_attendue) { reponse_recue = 2; m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; } break; } //------------------------------------------------------------------------------------ default : { sprintf(tmp, "L'état du séquenceur M-Bus.Protocole_Rx est inconnu [%i]. Il est repositionné au REPOS", m_bus.protocole_rx.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; break; } } if (reponse_recue != 0) break; } return(reponse_recue); } /***************************************************************************************************************************************************************** Fonction : Appelée en tâche de fond, cette fonction vérifie 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_m_bus(void) { uint16_t i; uint8_t tx_buffer[M_BUS_MAX_TAILLE_BUFFER_TX]; uint16_t lg_tx_buffer; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; switch(m_bus.protocole_tx.sequenceur.etat) { //-------------------------------------------------------- case ETAT_SEQUENCEUR_M_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_M_BUS_PROTOCOLE_TX_REPOS : { //.................................. // Recherche d'une trame à émettre : if (m_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 (m_bus.protocole_tx.sequenceur.compteur_time_out == 0) // On vérifie alors si le time-out a expiré : { m_bus.down.flag_attente_reponse = FALSE; // RAZ du flag (qui sera éventuellement remis à 1 si la répétition a lieu) m_bus.protocole_tx.repetitions ++; // Incrémentation du nombre de répétitions if (m_bus.protocole_tx.repetitions > m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].max_repetitions) // Si le nombre maxi de répétitions a été dépassé, on laisse tomber la commande { // !!! Il serait peut-être intéressant d'informer la couche supérieure que la commande est abandonnée sans avoir obtenu de réponse malgré N répétitions... // ==>>> Si oui, c'est ici qu'il faut le mettre.... m_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 (m_bus.down.ptr_debut_buffer_cdes != m_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission... { // ATTENTION : gérer les priorités ...! m_bus.down.reponse.etat = M_BUS_REPONSE_ETAT_REPOS; // On prépare le traitement de la réponse à venir. if (++ m_bus.down.ptr_debut_buffer_cdes == M_BUS_MAX_TAILLE_BUFFER_CDES) m_bus.down.ptr_debut_buffer_cdes = 0; #ifdef __DEBUG_M_BUS m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde[m_bus.down.buffer_cdes[m_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. if (m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].mode_protocole != M_BUS_PROTOCOLE_DATA_BINAIRE) affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Tx", (char *) m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_M_BUS); else { sprintf(tmp, "[%i data bytes]", m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].lg_cde); affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Tx", tmp, __DEBUG_M_BUS); // Cas d'une réponse en mode binaire } #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; for (i=0; (i < m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < M_BUS_MAX_TAILLE_BUFFER_TX); i++) { tx_buffer[lg_tx_buffer ++] = m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde[i]; } if (m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].mode_protocole != M_BUS_PROTOCOLE_DATA_BINAIRE) { tx_buffer[lg_tx_buffer ++] = ASCII_CR; } //............................. // Envoi au driver d'émission : write(config_serial0.filestream, tx_buffer, lg_tx_buffer); //.......................................................... // Mise à jour des paramètres après l'émission de la trame : m_bus.down.flag_attente_reponse = TRUE; m_bus.protocole_tx.sequenceur.compteur_time_out = m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].time_out; break; } //-------------------------------------------------------- default : { sprintf(tmp, "L'état du séquenceur M-Bus.Protocole_Tx est inconnu [%i]. Il est repositionné au REPOS", m_bus.protocole_tx.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_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 m_bus_next_write_buffer_cdes(uint8_t priorite) { char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; // !!! La gestion des priorités n'est pas traitée ! A vérifier mais si on écrase le début, celui-ci est probablement en cours d'utilisation... // Donc ATTENTION !!!! // Incrémentation : if (++ m_bus.down.ptr_fin_buffer_cdes == M_BUS_MAX_TAILLE_BUFFER_CDES) m_bus.down.ptr_fin_buffer_cdes = 0; // Vérification si buffer plein : if (m_bus.down.ptr_fin_buffer_cdes == m_bus.down.ptr_debut_buffer_cdes) { // Décrémentation du pointeur puisqu'on ne va pas accepter la nouvelle commande qui n'a pas une priorité suffisante : if (m_bus.down.ptr_fin_buffer_cdes == 0) m_bus.down.ptr_fin_buffer_cdes = (M_BUS_MAX_TAILLE_BUFFER_CDES - 1); else m_bus.down.ptr_fin_buffer_cdes --; // Message de débug : sprintf(tmp, "Buffer de commandes M-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) 'M', 0, NULL); return(-1); } else return (0); }