Files

462 lines
20 KiB
C

//**********************************************************************************************************************************************************************************
// Fichier G_BUS.C
//
// Contient toutes les fonctions liées à la gestion du G_Bus (liaison avec le récepteur GNSS ZOE-M8Q)
//**********************************************************************************************************************************************************************************/
#define __DEBUG_G_BUS // Commenter cette ligne : aucun message de debug de G_bus
// Includes :
#include "main.h"
// Prototypes :
int8_t init_g_bus(void);
void gestion_g_bus(void);
int8_t gestion_protocole_rx_g_bus(void);
void gestion_protocole_tx_g_bus(void);
int8_t g_bus_next_write_buffer_cdes(uint8_t priorite);
// Variables globales :
struct Struct_g_bus g_bus;
/**********************************************************************************************************************************************************************************
Fonction INIT_G_BUS
Fonction d'initialisation du C-Bus
**********************************************************************************************************************************************************************************/
int8_t init_g_bus(void)
{
if ((systeme.generation_shipbase == GENERATION_SHIPBASE_V2) || (systeme.generation_shipbase == GENERATION_SHIPBASE_V3))
{
//------------------------------------------------------------------------------------------------------
// Init des variables générales :
g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_INIT;
g_bus.sequenceur.compteur = G_BUS_TEMPO_INIT;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_INIT;
g_bus.protocole_rx.sequenceur.compteur = G_BUS_PROTOCOLE_RX_TEMPO_INIT;
g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_INIT;
g_bus.protocole_tx.sequenceur.compteur = G_BUS_PROTOCOLE_TX_TEMPO_INIT;
// Affichage du message de débug :
affichage_debug(__FILE__, __LINE__, "Initialisation du G-BUS\t\t: OK", TYPE_MESSAGE_DEBUG_OK);
}
else
{
// Affichage du message de débug :
affichage_debug(__FILE__, __LINE__, "Initialisation du G-BUS\t\t: Devalidé car non valide sur cette version de ShipBase", TYPE_MESSAGE_DEBUG_WARNING);
}
return(0);
}
/**********************************************************************************************************************************************************************************
Fonction GESTION_G_BUS
Fonction de plus haut niveau de gestion du C-Bus
**********************************************************************************************************************************************************************************/
void gestion_g_bus(void)
{
int8_t ret;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
if (systeme.generation_shipbase != GENERATION_SHIPBASE_V2) return;
switch(g_bus.sequenceur.etat)
{
//--------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_INIT :
{
if (g_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) :
g_bus.down.ptr_debut_buffer_cdes = 0;
g_bus.down.ptr_fin_buffer_cdes = 0;
//.........................................................
// Mise à jour des séquenceurs pour démarrage des envois :
g_bus.protocole_rx.sequenceur.compteur = 0;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS;
g_bus.protocole_tx.sequenceur.compteur = 0;
g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS;
g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_REPOS;
}
break;
}
//--------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_REPOS :
{
//=================================
// Gestion du G-Bus en émission :
gestion_protocole_tx_g_bus(); // Vérifie si une commande (Down) est en attente. Si oui, elle la traite et l'émet.
//=================================
// Gestion du G-Bus en réception :
ret = gestion_protocole_rx_g_bus();
if (ret != 0) // Vérifie si une réponse (à une commande Down) a été reçue.
{
#ifdef __DEBUG_G_BUS
// sprintf(tmp, "Retour GNSS : Class=%02Xh, ID=%02Xh, Length=%i", g_bus.down.reponse.classe, g_bus.down.reponse.id, g_bus.down.reponse.lg_reponse);
// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT);
#endif
g_bus_down_analyse_reponse_commande();
}
break;
}
//--------------------------------------------------------
default :
{
sprintf(tmp, "L'état du séquenceur M-bus est inconnu [%i]. Il est repositionné au REPOS", g_bus.sequenceur.etat);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_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 en provenance du GNSS.
Le protocole utilisé est le µBX uniquement (il est assez facile d'utiliser le protocole NMEA mais il est dévalidé au démarrage pour éviter d'avoir
les 2 simultanés et le µBX est bien plus complet...)
Reçoit : Rien
Renvoit : 0 si RAS, 1 si une réponse complète a été reçue
******************************************************************************************************************************************************************/
int8_t gestion_protocole_rx_g_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(g_bus.protocole_rx.sequenceur.etat)
{
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_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_G_BUS_PROTOCOLE_RX_REPOS :
{
if (c == 0xB5) // Attente de réception du 'µ' de début de trame
{
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_SYNC_2;
}
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_SYNC_2 :
{
if (c == 0x62) // Attente de réception du 'b' de début de trame
{
g_bus.down.reponse.ck_a_attendu = 0; // Init avant de recevoir le premier octet à intégrer au calcul...
g_bus.down.reponse.ck_b_attendu = 0;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CLASS;
}
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CLASS :
{
g_bus.down.reponse.ck_a_attendu += c; // Calcul ...
g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS
g_bus.down.reponse.classe = c;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_ID;
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_ID :
{
g_bus.down.reponse.ck_a_attendu += c; // Calcul ...
g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS
g_bus.down.reponse.id = c;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_1;
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_1 :
{
g_bus.down.reponse.ck_a_attendu += c; // Calcul ...
g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS
g_bus.down.reponse.lg_reponse_attendue = c;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_2;
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_2 :
{
g_bus.down.reponse.ck_a_attendu += c; // Calcul ...
g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS
g_bus.down.reponse.lg_reponse_attendue |= ((uint16_t) c) << 8;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_DATA;
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_DATA :
{
g_bus.down.reponse.ck_a_attendu += c; // Calcul ...
g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS
g_bus.down.reponse.reponse[g_bus.down.reponse.lg_reponse ++] = c;
if (g_bus.down.reponse.lg_reponse >= g_bus.down.reponse.lg_reponse_attendue)
{
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_A;
}
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_A :
{
g_bus.down.reponse.ck_a_recu = c;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_B;
break;
}
//------------------------------------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_B :
{
g_bus.down.reponse.ck_b_recu = c;
if ((g_bus.down.reponse.ck_a_attendu == g_bus.down.reponse.ck_a_recu) && (g_bus.down.reponse.ck_b_attendu == g_bus.down.reponse.ck_b_recu))
{
reponse_recue = 1;
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS;
}
else
{
#ifdef __DEBUG_G_BUS
sprintf(tmp, "Le CS reçu [%02Xh|%02Xh] ne correspond pas au CS calculé [%02Xh|%02Xh]. Le séquenceur de réception est repositionné au REPOS", g_bus.down.reponse.ck_a_recu, g_bus.down.reponse.ck_b_recu, g_bus.down.reponse.ck_a_attendu, g_bus.down.reponse.ck_b_attendu);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
#endif
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS;
}
break;
}
//------------------------------------------------------------------------------------
default :
{
sprintf(tmp, "L'état du séquenceur G-Bus.Protocole_Rx est inconnu [%i]. Il est repositionné au REPOS", g_bus.protocole_rx.sequenceur.etat);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_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 au ormat µBX
Reçoit : Rien
Renvoit : Rien
*****************************************************************************************************************************************************************/
void gestion_protocole_tx_g_bus(void)
{
uint16_t i;
uint8_t c;
uint8_t ck_a, ck_b;
uint8_t tx_buffer[G_BUS_MAX_TAILLE_BUFFER_TX];
uint16_t lg_tx_buffer;
char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG];
switch(g_bus.protocole_tx.sequenceur.etat)
{
//--------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_INIT :
{
// NB : le passage à l'état REPOS se fait par le séquenceur G-Bus, indépendemment de la présence de commande à émettre
break;
}
//--------------------------------------------------------
case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS :
{
//..................................
// Recherche d'une trame à émettre :
if (g_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 (g_bus.protocole_tx.sequenceur.compteur_time_out == 0) // On vérifie alors si le time-out a expiré :
{
g_bus.down.flag_attente_reponse = FALSE; // RAZ du flag (qui sera éventuellement remis à 1 si la répétition a lieu)
g_bus.protocole_tx.repetitions ++; // Incrémentation du nombre de répétitions
if (g_bus.protocole_tx.repetitions > g_bus.down.buffer_cdes[g_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....
g_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 (g_bus.down.ptr_debut_buffer_cdes != g_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission...
{
// ATTENTION : gérer les priorités ...!
g_bus.down.reponse.etat = G_BUS_REPONSE_ETAT_REPOS; // On prépare le traitement de la réponse à venir.
if (++ g_bus.down.ptr_debut_buffer_cdes == G_BUS_MAX_TAILLE_BUFFER_CDES) g_bus.down.ptr_debut_buffer_cdes = 0;
#ifdef __DEBUG_G_BUS
// g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde[g_bus.down.buffer_cdes[g_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_G_BUS, "Tx", (char *) g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_G_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;
ck_a = 0;
ck_b = 0;
// Ajout des caractères 'µb' en tête :
tx_buffer[lg_tx_buffer ++] = 0xB5; // 'µ'
tx_buffer[lg_tx_buffer ++] = 0x62; // 'b'
// Ajout de la commande avec calcul du CS :
for (i=0; (i < g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < G_BUS_MAX_TAILLE_BUFFER_TX); i++)
{
c = g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde[i]; // Simplifie la lecture et les accès...
ck_a += c; // Calcul du premier élément de CS
ck_b += ck_a; // Calcul du second élément de CS
tx_buffer[lg_tx_buffer ++] = c; // Ajout du caractère dans le buffer d'émission
}
// Ajout du CS en fin de trame :
tx_buffer[lg_tx_buffer ++] = ck_a;
tx_buffer[lg_tx_buffer ++] = ck_b;
//.............................
// 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 :
g_bus.down.flag_attente_reponse = TRUE;
g_bus.protocole_tx.sequenceur.compteur_time_out = g_bus.down.buffer_cdes[g_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", g_bus.protocole_tx.sequenceur.etat);
affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR);
g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS;
break;
}
}
}
/*****************************************************************************************************************************************************************
Fonction : Incrémente le pointeur du buffer de commandes en attente d'émission vers G-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 g_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 (++ g_bus.down.ptr_fin_buffer_cdes == G_BUS_MAX_TAILLE_BUFFER_CDES) g_bus.down.ptr_fin_buffer_cdes = 0;
// Vérification si buffer plein :
if (g_bus.down.ptr_fin_buffer_cdes == g_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 (g_bus.down.ptr_fin_buffer_cdes == 0) g_bus.down.ptr_fin_buffer_cdes = (G_BUS_MAX_TAILLE_BUFFER_CDES - 1);
else g_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);
}