Table des matières

Allocation d'interruption

Traduction Google …

Aperçu

L'ESP32 a deux cœurs, avec 32 interruptions chacun. Chaque interruption a un certain niveau de priorité, la plupart (mais pas toutes) les interruptions sont connectées au mux d'interruption.

Parce qu'il y a plus de sources d'interruption que d'interruptions, il est parfois logique de partager une interruption dans plusieurs pilotes. L' esp_intr_alloc()abstraction existe pour masquer tous ces détails d'implémentation.

Un pilote peut allouer une interruption pour un certain périphérique en appelant esp_intr_alloc()(ou esp_intr_alloc_intrstatus()). Il peut utiliser les drapeaux passés à cette fonction pour définir le type d'interruption alloué, en spécifiant un niveau particulier ou une méthode de déclenchement. Le code d'allocation d'interruption trouvera alors une interruption applicable, utilisera le mux d'interruption pour le connecter au périphérique et y installera le gestionnaire d'interruption et l'ISR donnés.

Ce code présente deux types d'interruptions différents, gérés différemment : les interruptions partagées et les interruptions non partagées. Les plus simples sont les interruptions non partagées : une interruption distincte est allouée par esp_intr_alloc()appel et cette interruption est uniquement utilisée pour le périphérique qui lui est attaché, avec un seul ISR qui sera appelé. D'autre part, les interruptions partagées peuvent être déclenchées par plusieurs périphériques, plusieurs ISR étant appelés lorsque l'un des périphériques connectés signale une interruption. Ainsi, les ISR destinés aux interruptions partagées doivent vérifier l'état d'interruption du périphérique qu'ils desservent afin de vérifier si une action est requise.

Les interruptions non partagées peuvent être déclenchées par niveau ou front. Les interruptions partagées ne peuvent être que des interruptions de niveau en raison du risque d'interruptions manquées lorsque des interruptions de front sont utilisées.

Par exemple, disons que DevA et DevB partagent une interruption. DevB signale une interruption, donc la ligne INT passe au niveau haut. Le gestionnaire ISR appelle le code pour DevA mais ne fait rien. Ensuite, le gestionnaire ISR appelle le code pour DevB, mais ce faisant, DevA signale une interruption. L'ISR de DevB est terminé, il efface l'état d'interruption pour DevB et quitte le code d'interruption. Maintenant, une interruption pour DevA est toujours en attente, mais parce que la ligne INT n'est jamais descendue, comme DevA l'a maintenue haute même lorsque l'interruption pour DevB a été effacée, l'interruption n'est jamais desservie. Problèmes multicœurs

Les périphériques pouvant générer des interruptions peuvent être divisés en deux types :

La gestion des interruptions diffère légèrement entre ces deux types de périphériques. Interruptions périphériques internes

Chaque cœur de processeur Xtensa possède son propre ensemble de six périphériques internes :

Les sources d'interruption internes sont définies dans esp_intr_alloc.h en tant que ETS_INTERNAL_*_INTR_SOURCE.

Ces périphériques ne peuvent être configurés qu'à partir du cœur auquel ils sont associés. Lors de la génération d'une interruption, l'interruption qu'ils génèrent est câblée à leur cœur associé ; il n'est pas possible, par exemple, qu'un comparateur de temporisation interne d'un cœur génère une interruption sur un autre cœur. C'est pourquoi ces sources ne peuvent être gérées qu'à l'aide d'une tâche exécutée sur ce noyau spécifique. Les sources d'interruption internes sont toujours attribuables en utilisant esp_intr_alloc()normalement, mais elles ne peuvent pas être partagées et auront toujours un niveau d'interruption fixe (à savoir, celui associé au matériel avec le périphérique). Interruptions périphériques externes

Les sources d'interruption restantes proviennent de périphériques externes. Ceux-ci sont définis dans soc/soc.h comme ETS_*_INTR_SOURCE.

Les emplacements d'interruption non internes dans les deux cœurs de processeur sont câblés à un multiplexeur d'interruption, qui peut être utilisé pour acheminer n'importe quelle source d'interruption externe vers l'un de ces emplacements d'interruption.

Des précautions doivent être prises lors de l'appel esp_intr_alloc()à partir d'une tâche qui n'est pas épinglée à un noyau. Lors du changement de tâche, ces tâches peuvent migrer entre les cœurs. Par conséquent, il est impossible de dire sur quel processeur l'interruption est allouée, ce qui rend difficile la libération du handle d'interruption et peut également entraîner des difficultés de débogage. Il est conseillé de l'utiliser xTaskCreatePinnedToCore()avec un argument CoreID spécifique pour créer des tâches qui alloueront des interruptions. Dans le cas de sources d'interruption internes, cela est nécessaire. Gestionnaires d'interruptions IRAM-Safe

L' ESP_INTR_FLAG_IRAMindicateur enregistre un gestionnaire d'interruption qui s'exécute toujours à partir de l'IRAM (et lit toutes ses données à partir de la DRAM), et n'a donc pas besoin d'être désactivé pendant les opérations d'effacement et d'écriture flash.

Ceci est utile pour les interruptions qui nécessitent une latence d'exécution minimale garantie, car les opérations d'écriture et d'effacement flash peuvent être lentes (les effacements peuvent prendre des dizaines ou des centaines de millisecondes).

Il peut également être utile de conserver un gestionnaire d'interruptions dans IRAM s'il est appelé très fréquemment, pour éviter les ratés du cache flash.

Reportez-vous à la documentation de l'API flash SPI pour plus de détails. Plusieurs gestionnaires partageant une source

Plusieurs gestionnaires peuvent être affectés à une même source, étant donné que tous les gestionnaires sont alloués à l'aide du ESP_INTR_FLAG_SHAREDdrapeau. Ils seront tous affectés à l'interruption, à laquelle la source est attachée, et appelés séquentiellement lorsque la source est active. Les gestionnaires peuvent être désactivés et libérés individuellement. La source est attachée à l'interruption (activée), si un ou plusieurs gestionnaires sont activés, sinon détachée. Un gestionnaire ne sera jamais appelé lorsqu'il est désactivé, tandis que sa source peut toujours être déclenchée si l'un de ses gestionnaires est activé.

Les sources attachées à une interruption non partagée ne prennent pas en charge cette fonctionnalité.

Bien que le framework prenne en charge cette fonctionnalité, vous devez l'utiliser avec beaucoup de prudence . Il existe généralement deux manières d'empêcher le déclenchement d'une interruption : désactiver la source ou masquer l'état de l'interruption périphérique . IDF ne gère que l'activation et la désactivation de la source elle-même, laissant les bits d'état et de masque à la charge des utilisateurs. Les bits d'état doivent soit être masqués avant que le gestionnaire qui en est responsable ne soit désactivé, soit être masqués puis correctement traités dans une autre interruption activée . Veuillez noter que le fait de laisser certains bits d'état non gérés sans les masquer, tout en désactivant les gestionnaires correspondants, entraînera le déclenchement indéfini de la ou des interruptions, entraînant ainsi un plantage du système.

Référence API

En tête de fichier

Les fonctions

esp_err_t esp_intr_mark_shared ( int intno , int cpu , bool is_in_iram )

  1. -intno - Le numéro de l'interruption (0-31)
  2. -cpu - CPU sur lequel l'interruption doit être marquée comme partagée (0 ou 1)
  3. -is_in_iram - L'interruption partagée est destinée aux gestionnaires qui résident dans l'IRAM et l'int peut être laissé activé pendant que le cache flash est désactivé.

ESP_ERR_INVALID_ARG si cpu ou intno est invalide ESP_OK sinon

esp_err_t esp_intr_reserve ( int intno , int cpu )

Paramètres

esp_err_t esp_intr_alloc ( int source , int flags , intr_handler_t handler , void * arg , intr_handle_t * ret_handle ) 

Paramètres

esp_err_t esp_intr_alloc_intrstatus ( int source , int flags , uint32_t intrstatusreg , uint32_t intrstatusmask , intr_handler_t handler , void * arg , intr_handle_t * ret_handle ) 

Allouer une interruption avec les paramètres donnés.

esp_err_t esp_intr_free ( intr_handle_t handle ) 

Désactiver et libérer une interruption.

  Lorsque le gestionnaire partage sa source avec d'autres gestionnaires, les bits d'état d'interruption dont il est responsable doivent être gérés correctement avant de le libérer. voir esp_intr_disablepour plus de détails. Veuillez ne pas appeler cette fonction dans esp_ipc_call_blocking.

Paramètres

int esp_intr_get_cpu ( intr_handle_t handle ) 

Obtenir le numéro de CPU auquel une interruption est liée.

Paramètres

      Le numéro de cœur où l'interruption est allouée

int esp_intr_get_intno ( intr_handle_t handle ) 

  Obtenir l'interruption allouée pour un certain handle.
  Paramètres
      handle – Le handle, tel qu'obtenu par esp_intr_alloc ou esp_intr_alloc_intrstatus
  Retour
      Le numéro d'interruption

esp_err_t esp_intr_disable ( intr_handle_t handle ) 

  Désactiver l'interruption associée au handle.
  Noter
      Pour les interruptions locales (sources ESP_INTERNAL_*), cette fonction doit être appelée sur le CPU auquel l'interruption est allouée. D'autres interruptions n'ont pas une telle restriction.
      Lorsque plusieurs gestionnaires partagent une même source d'interruption, les bits d'état d'interruption, qui sont gérés dans le gestionnaire à désactiver, doivent être masqués avant la désactivation ou gérés correctement dans d'autres interruptions activées. L'absence de gestion de l'état d'interruption entraînera des appels d'interruption infinis et finalement un plantage du système.
  Paramètres
      handle – Le handle, tel qu'obtenu par esp_intr_alloc ou esp_intr_alloc_intrstatus
  Retour
      ESP_ERR_INVALID_ARG si la combinaison d'arguments est invalide. ESP_OK sinon

esp_err_t esp_intr_enable ( intr_handle_t handle ) 

  Activez l'interruption associée au handle.
  Noter
  Pour les interruptions locales (sources ESP_INTERNAL_*), cette fonction doit être appelée sur le CPU auquel l'interruption est allouée. D'autres interruptions n'ont pas une telle restriction.
  Paramètres
      handle – Le handle, tel qu'obtenu par esp_intr_alloc ou esp_intr_alloc_intrstatus
  Retour
      ESP_ERR_INVALID_ARG si la combinaison d'arguments est invalide. ESP_OK sinon

esp_err_t esp_intr_set_in_iram ( intr_handle_t handle , bool is_in_iram ) 

  Définissez le statut "in IRAM" du gestionnaire.
  Noter
  Ne fonctionne pas sur les interruptions partagées.
  Paramètres
          handle – Le handle, tel qu'obtenu par esp_intr_alloc ou esp_intr_alloc_intrstatus
          is_in_iram – Indique si le gestionnaire associé à ce handle réside dans IRAM. Les gestionnaires résidant dans IRAM peuvent être appelés lorsque le cache est désactivé.
  Retour
      ESP_ERR_INVALID_ARG si la combinaison d'arguments est invalide. ESP_OK sinon

void esp_intr_noniram_disable ( void ) 

  Désactivez les interruptions qui ne sont pas spécifiquement marquées comme s'exécutant à partir d'IRAM.

void esp_intr_noniram_enable ( void ) 

  Réactivez les interruptions désactivées par esp_intr_noniram_disable.

void esp_intr_enable_source ( int inum ) 

  activer la source d'interruption en fonction de son numéro
  Paramètres
      inum - numéro d'interruption de 0 à 31

void esp_intr_disable_source ( int inum ) 

  désactiver la source d'interruption en fonction de son numéro
  Paramètres
      inum - numéro d'interruption de 0 à 31

statique en ligne int esp_intr_flags_to_level ( drapeaux int ) 

  Obtenez le niveau d'interruption le plus bas à partir des drapeaux.
  Paramètres
      flags - Les mêmes drapeaux qui passent à l' esp_intr_alloc_intrstatusAPI

Macros

ESP_INTR_FLAG_LEVEL1

  Indicateurs d'allocation d'interruption.
  Ces drapeaux peuvent être utilisés pour spécifier les qualités d'interruption dont le code appelant esp_intr_alloc* a besoin. Accepter un vecteur d'interruption de niveau 1 (priorité la plus basse)

ESP_INTR_FLAG_LEVEL2

  Acceptez un vecteur d'interruption de niveau 2.

ESP_INTR_FLAG_LEVEL3

  Acceptez un vecteur d'interruption de niveau 3.

ESP_INTR_FLAG_LEVEL4

  Acceptez un vecteur d'interruption de niveau 4.

ESP_INTR_FLAG_LEVEL5

  Acceptez un vecteur d'interruption de niveau 5.

ESP_INTR_FLAG_LEVEL6

  Acceptez un vecteur d'interruption de niveau 6.

ESP_INTR_FLAG_NMI

  Accepter un vecteur d'interruption de niveau 7 (priorité la plus élevée)

ESP_INTR_FLAG_SHARED

  L'interruption peut être partagée entre les ISR.

ESP_INTR_FLAG_EDGE

  Interruption déclenchée par le front.

ESP_INTR_FLAG_IRAM

  ISR peut être appelé si le cache est désactivé.

ESP_INTR_FLAG_INTRDISABLED

  Retour avec cette interruption désactivée.

ESP_INTR_FLAG_LOWMED

  Interruptions à priorité faible et moyenne. Ceux-ci peuvent être traités en C.

ESP_INTR_FLAG_HIGH

  Interruptions de haut niveau. Doit être manipulé lors du montage.

ESP_INTR_FLAG_LEVELMASK

  Masque pour tous les drapeaux de niveau.

ETS_INTERNAL_TIMER0_INTR_SOURCE

  Source d'interruption du temporisateur de plate-forme 0.
  Les fonctions esp_intr_alloc* peuvent allouer un int pour toutes les sources d'interruption ETS_*_INTR_SOURCE qui sont acheminées via le mux d'interruption. Outre ces sources, chaque cœur possède également des sources internes qui ne passent pas par le mux d'interruption. Pour allouer une interruption à ces sources, passez ces pseudo-sources aux fonctions.

ETS_INTERNAL_TIMER1_INTR_SOURCE

  Source d'interruption de la minuterie 1 de la plate-forme.

ETS_INTERNAL_TIMER2_INTR_SOURCE

  Source d'interruption de la minuterie 2 de la plate-forme.

ETS_INTERNAL_SW0_INTR_SOURCE

  Logiciel int source 1.

ETS_INTERNAL_SW1_INTR_SOURCE

  Logiciel int source 2.

ETS_INTERNAL_PROFILING_INTR_SOURCE

  Int source pour le profilage.

ETS_INTERNAL_UNUSED_INTR_SOURCE

  L'interruption n'est affectée à aucune source.

ETS_INTERNAL_INTR_SOURCE_OFF

  Fournit à SystemView des ID IRQ positifs, sinon les événements du planificateur ne s'affichent pas correctement

ESP_INTR_ENABLE ( numéro )

  Activer l'interruption par numéro d'interruption

ESP_INTR_DISABLE ( nombre )

  Désactiver l'interruption par numéro d'interruption

Définitions des types

typedef void ( * intr_handler_t ) ( void * arg ) 

  Prototype de fonction pour la fonction de gestionnaire d'interruption

typedef struct intr_handle_data_t intr_handle_data_t 

  Structure de données associée au gestionnaire d'interruptions

typedef intr_handle_data_t * intr_handle_t 

  Handle vers un gestionnaire d'interruption