RT-Thread est un système d’exploitation en temps réel embarqué open source avec de riches composants de niveau intermédiaire et un excellent écosystème matériel et logiciel, le tout offrant un support fantastique pour l’industrie de l’Internet des objets. Depuis sa création en 2006, RT-Thread a alimenté 1,5 milliard d’appareils, notamment des appareils portables, des appareils électroménagers intelligents, l’électronique automobile, l’électronique médicale, l’électronique grand public, l’énergie et de nombreuses autres industries.
Le 15 mars, RT-Thread s’est associé à NXP Semiconductors, STMicroelectronics et WCHElectronics pour proposer un webinaire IoT : Power the IoT Devices. Inscrivez-vous gratuitement ici.
Revenons au mécanisme d’auto-initialisation.
Initialisation générale
Lors du développement embarqué, nous adoptons principalement cette approche pour initialiser un périphérique.
int main(int argc, char *argv[])
{
clk_init();
led_init();
beep_init();
key_init();
.....
while(1)
{
...
}
}
L’ordre de cette initialisation est relativement clair, et il est assez facile de déterminer les périphériques qui ont été initialisés et l’ordre dans lequel ils sont initialisés. Cependant, le main
est particulièrement lourde, surtout lorsqu’il y a beaucoup de périphériques à initialiser.
Auto-Initialisation
Programmation C sur l’ordinateur pour imprimer un hello world
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello world\r\n");
return 1;
}
Ici, nous pouvons utiliser directement printf
imprimer sans aucune étape d’initialisation ; cette idée conduit à la RT-Thread
mécanisme d’initialisation automatique.
RT-Thread
Auto-Initialisation :
int led_init()
{
...
}
INIT_APP_EXPORT(led_init);
int main(int argc, char *argv[])
{
led_on();
rt_kprintf("hello rt thread\r\n");
return 1;
}
L’idée centrale de l’initialisation automatique est que l’initialisation de chaque périphérique est terminée avant de s’exécuter sur le main
fonction, et tous les périphériques peuvent être utilisés directement dans le main
fonction. Par exemple, le programme ci-dessus utilise directement rt_kprintf
pour la sortie et allumé le LED
.
API auto-initialisée
L’auto-initialisé API
intercepté de la RT-Thread
code source, comme illustré ci-dessous :
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
API
la liste des fonctions est présentée dans le tableau suivant
Analyse du mécanisme
Fonction INIT_EXPORT
En voyant les fonctions d’initialisation, nous apprenons que leur dernier appel est le INIT_EXPORT
fonction. Seuls les paramètres saisis sont différents. Voyons la définition de cette fonction :
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
Le INIT_EXPORT()
la fonction a deux paramètres ; le premier paramètre indique quelle fonction doit être initialisée, délivrant le pointeur de fonction (le nom de la fonction), et le second paramètre indique dans quel segment placer le pointeur de fonction. Passons ensuite à la macro, et il y a plusieurs conditions préalables à connaître avant de passer à la macro.
- RT_USED
- #définir l’attribut RT_USED((utilisé))
Le attribute__(used)
fonction a été marquée dans le fichier objet pour empêcher l’éditeur de liens de supprimer les sections inutilisées.
- type init_fn_t
- typedef int (*init_fn_t)(void);
Ici, une valeur de retour de int
est défini un type de pointeur de fonction avec le paramètre de fonction void
et renommé en init_fn_t
.
##
appartient au langage C, et son rôle est de combiner deux symboles de langage en un seul symbole de langage
- SECTION
- #définir l’attribut SECTION(x)((section(x)))
__attribute__((section(name)))
place les fonctions fonctionnelles ou les données dans un segment d’entrée spécifié nommé name
Avec la sauvegarde préliminaire ci-dessus, analysons les éléments suivants INIT_EXPORT
macro. Développez la macro comme ci-dessous :
RT_USED const init_fn_t __rt_init_fn SECTION(".rti_fn." level) = fn
La fonction de cette macro est d’affecter le pointeur à la fonction fn
à la variable __rt_init_fn
ce type de variable est RT_USED const init_fn_t
et il est stocké dans le segment spécifié .rti_fn.level
. Ainsi, une fois la fonction exportée à l’aide d’une macro d’initialisation automatique, les pointeurs vers chaque fonction d’initialisation seront stockés dans ces segments de données. Lorsque nous déréférencons, ces pointeurs seront pris au fur et à mesure que nous exécutons la fonction correspondante.
Division des segments
Les segments sont divisés en component.c
et le code source est le suivant :
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
static int rti_board_end(void)
{
return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end, "6.end");
La répartition des segments exportés ci-dessus à l’aide de la INIT_EXPORT
macro est indiquée dans le tableau suivant :
Après avoir ajouté les six segments exportés après l’initialisation automatique, la distribution de chaque segment est indiquée dans le tableau suivant :
Fonction rt_components_board_init
Tête à vérifier sur la mise en œuvre de la rt_components_board_init
fonction:
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif
}
Si ce n’est pas le cas, considérez le RT_DEBUG_INIT
il est clair de constater que le rt_components_board_init
exécute ce qui suit :
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
Le code ci-dessus définit un fn_ptr
pointeur, qui est déréférencé lorsque la plage du pointeur est dans la plage de __rt_init_rti_board_start
et rt_init_rti_board_end
, où le pointeur est le pointeur de fonction mis lors de l’initialisation automatique, c’est donc tout à fait l’exécution de la fonction. Autrement dit, la fonction exportée par INIT_BOARD_EXPORT(fn)
est exécuté.
Fonction rt_components_init
Code source:
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}
Si ce n’est pas le cas, considérez le RT_DEBUG_INIT
il est clair de constater que le rt_components_init
exécute ce qui suit :
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
Le code définit également un fn_ptr
pointeur, qui est déréférencé lorsque la plage du pointeur est dans la plage de __rt_init_rti_board_end
et __rt_init_rti_end
, où le pointeur est le pointeur de fonction placé lors de l’initialisation automatique ; encore une fois, nous obtenons cette fonction exécutée. C’est-à-dire que la fonction dérivée entre le INIT_PREV_EXPORT(fn)
pour INIT_APP_EXPORT(fn)
les segments sont exécutés
Exécution des fonctions auto-initialisées
Le processus de démarrage de RT-Thread
:
Le rt_components_board_init()
fonction et la rt_componenets_init()
fonction sont exécutées.
Goûter
Ajoutez le code de test suivant au main.c
fonction:
int led_init(void)
{
return 1;
}
INIT_APP_EXPORT(led_init);
Le compilé .map
fichier est affiché comme suit :
Le pointeur de fonction __rt_init_led_init
est situé dans le .rti_fn.6
segment, et la fonction rt_components_init()
déréférencera ce pointeur lors de son exécution, c’est-à-dire qu’il exécutera le led_init
fonction.