L’objectif est de mettre en oeuvre les E/S numériques de la carte Atmel SAM4S.
Remarque : Pour ce TP, vous aurez besoin de relier un adaptateur USB/RS232 sur le port série UART J7 et d’utiliser un logiciel d’émulation de terminal ($ picocom -b 115200 /dev/ttyUSB0
ou cutecom
sous GNU/Linux ou TeraTerm
sous Windows) sur le port série virtuel pour visualiser les messages de débuggage. On quitte picocom
en utilisant la combinaison de touches Ctrl-A
puis Ctrl-X
.
Les E/S numériques sont très utilisés dans les systèmes industriels. Elles sont aussi les plus simple à mettre en oeuvre car il suffit :
Par contre, il faut absolumment maîtriser la notion de masques pour gérer les E/S de manière individuelle. Pour cela, on utilise :
La seule difficulté pour le contrôle des entrées est de mettre en oeuvre la gestion des interruptions. Les interruptions permettent d’éviter au programme d’attendre indéfiniment le changement d’état ou de niveau d’une entrée. Elles sont donc souvent indispensables dans un système professionnel.
Dans le monde industriel :
Les entrées numériques sont souvent des touches, interrupteurs, boutons mais aussi des détecteurs de présence, des fins de courses etc … En fait ce sont tous les capteurs délivrant une information binaire : à deux états (appui/repos, présent/absent, etc …).
Les sorties numériques sont des indicateurs de visualisation (Led, voyant, etc..) ou tout actionneur fonctionnant à partir de deux états (on/off, allumé/éteint, etc …). Pour des actionneurs de puissance, on s’interfacera souvent avec des relais électromécanique ou statique, des thyristors optocouplé.
Remarque : les E/S peuvent être regroupées pour former un bus parallèle. Par exemple, un clavier matriciel 16 touches peut être géré avec un bus parallèle de 8 E/S décomposable en 4 lignes et 4 colonnes.
Pour faire le lien entre les E/S numériques et le MCU (MicroController Unit), il faut un circuit périphérique d’interface des E/S. Ici, c’est le rôle du PIO (Parallel Input Output) qui est un port parallèle d’entrée/sortie.
Un circuit périphérique est accessible à partir d’une adresse mémoire ce qui permet au programme de lire et/ou écrire dans les registres de ce composant et donc de gérer son fonctionnement.
Ceci est fourni au programmeur par l’intermédiaire du fichier /usr/local/asf/sam/utils/cmsis/sam4s/include/sam4s16c.h
#define PIOA ((Pio *)0x400E0E00U) /**< \brief (PIOA ) Base Address */
#define PIOB ((Pio *)0x400E1000U) /**< \brief (PIOB ) Base Address */
#define PIOC ((Pio *)0x400E1200U) /**< \brief (PIOC ) Base Address */
Remarque : le ‘U’ (UNSIGNED) indique que la valeur est non signée (cad strictement positive) et le 0x
que la valeur est exprimée en hexadécimale (le système de numération de base des informaticiens). Chaque symbole hexadécimal (de 0 à F) est codé sur 4 bits et représente une valeur décimale comprise entre 0 et 15 (F).
En programmation, on manipule les adresses mémoires en utilisant des variables spéciales appelées “pointeurs”. Un “pointeur” est donc une variable qui peut contenir une adresse mémoire. La notion de “pointeur” apparaît ici par la présence de l’étoile * : (Pio *)
et l’adresse du PIOA est 0x400E0E00
.
En informatique, une adresse mémoire représente une “case” qui peut contenir une valeur. La taille de base d’une “case” mémoire est de 8 bits soit 1 octet.
Remarque : les capacités mémoire seront donc toujours exprimées en octet en utilisant les multiples kilo, méga, giga … si la taille est importante.
Pour manipuler des valeurs, on utilise des variables “normales”. Par contre, ces variables doivent être “typées”. Ce type précise la convention d’interprétation de la séquence de bits qui constitue la variable. Le type de la variable spécifie aussi sa taille (la longueur de cette séquence de bits) soit habituellement 8 bits, 16 bits, 32 bits, 64 bits, … Par exemple le type int
permet de manipuler des valeurs entières signées sur 32 bits (4 octets en mémoire).
Pour manipuler les PIOx, le fichier /usr/local/asf/sam/utils/cmsis/sam4s/include/component/component_pio.h
déclare une structure Pio
permettant d’accèder à ses registres :
typedef struct {
__O uint32_t PIO_PER; /**< \brief (Pio Offset: 0x0000) PIO Enable Register */
__O uint32_t PIO_PDR; /**< \brief (Pio Offset: 0x0004) PIO Disable Register */
__I uint32_t PIO_PSR; /**< \brief (Pio Offset: 0x0008) PIO Status Register */
__I uint32_t Reserved1[1];
__O uint32_t PIO_OER; /**< \brief (Pio Offset: 0x0010) Output Enable Register */
__O uint32_t PIO_ODR; /**< \brief (Pio Offset: 0x0014) Output Disable Register */
__I uint32_t PIO_OSR; /**< \brief (Pio Offset: 0x0018) Output Status Register */
__I uint32_t Reserved2[1];
__O uint32_t PIO_IFER; /**< \brief (Pio Offset: 0x0020) Glitch Input Filter Enable Register */
__O uint32_t PIO_IFDR; /**< \brief (Pio Offset: 0x0024) Glitch Input Filter Disable Register */
__I uint32_t PIO_IFSR; /**< \brief (Pio Offset: 0x0028) Glitch Input Filter Status Register */
__I uint32_t Reserved3[1];
__O uint32_t PIO_SODR; /**< \brief (Pio Offset: 0x0030) Set Output Data Register */
__O uint32_t PIO_CODR; /**< \brief (Pio Offset: 0x0034) Clear Output Data Register */
__IO uint32_t PIO_ODSR; /**< \brief (Pio Offset: 0x0038) Output Data Status Register */
__I uint32_t PIO_PDSR; /**< \brief (Pio Offset: 0x003C) Pin Data Status Register */
__O uint32_t PIO_IER; /**< \brief (Pio Offset: 0x0040) Interrupt Enable Register */
__O uint32_t PIO_IDR; /**< \brief (Pio Offset: 0x0044) Interrupt Disable Register */
__I uint32_t PIO_IMR; /**< \brief (Pio Offset: 0x0048) Interrupt Mask Register */
__I uint32_t PIO_ISR; /**< \brief (Pio Offset: 0x004C) Interrupt Status Register */
__O uint32_t PIO_MDER; /**< \brief (Pio Offset: 0x0050) Multi-driver Enable Register */
__O uint32_t PIO_MDDR; /**< \brief (Pio Offset: 0x0054) Multi-driver Disable Register */
__I uint32_t PIO_MDSR; /**< \brief (Pio Offset: 0x0058) Multi-driver Status Register */
__I uint32_t Reserved4[1];
__O uint32_t PIO_PUDR; /**< \brief (Pio Offset: 0x0060) Pull-up Disable Register */
__O uint32_t PIO_PUER; /**< \brief (Pio Offset: 0x0064) Pull-up Enable Register */
__I uint32_t PIO_PUSR; /**< \brief (Pio Offset: 0x0068) Pad Pull-up Status Register */
__I uint32_t Reserved5[1];
__IO uint32_t PIO_ABCDSR[2]; /**< \brief (Pio Offset: 0x0070) Peripheral Select Register */
__I uint32_t Reserved6[2];
__O uint32_t PIO_IFSCDR; /**< \brief (Pio Offset: 0x0080) Input Filter Slow Clock Disable Register */
__O uint32_t PIO_IFSCER; /**< \brief (Pio Offset: 0x0084) Input Filter Slow Clock Enable Register */
__I uint32_t PIO_IFSCSR; /**< \brief (Pio Offset: 0x0088) Input Filter Slow Clock Status Register */
__IO uint32_t PIO_SCDR; /**< \brief (Pio Offset: 0x008C) Slow Clock Divider Debouncing Register */
__O uint32_t PIO_PPDDR; /**< \brief (Pio Offset: 0x0090) Pad Pull-down Disable Register */
__O uint32_t PIO_PPDER; /**< \brief (Pio Offset: 0x0094) Pad Pull-down Enable Register */
__I uint32_t PIO_PPDSR; /**< \brief (Pio Offset: 0x0098) Pad Pull-down Status Register */
__I uint32_t Reserved7[1];
__O uint32_t PIO_OWER; /**< \brief (Pio Offset: 0x00A0) Output Write Enable */
__O uint32_t PIO_OWDR; /**< \brief (Pio Offset: 0x00A4) Output Write Disable */
__I uint32_t PIO_OWSR; /**< \brief (Pio Offset: 0x00A8) Output Write Status Register */
__I uint32_t Reserved8[1];
__O uint32_t PIO_AIMER; /**< \brief (Pio Offset: 0x00B0) Additional Interrupt Modes Enable Register */
__O uint32_t PIO_AIMDR; /**< \brief (Pio Offset: 0x00B4) Additional Interrupt Modes Disables Register */
__I uint32_t PIO_AIMMR; /**< \brief (Pio Offset: 0x00B8) Additional Interrupt Modes Mask Register */
__I uint32_t Reserved9[1];
__O uint32_t PIO_ESR; /**< \brief (Pio Offset: 0x00C0) Edge Select Register */
__O uint32_t PIO_LSR; /**< \brief (Pio Offset: 0x00C4) Level Select Register */
__I uint32_t PIO_ELSR; /**< \brief (Pio Offset: 0x00C8) Edge/Level Status Register */
__I uint32_t Reserved10[1];
__O uint32_t PIO_FELLSR; /**< \brief (Pio Offset: 0x00D0) Falling Edge/Low Level Select Register */
__O uint32_t PIO_REHLSR; /**< \brief (Pio Offset: 0x00D4) Rising Edge/ High Level Select Register */
__I uint32_t PIO_FRLHSR; /**< \brief (Pio Offset: 0x00D8) Fall/Rise - Low/High Status Register */
__I uint32_t Reserved11[1];
__I uint32_t PIO_LOCKSR; /**< \brief (Pio Offset: 0x00E0) Lock Status */
__IO uint32_t PIO_WPMR; /**< \brief (Pio Offset: 0x00E4) Write Protect Mode Register */
__I uint32_t PIO_WPSR; /**< \brief (Pio Offset: 0x00E8) Write Protect Status Register */
__I uint32_t Reserved12[5];
__IO uint32_t PIO_SCHMITT; /**< \brief (Pio Offset: 0x0100) Schmitt Trigger Register */
__I uint32_t Reserved13[19];
__IO uint32_t PIO_PCMR; /**< \brief (Pio Offset: 0x150) Parallel Capture Mode Register */
__O uint32_t PIO_PCIER; /**< \brief (Pio Offset: 0x154) Parallel Capture Interrupt Enable Register */
__O uint32_t PIO_PCIDR; /**< \brief (Pio Offset: 0x158) Parallel Capture Interrupt Disable Register */
__I uint32_t PIO_PCIMR; /**< \brief (Pio Offset: 0x15C) Parallel Capture Interrupt Mask Register */
__I uint32_t PIO_PCISR; /**< \brief (Pio Offset: 0x160) Parallel Capture Interrupt Status Register */
__I uint32_t PIO_PCRHR; /**< \brief (Pio Offset: 0x164) Parallel Capture Reception Holding Register */
__IO uint32_t PIO_RPR; /**< \brief (Pio Offset: 0x168) Receive Pointer Register */
__IO uint32_t PIO_RCR; /**< \brief (Pio Offset: 0x16C) Receive Counter Register */
__I uint32_t Reserved14[2];
__IO uint32_t PIO_RNPR; /**< \brief (Pio Offset: 0x178) Receive Next Pointer Register */
__IO uint32_t PIO_RNCR; /**< \brief (Pio Offset: 0x17C) Receive Next Counter Register */
__I uint32_t Reserved15[2];
__O uint32_t PIO_PTCR; /**< \brief (Pio Offset: 0x188) Transfer Control Register */
__I uint32_t PIO_PTSR; /**< \brief (Pio Offset: 0x18C) Transfer Status Register */
} Pio;
Les membres ou champs de la structure représentent “physiquement” les registres internes du circuit ici un PIO. En théorie, on pourrait trouver des registres :
unsigned char
(ou uint8_t
)unsigned short
(ou uint16_t
)unsigned int
(ou uint32_t
)unsigned long
(ou uint64_t
)En pratique, on ne trouvera que des registres 32 bits car le MCU ici possède une architecture 32 bits. Ceci évitera des problèmes d’alignement et il est donc préférable, pour des raisons de vitesse d’accès d’avoir des registres 32 bits parfois sous-utilisés. En informatique, il y aura toujours une opposition entre vitesse et taille mémoire !
On obtient :
En langage C, pour accèder à un membre d’une structure, on utilise l’opérateur ->
dans le cas d’un pointeur. Par exemple pour écrire dans le registre Enable Register du PIOA :
// Activer la sortie numéro 19
PIOA->PIO_OER = PIO_PA19;
Les étiquettes des E/S des PIOx sont définies dans le fichier header /usr/local/asf/sam/utils/cmsis/sam4s/include/pio/pio_sam4s16c.h
et celles des registres dans le fichier header /usr/local/asf/sam/utils/cmsis/sam4s/include/instance/instance_pioa.h
pour le PIOA par exemple.
Remarque : ces fichiers d’en-têtes (header) sont automatiquement inclus par asf.h
.
La carte Atmel SAM4S dispose de 3 x PIO (32 bits) :
Remarque : les E/S ne sont pas toutes laissées disponibles. Pour la carte Atmel SAM4S, il y a 79 E/S accessibles.
On va tout d’abord mettre en oeuvre :
Les fonctionnalités essentielles des E/S PIO fournies par Atmel sont :
Le contrôle des E/S PIO peut être réalisé :
Les adresses mémoires des 3 PIOs sont :
Documentation : Chapitre 31 Parallel Input/Output Controller (PIO) page 567 du atmel-11100-32-bit-cortex-m4-microcontroller-sam4s_datasheet.pdf
Chaque PIO dispose d’environ 65 registres.
En utilisant seulement 3 registres, il est possible de contrôler basiquement une sortie du PIO :
Remarque :
Les entrées peuvent être gérées par deux modes :
Pour manipuler une entrée, on aura besoin des registres suivants :
Pour simplifier la programmation directe du PIO, la bibliothèque logicielle ASF fournit des fonctions de contrôle : http://asf.atmel.com/docs/latest/sam4s/html/group__sam__drivers__pio__group.html et http://asf.atmel.com/docs/latest/sam4s/html/group__ioport__group.html.
Une fonction, aussi appelée routine, sous-routine ou procédure, contient simplement une série d’instructions à réaliser. N’importe quelle fonction peut être appelée à n’importe quelle étape de l’exécution du programme, y compris à l’intérieur d’autres fonctions, voire dans la fonction elle-même (récursivité).
Remarque : Un module est un ensemble de structure de données et de fonctions. Les bibliothèques sont des modules.
Programmer par fonctions (cad la programmation procédurale) permet :
Remarque : Les programmes sont généralement plus facile à écrire, à comprendre et à maintenir lorsque chaque fonction réalise une SEULE ACTION logique et bien évidemment celle qui correspond à son nom.
Ici, les fonctions de la bibliotèque logicielle ASF, en masquant l’utilisation des registres des circuits de la carte, rendent plus facile la programmation.
On désire allumer et éteindre la LED bleue de la carte Atmel SAM4S :
La LED Bleue est reliée sur la sortie PA19 du PIOA.
Pour :
On crée une première version du programme principal qui utilise les directement registres du PIOA :
$ vim main.c
// Commande la LED Bleue de la carte Atmel SAM4S
// à partir des registres du PIOA
#include <asf.h>
int main (void)
{
my_init();
// Activer la sortie associée à la LED bleue
PIOA->PIO_OER = PIO_PA19; /**< \brief PIO_OER (Pio Offset: 0x0010) Output Enable Register */
// Allumer la LED bleue
PIOA->PIO_CODR = PIO_PA19; /**< \brief PIO_CODR (Pio Offset: 0x0034) Clear Output Data Register */
// Attendre 1000 ms
mdelay(1000);
// Eteindre la LED bleue
PIOA->PIO_SODR = PIO_PA19; /**< \brief PIO_SODR (Pio Offset: 0x0030) Set Output Data Register */
return 0;
}
Remarque : l’étiquette PIO_PA19 est définie dans le fichier /usr/local/asf/sam/utils/cmsis/sam4s/include/pio/pio_sam4s16c.h
et correspond à la broche 19 du PIOA soit (1 << 19)
. Il en existe pour chaque broche et cela permet de les manipuler facilement dans des masques.
Code source : tp1-led-bleue.zip
Test :
$ make
$ ./run.sh xxx_flash.elf
On crée une deuxième version qui utilise l’API IOPORT de la bibliothèque ASF :
$ vim main.c
// Commande la LED Bleue de la carte Atmel SAM4S
// à partir des fonctions ASF
#include <asf.h>
int main (void)
{
my_init();
// Allumer la LED bleue
ioport_set_pin_level(LED0_GPIO, IOPORT_PIN_LEVEL_LOW);
// Attendre 1000 ms
mdelay(1000);
// Eteindre la LED bleue
ioport_set_pin_level(LED0_GPIO, IOPORT_PIN_LEVEL_HIGH);
// Attendre 1000 ms
mdelay(1000);
// Inverser l'état de la LED bleue
ioport_toggle_pin_level(LED0_GPIO);
return 0;
}
Remarque : l’appel board_init()
se charge d’initialiser les 2 LEDs en appelant les fonctions ioport_init()
et gpio_configure_pin()
.
À vous de jouer :
Écrire les deux versions du programme pour commander la LED verte (PA20 du PIOA)
Écrire un programme qui permet de faire clignoter les deux LEDs
On désire compter le nombre d’appui sur le bouton USRPB1 de la carte Atmel SAM4S :
Le bouton USRPB1 est relié sur l’entrée PB3 du PIOB.
Pour lire simplement l’état d’une entrée, il suffit de faire :
// Désactive la sortie associée au bouton 1 pour la mettre en entrée
PIOB->PIO_ODR = PIO_PB3;
// Affiche l'état 0 ou 1 du bouton 1 à partir du registre PDSR
printf("PIOB PDSR : %ld\r\n", ((PIOB->PIO_PDSR & PIO_PB3) >> 3));
La lecture d’une seule entrée est basée sur une opération de masque (ET bit à bit) :
On va gérer le comptage par interruption sur front descendant du bouton USRPB1.
Remarque : La gestion des interruptions sur le PIOB nécessite d’activer l’horloge du PMC (Power Management Controller) associé à ce périphérique.
On crée une première version du programme principal qui utilise les directement registres du PIOB :
$ vim main.c
// Comptage des appuis sur le bouton USRPB1 de la carte Atmel SAM4S
// à partir des registres du PIOB
#include <asf.h>
volatile uint32_t appui_bouton_1 = 0;
int main (void)
{
my_init();
// Désactive la sortie associée au bouton 1 pour la mettre en entrée
PIOB->PIO_ODR = PIO_PB3;
// Active l'horloge associé au PIOB par le registre PCER0 (Peripheral Clock Enable Register 0)
PMC->PMC_PCER0 = 1 << ID_PIOB;
// Active la détection de front pour le bouton 1
PIOB->PIO_ESR = PIO_PB3;
// Active le déclenchement d'une interruption sur l'appui
PIOB->PIO_FELLSR = PIO_PB3; // front descendant
//PIOB->PIO_REHLSR = PIO_PB3; // front montant
// Active le filtre anti-rebond pour le bouton 1
PIOB->PIO_IFSCER = PIO_PB3;
// Active la résistance de pull-up
PIOB->PIO_PUER = PIO_PB3;
// Active le filtre
PIOB->PIO_IFER = PIO_PB3;
// Active la détection par front
PIOB->PIO_AIMER = PIO_PB3; // cf. PIO_ELSR and PIO_FRLHSR
//PIOB->PIO_AIMDR = PIO_PB3; // double détection (both-edge detection)
// Active les interruptions pour le PIOB
NVIC_EnableIRQ(PIOB_IRQn);
// Active la gestion d'interruption sur le bouton 1
PIOB->PIO_IER = PIO_PB3;
while (1)
{
printf("Nb appui bouton 1 : %ld\r\n", appui_bouton_1);
mdelay(500); /* Attend 500ms */
}
return 0;
}
Le gestionnaire d’interruption à définir est void PIOB_Handler(void)
:
void PIOB_Handler(void)
{
uint32_t status;
status = PIOB->PIO_ISR & PIOB->PIO_IMR;
//printf("PIOB status : %ld\r\n", status);
//printf("PIOB ISR : %ld\r\n", PIOB->PIO_ISR);
//printf("PIOB IMR : %ld\r\n", PIOB->PIO_IMR);
if((status & PIO_PB3) == PIO_PB3)
{
puts("Appui bouton 1\r");
appui_bouton_1++;
}
}
Attention : Il faut retirer de la compilation (cf. config.mk
) le fichier source sam/drivers/pio/pio_handler.c
car celui-ci définit déjà un gestionnaire void PIOB_Handler(void)
par défaut.
Test :
$ make
$ ./run.sh xxx_flash.elf
On crée une deuxième version qui utilise l’API PIO de la bibliothèque ASF :
$ vim main.c
// Comptage des appuis sur le bouton USRPB1 de la carte Atmel SAM4S
// à partir des fonctions ASF
#include <asf.h>
/** Niveau de priorité IRQ (plus la valeur est basse, plus la priorité est grande) */
#define IRQ_PRIOR_PIO 0
volatile uint32_t appui_bouton_1 = 0;
int main (void)
{
my_init();
// Active l'horloge associé au PIOB par le registre PCER0 (Peripheral Clock Enable Register 0)
pmc_enable_periph_clk(PIN_PUSHBUTTON_1_ID);
// Active le filtre anti-rebond pour le bouton 1
pio_set_debounce_filter(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_MASK, 10);
// Active le déclenchement d'une interruption sur l'appui du bouton 1
pio_handler_set(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_ID, PIN_PUSHBUTTON_1_MASK, PIN_PUSHBUTTON_1_ATTR, Bouton1_Handler);
// Active les interruptions pour le PIOB
NVIC_EnableIRQ((IRQn_Type) PIN_PUSHBUTTON_1_ID);
// Fixe un niveau de priorité
pio_handler_set_priority(PIN_PUSHBUTTON_1_PIO, (IRQn_Type)PIN_PUSHBUTTON_1_ID, IRQ_PRIOR_PIO);
// Active les interruptions pour le bouton 1
pio_enable_interrupt(PIN_PUSHBUTTON_1_PIO, PIN_PUSHBUTTON_1_MASK);
while (1)
{
printf("Nb appui bouton 1 : %ld\r\n", appui_bouton_1);
mdelay(500); /* Attend 500ms */
}
return 0;
}
Attention : Il faut maintenant ajouter sam/drivers/pio/pio_handler.c
dans la liste des fichiers à compiler (cf. config.mk
) car celui-ci définit la fonction pio_handler_set()
utilisée dans cette version.
Le gestionnaire d’interruption à définir est void Bouton1_Handler()
:
static void Bouton1_Handler(uint32_t id, uint32_t mask)
{
if (PIN_PUSHBUTTON_1_ID == id && PIN_PUSHBUTTON_1_MASK == mask)
{
puts("Appui bouton 1\r");
appui_bouton_1++;
}
}
Test :
$ make
$ ./run.sh xxx_flash.elf
À vous de jouer :
Écrire les deux versions du programme de test pour le bouton USRPB2 (PC12 du PIOC)
Écrire un programme qui permet de faire clignoter la LED Bleue à partir du bouton USRPB1 (marche/arrêt) et la LED verte à partir du bouton USRPB2 (marche/arrêt)
À partir de la documentation, rechercher une entrée et une sortie numériques de libre sur les ports PIO A, B et C.
Cabler un montage exploitant une entrée et une sortie numériques.
Écrire un programme de démonstration du montage réalisé.
Vous devez être capable de répondre aux questions suivantes :
Définir une E/S numérique, le rôle d’un PIO.
Définir le terme de registre.
Décrire la méthode de programmation d’une E/S numérique.
Quel est le nom du registre qui permet de lire le niveau d’une broche du PIO ?
Décrire le principe de gestion d’une interruption matérielle ?
À quoi sert une résistance de pull-up ? Qu’est-ce qu’une sortie à collecteur ouvert ?
Qu’est-ce qu’un front descendant ou montant ? un niveau haut ou bas ?
À quoi sert la résistance en série avec la LED ?
Quel est le front généré par l’appui sur le bouton USRPB1 ?
Pourquoi est-il habituel d’activer une sortie numérique par un niveau bas (0 actif en logique négative) ?
La fonction my_init()
fournie permet d’initialiser la carte (board_init()
), le système d’horloge (sysclk_init()
) et le port série UART (configure_uart()
). Une fonction mdelay()
fournit un moyen de réaliser des temporisations d’attente en millisecondes.
#define STRING_EOL "\r"
#define STRING_HEADER \
"-- Exemple LED Bleue - "BOARD_NAME" --\r\n" \
"-- "__DATE__" "__TIME__" --"STRING_EOL
/** UART Interface */
#define CONF_UART CONSOLE_UART
#define CONF_UART_BAUDRATE 115200
#define CONF_UART_PARITY UART_MR_PAR_NO
#define CONF_UART_CHAR_LENGTH US_MR_CHRL_8_BIT
#define CONF_UART_STOP_BITS US_MR_NBSTOP_1_BIT
volatile uint32_t g_ul_ms_ticks = 0;
static void my_init(void);
static void configure_uart(void);
static void mdelay(uint32_t ul_dly_ticks);
/**
* Initialise
*/
static void my_init(void)
{
board_init();
sysclk_init();
/* Initialise le port uart */
configure_uart();
/* Envoie une chaîne de caractères */
puts(STRING_HEADER);
/* Configure les tics à 1 ms */
if (SysTick_Config(sysclk_get_cpu_hz() / 1000))
{
puts("Erreur systick configuration\r");
while (1);
}
}
/**
* \brief Gestionnaire d'évènement pour les tics d'interruptions système
*
* Incremente le compteur g_ul_ms_ticks
*
*/
void SysTick_Handler(void)
{
g_ul_ms_ticks++;
}
/**
* \brief Temporisation d'attente en millisecondes (basée sur les tics systèmes du microcontrôleur)
*
* \param ul_dly_ticks temps d'attente en millisecondes
*/
static void mdelay(uint32_t ul_dly_ticks)
{
uint32_t ul_cur_ticks;
ul_cur_ticks = g_ul_ms_ticks;
while ((g_ul_ms_ticks - ul_cur_ticks) < ul_dly_ticks);
}
/**
* Configure UART.
*/
static void configure_uart(void)
{
const usart_serial_options_t uart_serial_options =
{
.baudrate = CONF_UART_BAUDRATE,
.charlength = CONF_UART_CHAR_LENGTH,
.paritytype = CONF_UART_PARITY,
.stopbits = CONF_UART_STOP_BITS,
};
sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
stdio_serial_init(CONF_UART, &uart_serial_options);
}