Raison
Eh bien, la raison est simple: le blocage est simple et à première vue, il semble fonctionner. Malheur à vous si vous voulez faire autre chose en attendant.
Donc, sans entrer dans beaucoup de détails, comme je ne connais pas le STM32, vous pouvez généralement vous sortir de ce problème de deux manières, en fonction de vos besoins.
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
/ * attendre la confirmation * /
while (! I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT));
Conversion en mode non bloquant
Soit vous implémentez un timeout pour toutes vos boucles while
. Cela signifie:
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
/ * attendre la confirmation * /
static unsigned long start = now ();
while (! I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT) && now () - démarrer < TIMEOUT);
if (now () - start > = TIMEOUT) {return ERROR_TIMEOUT; }
(Il s'agit bien sûr d'un pseudocode, vous voyez l'idée. N'hésitez pas à optimiser ou à ajuster vos préférences de codage si nécessaire.)
Vous devez vérifier les codes de retour lorsque vous voyagez dans la pile et choisissez le bon endroit où vous effectuez votre gestion du délai d'attente. Notez qu'il est utile de définir également une variable globale i2c_timeout_occured = 1
ou autre afin que vous puissiez rapidement abandonner d'autres appels I2C sans avoir à passer trop d'arguments.
Ce changement est plutôt indolore, espérons-le.
À l'envers
Si, à la place, vous avez vraiment besoin de faire un autre traitement pendant que vous attendez cet événement, alors vous devez vous débarrasser complètement de la boucle while interne. Vous faites cela comme ceci:
void main_loop () {
do_i2c_stuff (); // ne doit jamais bloquer
do_other_stuff ();
...
}
// Ne doit jamais bloquer. En supposant que toutes les fonctions I2C _... ne bloquent pas non plus.
void do_i2c_stuff () {
état int statique = ...;
if (état == 0) {
I2C_GenerateSTART (HMC5883L_I2C, ENABLE);
état = 1;
} else if (état == 1) {
if (I2C_CheckEvent (HMC5883L_I2C, I2C_EVENT_MASTER_MODE_SELECT))
état = 2;
} autre ...
}
Ce n'est pas forcément très compliqué, selon votre autre logique. Vous pouvez faire beaucoup avec une mise en retrait / des commentaires / un formatage appropriés pour ne pas perdre de vue ce que vous programmez.
La façon dont cela fonctionne est de créer une machine à états . Si vous regardez votre code d'origine, il ressemble à ceci:
code non bloquant
while (! nonblocking_function_call1 ());
code non bloquant
while (! nonblocking_function_call2 ());
Pour transformer cela en une machine à états, vous avez un état pour chacun:
état 0: code non bloquant
état 1: nonblocking_function_call1 ()
état 2: code non bloquant
état 3: nonblocking_function_call2 ()
Ensuite, comme indiqué dans l'exemple ci-dessus, vous appelez ce code dans une boucle sans fin (votre boucle principale), et n'exécutez que le code correspondant à votre état actuel (suivi dans une variable statique state
) . Le code non bloquant est trivial, il est inchangé par rapport à avant. Le code de blocage est remplacé par une variante qui ne bloque pas, mais ne met à jour que l ' état
lorsqu'il est terminé.
Notez que les boucles individuelles while
ont disparu; vous les avez remplacées par le fait que vous avez de toute façon votre boucle principale de niveau supérieur, qui appelle votre machine d'état à plusieurs reprises.
Cette solution peut être pénible lorsque vous avez beaucoup de code hérité car vous ne pouvez pas simplement adapter la fonction de blocage la plus interne, comme dans la première solution. Il brille lorsque vous commencez à écrire du nouveau code et que vous procédez de cette façon depuis le début. Combinez-le avec beaucoup d'autres choses qu'un µC pourrait faire (par exemple, attendre que vous appuyiez sur un bouton, etc.); si vous vous habituez à faire de cette façon tout le temps, vous obtenez gratuitement des capacités multitâches arbitraires.
Interruptions
Franchement, pour quelque chose comme ça (c'est-à-dire, simplement me débarrasser du blocage infini), je ferais de mon mieux pour rester à l'écart des interruptions à moins que vous n'ayez des besoins de timing extrêmes.Les interruptions rendent les choses compliquées, rapides, vous n'en avez peut-être pas assez de toute façon, et cela se résumera de toute façon à un code assez similaire, car vous ne voulez pas faire beaucoup plus à l'intérieur de l'interruption, sauf définir quelques indicateurs.