SYSTICK - Kasimashi/Systemes-embarques GitHub Wiki
Le tick système timer (SysTick) est un simple compteur décroissant 24 bits qui produit un petit quantum de temps fixe. Le Soft utilise le SysTick pour créer des delays ou ou produire des interruptions périodique et executer un traitement de façon répété dans le temps.
- Le compteur SysTick compte de facon décroissante d'une valeur N-1 jusqu'à 0. Le processeur génère une interruption lorsque le SysTick atteint la valeur 0.
- Après avoir atteint la valeur 0, Le SysTick charge la valeur stocké dans un registre spécial appelée le SysTick Reload Register et continue de compter en décroissant à nouveau.
- Le SysTick ne s'arrête jamais de compter même quand le processeur est en mode pause (debug session). Les interruptions continuent même en mode debug.
Un autre usage du SysTick timer est de créer un Timer qui pourrait être utile aux CPU équipé d'un RTOS. Quand plusieurs tâches tournent de façon concurrente. Le processeur alloue chacunes des tâches du temps CPU suivant la politique d'ordonnancement choisi (Preempif ou Round Robin). Pour faire cela, le processeur utilise le timer hardware pour générer des interruptions à interval régulier. Ces interruptions informent le processeur d'arrêter la tâche en cours d'execution, en sauvegardant le contexte de la tâche en cours d'execution dans la stack, et selectionne la nouvelle tâche qui se trouve dans la queue. Quand le timer SysTick est utilisé au niveau du système, le processeur peut parfois protéger le SysTick d'être modifié par software dans le mode "unprivileged mode".
Il y a 4 registres 32 bits pour configurer le system timer. Leurs addresse mémoires données ci dessous sont toujours les mêmes pour les processeurs ARM Cortex-M mais le nom de ces registres varient suivant le manufactureur.
Détaillons ces registres dans les chapitres suivants :
- La CLKSource permet de prendre la fréquence de AHB divisé par 8 ou bien la valeur du processor clock.
La sélection de la clock (MSI,HSI,PLLCLK,HSE) est faites par le registre RCC_CFGR via un multiplexeur.
Dans l'exemple ci dessous on utilise directement le processor Clock.
- TICKINT : Permet d'activer les interruptions sur le SysTick. 0 : Permet de déactiver l'interruption lorsqu'on atteint la valeur 0. 1 : Permet d'activer l'interruption quand on atteint la valeur 0.
- ENABLE : Permet d'activer (1) le compteur ou de le déactiver (0).
Ainsi pour avoir une interruption sur le SysTick il faut :
- Mettre le bit TICKINT à 1 pour activer l'interruption
- Activer le SysTick interrupt dans le NVIC vector. (activé par défault dans le NVIC)
- Mettre le flag ENABLE à 1 pour activer le compteur.
- COUNTFLAG permet d'indiquer quand un évenement spécial est arrivé. 1 = Le compteur est passé de 1 à 0 depuis la dernière lecture de SysTick_CTRL 0 = Le COUNTFLAG est néttoyé en lisant le SysTick_CTRL ou par écriture de SysTick_VAL.
Le registre LOAD permet de stocker la valeur à laquelle le SysTick va se remettre lorsqu'il atteint la valeur 0.
Si l'interruption SysTick a besoin d'être appelée tout les N clock pulses, le software doit configurer ce registre à la valeur N-1.
Ce register supporte une valeur sur 24 bits.
Par exemple si l'application a besoin de générer une SysTick interrupt tout les 100 clocks pulse, le SysTick Clock LOAD doit être setté à 99.
Quand le SyTick est activé, le compteur 24 bits est copié dans le registre SysTick_VAL. Cependant la valeur de ce registre est arbitraire lorsqu'on effectue un reset.
Le processeur décrémente automatiquement le registre à chaques pulse envoyé par le timer.
Ecrire une valeur sur ce registre remet ce registre à 0. (Redémarre le TIMER à la valeur SysTick_LOAD) au prochain pulse.
Lire une valeur sur ce registre permet d'afficher la valeur courante du SysTick.
Permet de calibrer le SysTick : c'est un registre en lecteur seul qui nous permet de voir la calibration de notre systick.
- La valeur TENMS stocké dans ce registre est un prérequis pour générer un interval de temps de 10ms (cad un timer à 100Hz). Le Software peut l'utiliser comme une horloge de référence externe de grande précision pour calibrer le système timer. Si la valeur TEMS vaut 0, nous pouvons calculer sa valeur à partir de la fréquence de la clock qui pilote le compteur du timer. La valeur TEMNS donne une valeur façon convenable pour générer un interval de temps particulier. Par exemple pour générer un SysTick interrupt tout les 1ms, nous pouvons setter le Systick_LOAD à TENMS/10.
- Le FLAG SKREW indique si la valeur TENMS est exacte ou non. Si la valeur vaut 0, le champ TENMS ne peux pas générer exactement 10ms à cause d'une petite variation sur la fréquence de la clock.
- Le FLAG NOREF indique si le processeur a implémenté une clock de référence. Si TENMS vaut 1. La clock de référence n'a pas été appliqué par le manufactureur.
Exemple de calcul d'un interval system timer :
Imaginons qu'on ai un SysTick_LOAD à 6. Si la clock du processeur est à 1MHz et que le Systick counter prend cette fréquence en horloge. Nous pouvons calculer la période d'interruption comme cela :
SysTick Interrupt period = ( 1 + SysTick_LOAD) * (1/ SysTick Counter Clock Frequency) = ( 1 + 6 ) * (1/1MHz) = 7us
Maintenant que nous connaissons tout les registres voyons comment programmer cela :
On pourrait par exemple activer les interruptions SysTick en faisant :
*((volatile uint32_t *) 0xE000E010 |= 1UL << 2;
Mais ce n'est pas une façon convenable de le faire car difficile à comprendre et à lire.
A la place nous pourrions utiliser une structure sur un block contigue et le mapper sur la mémoire physique. Comme nous l'avons vu le systick s'appui sur 4 regitres de 32 bits. Les addresses mémoires de ces registres sont convertis vers un pointeur variables qu'on peut déclarer dans une structure.
#define __I volatile const // define as read only
#define __O volatile // defines as write only
#define __IO volatile // allow both read and write
// Memory mapping structure for SysTick
typedef struct {
__IO uint32_t CTRL;
__IO uint32_t LOAD;
__IO uint32_t VAL;
__I uint32_t CALIB;
} SysTick_Type;
#define SysTick_BASE 0xE000E010
#define SysTick ((SysTick_Type *) SysTick_BASE)
En partant de ce code on peut ainsi une écrire une fonction pour initialiser notre SysTick : Nous pouvons aussi écrire une fonction delay personalisé.
La fonction ci dessous SysTick_Config()
initialise le systick et génère des interruptions à interval régulié. Le paramètre d'entrée ticks est égale à l'interval de temps du time divisé par la période de la clock.`
Ci dessous l'utilisation du systick d'un STM32F401RE que nous utilisons qu'on incrémente plutôt que de décrémenter mais le cas d'usage est le même.
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
Le SysTick interrupt Handler lui incrémente la valeur uwTick
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
uwTick += uwTickFreq;
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
La fonction Delay pourrait s'écrire par exemple :
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = uwTick;
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while((uwTick - tickstart) < wait)
{
}
}
Notons que la variable uwTick
est du type __IO uint32_t uwTick;
(volatile).
Nous pourrions pousser l'analyse plus loin et analyser le code assembleur.