Configuration et Développement Embarqué avec CubeMX pour STM32

Création du Projet et Configuration Initiale avec CubeMX

Pour commencer un projet basé sur le microcontrôleur STM32G431RBT6, CubeMX permet une initialisation rapide. Sélectionnez le chip dans l'interface, puis configurez les éléments fondamentaux :

  • Activez l'horloge externe (HSE) dans System Core pour une stabilité accrue.
  • Réglez le mode de débogage, par exemple via SWD.
  • Configurez la fréquence d'horloge principale à 80 MHz en ajustant les prédiviseurs.
  • Dans les paramètres de génération, optez pour la version minimale V5 du HAL, cochez l'option pour copier uniquement les fichiers bibliothèque nécessaires et séparez les fichiers .c et .h pour une meilleure organisation.

Affichage sur Écran LCD

Les broches de l'écran LCD, définies dans le schéma de câblage, doivent être configurées comme sorties GPIO en mode push-pull. Après avoir généré le code, intégrez les fichiers source et en-tête LCD fournis (lcd.c, lcd.h, fonts.h).

Pour afficher des données, utilisez la fonction LCD_DisplayStringLine combinée avec sprintf pour formater les chaînes de caractères. Il est conseillé de déclarer les buffers d'affichage en tant que variables globales pour éviter les dépassements de pile.

#include "lcd.h"
#include <stdio.h>

float voltage_reading = 0.0;
uint16_t output_frequency = 1000;
uint8_t duty_cycle = 40;
char display_buffer[21];  // Buffer pour l'affichage, taille ajustée

void Update_LCD_Display(void) {
    LCD_DisplayStringLine(Line1, (uint8_t *)"       DONNEES     ");
    sprintf(display_buffer, "   Tension: %4.2fV", voltage_reading);
    LCD_DisplayStringLine(Line3, (uint8_t *)display_buffer);
    sprintf(display_buffer, "   C.C.: %2d%%", duty_cycle);
    LCD_DisplayStringLine(Line5, (uint8_t *)display_buffer);
    sprintf(display_buffer, "   Freq: %-5dHz", output_frequency);
    LCD_DisplayStringLine(Line7, (uint8_t *)display_buffer);
}

Gestion des Boutons Poussoirs

L'implémentation des boutons doit assurer une détection unique par appui, avec reconnaissance du relâchement, sans blocage du programme. Évitez les délais de type Delay et privilégiez la gestion par polling périodique. Ajoutez une gestion de l'appui long si nécessaire.

Configuration dans CubeMX : les broches des boutons (par exemple PB0, PB1, PB2, PA0) sont configurées en mode GPIO entrée sans résistance de rappel interne.

Dans le code, on lit l'état des boutons et on détecte les fronts montants :

#include "key.h"

#define BTN_A  HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)
#define BTN_B  HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)
#define BTN_C  HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2)
#define BTN_D  HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)
#define BUTTONS_MASK ((BTN_A | (BTN_B << 1) | (BTN_C << 2) | (BTN_D << 3)) | 0xF0)

uint8_t press_trigger;  // Détection d'appui unique
uint8_t previous_state;

void Scan_Buttons(void) {
    uint8_t current_state = BUTTONS_MASK ^ 0xFF;  // Inversion pour logique active high
    press_trigger = current_state & (current_state ^ previous_state);
    previous_state = current_state;
}

#ifndef KEY_H
#define KEY_H
#include "main.h"
extern uint8_t press_trigger;
extern uint8_t previous_state;
#endif

Pour intégrer la gestion des boutons dans la boucle principale, utilisez un horodatage pour limiter la fréquence de scan :

uint32_t last_key_time = 0;
void Process_Buttons(void) {
    if (HAL_GetTick() - last_key_time < 20) return;  // Exécution toutes les 20 ms
    last_key_time = HAL_GetTick();
    Scan_Buttons();
    if (press_trigger & 0x01) {
        // Action pour le bouton A
    }
    if (press_trigger & 0x02) {
        // Action pour le bouton B
    }
    // etc. pour les autres boutons
}

Contrôle des LEDs avec Verroiullage

Les LEDs sont contrôlées via un registre à décalage avec une broche de verrouillage (PD2). Dans CubeMX, configurez PD2 en sortie GPIO. Le logiciel doit initialiser les LEDs en éteignant toutes les sorties sur PC8-PC15, puis utiliser la broche de verrouillage pour transférer les données.

#ifndef LED_CONTROL_H
#define LED_CONTROL_H
#include "main.h"
void Set_LED_State(uint8_t led_pattern);
#endif

#include "led_control.h"

void Set_LED_State(uint8_t pattern) {
    HAL_GPIO_WritePin(GPIOC, 0xFF00, GPIO_PIN_SET);  // Éteindre toutes les LEDs (actives à l'état bas)
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);   // Ouvrir le verrouillage
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); // Fermer le verrouillage

    HAL_GPIO_WritePin(GPIOC, (pattern << 8), GPIO_PIN_RESET);  // Allumer les LEDs sélectionnées
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}

Exemple d'utilisation dans une tâche périodique :

uint8_t led_pattern = 0x00;
uint32_t led_timer = 0;

void LED_Task(void) {
    if (HAL_GetTick() - led_timer < 100) return;  // Toutes les 100 ms
    led_timer = HAL_GetTick();

    if (validation_flag) {
        led_pattern |= 0x01;  // Allumer LED1
    } else {
        led_pattern &= ~0x01;  // Éteindre LED1
    }
    if (error_count >= 3) {
        led_pattern ^= 0x02;  // Clignoter LED2
    } else {
        led_pattern &= ~0x02;
    }
    Set_LED_State(led_pattern);
}

Temporisation Précise avec Interruptions

Pour des actions comme maintenir une LED allumée pendant 3 secondes, exploitez le minuteur SysTick. Dans le fichier d'interruptions (stm32g4xx_it.c), ajoutez la logique de gestion dans le gestionnaire d'interruption SysTick_Handler.

volatile uint32_t system_ticks = 0;
void SysTick_Handler(void) {
    HAL_IncTick();
    system_ticks++;
    if (system_ticks >= 3000) {  // 3 secondes à 1 kHz
        led_pattern &= ~0x01;  // Éteindre LED après délai
        system_ticks = 0;
    }
}

Entrée Analogique avec ADC

L'ADC permet de lire des tensions analogiques. D'après le schéma, PB15 correspond à R37 et PB12 à R38. Dans CubeMX, configurez un canal ADC en mode unipolaire, avec une résolution de 12 bits. La tension réelle se calcule par : tension = (valeur_ADC / 4096) * 3.3V.

Initialisation et calibration :

// Initialisation dans main()
MX_ADC2_Init();  // Généré par CubeMX

// Calibration une seule fois après l'initialisation
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);

Lecture de la valeur ADC pour R37 :

uint16_t adc_raw_value = 0;
float measured_voltage = 0.0;

void Read_Analog_Voltage(void) {
    HAL_ADC_Start(&hadc2);
    if (HAL_ADC_PollForConversion(&hadc2, 10) == HAL_OK) {
        adc_raw_value = HAL_ADC_GetValue(&hadc2);
        measured_voltage = (adc_raw_value * 3.3f) / 4096.0f;
    }
    HAL_ADC_Stop(&hadc2);
}

Le DAC fonctionne de manière inverse pour générer des tensions. Configurez-le dans CubeMX en sortie analogique, et utilisez HAL_DAC_SetValue pour produire un signal.

Sortie PWM et Capture de Fréquence

La sortie PWM est configurée sur des broches spécifiques (PA6, PA7) via les timers. La fréquence et le rapport cyclique sont ajustables. Pour la capture de fréquence, utilisez le mode input capture du timer.

Exemple de configuration pour une sortie PWM sur PA7 avec une fréquence de 1 kHz et un rapport cyclique de 40% :

// Dans la configuration du timer dans CubeMX, réglez la période et le prescaler.
// En code, modifiez dynamiquement :
__HAL_TIM_SET_AUTORELOAD(&htimx, new_period);
__HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_x, new_pulse);

Pour mesurer la fréquence d'un signal sur PA15, configurez le timer en mode capture avec interrupttion.

Communication Série

Configurez l'UART dans CubeMX avec les paramètres requis (baud rate, bits de données, etc.). L'envoi et la réception peuvent se faire en mode interruption ou DMA pour une efficacité accrue.

uint8_t tx_buffer[] = "Données à envoyer\n";
uint8_t rx_buffer[32];
volatile uint8_t data_received = 0;

void UART_Transmit_Data(void) {
    HAL_UART_Transmit(&huart1, tx_buffer, sizeof(tx_buffer)-1, 100);
}

// En mode interruption
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART1) {
        data_received = 1;
        HAL_UART_Receive_IT(&huart1, rx_buffer, 1);  // Préparer la prochaine réception
    }
}

Pour le mode DMA, activez-le dans CubeMX et utilisez les fonctions HAL_UART_Receive_DMA pour éviter les blocages.

Utilisation de l'EEPROM

L'accès à l'EEPROM se fait via l'interface I2C. Ajoutez le fichier source et en-tête eeprom.c/h. Les opérations de lecture/écriture doivent inclure la gestion des adresses et des délais nécessaires.

Notes de Débogage et Remarques

Exemple ciblé pour la carte de développement avec le STM32G431RBT6. En cas d'échec de téléchargement, essayez de réduire la vitesse de programmation. Assurez-vous que les fichiers bibliothèque sont correctement inclus et que la configuration des horloges est cohérente.

Étiquettes: STM32 CubeMX HAL ADC PWM

Publié le 5 juin à 01h54