Getting Started
Utilisation de STM32CubeIDE
Téléchargement du projet
- Télécharger le projet d'exemple disponible à l'url: https://github.com/vincentchoqueuse/STM32F746_Disco_Default_Synth/archive/refs/heads/main.zip
- Déplacer le projet sur votre disque local.
INFO
Attention à ne pas déplacer le projet sur un disque réseau non accessible par STM32Cube IDE.
Importation du projet
Ce projet est basé sur l'environnement de développement STM32Cube IDE. STM32CubeIDE est un outil de développement complet pour les microcontrôleurs STM32 qui intègre le développement, le débogage et la programmation de microcontrôleurs et de microprocesseurs STM32.
Pour charger un projet existant dans STM32CubeIDE, suivez ces étapes.
Ouvrir STM32CubeIDE: Lancez l'IDE STM32CubeIDE sur votre ordinateur. Si vous ne l'avez pas encore installé, vous devrez le télécharger depuis le site Web de STMicroelectronics et suivre les instructions d'installation.
Sélectionner l'Espace de Travail: À l'ouverture, l'IDE vous demande de choisir un espace de travail. Un espace de travail est un dossier où vos projets seront stockés. Vous pouvez utiliser l'emplacement par défaut ou en choisir un nouveau. Après votre choix, cliquez sur "Launch".
Importer le Projet:
- Allez dans le menu
File
>Import
. - Dans la fenêtre d'importation, étendez
General
et sélectionnezExisting Projects into Workspace
. Cliquez surNext
. - Sur la page suivante, localiser votre projet en choississant Select root directory. Utilisez le bouton
Browse
pour naviguer jusqu'au dossier contenant le projet que vous souhaitez charger. STM32CubeIDE recherchera automatiquement les projets dans ce dossier. - Une fois le projet ou les projets détectés, ils apparaîtront dans la zone de projets. Assurez-vous que le projet que vous souhaitez importer est coché.
- Cliquez sur
Finish
pour importer le projet dans votre espace de travail.
- Allez dans le menu
Explorer le Projet Importé: Après l'importation, le projet apparaîtra dans la vue
Project Explorer
de STM32CubeIDE. Vous pouvez maintenant explorer ses dossiers et fichiers, modifier le code, compiler le projet, et le téléverser sur votre microcontrôleur STM32.- Ouvrez le fichier
main.c
disponible dans le repertoire/src
- Ouvrez le fichier
Lancement du projet
Le projet doit être executé comme une application
STM32 C/C++ Application
- Lancer le projet:
- Connectez la carte STM32 en USB
- Appuyez sur l'icone circulaire verte avec une fleche blanche (
Run STM32f746Disco Debug
)
- Affichage d'un message sur l'écran LCD
- Dans le fichier
main.c
, après la ligne 27, ajoutez la ligne suivante.
- Dans le fichier
render_lcd("Hello, World!");
- Vérifier le bon fonctionnement
- Recompilez le programme,
- Vérifiez que la carte affiche bien le message
Hello, World!
.
INFO
La première compilation peut prendre du temps. Soyez patient.
Structure du Projet
Organisation générale
Le projet de base possède l'arborescence suivante.
.
├── Debug
│ ├── ...
│ ├── stm32f746_disco.bin
│ └── stm32f746_disco.elf
├── inc
│ ├── clockconfig.h
│ ├── main.h
│ ├── audio_processor.h
│ ├── midi_processor.h
│ ├── stm32f7xx_hal_conf.h
│ ├── stm32f7xx_it.h
│ ├── synth.h
│ └── ...
├── src
│ ├── main.c
│ ├── audio_processor.c
│ ├── midi_processor.c
│ ├── stm32f7xx_it.c
│ ├── syscalls.c
│ ├── system_stm32f7xx.c
│ ├── usbh_conf.c
│ └── ...
├── startup
│ └── startup_stm32f746xx.S
├── LinkerScript.ld
├── Makefile
├── ...
├── STM32F746G-DISCO.xml
└── stm32f746_disco Debug.launch
Makefile : Ce fichier contient les instructions pour compiler le projet. Il définit les commandes pour assembler, compiler, et lier le programme.
LinkerScript.ld : Ce script de l'éditeur de liens définit la disposition de la mémoire du microcontrôleur. Il est essentiel pour s'assurer que le code compilé et les données sont correctement alloués dans la mémoire.
Fichiers dans le dossier
Debug
:- stm32f746_disco.elf : Le fichier exécutable et linkable format (ELF) contient le programme compilé avec des informations de débogage. Il est utilisé par les débogueurs.
- stm32f746_disco.bin : Le fichier binaire qui peut être directement chargé sur le microcontrôleur.
- Ces fichiers sont importants pour le débogage et le chargement du programme sur le microcontrôleur.
Fichiers source dans
src
:- main.c : Contient la fonction principale du programme. C'est le point d'entrée de votre application.
- midi_processor.c, stm32f7xx_it.c, system_stm32f7xx.c, etc. : Ces fichiers contiennent le code source spécifique à votre application et à la configuration du matériel.
Fichiers d'en-tête dans
inc
:- main.h, midi_processor.h, stm32f7xx_hal_conf.h, etc. : Ces fichiers d'en-tête déclarent les fonctions, les variables globales, et les configurations spécifiques au matériel.
Fichier de démarrage :
- startup_stm32f746xx.S : Ce fichier contient le code d'initialisation du processeur, y compris la configuration du vecteur d'interruption. Il est exécuté au démarrage du microcontrôleur.
Fichier de projet et de configuration :
- STM32F746G-DISCO.xml et stm32f746_disco Debug.launch : Ces fichiers sont utilisés par des environnements de développement intégrés (IDE) pour configurer le projet, y compris les paramètres de débogage.
Fichier main
La fonction main
est le point d'entrée du programme. Elle initialise le système, y compris la mise en cache du CPU, le système d'exploitation HAL (Hardware Abstraction Layer), la configuration de l'horloge système, les périphériques (LED, GPIO, bouton poussoir, SDRAM), et le système USB Host. Elle enregistre également la classe MIDI pour le traitement USB et démarre le système USB. Après l'initialisation, elle entre dans une boucle infinie qui exécute l'application MIDI et le traitement des événements USB.
#include "main.h"
static void process_usbh_message(USBH_HandleTypeDef *pHost, uint8_t vId);
extern MIDI_ApplicationTypeDef Appli_state;
extern USBH_HandleTypeDef hUSBH;
int main(void)
{
// Intialization
CPU_CACHE_Enable();
HAL_Init();
SystemClock_Config();
BSP_LED_Init(LED_GREEN);
BSP_GPIO_Init();
BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO); // configure the blue user pushbutton in GPIO mode
BSP_SDRAM_Init();
USBH_Init(&hUSBH, process_usbh_message, 0);
USBH_RegisterClass(&hUSBH, USBH_MIDI_CLASS);
USBH_Start(&hUSBH);
start_audio();
start_lcd();
while(1)
{
midi_application();
USBH_Process(&hUSBH);
}
}
void process_usbh_message(USBH_HandleTypeDef *usbHost, uint8_t eventID) {
switch (eventID) {
case HOST_USER_SELECT_CONFIGURATION:
break;
case HOST_USER_DISCONNECTION:
Appli_state = APPLICATION_DISCONNECT;
break;
case HOST_USER_CLASS_ACTIVE:
{
Appli_state = APPLICATION_READY;
BSP_LED_Toggle(LED_GREEN);
}
break;
case HOST_USER_CONNECTION:
{
Appli_state = APPLICATION_START;
BSP_LED_Toggle(LED_GREEN);
}
break;
default:
break;
}
}
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
/* Includes ------------------------------------------------------------------*/
#include "stdio.h"
#include "stm32746g_discovery.h"
#include "stm32746g_discovery_lcd.h"
#include "stm32746g_discovery_sdram.h"
#include "clockconfig.h"
#include "audio_processor.h"
#include "midi_processor.h"
#include "lcd_monitor.h"
typedef enum {
APP_IDLE = 0, APP_START, APP_READY, APP_RUNNING, APP_DISCONNECT
} AppState;
#endif /* __MAIN_H */
Fichier audio_processor.c
Le moteur audio est géré par le fichier audio_processor.c
. Ce fichier gère à la fois l'initialisation du codec audio et du DMA, la synchronisation du buffer audio controlé par le DMA(Direct Memory Access), et le moteur de synthèse sonore.
#include "audio_processor.h"
//DON'T TOUCH
uint8_t audio_buf[AUDIO_DMA_BUFFER_SIZE] = {0};
//CREATE YOUR SOUND HERE
void start_audio() {
BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, VOLUME, SAMPLE_RATE);
BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
BSP_AUDIO_OUT_SetVolume(1);
BSP_AUDIO_OUT_Play((uint16_t *)audio_buf, AUDIO_DMA_BUFFER_SIZE);
}
void render_audio(int16_t *buf, int16_t length) {
//CREATE YOUR SOUND HERE
static float y = 1.0;
for (uint16_t i = 0; i < length; i = i+2) {
//change sample y here
int16_t value = ((int16_t) ((32767.0f) * y)); // conversion float -> int
buf[i] = value; // left channel sample
buf[i+1] = value; // right channel sample
}
}
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void) {
render_audio((int16_t *)&audio_buf[0], AUDIO_DMA_BUFFER_SIZE4);
}
void BSP_AUDIO_OUT_TransferComplete_CallBack(void) {
render_audio((int16_t *)&audio_buf[AUDIO_DMA_BUFFER_SIZE2], AUDIO_DMA_BUFFER_SIZE4);
}
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef AUDIO_PROCESSOR_H_
#define AUDIO_PROCESSOR_H_
/* Includes ------------------------------------------------------------------*/
#include "stm32746g_discovery_audio.h"
#include "stm32746g_discovery.h"
#include "main.h"
/* Constants -----------------------------------------------------------------*/
#define VOLUME 90
#define SAMPLE_RATE 44100
#define AUDIO_DMA_BUFFER_SIZE 512
#define AUDIO_DMA_BUFFER_SIZE2 (AUDIO_DMA_BUFFER_SIZE >> 1)
#define AUDIO_DMA_BUFFER_SIZE4 (AUDIO_DMA_BUFFER_SIZE >> 2)
#define AUDIO_DMA_BUFFER_SIZE8 (AUDIO_DMA_BUFFER_SIZE >> 3)
/* Exported types ------------------------------------------------------------*/
typedef enum {
AUDIO_ERROR_NONE = 0,
AUDIO_ERROR_NOTREADY,
AUDIO_ERROR_IO,
AUDIO_ERROR_EOF,
}AUDIO_ErrorTypeDef;
/* Exported macro ------------------------------------------------------------*/
void start_audio(void);
void render_audio(int16_t *buf, int16_t length);
#endif
start_audio()
: Cette fonction permet d'initialiser le système audio du dispositif. Elle configure le périphérique de sortie audio , définit le volume et la fréquence d'échantillonnage pour la lecture audio, et spécifie le slot du codec audio. Ensuite, elle démarre la lecture audio en utilisant le buffer DMA (audio_buf
), préparant ainsi le système pour jouer ou traiter le son.render_audio()
: Cette fonction est le point d'entrée pour la génération des échantillons du signal audio.BSP_AUDIO_OUT_HalfTransfer_CallBack()
: Cette fonction est un callback appelé automatiquement lorsque la moitié du buffer DMA a été transférée au périphérique audio.BSP_AUDIO_OUT_TransferComplete_CallBack()
: Similaire au callback de demi-transfert, cette fonction est appelée une fois que le buffer entier a été joué. Elle permet de remplir la deuxième moitié du buffer avec de nouvelles données audio.
Génération d'un signal carré
Pour vérifier le bon fonctionnement de la carte, nous allons tout d'abord générer un oscillateur carré de fréquence fixe.
Modèle de signal
Un signal carré peut être modélisé par l'équation :
Implémentation
Le moteur de synthèse audio est géré dans le fichier audio_processor.c
. L'oscillateur carré peut être implémenté de la manière suivante :
void render_audio(int16_t *buf, int16_t length) {
static float y = 1.0;
static int counter = 0;
for (uint16_t i = 0; i < length; i = i+2) {
if (counter==0)
{
y *= -1.0;
}
counter = (counter+1)%512;
int16_t value = ((int16_t) ((32767.0f) * y)); // conversion float -> int
buf[i] = value; // left channel sample
buf[i+1] = value; // right channel sample
}
}
INFO
Attention aux oreilles ! Ne laissez jamais votre casque sur les oreilles pendant la compilation et le téléversement de votre programme. Si vous souhaitez reduire le volume, il est possible d'ajouter à la fin de la fonction start_audio()
, l'appel suivant : BSP_AUDIO_OUT_SetVolume(1);
Manipulation 1
Générez le signal carré et observez-le à l'oscilloscope. Sachant que la fréquence d'échantillonnage est spécifiée par la constante SAMPLE_RATE
, déterminez la fréquence de ce signal.