Chapitre 12_Expérience d'interruption externe dans la série de didacticiels d'introduction à base zéro du MCU Renesas

Ce tutoriel est écrit sur la base de la carte de développement DShanMCU-RA6M5 produite par Wei Dongshan Baiwen.com . Les étudiants qui en ont besoin peuvent l'obtenir ici : https://item.taobao.com/item.htm?id=728461040949

Accès aux supports : https://renesas-docs.100ask.net

Résumé du didacticiel de la série d'entrées à base zéro Renesas MCU : https://blog.csdn.net/qq_35181236/article/details/132779862


Chapitre 12 Expérience d'interruption externe

Objectifs de ce chapitre

  • Utilisez RASC pour configurer rapidement le mode d'interruption externe du GPIO
  • Apprenez à utiliser l'interruption externe du GPIO pour traiter les informations clés

12.1 Le module ICU configure les interruptions externes

Il est mentionné dans le livre "Chapitre 10 Introduction à l'unité de contrôle d'interruption" que l'événement d'interruption de l'ICU peut être connecté à la fonction de service d'interruption définie par l'utilisateur via la liaison d'événements, et que l'interruption définie par l'utilisateur peut être gérée par l'utilisateur. Bien entendu, des interruptions externes peuvent également le faire. Liez l'interruption externe correspondant au GPIO du bouton à une fonction d'interruption personnalisée, telle que key_irq. Dans key_irq, le registre est lu, écrit, effacé et le code utilisateur est exécuté.

L'avantage est que les développeurs peuvent concevoir des fonctions d'interruption avec un degré élevé de liberté et avoir un contrôle extrêmement élevé sur le traitement des interruptions. Cependant, la fonction de configuration de RASC n'est pas bien utilisée. Utilisez RASC pour configurer le module ICU pour utiliser les interruptions. Vous n'avez pas besoin de vous soucier du traitement des registres sous-jacents. Vous devez uniquement vous soucier de la méthode de déclenchement des interruptions et de l'enregistrement de la fonction de rappel d'interruption.

La méthode utilisée dans ce livre consiste à utiliser RASC pour configurer le module ICU afin d'utiliser des interruptions externes pour obtenir l'état du bouton. De plus, ce chapitre utilisera les fonctions Led et printf. Veuillez vous référer aux chapitres précédents pour configurer les modules LED GPIO et UART. Et la fonction pilote du tick timer sera également utilisée. Les lecteurs sont invités à transplanter le fichier pilote hal_sysstick.c/.h du tick timer dans le "Chapitre 11 SysTick" dans ce projet.

12.1.1 Configurer le canal ICU

Ce qui doit être configuré dans ce chapitre, c'est le module ICU. Après avoir créé le projet dans RASC, recherchez « Entrée : ICU » dans « Périphériques » dans « Broches » de l'interface de configuration, sélectionnez « ICU0 » et modifiez le « Mode de fonctionnement ». de « Désactiver » à « Activer », puis sélectionnez le canal IRQ et la broche utilisée, comme indiqué dans la figure suivante :

Lors de la sélection d'un canal, vous devez le sélectionner en fonction de l'E/S utilisée dans la conception matérielle. Par exemple, le bouton IO utilisé dans ce livre est P000. Le diagramme schématique est le suivant :

À quel canal IRQ correspond P000 ? Cela nécessite de vérifier la fiche technique de la puce. Dans la fiche technique du RA6M5, le canal IRQ utilisé par la broche P000 est de 6 canaux, comme le montre la figure :

Sélectionnez donc IRQ06 de ICU0 dans la configuration RASC et sélectionnez la broche comme P000.

Allez ensuite dans "Stacks" dans l'interface de configuration RASC pour ajouter le module ICU. Dans « Nouvelle pile », développez « Entrée » et sélectionnez « IRQ externe (r_icu) ». comme le montre la photo :

Enfin, configurez la fonction de rappel de canal et d'interruption de ce module. Tout d'abord, jetons un œil aux résultats de configuration :

La configuration principale est constituée des paramètres encerclés sur l'image :

  • nom : suggestion et correspondance de chaîne ;
  • Canal : selon la sélection de configuration mentionnée ci-dessus, ce livre utilise le canal 6 ;
  • Mode de déclenchement : prend en charge le déclenchement du bord montant/descendant/double bord/niveau bas. Selon la conception matérielle, ce livre utilise le déclenchement du bord descendant ;
  • Fonction de rappel d'interruption : définie par l'utilisateur ;
  • Priorité d'interruption : choisissez en fonction des besoins. Ce livre utilise une priorité de 12 niveaux ;

Après avoir défini la configuration ci-dessus, vous pouvez cliquer sur « Générer le contenu du projet » de RASC pour générer le projet.

12.1.2 Interprétation des informations de configuration

Après avoir configuré l'ICU à l'aide de RASC, les informations de configuration des broches correspondant au canal IRQ de l'ICU et les informations de configuration de l'ICU elle-même sont générées. Prenons l'exemple de l'expérience "1201_icu_irq" dans ce chapitre.

  1. Informations sur la configuration des broches

Ces informations seront générées dans le fichier 1201_icu_irq\ra_gen\pin_data.c. Chaque broche configurée dans RASC générera un élément de tableau ioport_pin_cfg_t dans pin_data.c, et le contenu à l'intérieur est cohérent avec les paramètres sélectionnés lors de la configuration. le code s'affiche comme ci-dessous :

const ioport_pin_cfg_t g_bsp_pin_cfg_data[] =
{
    
    
      {
    
     .pin = BSP_IO_PORT_00_PIN_00, 
        .pin_cfg = ((uint32_t) IOPORT_CFG_IRQ_ENABLE
                  | (uint32_t) IOPORT_CFG_PORT_DIRECTION_INPUT) },
      ......(省略信息)
      {
    
     .pin = BSP_IO_PORT_04_PIN_00, 
        .pin_cfg = ((uint32_t) IOPORT_CFG_DRIVE_HIGH | (uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT |
                  (uint32_t) IOPORT_CFG_PORT_OUTPUT_LOW) },
};
  • Lignes 03 à 05 : configurez la broche du bouton P000 comme direction d'entrée et activez l'IRQ ;
  • Lignes 07 à 09 : configurez la broche LED P0400 comme direction de sortie et niveau haut de sortie par défaut ;
  1. Informations de configuration de l'USI

Les informations de configuration de l'ICU sont divisées en deux parties : les informations sur le module ICU et la fonction de service d'interruption ICU.

Les informations sur le module ICU seront générées dans le fichier 1201_icu_irq\ra_gen\common_data.c. Le canal IRQ, le mode de déclenchement et la fonction de service d'interruption configurés dans RASC généreront une structure external_irq_cfg_t dans common_data.c, et le contenu à l'intérieur est cohérent avec les paramètres sélectionnés lors de la configuration. le code s'affiche comme ci-dessous :

const external_irq_cfg_t g_external_irq6_cfg =
{
    
     .channel = 6,
  .trigger = EXTERNAL_IRQ_TRIG_FALLING,
  ......(省略内容)
  .p_callback = ex_irq6_callback,
  ......(省略内容)
        };
  • Ligne 02 : sélectionnez la chaîne ;
  • Ligne 03 : définir le mode de déclenchement ;
  • Ligne 05 : enregistrer la fonction de rappel d'interruption ;

L'enregistrement de la fonction du service d'interruption ICU est complété dans vector_data.c. Une table de vecteurs d'interruption sera définie dans ce fichier. Le code est le suivant :

#if VECTOR_DATA_IRQ_COUNT > 0
    BSP_DONT_REMOVE const fsp_vector_t g_vector_table[BSP_ICU_VECTOR_MAX_ENTRIES] BSP_PLACE_IN_SECTION(BSP_SECTION_APPLICATION_VECTORS) =
    {
    
    
        [0] = sci_uart_rxi_isr, /* SCI7 RXI (Received data full) */
        [1] = sci_uart_txi_isr, /* SCI7 TXI (Transmit data empty) */
        [2] = sci_uart_tei_isr, /* SCI7 TEI (Transmit end) */
        [3] = sci_uart_eri_isr, /* SCI7 ERI (Receive error) */
        [4] = r_icu_isr, /* ICU IRQ6 (External pin interrupt 6) */
    };
    const bsp_interrupt_event_t g_interrupt_event_link_select[BSP_ICU_VECTOR_MAX_ENTRIES] =
    {
    
    
        [0] = BSP_PRV_IELS_ENUM(EVENT_SCI7_RXI), /* SCI7 RXI (Received data full) */
        [1] = BSP_PRV_IELS_ENUM(EVENT_SCI7_TXI), /* SCI7 TXI (Transmit data empty) */
        [2] = BSP_PRV_IELS_ENUM(EVENT_SCI7_TEI), /* SCI7 TEI (Transmit end) */
        [3] = BSP_PRV_IELS_ENUM(EVENT_SCI7_ERI), /* SCI7 ERI (Receive error) */
        [4] = BSP_PRV_IELS_ENUM(EVENT_ICU_IRQ6), /* ICU IRQ6 (External pin interrupt 6) */
    };
#endif
  • Lignes 04 à 07 : enregistrez la fonction de service d'interruption pour la communication UART ;
  • Ligne 08 : Enregistrez la fonction de service d'interruption ICU ;
  • Lignes 10 à 17 : tableau des fonctions du service d'interruption de liaison ;

Ce que vous devez comprendre dans ce chapitre est l'implémentation de la fonction de service d'interruption d'ICU r_icu_isr. Son code source est le suivant :

void r_icu_isr (void)
{
    
    
    /* Save context if RTOS is used */
    FSP_CONTEXT_SAVE

    IRQn_Type             irq    = R_FSP_CurrentIrqGet();
    icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) R_FSP_IsrContextGet(irq);

    bool level_irq = false;
    if (EXTERNAL_IRQ_TRIG_LEVEL_LOW == R_ICU->IRQCR_b[p_ctrl->channel].IRQMD)
    {
    
    
        level_irq = true;
    }
    else
    {
    
    
        /* Clear the IR bit before calling the user callback so that if an edge is detected while the ISR is active
         * it will not be missed. */
        R_BSP_IrqStatusClear(irq);
    }

    if ((NULL != p_ctrl) && (NULL != p_ctrl->p_callback))
    {
    
    
#if BSP_TZ_SECURE_BUILD
    ......(省略内容)
#else
        /* Set data to identify callback to user, then call user callback. */
        external_irq_callback_args_t args;
        args.channel   = p_ctrl->channel;
        args.p_context = p_ctrl->p_context;
        p_ctrl->p_callback(&args);
#endif
    }
    ......(省略内容)
}
  • Lignes 06 à 07 : obtenez des informations sur les interruptions ;
  • Ligne 18 : effacez l'interruption ;
  • Lignes 27 à 29 : enregistrez les informations d'interruption, qui seront transmises à la fonction de service d'interruption en tant que paramètre ;
  • Ligne 30 : Exécuter la fonction de rappel d'interruption

1.1.3 Fonction de rappel d'interruption

Le prototype de la fonction de rappel d'interruption a été déclaré dans common_data.h, et les utilisateurs doivent l'implémenter dans leurs propres programmes. Le code du prototype est le suivant :

void ex_irq6_callback(external_irq_callback_args_t *p_args);

Son type de paramètre est le pointeur de structure external_irq_callback_args_t. Le prototype de cette structure est le suivant :

typedef struct st_external_irq_callback_args
{
    
    
    /** Placeholder for user data. Set in @ref external_irq_api_t::open function in @ref external_irq_cfg_t. */
    void const * p_context;
    uint32_t     channel;  ///< The physical hardware channel that caused the interrupt.
} external_irq_callback_args_t;

Cette structure indique uniquement le contenu (contexte) et le canal (canal) de l'interruption externe, et le contexte est défini sur NULL lors de l'initialisation et n'est pas utilisé. Par conséquent, seule la valeur du canal peut être utilisée : utilisée pour déterminer quelle interruption de canal s'est produite.

Par exemple, l'expérience de ce chapitre est la suivante : après avoir découvert qu'une interruption de touche s'est produite dans la fonction de rappel d'interruption, utilisez un minuteur de tick pour éliminer la gigue. Le code est le suivant :

static volatile uint32_t uwPressTick = 0;
void ex_irq6_callback(external_irq_callback_args_t * p_args)
{
    
    
    if(p_args->channel == 6)    // 按键的GPIO的ICU通道是6
    {
    
    
        uwPressTick = HAL_GetTick() + 100;
    }
}

Le principe de l'élimination du jitter est expliqué dans les chapitres suivants.

12.1.4 Interface API et son utilisation

L'interface du module ICU est définie dans le chemin 1201_icu_irq/ra/fsp/inc/api/r_external_irq_api.h. Elle définit un type de structure external_irq_api_t avec le contenu suivant :

typedef struct st_external_irq_api
{
    
    
    fsp_err_t (* open)(external_irq_ctrl_t * const p_ctrl, 
                       external_irq_cfg_t const * const p_cfg);
    fsp_err_t (* enable)(external_irq_ctrl_t * const p_ctrl);
    fsp_err_t (* disable)(external_irq_ctrl_t * const p_ctrl);
    fsp_err_t (* callbackSet)(external_irq_ctrl_t * const p_api_ctrl,
                              void (* p_callback)(external_irq_callback_args_t *),
                              void const * const p_context,
                             external_irq_callback_args_t * const p_callback_memory);
    fsp_err_t (* close)(external_irq_ctrl_t * const p_ctrl);
} external_irq_api_t;

Dans un fichier C spécifique, une variable de structure external_irq_api_t doit être définie. Par exemple, la définition de la variable de structure suivante est implémentée dans r_icu.c :

const external_irq_api_t g_external_irq_on_icu =
{
    
    
    .open        = R_ICU_ExternalIrqOpen,
    .enable      = R_ICU_ExternalIrqEnable,
    .disable     = R_ICU_ExternalIrqDisable,
    .callbackSet = R_ICU_ExternalIrqCallbackSet,
    .close       = R_ICU_ExternalIrqClose,
};

Pour utiliser l'interface de contrôle ICU, vous pouvez appeler chaque pointeur de fonction dans la structure g_external_irq_on_icu, ou appeler directement chaque fonction implémentée dans r_icu.c.

  1. Activer/désactiver l'interruption externe

La fonction d'ouverture d'interruption externe est utilisée pour configurer le type d'interruption externe, définir les fonctions de rappel, définir les canaux, etc. Le prototype de fonction est le suivant :

fsp_err_t (* open)(external_irq_ctrl_t * const p_ctrl, external_irq_cfg_t const * const p_cfg);

La mise en œuvre est la suivante :

fsp_err_t R_ICU_ExternalIrqOpen (external_irq_ctrl_t * const p_api_ctrl, 
                                 external_irq_cfg_t const * const p_cfg)
{
    
    
    icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
    ......(省略内容)
    p_ctrl->irq = p_cfg->irq;	// 中断类型
    ......(省略内容)
    /* Initialize control block. */
    p_ctrl->p_callback = p_cfg->p_callback;
    p_ctrl->p_context  = p_cfg->p_context;
    p_ctrl->channel    = p_cfg->channel;
    ......(省略内容)
    p_ctrl->open = ICU_OPEN;

    return FSP_SUCCESS;
}

Le premier paramètre de la fonction open est de type external_irq_ctrl_t. Comme beaucoup de périphériques précédents, il s'agit d'une structure de type void :

typedef void external_irq_ctrl_t;

Le deuxième paramètre de la fonction open est external_irq_cfg_t. Cette structure décrit le canal, la priorité, le type de terminal, la méthode de déclenchement, etc. de l'interruption externe. Le code est le suivant :

typedef struct st_external_irq_cfg
{
    
    
    uint8_t                 channel;   ///< Hardware channel used.
    uint8_t                 ipl;       ///< Interrupt priority
    IRQn_Type               irq;       ///< NVIC interrupt number assigned to this instance
    external_irq_trigger_t  trigger;   ///< Trigger setting.
    external_irq_pclk_div_t pclk_div;  ///< Digital filter clock divisor setting.
    bool filter_enable;                ///< Digital filter enable/disable setting.

    /** Callback provided external input trigger occurs. */
    void (* p_callback)(external_irq_callback_args_t * p_args);

    /** Placeholder for user data.  Passed to the user callback in @ref external_irq_callback_args_t. */
    void const * p_context;
    void const * p_extend;             ///< External IRQ hardware dependent configuration.
} external_irq_cfg_t;

Il est relativement simple de fermer la fonction de fermeture d'interruption externe. FSP désactive les interruptions. Le code est le suivant :

fsp_err_t R_ICU_ExternalIrqClose (external_irq_ctrl_t * const p_api_ctrl)
{
    
    
    icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
	......(省略内容)
    /* Cleanup. Disable interrupt */
    if (p_ctrl->irq >= 0)
    {
    
    
        /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
        R_BSP_IrqDisable(p_ctrl->irq);
        R_FSP_IsrContextSet(p_ctrl->irq, NULL);
    }
    p_ctrl->open = 0U;
    return FSP_SUCCESS;
}
  • Ligne 09 : désactiver l'interruption ;
  • Ligne 12 : Supprimez le drapeau ouvert ;

Les développeurs appellent la fonction open pour initialiser les interruptions externes et enregistrer les fonctions de rappel d'interruption. Cependant, il convient de noter que la fonction open n'active pas les interruptions. Vous pouvez vous référer au code suivant pour activer les interruptions :

fsp_err_t err = g_external_irq6.p_api->open(g_external_irq6.p_ctrl, 
                                            g_external_irq6.p_cfg);
if(FSP_SUCCESS == err)
{
    
    
	printf("Success to open Key device: %s!\r\n", _iodev->DevInfo.name);
}
  1. Activer/désactiver l'interruption externe

La fonction pour activer et désactiver les interruptions externes est relativement simple. Il suffit de passer un paramètre de type external_irq_ctrl_t de la structure de contrôle de l'interface d'interruption externe. Le prototype est le suivant :

fsp_err_t (* enable)(external_irq_ctrl_t * const p_ctrl);
fsp_err_t (* disable)(external_irq_ctrl_t * const p_ctrl);

La mise en œuvre de ces deux fonctions est également très simple :

fsp_err_t R_ICU_ExternalIrqEnable (external_irq_ctrl_t * const p_api_ctrl)
{
    
    
    icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
    ......(省略内容)
    /* Clear the interrupt status and Pending bits, before the interrupt is enabled. */
    R_BSP_IrqEnable(p_ctrl->irq);
    return FSP_SUCCESS;
}

fsp_err_t R_ICU_ExternalIrqDisable (external_irq_ctrl_t * const p_api_ctrl)
{
    
    
    icu_instance_ctrl_t * p_ctrl = (icu_instance_ctrl_t *) p_api_ctrl;
    ......(省略内容)
    /* Disable the interrupt, and then clear the interrupt pending bits and interrupt status. */
    R_BSP_IrqDisable(p_ctrl->irq);
    return FSP_SUCCESS;
}
  • Ligne 06 : appelez la fonction en ligne R_BSP_IrqEnable pour activer l'interruption ;
  • Ligne 15 : appelez la fonction en ligne R_BSP_IrqDisable pour désactiver l'interruption ;

Les développeurs peuvent se référer au code suivant pour activer les interruptions externes :

fsp_err_t err = g_external_irq6.p_api->enable(g_external_irq6.p_ctrl);
if(FSP_SUCCESS == err)
    printf("Success to enable %s's irq!\r\n", _iodev->DevInfo.name);

12.2 Interruption pour obtenir une expérience sur l'état du bouton

12.2.1 Connexion matérielle

L'expérience de ce chapitre utilise le bouton intégré et son GPIO est P000. Le diagramme schématique est présenté dans la figure :

On peut voir sur la figure que lorsque le bouton est enfoncé, P000 est directement mis à la terre, indiquant un niveau bas ; une fois le bouton relâché, P000 est tiré vers VDD par R15, indiquant un niveau haut.

12.2.2 Analyse du pilote de bouton

Une fois le bouton enfoncé et relâché, le changement de niveau ne passe pas immédiatement du niveau haut au niveau bas ou du niveau bas au niveau haut. Il y aura une période de gigue au milieu, comme le montre la figure suivante :

Les développeurs doivent éviter la période de gigue et obtenir l’état du bouton lorsque celui-ci est stable.

12.2.3 Pilote de bouton

  1. Fonction de rappel d'interruption

Dans la fonction de rappel d'interruption, mettez à jour le temps d'appui sur le bouton T+100, le code est le suivant :

static volatile uint32_t uwPressTick= 0;
void ex_irq6_callback(external_irq_callback_args_t * p_args)
{
    
    
    if(p_args->channel == 6)    // 按键的GPIO的ICU通道是6
    {
    
    
        uwPressTick = HAL_GetTick() + 100;
    }
}

Pour un bouton mécanique, si l'utilisateur appuie une fois dessus, son interrupteur mécanique peut vibrer plusieurs fois et déclencher plusieurs interruptions. Le principe de l'élimination de la gigue est le suivant : lorsqu'une interruption se produit, ne la traitez pas immédiatement, mais « reportez le traitement ». À la ligne 6, uwPressTick est mis à jour à « heure actuelle + 100 » lorsqu'une interruption se produit. Par exemple, 20 vibrations se sont produites sur une courte période, entraînant 20 interruptions, et uwPressTick a été mis à jour et reporté 20 fois. Lorsque la vibration mécanique s'est arrêtée, l'uwPressTick mis à jour par la dernière interruption n'a plus changé. Lorsque le dwTick de SysTick atteint uwPressTick, le programme utilisateur est à nouveau exécuté. De cette manière, 20 interruptions n'entraînent qu'une seule exécution du programme utilisateur, ce qui élimine le jitter.

  1. Encapsulation d'objets IO

Dans cette expérience, les LED et les boutons sont regroupés sous forme d'objet périphérique GPIO, et une structure IODev est utilisée pour représenter le périphérique. La description, l'initialisation, la lecture et l'écriture du périphérique sont implémentées dans la structure. Le code structure est le suivant :

typedef struct IODev{
    
    
    char         *name;
    int          (*Init)(struct IODev *ptDev);
    int          (*Write)(struct IODev *ptDev, IODevState_t level);
    IODevState_t (*Read)(struct IODev *ptDev);
}IODev, *PIODev;

Il existe deux types d'états de niveau des périphériques IO : haut et bas. Le type d'énumération IODevState_t est le suivant :

typedef enum{
    
    
    LowLevel,
    HighLevel,
    ErrLevel = -1
}IODevState_t;
  1. Initialisation d'interruption externe

L'expérience de ce chapitre utilise des boutons pour contrôler les LED, vous devez donc initialiser le bouton et activer son interruption, ainsi que initialiser le GPIO de la LED. Le code est le suivant :

static int IODrvInit(struct IODev *ptdev)
{
    
    
    if(ptdev->name == NULL)     return -1;
    /* 初始化按键对应的外部中断且使能 */
    if(strcmp(ptdev->name, "UserKey") == 0)
    {
    
    
        fsp_err_t err = g_external_irq6.p_api->open(g_external_irq6.p_ctrl, 
                                                    g_external_irq6.p_cfg);
        if(FSP_SUCCESS != err)
        {
    
    
            printf("Failed to open Key device: %s!\r\n", ptdev->name);
            return -1;
        }
        err = g_external_irq6.p_api->enable(g_external_irq6.p_ctrl);
        if(FSP_SUCCESS == err)
            printf("Success to enable %s's irq!\r\n", ptdev->name);
        else
        {
    
    
            printf("Failed to enable %s's irq!\r\n", ptdev->name);
            return -1;
        }
    }
    /* 初始化LED灯GPIO */
    if(strcmp(ptdev->name, "UserLed") == 0)
    {
    
    
        fsp_err_t err = g_ioport.p_api->open(g_ioport.p_ctrl, g_ioport.p_cfg);
        if(FSP_ERR_ALREADY_OPEN == err)
        {
    
    
            printf("Error. GPIOs are already open and init.\r\n");
            return -1;
        }
        else if(FSP_SUCCESS == err)
            printf("Success to open Led device: %s!\r\n", ptdev->name);
    }
    return 0;
}
  • Lignes 08 à 15 : Si l'interruption externe du bouton GPIO est activée avec succès, l'interruption externe est activée ;
  • Lignes 26 à 36 : initialisez le GPIO de la LED ;
  1. Traitement de la gigue des touches

Mettez à jour uwPressTick dans la fonction de rappel d'interruption à "heure actuelle + 100", dans l'intention de le traiter après 100 ms. Qui s’en chargera ? Traité dans la fonction d'interruption SysTick, le code est le suivant :

void SysTick_Handler(void)
{
    
    
    dwTick += 1;
    extern void KeyProcessJitter(uint32_t tick);
    KeyProcessJitter(dwTick);
}

l Ligne 03 : Laisser dwTick accumuler 1 ;
l Ligne 05 : Appeler la fonction KeyProcessJitter pour traiter les clés

La clé est la fonction KeyProcessJitter, qui est appelée à chaque interruption SysTick, mais qui ne fait fonctionner la LED que lorsque la valeur dwTick est égale à uwPressTick :

static volatile IODevState_t gLedLevel = HighLevel;
void KeyProcessJitter(uint32_t tick)
{
    
    
    if(tick == uwPressTick)
    {
    
    
        gLedDev.Write(&gLedDev, gLedLevel);
        gLedLevel = !gLedLevel;
    }        
}

12.2.4 Procédures d'essai

Étant donné que les pressions sur les touches et les changements d'état des LED sont traités sous forme d'interruptions, il n'est pas nécessaire d'écrire du code de test dans main pour cette expérience.

12.2.5 Expérience informatique

Initialisez simplement le timer tick, l'UART, le périphérique clé et le périphérique LED dans la fonction hal_entry(). Lorsque la touche est enfoncée, la fonction de service d'interruption est appelée. Le code de la fonction han_entry() est le suivant :

#include "drv_uart.h"
#include "drv_gpio.h"
#include "hal_systick.h"
#include "hal_data.h"
#include <stdio.h>
void hal_entry(void)
{
    
    
    /* TODO: add your own code here */
    SystickInit();
    UARTDrvInit();
    IODev *ptKeyDev = IOGetDecvice("UserKey");
    if(NULL == ptKeyDev)
    {
    
    
        printf("Failed to get UserKey!\r\n");
        return;
    }
    IODev *ptLedDev = IOGetDecvice("UserLed");
    if(NULL == ptLedDev)
    {
    
    
        printf("Failed to get UserLed!\r\n");
        return;
    }
    if(ptKeyDev->Init(ptKeyDev) !=0)    return;
    if(ptLedDev->Init(ptLedDev) !=0)    return;
}

Gravez le fichier exécutable binaire compilé dans la puce et exécutez-le, utilisez l'assistant de débogage du port série pour observer les informations imprimées et appuyez sur les boutons de la carte de développement pour contrôler la LED.


fin du chapitre

Je suppose que tu aimes

Origine blog.csdn.net/qq_35181236/article/details/132789410
conseillé
Classement