Microchip : Demo Finite State Machine

De Wiki_du_Réseau_des_Electroniciens_du_CNRS
Sauter à la navigation Sauter à la recherche

Introduction aux microcontrôleurs PIC32

Pour tous ceux qui démarre dans la mise en oeuvre de système embarqué et pour ceux qui souhaiteraient découvrir la famille PIC32MX des microcontrôleurs PIC, voici une première prise en main indispensable pour comprendre comment utiliser par la suite les outils de Microchip.

En effet, la mise en oeuvre de machine à états est la base de la construction d'un programme (firmware). Nous pouvons nous en passer pour des codes simples mais dans la majorité des cas il est préférable de les utiliser. Vous pouvez n'avoir qu'une machine d'états ou bien plusieurs afin de rendre votre programme plus structuré (en ayant une machine d'états par fonctionnalité pour votre application embarqué). Cela permet aussi de rendre votre code plus lisible, mais surtout si vous respectez le faites de ne jamais réaliser de code bloquant, cela le rend plus fluide et plus robuste, voir State Machine Programming Model (page 11)

Avant de nous lancer dans cette implémentation, découvrons d'abord la famille PIC32MX :


Configuration du module d'horloge

Oscillator module, "Section 6. Oscillators":

PIC32MX Oscillator

A partir du choix de la source d'horloge et si l'on ne considère pas la partie de la branche d'horloge du module USB, on peut dire que le système d'horloge de la famille PIC32MX est divisé en 2 branches, l'une pour le cœur du processeur et l'autre pour les périphériques :

  • SYSCLK (CPU and Select Peripherals)
  • PBCLK (Peripherals)


PIC32 oscillator configuration spreadsheet


Mise en oeuvre d'un Timer par interruption

Sur la carte chipKIT WF32, nous vous proposons de réaliser un programme simple, à partir de l'utilisation des outils de base de l'IDE MPLAB X, afin de faire clignoter la LED LD6 (pin 13 sur le connecteur J7 compatible Arduino) sans intervention de la fonction main. Le changement d'état de LD6 se fera par interruption du Timer 2 toutes les 100 ms.

Blink 100ms.png

Ce premier programme vous permettra de découvrir le code minimum pour l'architecture PIC32MX, ainsi que la mise en oeuvre des Timer et l'implémentation des interruptions :

Mchp pic32 timer.png Timer period interrupt.png

Code source du programme

Voici le contenu du fichier "main.c" à ajouter dans un nouveau projet. Vous pouvez aussi télécharger le projet complet depuis "Exercise Files" sur la page "Interrupt Code Example in C using chipKIT™ WF32", déplacez alors le dossier "interrupt-usage" dans votre répertoire "C:\microchip\projects".

/*
** main.c
**
** Project: 	interrupt-example
**
** Date:    	04/06/2015
** Revision:	12/12/2016
** Author:  	DC
**
** Comments:    Timer 2 driven ISR used to toggle LED LD6 every 100 ms
**
** Hardware Design Reference:
**  "chipKIT WF32 Rev B.0" http://www.microchip.com/developmenttools/productdetails.aspx?partno=tdgl021
**
** Dependencies:
**  None
**
** User Guide:
**  http://microchip.wikidot.com/32bit:mx-code-examples-interrupt-usage
**
*/

/*******************************************************************************

SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP TECHNOLOGY INC OR ITS LICENSORS BE LIABLE OR OBLIGATED
UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY,
OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

*******************************************************************************/

/** INCLUDES ******************************************************************/

#include <xc.h>             /* contains Vector Name/Number Macros */
#include <sys/attribs.h>    /* contains __ISR() Macros */

/** CONFIGURATION *************************************************************/

// Key Settings:
// OSCILLATOR: 8 MHz XT Oscillator w. PLL
// SYSCLK = 80 MHz (set in config bits), PBCLK = 10 MHz (= SYSCLK/8 - set in config bits))
// JTAG PORT: Disabled
// WATCHDOG TIMER: Disabled
// DEBUG/PGM PINS: PGEC2/PGED2

// DEVCFG3
// USERID = No Setting
#pragma config FSRSSEL = PRIORITY_7     // SRS Select (SRS Priority 7)
#pragma config FMIIEN = ON              // Ethernet RMII/MII Enable (MII Enabled)
#pragma config FETHIO = ON              // Ethernet I/O Pin Select (Default Ethernet I/O)
#pragma config FUSBIDIO = ON            // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON           // USB VBUS ON Selection (Controlled by USB Module)

// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config UPLLIDIV = DIV_12        // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF             // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_1         // System PLL Output Clock Divider (PLL Divide by 1)

// DEVCFG1
#pragma config FNOSC = PRIPLL           // Oscillator Selection Bits (Primary Osc (XT,HS,EC) w. PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = ON                // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = XT             // Primary Oscillator Configuration (XT osc mode)
#pragma config OSCIOFNC = OFF           // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_8           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))

// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)


/** VARIABLES *****************************************************************/


/** LOCAL MACROS **************************************************************/

#define SYS_CLK_FREQUENCY   (80000000ull)           // Fsys = 80 MHz
#define PB3_CLK_FREQUENCY   SYS_CLK_FREQUENCY/8     // Fpb  = 10 MHz

// LED_LD6 (RG6)
#define LED_LD6       	LATGbits.LATG6
#define TRIS_LED_LD6  	TRISGbits.TRISG6
#define LED_LD6_SET()   LATGSET = _LATG_LATG6_MASK;
#define LED_LD6_CLR()   LATGCLR = _LATG_LATG6_MASK;
#define LED_LD6_INV()   LATGINV = _LATG_LATG6_MASK;

/** LOCAL PROTOTYPES **********************************************************/

void InitializeSystem(void);    // Initialize hardware and global variables

/** main() ********************************************************************/

int main(void)
{
    InitializeSystem();

    while(1);
    
} // main()

/******************************************************************************
 * Function:        void InitializeSystem(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This routine takes care of all of the system
 *                  initialization that is required.
 *
 * Note:
 *
 *****************************************************************************/
void InitializeSystem(void)
{
    // PIC32MX CPU Speed Optimizations (Cache/Wait States/Peripheral Bus Clks)
    // On reset, and after c-startup:
    // - Prefetch Buffer is disabled,
    // - I Cache is disabled,
    // - PFM wait States set to max setting (7 = too slow!!!)
    // - Data Memory SRAM wait states set to max setting (1 = too slow!!!)
    
    // PBCLK - already set to SYSCLK/8 via config settings
    
    // Data Memory SRAM wait states: Default Setting = 1; set it to 0
    BMXCONbits.BMXWSDRM = 0;

    // Flash PM Wait States: MX Flash runs at 2 wait states @ 80 MHz
    CHECONbits.PFMWS = 2;

    // Prefetch-cache: Enable prefetch for cacheable PFM instructions
    CHECONbits.PREFEN = 1;

    // PIC32MX695-Specific
    // JTAG: Disable on PORTA
    DDPCONbits.JTAGEN = 0;
    
    /* Initialize LED LD6 */
    LED_LD6 = 0;
    TRIS_LED_LD6 = 0;

    /* Initialize Timer 2 Peripheral Settings */
    // Turn off the timer
    T2CONbits.TON = 0;
    // Pre-Scale = 1:256 (T2Clk: 39062.5Hz)
    T2CONbits.TCKPS = 7;
    // Set T2 period ~ 100mS
    PR2 = 3906;
    // Clear counter
    TMR2 = 0;

    /* Initialize Timer 2 Interrupt Controller Settings */
    // Set the interrupt priority to 4
    IPC2bits.T2IP = 7;
    // Reset the Timer 2 interrupt flag
    IFS0bits.T2IF = 0;
    // Enable interrupts from Timer 2
    IEC0bits.T2IE = 1;
  
    /* Set Interrupt Controller for multi-vector mode */
    INTCONSET = _INTCON_MVEC_MASK;

    /* Enable Interrupt Exceptions */
    // set the CP0 status IE bit high to turn on interrupts globally
    __builtin_enable_interrupts();

    /* Enable the peripheral */
    T2CONbits.TON = 1;

} // InitializeSystem()

/*****************************************************************************
  Function:
	void __ISR(_TIMER_2_VECTOR, IPL4SRS) T2Interrupt(void);

  Description:
	Toggles LED LD6 when the Timer 2 interrupt occurs.

  Precondition:
        Peripheral interrupt config must set to matching ISR's IPL 

  Parameters:
	None

  Returns:
  	None
  ***************************************************************************/

void __ISR (_TIMER_2_VECTOR, IPL7SRS) T2Interrupt(void)
{
	// Toggle LED LD6
	LED_LD6_INV();

	// Reset interrupt flag
	IFS0bits.T2IF = 0;
}


Configuration Bits

Il est possible de visualiser, modifier puis importer les valeurs des registres de configuration du PIC32MX depuis la fenêtre "Configuration Bits" :

Mchp configuration bits.png

Une fois que vous aurez modifié les registres de configuration depuis cette fenêtre, il vous faudra générer le code source (bouton "Generate Source Code to Output"), puis copier-coller le contenu de la fenêtre "Output" dans le fichier main.c.


Mise en d'une machine à états

Instructions de commutation : Switch Case

Exercice

Ajoutez dans le répertoire "C:\microchip\projects" un dossier "firmware_state_machine", vous y placerez un nouveau projet "state_machine" que vous créerez avec MPLAB X (voir "Create a Standalone Project").

Ajoutez un fichier "main.c" que vous placerez dans votre répertoire source "C:\microchip\projects\firmware_state_machine\src" que vous aurez également ajouter au dossier principale de votre projet.

Voici quelques éléments pour vous guider, l'objectif est de réaliser un programme qui allume chacune des LEDs (LD5, LD4 et LD3), une par état, en respectant la durée de chaque état.

    while ( 1 )
    {
        /* Pseudo code algorithm */

        // What is the value of the variable "counter_100ms" ?
        //
        // if counter_100ms <= 5
        // --> go to step 1
        //
        // if counter_100ms > 5 & <= 10
        // --> go to step 2
        //
        // if counter_100ms > 10
        // --> go to step 3
        //
        // if counter_100ms = 20
        // --> reset counter_100ms & go to step 1

    }
    switch ( app_state )
    {
        case 1:
            // do something when "app_state" equals 1: 
            break;

        case 2:
            // do something when "app_state" equals 2
            break;

        case 3:
            // do something when "app_state" equals 3
            break;

        default:
            // if nothing else matches, do the default
            // default is optional
            break;
    }


Durant notre formation Interface microcontrôleur PIC32 et IHM Python, nous profitons de ce premier exercice pour montrer les différents fonctionnalités de l'IDE MPLAB X, comme par exemple le gestionnaire de projet, mais aussi comment mieux implémenter en C vos différentes routines et les méthodes pour le passage de paramètre ou la lecture de valeur, voir Modularity Guidelines (page 22)


Board Support Package (BSP)

Vous pouvez ajouter à votre projet dans le dossier "C:\microchip\projects\firmware_state_machine\src" le fichier "chipkit_wf32.h" avec le contenu suivant :

#ifndef _CHIPKIT_WF32_H
#define _CHIPKIT_WF32_H

/* LED_LD3 (RA0) */
#define LED_LD3       	LATAbits.LATA0
#define TRIS_LED_LD3  	TRISAbits.TRISA0
#define LED_LD3_SET()   LATASET = _LATA_LATA0_MASK;
#define LED_LD3_CLR()   LATACLR = _LATA_LATA0_MASK;
#define LED_LD3_INV()   LATAINV = _LATA_LATA0_MASK;

/* LED_LD4 (RA1) */
#define LED_LD4       	LATAbits.LATA1
#define TRIS_LED_LD4  	TRISAbits.TRISA1
#define LED_LD4_SET()   LATASET = _LATA_LATA1_MASK;
#define LED_LD4_CLR()   LATACLR = _LATA_LATA1_MASK;
#define LED_LD4_INV()   LATAINV = _LATA_LATA1_MASK;

/* LED_LD5 (RF0) */
#define LED_LD5       	LATFbits.LATF0
#define TRIS_LED_LD5  	TRISFbits.TRISF0
#define LED_LD5_SET()   LATFSET = _LATF_LATF0_MASK;
#define LED_LD5_CLR()   LATFCLR = _LATF_LATF0_MASK;
#define LED_LD5_INV()   LATFINV = _LATF_LATF0_MASK;

/* LED_LD6 (RG6) */
#define LED_LD6       	LATGbits.LATG6
#define TRIS_LED_LD6  	TRISGbits.TRISG6
#define LED_LD6_SET()   LATGSET = _LATG_LATG6_MASK;
#define LED_LD6_CLR()   LATGCLR = _LATG_LATG6_MASK;
#define LED_LD6_INV()   LATGINV = _LATG_LATG6_MASK;

#endif


Comme vous pouvez le remarque l'ensemble de ces définitions fournit une interface simple entre votre programme et la partie bas niveau permettant de contrôler les broches du microcontrôleur pour le pilotage des LEDs de la carte. Mais si nous devions écrire ce code pour différentes cartes il serait plus judicieux d'utiliser des noms génériques comme BSP_LED_1On() ou BSP_LED_1Off() et non propre à la carte. Nous avons donc montré pendant cette semaine comment ajouter une nouvelle configuration pour inclure une 2ème carte dans un même projet en utilisant l'écriture standardisé comme proposé avec l'utilisation du "Board Support Package (BSP) Library Interface".

chipkit_wf32/bsp.h chipkit_wifire/bsp.h
#ifndef _CHIPKIT_WF32_H
#define _CHIPKIT_WF32_H

/*** Application Defined Pins ***/
// https://reference.digilentinc.com/chipkit_wf32/chipkit_wf32

/*** Functions for BSP_LED_1 pin ***/
#define BSP_LED_1Toggle()       LATAINV = _LATA_LATA0_MASK;
#define BSP_LED_1On()           LATASET = _LATA_LATA0_MASK;
#define BSP_LED_1Off()          LATACLR = _LATA_LATA0_MASK;
#define BSP_LED_1StateGet()     LATAbits.LATA0
#define BSP_LED_1Initialize()   TRISAbits.TRISA0 = 0;
    
/*** Functions for BSP_LED_2 pin ***/
#define BSP_LED_2Toggle()       LATAINV = _LATA_LATA1_MASK;
#define BSP_LED_2On()           LATASET = _LATA_LATA1_MASK;
#define BSP_LED_2Off()          LATACLR = _LATA_LATA1_MASK;
#define BSP_LED_2StateGet()     LATAbits.LATA1
#define BSP_LED_2Initialize()   TRISAbits.TRISA1 = 0;

/*** Functions for BSP_LED_3 pin ***/
#define BSP_LED_3Toggle()       LATFINV = _LATF_LATF0_MASK;
#define BSP_LED_3On()           LATFSET = _LATF_LATF0_MASK;
#define BSP_LED_3Off()          LATFCLR = _LATF_LATF0_MASK;
#define BSP_LED_3StateGet()     LATFbits.LATF0
#define BSP_LED_3Initialize()   TRISFbits.TRISF0 = 0;

/*** Functions for BSP_LED_4 pin ***/
#define BSP_LED_4Toggle()       LATGINV = _LATG_LATG6_MASK;
#define BSP_LED_4On()           LATGSET = _LATG_LATG6_MASK;
#define BSP_LED_4Off()          LATGCLR = _LATG_LATG6_MASK;
#define BSP_LED_4StateGet()     LATGbits.LATG6
#define BSP_LED_4Initialize()   TRISGbits.TRISG6 = 0;

#endif
#ifndef _CHIPKIT_WIFIRE_H
#define _CHIPKIT_WIFIRE_H

/*** Application Defined Pins ***/
// https://reference.digilentinc.com/chipkit_wifire/chipkit_wifire

/*** Functions for BSP_LED_1 pin ***/
#define BSP_LED_1Toggle()       LATGINV = _LATG_LATG6_MASK;
#define BSP_LED_1On()           LATGSET = _LATG_LATG6_MASK;
#define BSP_LED_1Off()          LATGCLR = _LATG_LATG6_MASK;
#define BSP_LED_1StateGet()     LATGbits.LATG6
#define BSP_LED_1Initialize()   TRISGbits.TRISG6 = 0;

/*** Functions for BSP_LED_2 pin ***/
#define BSP_LED_2Toggle()       LATDINV = _LATD_LATD4_MASK;
#define BSP_LED_2On()           LATDSET = _LATD_LATD4_MASK;
#define BSP_LED_2Off()          LATDCLR = _LATD_LATD4_MASK;
#define BSP_LED_2StateGet()     LATDbits.LATD4
#define BSP_LED_2Initialize()   TRISDbits.TRISD4 = 0;

/*** Functions for BSP_LED_3 pin ***/
#define BSP_LED_3Toggle()       LATBINV = _LATB_LATB11_MASK;
#define BSP_LED_3On()           LATBSET = _LATB_LATB11_MASK;
#define BSP_LED_3Off()          LATBCLR = _LATB_LATB11_MASK;
#define BSP_LED_3StateGet()     LATBbits.LATB11
#define BSP_LED_3Initialize()   TRISBbits.TRISB11 = 0;

/*** Functions for BSP_LED_4 pin ***/
#define BSP_LED_4Toggle()       LATGINV = _LATG_LATG15_MASK;
#define BSP_LED_4On()           LATGSET = _LATG_LATG15_MASK;
#define BSP_LED_4Off()          LATGCLR = _LATG_LATG15_MASK;
#define BSP_LED_4StateGet()     LATGbits.LATG15
#define BSP_LED_4Initialize()   TRISGbits.TRISG15 = 0;

#endif