Langage C intégré (in)

utilisation volatile

La signification originale de volatile est «volatile». La variable déclarée avec le mot-clé volatile dans l'environnement embarqué obtiendra la valeur de l'adresse d'origine à chaque fois que sa valeur est référencée. En raison de la nature «variable» de cette valeur, toute opération d'affectation ou d'obtention de valeur sur celle-ci sera exécutée (non optimisée). En raison de cette fonctionnalité, ce mot clé est souvent utilisé pour éliminer l'optimisation du compilateur dans l'environnement de compilation intégré. Il peut être divisé en trois scénarios:

  1. Modifier les registres matériels;
  2. Modifiez les variables non automatiques dans la fonction de service d'interruption;
  3. Modifier les variables qui seront modifiées par plusieurs applications dans des projets avec des systèmes d'exploitation;

Modification des registres matériels
En prenant la définition de GPIO dans la fonction de bibliothèque HAL de STM32F103 comme exemple, voici la définition du registre GPIO dans la bibliothèque HAL:

/**
* @brief General Purpose I/O
*/
typedef struct
{
    
    
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

La définition de __IO est:

#define __IO volatile /*!< Defines 'read / write' permissions */

Définissez ensuite GPIO comme:

#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *)GPIOG_BASE)

La définition de GPIOx_BASE est la suivante:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x00001000UL)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x00001400UL)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x00001800UL)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x00001C00UL)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x00002000UL)

La définition de l'adresse de base périphérique APB2:

#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)

Enfin, regardez la définition de l'adresse de base périphérique:

#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */

Prises ensemble, développez les définitions de macro une par une, utilisez simplement GPIOA pour voir, les autres peuvent être déduites par analogie:

#define GPIOA ((GPIO_TypeDef *)(0x40000000UL + 0x00010000UL + 0x00000800UL))

Après cette définition, l'adresse CRL de GPIOA est:

(volatile uint16_t *)(0x40000000UL + 0x00010000UL + 0x00000800UL)

L'adresse de la CRH est:

(volatile uint16_t *)(0x40000000UL + 0x00010000UL + 0x00000800UL + 2)

Les registres suivants sont déduits par analogie, ils sont donc utilisés dans le programme:

GPIOA->CRH |= 0x01;

Ensuite, la fonction réalisée est d'extraire le bit le plus bas du registre CRH de GPIOA. Si __IO uint16_t n'est pas utilisé dans la structure de registre qui définit GPIO, mais que seul uint16_t est utilisé, utilisez l'instruction dans le programme:

GPIOA->CRH |= 0x01;

Il peut être optimisé par le compilateur et ne pas exécuter cette instruction, ce qui rendra impossible la fonction d'élever le bit le plus bas de CRH; mais si volatile est utilisé dans la fonction de bibliothèque à modifier, le compilateur n'optimisera pas cette instruction. Chaque fois que cette instruction est exécutée, la valeur sera récupérée ou stockée à partir de l'adresse mémoire correspondant à la CRH, garantissant que chaque exécution est valide.

Modifier les variables qui seront modifiées par plusieurs tâches dans les projets avec des systèmes d'exploitation.
Dans le développement embarqué, il n'y a pas seulement le développement de microcontrôleurs à puce nue, mais aussi le développement avec des systèmes d'exploitation. Habituellement, les deux utilisent le langage C pour développer davantage. Dans les conceptions avec des systèmes d'exploitation (tels que RTOS, UCOS-II, Linux, etc.), si plusieurs tâches attribuent ou récupèrent la même variable, ce type de variable doit également être modifié avec volatile pour assurer sa visibilité. La soi-disant visibilité signifie que la tâche en cours a modifié la valeur de cette variable, et en même temps, la valeur de cette variable dans d'autres tâches a également changé.

utilisation de la structure

L'une des étapes les plus importantes de la conception d'un programme consiste à choisir une bonne façon de représenter les données. Dans la plupart des cas, l'utilisation de variables simples ou même de tableaux ne suffit pas. C utilise des variables structurelles pour améliorer encore la capacité de représenter les données. La forme de base de la structure C est suffisante pour représenter de manière flexible une variété de données et peut créer de nouvelles formes.

Le format de déclaration de la structure de C est le suivant:

struct [结构体名] {
    
    
类型标识符 成员名 1;
类型标识符 成员名 2...
类型标识符 成员名 n;
};

Cette instruction décrit une structure composée de n membres de type de données. Elle ne crée pas un objet de données réel, mais décrit uniquement de quoi l'objet est composé. Analysez les détails de la déclaration de structure. Le premier est le mot-clé struct, qui indique qu'une structure est suivie, suivi d'une balise facultative, qui peut être utilisée dans les programmes suivants pour faire référence à la structure, afin que nous puissions la suivre Le programme peut être déclaré comme ceci:

struct [结构体名] 结构体变量;

La liste des membres de la structure est entourée d'une paire d'accolades dans la déclaration de structure. Chaque membre est décrit avec sa propre déclaration. Les membres peuvent être de n'importe quel type de données C, et même d'autres structures. Le point-virgule après l'accolade fermante est nécessaire pour la déclaration, indiquant la fin de la définition de la disposition de la structure, par exemple:

struct students
{
    
    
char name[50];
char sex[50];
int age;
float score;
};
int main(void) {
    
    
struct students student;
printf("Name: %s\t",student.name[0]);
printf("Sex: %s\t", student.sex);
printf("Age: %d\t", student.age);
printf("Score: %f\r\n", studen.score);
return 0; }

La déclaration de la structure peut être placée en dehors de toutes les fonctions ou à l'intérieur d'une fonction. Si une structure est déclarée à l'intérieur d'une fonction, alors sa marque est limitée à l'utilisation interne de la fonction; si la structure est déclarée en dehors de toutes les fonctions, alors toutes les fonctions après la déclaration peuvent utiliser sa marque.

La structure a deux significations, une signification est "disposition structurelle", comme le structstudent {...} dans l'exemple ci-dessus, indique au compilateur comment représenter les données, mais il ne laisse pas le compilateur allouer de l'espace pour les données; l'autre signification est de créer des variables de structure, telles que la structure étudiants student dans l'exemple ci-dessus; le compilateur exécute cette ligne de code pour créer une variable de structure student, et le compilateur utilise le modèle des étudiants pour allouer de l'espace à la variable: char array 1, 50 contenant 50 éléments Un tableau de type char de 2 éléments, une variable int et une variable float, ces espaces de stockage sont combinés avec un étudiant nommé student, comme le montre la Figure 5.3.3.

Insérez la description de l'image ici
Les membres de cette structure sont également stockés en permanence en mémoire. Dans la programmation normale, struct sera également utilisé avec typedef, et les détails seront introduits dans la section "Utilisation de Typedef" plus tard.

utilisation enum

Enum est un mot clé utilisé pour modifier les variables de type énumération dans le langage C. En langage C, vous pouvez utiliser des noms de symboles de déclaration de type énumération pour représenter des constantes entières. Utilisez le mot-clé enum pour créer un nouveau "type" et spécifier la valeur qu'il peut avoir (en fait, les constantes enum sont de type int, donc tant que vous pouvez Les types d'énumération peuvent être utilisés là où le type int est utilisé). Le but du type énumération est d'améliorer la lisibilité du programme, et sa syntaxe est la même que celle de la structure, comme suit:

enum [枚举类型名] {
    
    
枚举符 1,
枚举符 2 ...
枚举符 n,
};

Par exemple:

enum color
{
    
    
red,
green,
blue,
yellow
};

Constantes
Enum Dans l'exemple ci-dessus, que sont exactement le rouge, le gris, le bleu et le jaune? Techniquement parlant, ce sont des constantes entières de type int, par exemple, elles peuvent être utilisées comme ceci:

printf("red=%d, green=%d", red, green);

On constate que la dernière information imprimée est: rouge = 0, vert = 1. le rouge devient une constante nommée, représentant l'entier 0. De même, d'autres énumérateurs sont appelés constantes, représentant respectivement 1 à 3. Les constantes d'énumération peuvent être utilisées tant que des constantes entières peuvent être utilisées. Par exemple, les constantes d'énumération peuvent être utilisées pour indiquer la taille du tableau lors de la déclaration de tableaux, et les constantes d'énumération peuvent être utilisées comme étiquettes dans les instructions switch.


Enum valeur par défaut Par défaut, les constantes de la liste d'énumération sont affectées de 0, 1, 2, etc., donc dans la déclaration suivante, la valeur de apple est 2:

enum fruit{
    
    banana, grape, apple};

Affectation d'
énumération Dans les types d'énumération, vous pouvez spécifier des valeurs entières pour les constantes d'énumération:

enum levels{
    
    low=90, medium=80, high=100};

Si vous affectez une valeur à une seule constante d'énumération et que vous n'affectez pas de valeur à la constante d'énumération suivante, la constante suivante se verra attribuer la valeur suivante, par exemple:

enum feline{
    
    cat, lynx=10, puma, tiger};

Alors cat = 0, les valeurs de lynx, puma, tiger sont respectivement 10, 11 et 12.

Utilisation de
typedef L' outil typedef est une fonctionnalité de données avancée. Utilisez typedef pour personnaliser le nom d'un certain type. Cet aspect est similaire à #define, mais il existe trois différences entre les deux:

  1. Contrairement à #define, les symboles créés par typedef sont uniquement limités au type et ne peuvent pas être utilisés pour les valeurs;
  2. tyedef est interprété par le compilateur, pas par le préprocesseur;
  3. Dans sa portée limitée, typedef est plus flexible que #define;

Supposons que vous souhaitiez utiliser BYTE pour représenter un tableau de 1 octet, il vous suffit de définir BYTE comme une variable de type char, puis d'ajouter le mot-clé typedef avant la définition:

typedef unsigned char BYTE;

Vous pouvez ensuite utiliser BYTE pour définir des variables:

BYTE x, y[10];

La portée de la définition dépend de l'endroit où le typedef est défini. S'il est défini dans une fonction, il a une portée locale et est limité par la fonction dans laquelle il est défini. S'il est défini en dehors de la fonction, il a une portée de fichier.

Créer un nom pour un type existant peut sembler redondant, mais cela peut parfois être utile. Dans l'exemple précédent, remplacer char non signé par BYTE indique que vous avez l'intention d'utiliser une variable de type BYTE pour représenter des nombres au lieu de caractères. L'utilisation de typedef peut également améliorer la portabilité du programme.

Lorsque vous utilisez typedef pour nommer un type de structure, vous pouvez omettre la balise structure (struct):

typedef struct
{
    
    
char name[50];
unsigned int age;
float score;
}student_info;
student_info student={
    
    “Bob”, 15, 90.5};

De cette façon, le nom du type défini par typedef sera traduit en:

struct {
    
    char name[50]; unsigned int age; float score;}
student = {
    
    “Bob”, 15, 90.5};

La deuxième raison d'utiliser typedef est: tyedef est souvent utilisé pour nommer des types complexes, tels que:

typedef void (*pFunction)(void);

Déclarez pFunction comme une fonction, la fonction renvoie un pointeur, le pointeur pointe vers un type void.
Lorsque vous utilisez typdef, rappelez-vous que typedef ne crée pas de nouveaux types, il ajoute simplement une étiquette pratique à un type existant.

Préprocesseurs et directives de prétraitement

Cette section présentera brièvement le préprocesseur du langage C et ses instructions de prétraitement. La première concerne les instructions de prétraitement, ce sont:

#define、#include、#ifdef、#else、#endif、#ifndef、#if、#elif、#line、#error、#pragma

Parmi ces instructions, #line, #error, #pragma sont relativement rares dans le développement de base, et les autres sont souvent rencontrées et fréquemment utilisées dans le processus de programmation, nous présenterons donc principalement ces instructions couramment utilisées dans les chapitres suivants.

Le langage C est basé sur des mots-clés, expressions, instructions et règles appropriés pour les utiliser. Cependant, le standard C décrit non seulement le langage C, mais décrit également comment exécuter le préprocesseur C.

Le préprocesseur C regarde le programme avant de l'exécuter, il est donc appelé un préprocesseur. Selon les instructions de prétraitement du programme, le préprocesseur remplace l'abréviation du symbole par le contenu qu'il représente (#define). Le préprocesseur peut inclure d'autres fichiers nécessaires au programme (#include), et vous pouvez choisir quel code laisser le compilateur voir (compilation conditionnelle). Le préprocesseur ne connaît pas le C et son travail consiste essentiellement à convertir du texte en un autre texte.

Étant donné que la longueur de l'expression de prétraitement doit être une ligne logique (vous pouvez utiliser le caractère de nouvelle ligne '\' pour transformer la ligne logique en plusieurs lignes physiques), afin que le préprocesseur obtienne la ligne logique correcte, il y aura Pendant un processus de compilation, le compilateur localise chaque barre oblique inverse suivie d'un exemple de nouvelle ligne et les supprime, comme:

printf(“Hello, Chi\
na”);

Converti en ligne logique:

printf(“Hello, China”);

De plus, le compilateur divise le texte en séquence de jetons de prétraitement, séquence vide et séquence de commentaires (un jeton est un élément séparé par des espaces, des tabulations ou des retours à la ligne). Il convient de noter que le compilateur remplacera chaque entrée par un caractère d'espace. Remarques , par exemple:

char/*这是一条注释*/str;

Va devenir:

char str;

Après la compilation et le traitement de cette manière, le programme est prêt à entrer dans la phase de prétraitement et le préprocesseur recherche les instructions de prétraitement commençant par # dans une ligne. Ensuite, nous commençons à expliquer ces directives de prétraitement à partir de la directive #define.

Le fichier comprend #include

Lorsque le préprocesseur trouve la directive de prétraitement #include, il examine le nom de fichier suivant et inclut le contenu du fichier dans le fichier actuel, c'est-à-dire remplace la directive #include dans le fichier. Cela équivaut à saisir tout le contenu du fichier inclus à l'emplacement où se trouve la directive #include du fichier source.

La directive #include a deux formes:

#include <stdio.h> // 文件名在尖括号内
#include “myfile.h” // 文件名在双引号内

Sous UNIX, les crochets <> indiquent au préprocesseur de rechercher le fichier dans le répertoire système standard, et les guillemets doubles "" indiquent au préprocesseur de rechercher le fichier dans le répertoire courant (ou le répertoire avec le chemin spécifié) d'abord, puis recherchez-le s'il n'est pas trouvé. Catalogue système standard:

#include <stdio.h> // 在标准系统目录中查找 stdio.h 文件
#include “myfile.h” // 在当前目录中查找 myfile.h 文件
#include “/project/header.h” // 在 project 目录查找
#include “../myheader.h” // 在当前文件的上一级目录查找

L'environnement de développement intégré (IDE, tel que l'environnement de développement keil de la carte de développement) a également un chemin standard ou un chemin de fichier d'en-tête système. De nombreux environnements intégrés fournissent des options de menu pour spécifier le chemin de recherche lorsque des chevrons sont utilisés.

Pourquoi avons-nous besoin d'inclure des fichiers? Étant donné que le compilateur a besoin des informations de ces fichiers, par exemple, stdio.h contient généralement les définitions de EOF, NULL, getchar () et putchar (). En outre, le fichier contient également d'autres fonctions d'E / S C. Pour nos fichiers personnalisés, pour le développement intégré, ces fichiers peuvent avoir des définitions de macros de broches, des définitions de macros de fonctions simples, etc. qui doivent être utilisées, ainsi que les variables globales et les fonctions d'un certain fichier source. Instruction etc.

Le langage C est utilisé pour utiliser le suffixe .h pour indiquer les fichiers d'en-tête, qui contiennent des informations qui doivent être placées en haut du programme. Les fichiers d'en-tête contiennent souvent des instructions de prétraitement. Certains fichiers d'en-tête sont fournis par le système ou peuvent être personnalisés.

Voici un exemple de personnalisation d'un fichier d'en-tête: gpio.h, main.c

/*gpio.h*/
#ifndef __GPIO_H
#define __GPIO_H
#include <stdio.h>
typedef struct
{
    
    
uint8_t cnt;
uint16_t sum;
float result;
}MyStruct;
typedef enum
{
    
    
GPIO_RESET = 0,
GPIO_SET = 1,
}GPIO_STATE;
#define ABS(x) ((x>0) ? (x) : (-x))
#endif
/* main.c */
#include “gpio.h”
int main(void) {
    
    
MyStruct my_struct = {
    
    0, 25, 3.14};
GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_SET);
printf(“cnt=%d, sum=%d, result=%f\n\r”, my_struct.cnt, my_struct.sum, my_struct.result); }

La directive #include n'inclut pas seulement les fichiers .h, elle peut également inclure des fichiers .c.


Forum technologique de Baiwen:
http://bbs.100ask.net/

Site officiel de la vidéo intégrée Baiwen.com:
https://www.100ask.net/index

Conseil de développement de Baiwen.com:
Taobao: https://100ask.taobao.com/Tmall
: https://weidongshan.tmall.com/

Technology Exchange Group (
Hongmeng Development / Linux / Embedded / Driver / Data Download) Groupe QQ: 869222007

Groupe d'échange Linux intégré MCU:
Groupe QQ: 536785813

Je suppose que tu aimes

Origine blog.csdn.net/thisway_diy/article/details/115295522
conseillé
Classement