Avant-propos
Dans le processus conventionnel de développement de logiciels embarqués, il faut généralement passer par des étapes telles que l'analyse des exigences, la conception des fonctions, la mise en œuvre du codage, les tests du système, la maintenance des versions, etc., pour réaliser l'ensemble du cycle de vie du produit. Cependant, en raison du démarrage simultané du logiciel et du matériel et de l'apparition de situations inattendues, il y aura souvent un écart entre la fonction de conception et la fonction réelle. De plus, des fonctions spécifiques dans le développement de logiciels embarqués dépendent de la conception du matériel, et il est difficile de s'adapter de manière flexible aux petits changements de fonction, tels que la liaison des canaux d'entrée et de sortie numériques, les changements de canaux d'acquisition analogiques, etc. Cela crée des dépenses supplémentaires pour les lancements ultérieurs de produits avec le même positionnement mais avec des configurations différentes. Afin de résoudre ces problèmes, nous proposons une méthode de conception logicielle dans laquelle le matériel et le logiciel peuvent être complètement séparés. Ce procédé résume la fonction de définition de la cible en tant qu'objet et, en instanciant l'objet, modifie dynamiquement la configuration du programme pendant le fonctionnement, de manière à atteindre l'objectif de commutation flexible du micrologiciel entre des produits ayant des configurations différentes au même emplacement. Cette méthode peut non seulement réduire efficacement les dépenses causées par les problèmes de micrologiciel, mais également améliorer la flexibilité et la maintenabilité du produit.
qu'est-ce qu'un objet
Dans la programmation orientée objet, un objet (Object) fait référence à une entité dotée de certaines propriétés et méthodes, qui constitue l'unité de base d'un programme. Nous utilisons généralement des classes (Class) pour définir les propriétés et les méthodes des objets. Une classe peut être considérée comme un modèle ou un modèle pour un objet, qui définit ses caractéristiques et son comportement. Lorsque nous créons un objet, nous créons en fait une instance d'une classe, qui possède les propriétés et les méthodes définies par la classe. Nous utilisons généralement des objets pour représenter des choses ou des concepts du monde réel, tels que des personnes, des voitures, des livres, etc. sur. Les objets ont deux caractéristiques de base : l'état (propriétés) et le comportement (méthodes), qui peuvent être instanciés (créés) et utilisés pour construire différentes parties du programme.
Ce qui précède est une explication populaire des objets, mais un point important a été soulevé ci-dessus, à savoir la programmation orientée objet. Comme nous le savons tous, le langage C n'est pas un langage de développement orienté objet, alors comment pouvons-nous utiliser le langage C pour compléter Développement orienté objet ?
En fait, il existe des méthodes, mais le langage C ne prédéfinit pas les objets comme les autres langages de haut niveau, mais doit être implémenté par lui-même. En langage C, la structure (Struct) est un type de données défini par l'utilisateur, qui nous permet de définir notre propre Une structure de données qui contient plusieurs données membres de différents types. Un type de structure est défini par le mot-clé struct, suivi du nom de la structure et des noms et types des données membres. Par exemple, le code suivant définit un type de structure nommé Personne, qui contient deux données entières, âge et nom :
struct Personne {
âge entier ;
genre booléen ;
nom du caractère[20] ;
} ;
Après avoir défini un type struct, nous pouvons l'utiliser pour définir des variables struct comme suit :
struct Personne personne1 ;
Après avoir défini une variable de structure, nous pouvons initialiser ses données membres par affectation, par exemple :
personne 1.âge = 20 ;
personne1.bool = mâle ;
strcpy(personne1.nom, "Alice");
Nous pouvons également accéder aux données membres de la variable de structure via l'opérateur d'accès [], par exemple :
printf("Nom de la personne : %s, âge : %d\n", [personne1.name](http://personne1.name), personne1.age);
Dans le langage C, le type structure prend également en charge l'héritage multiple (struct et classe), la structure et le pointeur, le pointeur de structure et la fonction et d'autres utilisations. La structure est un type de données très puissant en langage C, qui peut facilement représenter et manipuler des structures de données complexes. Par conséquent, nous pouvons également définir de manière abstraite les fonctions du produit sous forme de structures.
créer un objet
En supposant que notre produit est un dispositif de contrôle IO, les principales fonctions que nous devons concevoir sont principalement le contrôle et la détection des IO. Après avoir classé les exigences ci-dessus, toutes ses fonctions peuvent être classées en opérations IO. Nous pouvons ensuite développer sur cette base et extraire les parties communes.
#définir MAX_PIN_ITEM 10
struct Object_pin{
mode booléen ;
état booléen ;
broche uint32_t ;
port vide* ;
} ;
structure io_funt
{
void (*gpio_set)(void* port, broche uint32_t);
void (*gpio_reset)(void* port, broche uint32_t);
bool (*gpio_read)(void* port, broche uint32_t);
} ;
struct Objet_IO
{
struct Object_pin io_list[MAX_PIN_ITEM] ;
structure io_funt IO_FUN ;
void (*exetuct_fun)(struct Object_pin* pin);
void (*gpio_init)(struct Object_pin* pin);
} ;
struct Object_IO SYS_IO ;
void test_exetuct_fun (struct Object_pin* broche)
{
si (pin-> mode)
{
si (pin-> état)
{
SYS_IO.IO_FUN.gpio_set(pin->port, pin->pin);
}
autre
{
SYS_IO.IO_FUN.gpio_reset(pin->port, pin->pin);
}
}
autre
{
pin->state = SYS_IO.IO_FUN.gpio_read(pin->port,pin->pin);
}
}
void your_gpio_set (port void*, broche uint32_t) ;
void your_gpio_reset (void* port, broche uint32_t) ;
bool your_gpio_read (port vide*, broche uint32_t) ;
void your_sys_gpio_init_function(struct Object_pin* pin);
vide system_io_init (vide)
{
SYS_IO.IO_FUN.gpio_set = votre_gpio_set ;
SYS_IO.IO_FUN.gpio_reset = votre_gpio_reset ;
SYS_IO.IO_FUN.gpio_read = votre_gpio_read ;
SYS_IO.gpio_init = votre_sys_gpio_init_function ;
SYS_IO.exetuct_fun = test_exetuct_fun ;
for(uint8_t ii = 0; ii < sizeof(SYS_IO.io_list)/sizeof(SYS_IO.io_list[0]); ii++)
{
SYS_IO.gpio_init(&SYS_IO.io_list[ii]);
}
}
vide system_io_handler (vide)
{
for(uint8_t ii = 0; ii < sizeof(SYS_IO.io_list)/sizeof(SYS_IO.io_list[0]); ii++)
{
SYS_IO.exetuct_fun(&SYS_IO.io_list[ii]);
}
}
Dans l'extrait de code ci-dessus, nous utilisons la structure struct Object_pin pour définir une seule IO, utilisons la structure struct Object_IO pour compléter la définition de l'intégralité de la liste IO et de l'interface de fonction d'exécution correspondante, et définissons la fonction d'initialisation du système et la fonction d'exécuteur au niveau En même temps, faites attention au Code. La fonction d'exécution réelle n'est pas écrite en , et doit être implémentée et définie par l'utilisateur réel. Des amis prudents ont peut-être découvert que dans l'exemple, le nombre de membres est entièrement défini par la macro MAX_PIN_ITEM. Évidemment, cela ne peut pas résoudre le problème de la modification dynamique du nombre d'IO dans le programme, nous devons donc modifier le programme, comme suit :
struct Object_pin{
mode booléen ;
état booléen ;
broche uint8_t ;
port uint8_t ;
} ;
struct Objet_IO
{
uint8_t li_cnt;
struct Object_pin *io_list;
structure io_funt IO_FUN ;
void (*exetuct_fun)(struct Object_pin* pin);
void (*gpio_init)(struct Object_pin* pin);
} ;
struct Object_IO SYS_IO ;
Dans le code ci-dessus, nous avons modifié le membre de structure io_list d'un objet tableau en objet pointeur, et en même temps modifié les variables membres du type de structure Object_pin, ce qui a fait que io_list ne stockait pas directement des objets de structure PIN spécifiques, mais c'est un espace inconnu, alors comment devrions-nous utiliser cet objet pointeur inconnu ? Cela nécessite de fournir un analyseur système et le fichier de configuration correspondant. L'analyseur analyse le fichier de configuration pour générer un objet de structure PIN et pointe la io_list vers l'objet nouvellement généré pour atteindre l'objectif de configuration dynamique.
fichier de configuration
Selon le type d'objet io_list dans la structure, le pointeur pointe vers l'adresse du type struct Object_pin, il suffit donc de créer la liste de structures correspondante en fonction du type de structure Object_pin. Tout d'abord, vérifiez le type de membre de structure de la struct Object_pin , On constate qu'il peut être abstrait sous forme de paire clé-valeur pour le stockage, de sorte que le format json puisse être sélectionné pour identifier la configuration. Le contenu de l'objet Object_pin peut être facilement identifié grâce au format json. En même temps , json fournit un type de tableau qui peut exprimer clairement plusieurs objets. Vous pouvez choisir de stocker le fichier dans le propre espace de stockage de l'appareil ou dans un espace externe et de charger le fichier dans la mémoire pour analyse lors de son utilisation.
[{
"mode": 1,
"état": 0,
"broche" : 0,
"port": 0
},
{
"mode": 1,
"état": 0,
"épingle" : 1,
"port": 0
},{
"mode" : 0,
"état": 0,
"broche" : 0,
"port" : 1
},
{
"mode" : 0,
"état": 0,
"épingle" : 1,
"port" : 1
}]
analyseur
Une fois que le fichier de configuration a déterminé que le format json est sélectionné, de nombreuses bibliothèques d'analyse peuvent être sélectionnées. Vous pouvez choisir la célèbre bibliothèque d'analyse cjson ou d'autres bibliothèques d'analyse, ou vous pouvez écrire vous-même la bibliothèque d'analyse correspondante. Ici, nous choisissons notre propre bibliothèque de logiciels pour effectuer la tâche d'analyse, l'interface principale utilisée est
jsonObj* jsonParse(char* str);
int getJsonObjInteger(jsonObj* obj, const char* clé);
Après avoir importé la bibliothèque logicielle, créez une variable de type jsonObj pour stocker la variable membre analysée _root, utilisez la fonction jsonParse pour analyser l'intégralité de la chaîne de format json et stockez-la dans la variable _root, et utilisez la fonction getJsonObjInteger pour la récupérer à partir du Variable _root Récupérez simplement les membres que nous voulons. En stockant la variable analysée et en l'attribuant au pointeur io_list dans SYS_IO, et en attribuant la quantité analysée au membre li_cnt dans SYS_IO pour marquer le nombre d'objets stockés dans l'adresse actuelle du pointeur, la dynamique charge la configuration IO.
Cet article présente une méthode de conception de logiciels basée sur les objets avec du matériel et des logiciels complètement séparables pour résoudre certains problèmes de développement de logiciels embarqués. Cette méthode résume chaque module du système en un objet et modifie dynamiquement la configuration du programme via l'instanciation de l'objet. L'article explique en détail le concept et la définition des objets, ainsi que comment utiliser le langage C pour la programmation orientée objet. De plus, l'article présente également l'abstraction fonctionnelle et la définition des opérations d'E/S dans le système, propose une méthode de chargement dynamique des configurations d'E/S et réalise une commutation flexible des E/S via des fichiers de configuration et des analyseurs. Le procédé peut réduire les dépenses provoquées par le problème du micrologiciel dans le développement de logiciels intégrés, et améliorer la flexibilité et la maintenabilité du produit. À la fin de l'article, les exemples de code pertinents et le format du fichier de configuration sont donnés.