Interface logicielle (API) - Cœur (Cortex-M*) - Registres

De Wiki Techno-Innov
< Technique‎ | Logiciel‎ | API‎ | Use‎ | Core
Aller à la navigation Aller à la recherche


Registres de configuration

Principe

Le processeur du micro-contrôleur ne voit que de la mémoire autour de lui. Il n'a pas accès directement aux signaux électriques. Pour accéder aux fonctions spéciales comme l'état électrique d'une sortie le processeur doit donc modifier des données dans une zone mémoire spécifique, dédiée à cette sortie, que l'on appelle un registre.

La configuration de toutes les fonctions du micro-contrôleur se fait par le biais de ces registres.

L'ensemble des registres est organisé en blocs, regroupés par fonctions avec une structure précise, décrite dans la documentation du micro-contrôleur dans la description des registres de chaque bloc fonctionnel.

Structures

Ces descriptions ont été traduites en C sous forme de structures, chaque champ correspondant à un registre différent. Chaque structure est associée à une adresse de base (ou plusieurs lorsque plusieurs modules identiques sont présents) qui correspond à l'adresse du premier registre de la structure.

Cette façon de faire est très différente de celle habituellement utilisée pour décrire les adresses des registres des micro-contrôleurs à base de multiples "#define", qui rend le code illisible et montre une grande méconnaissance du langage C.
L'utilisation de structures correspond à la méthode utilisée pour le noyau Linux, et est beaucoup plus logique pour décrire le contenu d'une structure. Cela permet aussi de définir la taille de chaque champ, ce qui n'est pas possible à l'aide de "#define".

Une grande partie des structures correspondant aux blocs fonctionnels ont été réparties dans les drivers correspondant (voir les fichiers include/drivers/*.h) ainsi que dans les fichiers relatifs au fonctionnement du cœur du micro-contrôleur (voir les fichiers include/core/*.h)

Chaque driver fourni une définition permettant la déclaration d'un pointeur vers la structure correspondant à ses registres, sous la forme suivante :

#define LPC_SYS_CONFIG  ((struct lpc_sys_config *) LPC_SYSCON_BASE)
#define LPC_IO_CONTROL  ((struct lpc_io_control *) LPC_IOCON_BASE)

Adresses de base

#include "core/lpc_regs.h"

Les adresses de base des blocs sont définies dans le fichier "include/core/lpc_regs.h"

Ce fichier n'est inclus que par les drivers ayant besoin de connaître l'adresse de base de leur structure.

Accès

Lorsque vous devez accéder à un registre particulier, vous devez commencer par déclarer un pointeur vers la structure correspondant aux registres du bloc fonctionnel dont il fait partie.
Par exemple pour le registre correspondant à la sélection de l'horloge principale du micro-contrôleur, il s'agit d'un registres "système" présent dans la structure lpc_sys_config :

struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;

sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_PLL_OUT;

Notez l'utilisation de la définition "LPC_MAIN_CLK_SRC_PLL_OUT" pour définir la valeur écrite dans ce registre, qui est beaucoup plus lisible que "0x00000003", qui n'a aucun sens lors de la lecture du code.

De nombreuses valeurs sont ainsi définies dans les fichiers d'entête contenant les définitions des structures des registres des différents blocs fonctionnels. Certaines définitions peuvent manquer, n'hésitez pas à les compléter en fonction de vos besoins.

Lorsqu'un registre regroupe plusieurs champs, ces définitions peuvent être regroupées pour permettre la configuration du registre en un accès unique, par exemple pour le registre sys_AHB_clk_ctrl de la structure lpc_sys_config :

sys_config->sys_AHB_clk_ctrl = (LPC_SYS_ABH_CLK_CTRL_MEM_ALL | LPC_SYS_ABH_CLK_CTRL_UART0);

Attention, l'exemple ci-dessus écrase l'ancienne valeur du registre.
Lorsque vous voulez seulement ajouter une valeur (définir un seul des champs présents dans le registre) sans modifier les autres, vous pouvez utiliser une des notation suivantes :

  • Positionner un bit unique :
sys_config->sys_AHB_clk_ctrl |= LPC_SYS_ABH_CLK_CTRL_ADC;
  • Enlever un bit unique :
sys_config->sys_AHB_clk_ctrl &= ~(LPC_SYS_ABH_CLK_CTRL_UART0);
  • Modifier un groupe de bits :
sys_config->sys_AHB_clk_ctrl &= ~(LPC_SYS_ABH_CLK_CTRL_MEM_ALL); /* Mise à 0 des bits correspondant à MEM_ALL : 0x0000001F */
sys_config->sys_AHB_clk_ctrl |= (LPC_SYS_ABH_CLK_CTRL_SYSTEM | LPC_SYS_ABH_CLK_CTRL_RAM)

Les fichiers d'entête définissent parfois des masques permettant la remise à 0 d'un ensemble de bits correspondant à la configuration d'une valeur représentée sur plusieurs bits. Dans la majorité des cas ces valeurs ont une définition dont le nom se termine par "_MASK", même si ce n'est pas le cas dans l'exemple ci-dessus.

Registres spéciaux

#include "core/lpc_core.h"

Certains registres de fonctionnement du micro-contrôleur ne sont pas accessibles directement par une adresse. Le cœur du micro-contrôleur dispose d'instructions spécifiques pour lire ou modifier ces registres.

Il s'agit des registres suivants:

  • APSR (Application Program Status Register)
  • IPSR (Interrupt Program Status Register)
  • CONTROL (Control)
  • PSP (Process Stack Pointer)
  • MSP (Main Stack Pointer)
  • PRIMASK (Priority Mask)
  • BASEPRI_MAX / BASEPRI (Base Priority)
  • FAULTMASK (Fault Mask)

L'accès à ces registres se fait par des instructions spécifiques qui ne font pas partie du langage C. Voir la page Jeu d'instructions et instructions spéciales pour plus d'informations.