Interface logicielle (API) - Cœur - System Tick
Présentation
#include "core/systick.h"
Les micro-contrôleurs ARM Cortex-M* (au moins ceux de la famille LPC de NXP) disposent d'un timer "système" destiné à produire un "tick" utilisable pour réaliser l'ordonnancement des tâches d'un d'un système multi-tâches.
Ce timer produit une interruption "interne" (une exception) lorsqu'il "expire" et est automatiquement rechargé.
Dans l'API présentée ici, ce timer est utilisé pour compter le temps avec une précision de un tick et permet de faire appel à des fonctions de rappel (callback) à l'expiration d'un délai donné (alarmes).
Par défaut, les exemples utilisent une période de 1ms (une milliseconde) pour le tick système, ce qui simplifie les calculs de temps, mais ceci n'est pas une obligation.
L'API "systick" utilise deux compteurs internes : global_wrapping_system_ticks et global_wrapping_system_clock_cycles.
- global_wrapping_system_ticks : Compteur de ticks, depuis le démarrage du timer (ou le dernier reset). Avec un tick toutes les millisecondes ce compteur boucle après 50 jours.
- global_wrapping_system_clock_cycles : Compteur de coups d'horloge. Ce compteur est utilisé pour indiquer le nombre de coups d'horloge depuis la dernière fois que ce compteur a bouclé. Il est utilisé pour mesurer des durées précises et donner une image du nombre de coups d'horloge écoulés entre deux lectures du compteur.
Configuration
void systick_timer_on(uint32_t ms); void systick_timer_off(void);
La configuration du timer système est propre à l'utilisation faite par l'API.
La seule configuration possible pour l'utilisateur est la durée entre chaque tick, en millisecondes, passée à la fonction systick_timer_on() par le paramètre "ms".
Attention, toutes les fonctions systick_*() travaillent sur un nombre de ticks et non pas sur un nombre de millisecondes.
Seules les fonctions *sleep() travaillent sur des valeurs de temps (millisecondes, microsecondes).
Initialisation / contrôle
void systick_start(void); void systick_reset(void); void systick_stop(void); uint32_t is_systick_running(void); uint32_t systick_get_timer_reload_val(void); uint32_t systick_get_tick_ms_period(void);
Une fois le timer système configuré, il est nécessaire de le démarrer en faisant appel à la fonction systick_start().
Il est aussi possible de stopper le timer système en faisant appel à la fonction systick_stop(). Cette fonction provoque aussi la remise à zéro des compteurs internes (compteur de ticks et compteur de coups d'horloge).
La fonction systick_reset() remet à zéro les compteurs internes (compteur de ticks et compteur de coups d'horloge) et ré-initialise le compteur du timer système sans le stopper.
Les fonctions is_systick_running(), systick_get_timer_reload_val() et systick_get_tick_ms_period() servent à récupérer les valeurs de configuration et d'état du timer système.
La fonction is_systick_running() renvoie 1 si le timer système est actif, 0 sinon.
La fonction systick_get_timer_reload_val() permet de récupérer la valeur calculée par la fonction d'initialisation et utilisée comme valeur de rechargement du timer système. Il s'agit du nombre de coups d'horloge entre deux ticks successifs.
La fonction systick_get_tick_ms_period() renvoie le nombre de millisecondes par tick (valeur utilisée lors de la configuration du timer système).
Utilisation
Le driver systick fournit trois sous-ensembles de fonctionnalités différentes liées au timer système : les fonctions d'attente active (sleep), un système d'alarmes périodiques, et des compteurs de temps.
msleep(), usleep() and usleep_short()
void msleep(uint32_t ms) void usleep(uint32_t us) void usleep_short(uint32_t us);
Les fonctions d'attente active sont des fonctions qui ne font "rien" tant que le délai demandé n'est pas écoulée.
Ces fonctions ne garantissent pas un délai d'attente exact.
La fonction msleep() prends en paramètre un nombre de millisecondes "ms", et garanti un délai de "ms - (1 * tick_ms)" à "ms + 1" millisecondes (avec "tick_ms" le nombre de millisecondes par tick).
Les fonctions usleep() et usleep_short() prennent en paramètre un nombre de microsecondes "us" et garantissent un delai de "us" à "us + 50" microsecondes pour des temps d'attente inférieurs à 1000 micro-secondes, et un délais de "us" à "us + 1 tick" au dela de 1000 micro-secondes.
La différence de temps d'attente est d'autant plus grande que le délai est court et que la fréquence du micro-contrôleur est faible.
Les délais d'attente très courts sont très imprécis, et la fonction usleep() ne permet pas de délai inférieur à 10 microsecondes.
La fonction usleep_short() est similaire à la fonction usleep() mais ne fait aucun test préalable et ne doit pas être utilisée pour des temps d'attente supérieurs à 1000 micro-secondes. Il est impératif que le timer système soit configuré et démarré pour utiliser cette fonction.
Pour obtenir des délais d'attente très précis, il est nécessaire d'utiliser les timers.
Attention, compte tenu du temps nécessaires à l'exécution des instructions de configuration des timers, l'obtention de délais de l'ordre de quelques microsecondes n'est pas possible non plus à l'aide des timers.
Enregistrement d'alarmes périodiques
int add_systick_callback(void (*callback) (uint32_t), uint16_t period); int remove_systick_callback(void (*callback) (uint32_t)); #define MAX_SYSTICK_CALLBACKS 4
Le driver systick permet d'enregistrer plusieurs alarmes (4 par défaut pour limiter la mémoire et le temps de traitement, mais il est possible de changer la valeur "MAX_SYSTICK_CALLBACKS" définie dans le fichier d'entêtes "include/core/systick.h")
Une alarme est un mécanisme qui consiste à appeler une fonction spécifique à un instant défini. Cette fonction est appelée "callback", et est enregistrées par le biais du premier paramètre de la fonction add_systick_callback(). Le deuxième paramètre de cette fonction définit la période entre deux appels de l'alarme, en nombre de ticks.
La fonction add_systick_callback() renvoie le numéro sous lequel est enregistré le callback si l'enregistrement se passe bien, ou une valeur négative si l'enregistrement est impossible.
La fonction enregistrée pour traiter l'alarme reçoit en paramètre la valeur du compteur interne de nombre de ticks.
Il est possible d'obtenir une exécution unique de l'alarme en utilisant la fonction remove_systick_callback() à l'intérieur du callback de gestion de l'alarme.
La fonction remove_systick_callback() prend en paramètre l'adresse du callback (utilisée pour enregistrer le callback) et renvoie 0 si le callback a bien été supprimé, ou une valeur négative indiquant l'erreur sinon.
Note : les alarmes sont enregistrées pour une exécution périodique, avec un intervalle en nombre de ticks, et non pas à une date donnée.
L'exécution d'une alarme à une date donnée (en nombre de ticks) est possible, en calculant la période à utiliser en paramètre lors de l'enregistrement du callback.
il est aussi possible d'utiliser une RTC pour obtenir une alarme à une date donnée, si le micro-contrôleur ou la carte électronique en fournit une.
Valeurs des compteurs
uint32_t systick_get_timer_val(void); uint32_t systick_get_tick_count(void); uint32_t systick_get_clock_cycles(void);
L'interface du driver systick fournit trois fonctions pour accéder aux différents compteurs internes :
- systick_get_timer_val() : accès à la valeur du compteur interne du timer.
- systick_get_tick_count() accès à la variable interne servant de compteur de nombre de ticks.
- systick_get_clock_cycles() accès à la variable interne servant de compteur de coups d'horloge.
Notes :
- Les valeurs du compteur interne du timer et du compteur de coups d'horloge évoluent à la fréquence de l'horloge principale du micro-contrôleur. Leur valeur est donc "une image de la valeur réelle au moment de l'appel de la fonction".
- La valeur du compteur interne du timer décroît de systick_get_timer_reload_val() à 0. Elle repart de systick_get_timer_reload_val() à chaque tick.
- La valeur du compteur interne de coups d'horloge croit de 0 à (2^32 - 1) et boucle régulièrement (toutes les 89.48 secondes avec une horloge à 48MHz)
- La valeur du compteur de ticks croit de 0 à (2^32 - 1) et boucle tous les 49.71 jours (1193 heures) avec un tick toutes les 1 millisecondes.
Voir la bibliothèque "time.h" pour une gestion du temps en secondes, et un débordement du compteur de secondes tous les 49710 jours (136 ans).
Utilisez une horloge RTC externe si le besoin est de connaître la date et l'heure courantes.
Bases d'ordonnancement
La fonction add_systick_callback() permet d'enregistrer une fonction qui sera appelée de façon périodique. Ce mécanisme permet d'appeler une fonction qui servira d'ordonnanceur pour un système multi-tâches. L'implémentation d'un tel système n'est pas encore faite, et la piste la plus probable sera l'utilisation d'un système multi-tâches libre existant, comme RIOT-OS.
Spécificités par micro-contrôleur
Tous les micro-contrôleurs actuellement supportés fournissent la même interface.