Crans
  • Commentaires
  • Page immuable
  • Menu
    • Affichage
    • Carte locale du site
    • Pièces jointes
    • Informations
    • Code source
  • Connexion

Navigation

  • Modifications récentes
  • Recherche avancée
  • Aide
Version 33 à la date du 2018-10-27 07:14:53
CransWiki:
  • VieEns
  • LesDépartements
  • DépartementEea
  • EEA-UE441

Présentation

Cette page a pour but de répertorier des exemples de code utiles pour les TP (et pour le partiel) de l'UE441 du M1 IST de EEA. Vous pouvez évidement contribuer à cette page mais assurez vous de rajouter du code compréhensible (commentaires, nom de variables explicites, etc), fonctionnel, et bien répertorié (cette page a des sections, respectez les). Un fichier de présentation un peu général a été rédigé par M. Juton et est disponible ici : Creation_projet_MCUXPRESSOIDE.pdf.

Documentation

Un de vos meilleurs amis et de vos pires ennemis lorsque vous programmerez sur les LPC804 sera le LPC804_UserManual.pdf. Ce monstre de 355 pages regroupe toutes les fonctions utiles et toute la documentation nécessaire pour programmer sur la carte. Le problème est que, si on ne l'a jamais fait, lire cette doc est un peu compliqué. Voici un exemple rapide de lecture de ce document pour réaliser une fonction demandée. Dans le sujet0 fourni pour préparer le partiel de cette année, il est demandé de paramétrer un convertisseur analogique/numérique. Pour cela on nous indique le chapitre 22 de la doc. Il faut dans un premier temps alimenter le ADC. Pour cela on suit la doc à partir du paragraphe d'introduction et on trouve qu'il faut aller voir dans le registre LPC_SYSCON->PDRUNCFG. Dans la table indiquée (Table 84), on trouve que pour alimenter le ADC il faut mettre le bit 4 (première colonne sur la ligne ADC_PD) à 0 (Powered). On en déduit qu'il faut utiliser l'instruction

   1 LPC_SYSCON->PDRUNCFG &= ~(1<<4)

De même pour toutes les fonctions demandées. Tout est dans la doc, il suffit de trouver la bonne page.

Code de base

LED

La LED est l'élément de base pour sortir une info basique sur ce que fait le programme. Comme toutes les entrée/sortie, il faut définir le sens avec la variable DIR0. Pour une LED, il faut la positionner sur sortie (1). Le registre LPC_GPIO_PORT -> DIR0 regroupe les directions pour toutes les entrées sorties. Il faut donc faire un OU logique avec la variable blue (1<<11) qui est un 1 décalé de 11 bits, ce qui correspond à la LED bleue. Pour choisir ensuite l'état de de la LED, il faut faire la même opération avec le registre LPC_GPIO_PORT -> PIN0. Le programme ci-dessous permet de faire clignoter la LED bleue de la carte à un fréquence proche de 1Hz. Si l'on considère que la fréquence est réellement de 1Hz, il permet aussi de déduire le temps d'une instruction machine élémentaire (en comptant le nombre d'instructions nécessaires pour finir la boucle de temporisation).

   1 #include <cr_section_macros.h>
   2 #include <LPC8xx.h>
   3 #include <syscon.h>
   4 
   5 //bits de GPIO utilises
   6 #define blue (1<<11)
   7 
   8 int main(void){
   9     int cpt;
  10     LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON GPIO0);
  11     LPC_GPIO_PORT -> DIR0 |= blue;
  12 
  13         while(1){
  14                 LPC_GPIO_PORT -> PIN0 ^= blue; // OU exclusif pour changer l'état de la LED
  15                 for(cpt=0;cpt<200000;cpt++); // temporisation sale pour attendre à peu près 1s
  16         }
  17 }

Sens et état de la LED Bien que cela fonctionne, il est fortement déconseillé de changer le sens de la sortie correspondant à la LED pour la faire clignoter. Si vous rendez ça comme solution elle sera très probablement considérée comme fausse ou au moins très fortement pénalisée

Boutons

Lancer des commandes, c'est bien. Mais récupérer un peu d’interactions avec l'utilisateur, c'est mieux ! Le plus simple pour cela est d'utiliser les boutons. La carte de base en comporte 3 (dont un reset qui peut à tout moment "griller" votre carte donc on va dire 2...). Ils sont montés selon un schéma spécial qui fait que tout appui sur un bouton allume directement la LED associée (basiquement, chaque LED est en série avec un bouton).

L'utilisation des boutons, comme toutes les I/O du GPIO, se fait au travers du registre LPC_GPIO_PORT.

   1 #include <cr_section_macros.h>
   2 #include <LPC8xx.h>
   3 #include <syscon.h>
   4 
   5 //bits de GPIO utilises
   6 #define button (1<<13)
   7 
   8 int main(void){
   9     int cpt;
  10     LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON GPIO0);
  11 
  12     while(1){
  13         if ((LPC_GPio_PORT -> PIN0 & button)==0){ // le bouton est enfoncé
  14             // Des instructions...
  15         }
  16         else{ // il ne l'est pas
  17             // Encore des instructions...
  18         }
  19         // Toujours des instructions...
  20     }
  21 }

D'autres boutons sont utilisables avec l'extension de la carte mais il sont définis dans les header fournis avec.

Timers

La carte utilisée possède différents timers, le plus utilisé étant le timer standard CTIMER0. Son paramétrage (activation, reset, démarrage, division de fréquence...) se fait donc dans le registre LPC_CTIMER0.

Diviseur de Fréquence

Le compteur principal TC est incrémenté à chaque fois que le prescale counter PC (cadencé à la fréquence d'horloge, ici 12MHz) atteint la valeur enregistrée dans le prescale register PR. En pratique on a au début du main :

   1 // Préparation des entrées/sorties et périphériques
   2 LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON | GPIO0 | CTIMER0 );
   3 LPC_GPIO_PORT->DIR0 |= (SEG_SDAT|SEG_SCLK|SEG_LCLK); // Afficheur 4*7 segments
   4 LPC_GPIO_PORT->DIR0 |= (LED1|LED2|LED3|LED4); // 4 leds
   5 LPC_CTIMER0->TCR = 1; // Déclenchement du compteur Timer Control Register
   6 LPC_CTIMER0->PR = 12000; // Diviseur de fréquence
   7 

PWM

Pour générer une pwm sur une sortie de la carte, il faut vérifier plusieurs paramètres. Ces paramètres sont détaillés en fin de sujet du TP3.

  • La sélection de l’horloge: par défaut l’horloge CPU.
  • Préscalaire dans LPC_CTIMER0->PR

  • Période du timer LPC_CTIMER0->MR[3]

  • Demander au timer de reprendre à 0 une fois la période atteinte LPC_CTIMER0->MCR

  • Le temps à l'état bas de la PWM LPC_CTIMER0->MR[1]

  • Placer la sortie en mode PWM LPC_CTIMER0->PWMC

  • Assigner la PWN à la pin voulue (par exemple 19) LPC_SWM->PINASSIGN4

En plus de tout ça il faut évidement allumer le timer  LPC_SYSCON->SYSAHBCLKCTRL0 , activer la pin 19 en mode sortie LPC_GPIO_PORT->DIR0 et lancer le timer LPC_CTIMER0->TCR comme présenté precedement.

Une fois la PWM configurée, il reste à choisir sa forme à l'aide des Shadow Register : LPC_CTIMER0->MSR[3] pour la période et LPC_CTIMER0->MSR[1] pour le temps bas.

Le code suivant permet de faire "battre" une LED. C'est un exercice du TP2.

   1 #include <cr_section_macros.h>
   2 #include "LPC8xx.h"
   3 #include "syscon.h"
   4 #include "Multi_Shield.h"
   5 
   6 #define CTIMER (1<<25)
   7 
   8 int main(void) {
   9         // préparation des entrées/sorties et périphériques
  10         LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON | GPIO0);
  11         LPC_GPIO_PORT->DIR0 |= (SEG_SDAT|SEG_SCLK|SEG_LCLK); // afficheur 4*7 segments
  12 
  13         // Initialisation du timer principal (Timer Counter)
  14         LPC_SYSCON->SYSAHBCLKCTRL0 |= CTIMER;   // Activation
  15         LPC_SYSCON->PRESETCTRL0 &= ~CTIMER;    // Allumer le reset
  16         LPC_SYSCON->PRESETCTRL0 |= CTIMER;    // Éteindre le reset
  17 
  18         // CETTE LIGNE NECESSITE DES COMMENTAIRES
  19         // Initialisation de la SM
  20         LPC_SYSCON->SYSAHBCLKCTRL0 |= SWM;              // Activation
  21         LPC_SWM->PINASSIGN4 = 0xFFFF01FF;               // Association de MATCH1 et LED1
  22         LPC_SYSCON->SYSAHBCLKCTRL0 &= ~SWM;             // Extinction de la SM
  23 
  24         LPC_CTIMER0->PR = 469; // réglage du diviseur de fréquence
  25 
  26         // MCR (Match Control Register) sert à configurer quoi faire quand un des Match Register atteint le Timer Counter
  27         LPC_CTIMER0->MCR |= 1 << 1;             // Active le reset du Timer Counter quand il match MR[0]
  28         LPC_CTIMER0->MR[0] = 255;               // Valeur stockée dans le Match Register 0. Lorsque la valeur du Timer Counter atteindra celle ci, le TC se resetera.
  29 
  30         LPC_CTIMER0->MR[1] = 0;                 // Idem pour le Match Register 1 à priori mais on ne lui a pas assigné d'action. /!\ Cette ligne est possiblement inutile /!\
  31 
  32         // Le PWMC (PWM Controle register) sert à configurer la sortie de la PWN
  33         LPC_CTIMER0->PWMC = 1 << 1;     // PWMEN1 à 1 enable PWM for chanel 1
  34 
  35         // TCR (Time Control Register) sert à controler le Timer Counter. On l'allume + reset
  36         LPC_CTIMER0->TCR = 3; // équivalent à (1<<1)|(1<<2) (enable: bit 1 , reset: bit 2)
  37         LPC_CTIMER0->TCR = 1; // On aurait pu mettre aussi (1<<1) ou encore LPC_CTIMER0->TCR &= ~(1<<2) pour juste enlever le 1 sur le bit de reset
  38 
  39         int largeur = 0; // gestion de la largeur en cours de la PWM
  40         int sens = 0; // gestion du sens de variation (plus ou moins intense)
  41 
  42         while(1) {
  43              if((LPC_CTIMER0->TC & 0x3F) == 0){
  44                 if(largeur == 0 || largeur == 255) // si on atteint un extrême, on change de sens
  45                         sens = !sens;
  46                 if(sens)
  47                         largeur = (largeur + 1) % 256; // augmentation de la largeur
  48                 else
  49                         largeur = (largeur + 255) % 256; // diminution de la largeur
  50 
  51                 LPC_CTIMER0->MR[1] = largeur; // Assignation de la largeur
  52         }
  53 
  54         segments(largeur & 0xFFFF,0);
  55     }
  56     return 0 ;
  57 }

Ce code sert à faire "battre" la LED. C'est l'objet d'une question dans le TP2.

Ecran LCD

L'écran LCD est présent sur les extensions de carte pour le LPC804. Son utilisation est assez simple et il fait l'objet d'une petite partie du TP3 dans laquelle on s’intéresse à l'espace mémoire occupé par la fonction sprintf. De plus, il peut servir d'affichage très rudimentaire pour le debug (si on n'a pas envie d'utiliser la console de mcuxpresso ni la visualisation des variables).

code

Voici un code simple d'utilisation de l'écran LCD. D'autres fonctions sont disponibles.

   1 #include "lib_UE441_lcd.h" // contient les fonctions utiles pour l'utilisation de l'écran
   2 
   3 int main(void) {
   4         char text[32];// contiendra le texte à afficher
   5 
   6         init_lcd();
   7         lcd_position(0,0); // on positionne le curseur au début (ligne,colonne). 16 colonnes sur l'écran.
   8         sprintf(text,"\O_O/ \O_O/ \O_O/");// formate la chaîne pour l'affichage et la stocke dans text
   9         lcd_puts(text);// affiche le texte
  10         lcd_position(1,0);// seconde ligne
  11         lcd_puts("Second line bitch !");// On n'est pas obligé d'utiliser la fonction sprintf si le texte est simple
  12         while(1); // si on attend pas, on verra rien
  13 
  14     return 0 ;
  15 }

Interruptions

Par bouton

Le principe des interruptions est que l'on va assigner une pine à un type d’interruption et on va lui donner une priorité. Si l’événement attendu survient (input, timer, etc), une certaine fonction va être appelée et l'on va interrompre le programme principal. Un des intérêts est que les interruptions sont gérées à un niveau plus bas et sont donc beaucoup plus rapidement prises en compte que si l'on vérifiait l’appui sur un bouton. En contrepartie, c'est chiant à coder.

   1 int main(void) {
   2 
   3         char text[32];
   4         uint32_t i;
   5 
   6         //Configuration de l'horloge à 15 MHz
   7         LPC_PWRD_API->set_fro_frequency(30000);
   8 
   9         // Peripheral reset to the GPIO0 and pin interrupt modules. '0' asserts, '1' deasserts reset.
  10         LPC_SYSCON->PRESETCTRL0 &=  (GPIO0_RST_N & GPIOINT_RST_N);
  11         LPC_SYSCON->PRESETCTRL0 |= ~(GPIO0_RST_N & GPIOINT_RST_N);
  12 
  13         //Mise en fonctionnement des périphériques utilisés
  14         LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON | GPIO0 | SWM | CTIMER0 | GPIO_INT);
  15 
  16         //initialisation de l'affichuer lcd avec un affichage
  17         init_lcd();
  18         sprintf(text,"Waiting...");
  19         lcd_gohome();
  20         lcd_puts(text);
  21 
  22     // Configuration des interruptions sur front descendant des boutons
  23 
  24     // Configure P0.18, P0.19 as pin interrupts 1, 0
  25     // Make PORT0.18, PORT0.19 outputs driving '0'.
  26         LPC_GPIO_PORT->DIR0 |= (1<<19)|(1<<17)|(1<<21)|(1<<11);
  27 
  28         // Configure P0.18 - P0.19 as pin interrupts 1 - 0 by writing to the PINTSELs in SYSCON
  29         LPC_SYSCON->PINTSEL[0] = 13;  // PINTSEL0 is P0.13
  30         LPC_SYSCON->PINTSEL[1] = 12;  // PINTSEL1 is P0.12
  31 
  32         // Configure the Pin interrupt mode register (a.k.a ISEL) for edge-sensitive on PINTSEL1,0
  33         LPC_PIN_INT->ISEL = 0x0;
  34 
  35         // Configure the IENR (pin interrupt enable rising) for rising edges on PINTSEL0,1
  36         //LPC_PIN_INT->IENR = 0x0;
  37 
  38         // Configure the IENF (pin interrupt enable falling) for falling edges on PINTSEL0,1
  39         LPC_PIN_INT->IENF = 0x3;
  40 
  41         // Clear any pending or left-over interrupt flags
  42         LPC_PIN_INT->IST = 0xFF;
  43 
  44         // Enable pin interrupts 0 - 1 in the NVIC (see core_cm0plus.h)
  45         NVIC_EnableIRQ(PININT0_IRQn);
  46         NVIC_EnableIRQ(PININT1_IRQn);
  47 
  48         // config priority. De base elles sont prioritaires mais on redéfinit leurs priorités
  49         NVIC->IP[6]= (0<<6)|(3<<14);
  50 
  51     while(1) {
  52         // affichage du timer sur l'écran LCD
  53                 sprintf(text," %d ",i++);
  54                 lcd_position(1,1);
  55                 lcd_puts(text);
  56     }
  57     return 0 ;
  58 }
  59 
  60 //fonction interruption 0 GPIO sur front descendant
  61 void PININT0_IRQHandler(void){
  62         LED1= !LED1;               // Falling edge on PIN INT0, LED off
  63         LPC_PIN_INT->FALL = 1<<0;       // Clear the interrupt flag
  64     return;
  65 }
  66 
  67 }

Par Timer

Voilà pour les interruptions par bouton. Mais on peut également faire des interruptions par timer comme explicité dans le code suivant :

   1 #include <cr_section_macros.h>
   2 #include <stdio.h>
   3 #include "LPC8xx.h"
   4 #include "fro.h"
   5 #include "rom_api.h"
   6 #include "syscon.h"
   7 #include "swm.h"
   8 #include "i2c.h"
   9 #include "ctimer.h"
  10 #include "core_cm0plus.h"
  11 
  12 #include "lib_UE441_lcd.h"
  13 
  14 //periode en ms des notes de la gamme LA440
  15 #define P_DO  3822
  16 #define P_RE  3405
  17 #define P_MI  3033
  18 #define P_FA  2862
  19 #define P_SOL 2550
  20 #define P_LA  2271
  21 #define P_SI  2024
  22 #define P_DOA 1911
  23 int Gamme[8]={P_DO,P_RE,P_MI,P_FA,P_SOL,P_LA,P_SI,P_DOA};
  24 
  25 //boutons et leds de la carte
  26 #define BP1 LPC_GPIO_PORT->B0[13]
  27 #define BP2 LPC_GPIO_PORT->B0[12]
  28 #define LED1 LPC_GPIO_PORT->B0[19] //utilisée ici en PWM
  29 #define LED2 LPC_GPIO_PORT->B0[17]
  30 #define LED3 LPC_GPIO_PORT->B0[21]
  31 #define LED4 LPC_GPIO_PORT->B0[11]
  32 
  33 volatile int temps=0; // temps actuel qui descend toutes les secondes
  34 volatile int total=1; // temps total de secondes que l'on doit compter
  35 volatile int prochain=1; // Prochain nombre de secondes que l'on comptera
  36 
  37 int main(void) {
  38         char text[32]; //text utilisé pour l'afficheur LCD
  39 
  40         //Configuration de l'horloge a 15 MHz
  41         LPC_PWRD_API->set_fro_frequency(30000);
  42 
  43         // Peripheral reset to the GPIO0 and pin interrupt modules. '0' asserts, '1' deasserts reset.
  44         LPC_SYSCON->PRESETCTRL0 &=  (GPIO0_RST_N & GPIOINT_RST_N);
  45         LPC_SYSCON->PRESETCTRL0 |= ~(GPIO0_RST_N & GPIOINT_RST_N);
  46 
  47         //Mise en fonctionnement des peripheriques utilisés
  48         LPC_SYSCON->SYSAHBCLKCTRL0 |= (IOCON | GPIO0 | SWM | CTIMER0 | GPIO_INT);
  49 
  50         //initialisation de l'affichuer lcd
  51         init_lcd();
  52 
  53         // Configuration de l'interruption pour le bouton comme précédemment
  54         LPC_GPIO_PORT->DIR0 |= (1<<19)|(1<<17)|(1<<21)|(1<<11);
  55         LPC_SYSCON->PINTSEL[1] = 12;  // PINTSEL1 is P0.12
  56         LPC_PIN_INT->ISEL = 0x0;
  57         LPC_PIN_INT->IENF = 0x3;
  58         LPC_PIN_INT->IST = 0xFF;
  59         NVIC_EnableIRQ(PININT1_IRQn);
  60 
  61         // config priority. De base elles sont prioritaires
  62         NVIC->IP[6]= (0<<6)|(3<<14);
  63 
  64         // configuration du SysTick Timer
  65         SysTick -> CTRL |= (1<<0); // active le timer
  66         SysTick -> LOAD = 0xb71aff; // compte à peu près une seconde
  67         SysTick -> CTRL |= (1<<1); // active les interruptions par le SysTick
  68 
  69         //Affichage du texte de base qui restera sur l'ecran LCD.
  70         sprintf(text,"Temps: ",temps,total);
  71         lcd_position(0,0);
  72         lcd_puts(text);
  73         sprintf(text,"Prochain: ",temps,total);
  74         lcd_position(1,0);
  75         lcd_puts(text);
  76 
  77     while(1) {
  78         // efface l'espace des infos
  79         sprintf(text,"         ",prochain);
  80         lcd_position(0,7);
  81         lcd_puts(text);
  82         sprintf(text,"  ",prochain);
  83         lcd_position(1,10);
  84         lcd_puts(text);
  85 
  86         // affichage les nouvelles infos
  87         sprintf(text,"%u sur %u ",temps,total);
  88         lcd_position(0,8);
  89         lcd_puts(text);
  90         sprintf(text,"%u",prochain);
  91         lcd_position(1,10);
  92         lcd_puts(text);
  93     }
  94     return 0 ;
  95 }
  96 
  97 //Interruption du timer
  98 void SysTick_Handler(void){
  99         temps++; // incrémente le nombre de secondes
 100 
 101         if(temps>=total){// on est arrivé à la fin du temps
 102                 LED1= !LED1; // la LED change
 103                 temps=0; // le compte revient a 0
 104                 total=prochain; // on prend en compte le prochain temps
 105         }
 106 }
 107 
 108 //Interruption du bouton
 109 void PININT1_IRQHandler(void){
 110         prochain=(prochain+1) % 20; //Incrémentation du prochain temps pris en compte
 111         LPC_PIN_INT->FALL = 1<<1;       // Efface l'interruption
 112   return;
 113 
 114 }

Sujet 0

Voici un début de correction du sujet 0 proposé pour préparer le partiel. Gardez un œil très critique avec ce qui est proposé dans ce document. sujet0_Grizzly.pdf

  • Propulsé par MoinMoin
  • Mentions légales