Emulation d’un port COM avec l’USB
Il est possible d’émuler un port COM avec la prise USB OTG (connecteur micro-AB).
Cet exemple utilise l’USB CDC (USB Communications Device Class) disponible dans le STM32 Cube F4.
Cet exemple contient les modules suivants :
- La LED 5 clignotte avec une période de 2 secondes (1 sec. on, 1 sec. off)
- La LED 4 correspond au statut du bouton utilisateur (appuyé ou relaché)
- L’USB OTG émule un nouveau port de communication qui recopie les caractères entrant sur la sortie
Vous pouvez télécharger le code ICI ou sur mon dépots GitHub
Pour compiler les fichiers source avec em::Blocks, vous devez :
- Télécharger les fichiers source et les dé-zipper
- Ouvrir un projet existant en sélectionnant le projet « Usb-HAL.ebp »
- Vérifier que la constants USE_USB_FS est bien défnine au niveau du projet.
Pour faire cette vérifiaction, faites un clique droit sur le projet dans la fenêtre « management « , puis sélectionner l’option « build Option… » dans le menu contextuel.
Cliquer sur « Usb-Hal » dans la partie gauche de la boite de dialogue pour sélectionner les paramètres globaux du projet.
Dans l’onglet « #defines » des options du compilateur, vérifier que USE_USB_FS est présent.
Attention: Il y a un bug dans la distribution STM32 Cube F4 (corrigé ici)
Dans le fichier usbd_cdc.c, dans la fonction USBD_CDC_TransmitPacket(), la mise à jour de l’état de la transmission est faite après l’appel de la fonction d’émission (qui fonctionne sous interruption et met à jour également cet état).
USBD_LL_Transmit(...); hcdc->TxState = 1;
Donc en cas de transmission courte (1 octet par exemple), la fonction Transmit peut se terminer avant que la fonction en cours mette à jour l’état avec une mauvaise valeur dans ce cas. La solution : inverser les 2 lignes pour indiquer la transmission avant d’envoyer les octets.
hcdc->TxState = 1; USBD_LL_Transmit(...);
FreeRTOS pour em::Blocks & STM32F4
Dans l’environnement em::Blocks, il est possible de créer des templates qui permettent d’initialiser facilement des nouveaux projets.
Cet article décrit comment créer un nouveau template pour em::Blocks contenant :
- FreeRTOS V8.12
- STM32F4xx HAL Driver (la nouvelle couche d’abstraction STM32CubeF4 de ST)
- La configuration spécifique pour la carte the STM32F4-Discovery
Installation du template téléchargé
Comme il est un peu long de créer ce template, vous pouvez l’installer directement dans em::Blocks à partir de la version téléchargée.
- Télécharger le template ICI ou sur mon repertoire GitHub
- Dézipper le fichier pour créer le répertoire « STM32F4-Discovery-Template »
- Copier « STM32F4-Discovery-Template » dans le répertoire Windows %AppData% pour em::Blocks en lançant la commande suivante :
xcopy /S /I STM32F4-Discovery-Template %APPDATA%\EmBlocks\2.00\UserTemplates\STM32F4-Discovery
Pour créer un nouveau projet dans em::Blocks, utilisez le menu « File / New / From template… » et sélectionnez le nouveau template. Ensuite sélectionnez le répertoire cible et entrez le nom du projet.
Comment créer ce template
Créez un nouveau projet comme décrit dans la première partie de l’article « Utiliser em::Blocks avec la STM32F4-Discovery« .
Nommez votre projet « STM32F4-Discovery » et ne pas faire la fin « Adaptation pour la STM32F4-Discovery ».
Ajout des fichiers à ce template
Créer un répertoire STM32F4 directory sous C:\
Dézipper le fichier stm32cube_fw_f4_v130.zip dans ce répertoire (voir l’article précédent pour le télécharger)
Télécharger et lancer le programme FreeRTOSV8.1.2.exe pour dézipper les fichiers dans ce répertoire
Le résultat pour ce répertoire STM32F4 est:
- FreeRTOSV8.1.2 : La distribution freeRTOS
- STM32Cube_FW_F4_V1.3.0 : Le firmware STM32Cube F4
- STM32F4-Discovery : Le nouveau projet créé pour réaliser ce template
Aller dans « STM32F4-Discovery » et supprimer le répertoire « SPL » qui contient l’ancien frimware.
Supprimer le contenu du répertoire « Inc ». Les fichiers seront remplacés par la version HAL.
Creation de la partie FreeRTOS
– Aller dans « c:\STM32F4\STM32F4-Discovery » (le répertoire de votre nouveau projet) et y créer le répertoire « Firmware ».
– Aller dans ce répertoire « Firmware » et y créer le répertoire « FreeRTOS ».
– Se positionner dans « FreeRTOS ».
– Copier les fichiers sources (*.c) de « c:\STM32F4\FreeRTOSV8.1.2\FreeRTOS\Source\*.c ».
– Copier les fichiers d’inclusion (*.h) de « c:\STM32F4\FreeRTOSV8.1.2\FreeRTOS\Source\include\*.h ».
– Copier le contenu de « c:\STM32F4\FreeRTOSV8.1.2\FreeRTOS\Source\portable\GCC\ARM_CM4F\*.* ».
Le fchier source de la gestion mémoire doit être ajouté (5 fichiers existent, consultez la documentation pour plus de détails).
– La première version convient dans la plus part des cas, donc copier le fichier « c:\STM32F4\FreeRTOSV8.1.2\FreeRTOS\Source\portable\MemMang\heap_1.c » dans ce répertoire « FreeRTOS ».
– Copier le fichier « c:\STM32F4\FreeRTOSV8.1.2\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK\FreeRTOSConfig.h » dans ce répertoire « FreeRTOS ».
Un fichier supplémentaire doit être créé:
– Créer dans le répertoire « FreeRTOS » un fichier appelé « FreeRTOS-hook.c », l’éditer pour ajouter le code suivant :
#include "FreeRTOS.h" #include "task.h" #include "stm32f4xx_hal.h" void vApplicationTickHook( void ) { HAL_IncTick(); } /*-----------------------------------------------------------*/ void vApplicationMallocFailedHook( void ) { /* vApplicationMallocFailedHook() will only be called if configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook function that will get called if a call to pvPortMalloc() fails. pvPortMalloc() is called internally by the kernel whenever a task, queue, timer or semaphore is created. It is also called by various parts of the demo application. If heap_1.c or heap_2.c are used, then the size of the heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used to query the size of free heap space that remains (although it does not provide information on how the remaining heap might be fragmented). */ taskDISABLE_INTERRUPTS(); for( ;; ); } /*-----------------------------------------------------------*/ void vApplicationIdleHook( void ) { /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle task. It is essential that code added to this hook function never attempts to block in any way (for example, call xQueueReceive() with a block time specified, or call vTaskDelay()). If the application makes use of the vTaskDelete() API function (as this demo application does) then it is also important that vApplicationIdleHook() is permitted to return to its calling function, because it is the responsibility of the idle task to clean up memory allocated by the kernel to any task that has since been deleted. */ } /*-----------------------------------------------------------*/ void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName ) { ( void ) pcTaskName; ( void ) pxTask; /* Run time stack overflow checking is performed if configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is called if a stack overflow is detected. */ taskDISABLE_INTERRUPTS(); for( ;; ); } /*-----------------------------------------------------------*/
Création de la partie STM32CubeF4
– Remonter dans « c:\STM32F4\STM32F4-Discovery\Firmware »
– Copier le répertoire « STM32F4xx_HAL_Driver » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers ».
– Copier le répertoire « STM32F4-Discovery » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers\BSP ».
– Copier le répertoire « Components » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers\BSP ».
– Copier le répertoire « STM32_Audio » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Middlewares\ST ».
– Se positionner dans le répertoire « c:\STM32F4\STM32F4-Discovery\inc »
– Copier le fichier « stm32f4xx_hal_conf.h » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Projects\STM32F4-Discovery\Templates\Inc ».
– Copier le fichier « stm32f407xx.h » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include ».
– Copier le fichier « stm32f4xx.h » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include ».
– Copier le fichier « system_stm32f4xx.h » situé dans « c:\STM32F4\STM32Cube_FW_F4_V1.3.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include ».
Configuration de em::Blocks IDE
– Dans la fenêtre de Workspace, faire un clique droit sur le projet STM32F4-Discovery et sélectionner « Build options… »
– Dans l’onglet « Compiler settings », sélectionner le sous onglet « #define ».
Ajouter les deux constantes STM32F407xx et USE_HAL_DRIVER
– Dans l’onglet « Compiler settings », sélectionner le sous onglet « Other options ».
Ajouter l’option -Wno-unused-but-set-variable et valider avec le bouton « Ok » (pour supprimer le warning: variable ‘xxx’ set but not used).
– Editer le fichier source « Source/src/system_stm32f4xx.c » et à la ligne 156 changer la valeur de #define PLL_M à 8 (Le Quartz est à 8Mhz).
– Faire un clique droit sur la racine « STM32F4-Discovery » dans la fenêtre Workspace. Sélectionner « Remove files… » et valider les 2 boites de dialogue suivantes pour supprimer tous les fichiers de l’arborescence projet.
– Faire un clique droit sur la racine « STM32F4-Discovery » et sélectionner « Add files recursively… » pour re-créer la nouvelle structure du projet :
* valider la première boite de dialogue pour inclure le contenu du répertoire « STM32F4-Discovery ».
* dans la seconde boite de dialogue, aller en bas de la liste des fichiers et cocher les deux derniers fichiers « stm32f407vg_flash.ld » et « stm32f407vg_sram.ld ». Ensuite cliquer sur « Ok »
* cliquer sur « Ok » à la troisième boite de dialogue.
(si vous avez un répertoire « D sources » dans l’arborescence projet, due à une complation précédente importée, ouvrez le et faites un clique droit sur le répertoire « obj », sélectionner « remove « obj\* »)
– Editer le fichier « Headers/Firmware/STM32F4-Discovery/stm32f4_discovery_audio.h » et à la ligne 52 remplacer #include « ..\..\..\Middlewares\ST\STM32_Audio\Addons\PDM\pdm_filter.h »
par « #include « pdm_filter.h »
– Editer le fichier « Header/Firmware/FreeRTOS/FreeRTOSConfig.h » et déplacer la ligne 85 en dehors du bloc « #ifdef __ICCARM__ ».
On doit avoir :
#ifdef __ICCARM__ #include <stdint.h> #endif extern uint32_t SystemCoreClock;
– Editer le fichier « main.c » pour remplacer le contenu par :
/* ** ** Main.c ** ** **********************************************************************/ #include "stm32f4xx.h" #include "stm32f4_discovery.h" #include "FreeRTOS.h" #include "task.h" #define TASK_PRIORITY (tskIDLE_PRIORITY + 1) #define STACK_SIZE (configMINIMAL_STACK_SIZE + 100) // Task prototypes void LedTaskFunction( void *pvParameters ); void BtnTaskFunction( void *pvParameters ); int main(void) { // Initialization HAL_Init(); // create the task xTaskCreate( LedTaskFunction, /* Pointer to the function that implements the task. */ "Flash Led", /* Text name for the task. This is to facilitate debugging only. */ STACK_SIZE, /* Stack depth */ NULL, /* We are not using the task parameter. */ TASK_PRIORITY, /* This task will run at priority 1. */ NULL ); /* We are not going to use the task handle. */ // Create the BTN task xTaskCreate( BtnTaskFunction, "BTN Task", STACK_SIZE, NULL, TASK_PRIORITY, NULL ); // Start the scheduler so the tasks start executing vTaskStartScheduler(); while(1) ; } void LedTaskFunction( void *pvParameters ) { BSP_LED_Init(LED6); for (;;) { BSP_LED_Toggle(LED6); vTaskDelay(500); // Delay 500 ms } } void BtnTaskFunction( void *pvParameters ) { BSP_LED_Init(LED3); BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO); for (;;) { if (BSP_PB_GetState(BUTTON_KEY)) BSP_LED_On(LED3); else BSP_LED_Off(LED3); } }
On peut maintenant compiler ce projet sans erreur.
Si on le lance, une première tâche fait cligotter la LED 6 avec une période de 1s et une seconde tâche recopie l’état du bouton utilisateur sur la LED 3.
Maintenant que le projet est prêt (et comme ça nous a pris pas mal de temps pour le construire 😉 ), il faut le sauver sous forme d’un template dans em::Blocks.
Utiliser le menu « File / Save Project as Template… »
Complément pour le STM32CubeF4 V1.9.0
– Se positionner dans le répertoire « c:\STM32F4\STM32F4-Discovery\cmsis ».
– Effacer son contenu
– Y recopier le contenu du réperoire « c:\STM32F4\STM32Cube_FW_F4_V1.9.0\Drivers\CMSIS\Include »
Utiliser em::Blocks avec la STM32F4-Discovery
Après avoir beaucoup essayé d’IDE pour programmer la carte STM32F4-Discovery, mon choix s’est porté sur em::Blocks.
C’est un environnement gratuit, sans limitation de taille de code (contrairement aux version Free des IDE payants). C’est un fork de code::Blocks destiné à la génération de code embarqué. Il reconnait nativement de nombreuse cartes d’évaluation, dont la STM32F4-Discovery.
Le logiciel peut être téléchargé sur le site de em::Blocks.
Prérequis
– em:Blocks téléchargé et installé 😉 La version utilisée dans cet article est la 2.30
– STM32F4 drivers installés : a télécharger sur la page du site de ST (http://www.st.com/web/en/search/partNumberKeyword) en cherchant la référence STSW-LINK (stsw-link008 pour les driver Windows 7).
– Firmware pour la carte STM32F4-Discovery : sur la même page de recherche que ci-dessus, chercher la référence STSW-STM32068 pour pouvoir télécharge le fichier stsw-stm32068.zip.
– Créer un répertoire de travail « C:\STM32F4 » et dézipper le fichier stsw-stm32068.zip dedans. Cela doit donner :
C:\STM32F4\STM32F4-Discovery_FW_V1.1.0
Création d’un nouveau projet
Génération du code de base
1) Lancer em::Blocks. Ouvrir le menu « File / New / Project… »
2) Selectionner « STmicro-ARM »
3) Entrer le nom du nouveau projet et le chemin pour ce projet
4) Laisser les options par défaut
5) Choisir Cortex M4 (F3xx-F4xx)
6) Choisir STM32F4xx
7) Choisir le micro-contrôleur STM32F407VG.
8) Laisser la configuration par défaut
9) Sélectionner l’onglet « Target settings » et cocher la case « Run to main() ». Cela permet lors du lancement du mode debug de s’arrêter sur la première ligne du code plutôt que sur la première ligne du loader assembleur.
==> Le code est généré et est compilable.
Adaptation pour la STM32F4-Discovery
1) Recopier le firmware dans le répertoire du projet :
copier le répertoire
c:\STM32F4\STM32F4-Discovery_FW_V1.1.0\Utilities\STM32F4-Discovery
dans
c:\STM32F4\Test
2) Importer les modifications dans le projet
Faire un clique droit sur le nom du projet dans la fenêtre de gauche. Dans le menu choisir « Add files recusrsively ».
3) Sélectionner le nouveau répertoire rajouté au projet
4) Valider les 2 boites de dialogue qui listent les fichiers à importer
![]() |
![]() |
5) Pour éviter d’inclure d’autres dépendances, il faut supprimer les fichiers de gestion « audio »
Ouvrir le répertoire STM32F4-Discovery dans l’espace projet.
Faire un clique droit sur le fichier audio « stm32f4_discovery_audio_codec.c », puis choisir « Remove file from project ».
5) Prise en compte du quartz à 8Mhz
Dans le répertoire src, éditer le fichier system_stm32f4xx.c
C’est le fichier de configuration qui est automatiquement appelé au lancement du programme. Contrairement à ce qui est indiqué dans les commentaire en ligne 59, la fréquence du quartz est de 8 Mhz. Il faut onc chnager la valeur du PLL_M pour le passer à 8.
Editer la ligne 156 ou recherchez la constante PLL_M et forcer sa valeur à 8 :
#define PLL_M 8
6) Le projet maintenant prêt à être compilé.
Pour tester, je vous propose le code suivant pour main.c :
#include « stm32f4_discovery.h »
int main(void)
{
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDOn(LED3);
while(1) ;
}
En cas d’erreur lors de la compilation indiquant des erreurs sur le fichier audio, effacer le répertoire « c:\STM32F4\Test\obj\Debug » et relancer la compilation.
Génération d’un template
Pour éviter de refaire tout ce travail lors chaque nouveau projet, il est possible de créer un template de projet.
En utilisant ce projet « Test » qui est configuré correctement, ouvrir le menu « File / Save project as template… » et choisir le nom du template.
Pour les nouveaux projets, il suffit de faire « File / New / From Template… » et de choisir votre template pour avoir automatiquement toute la configuration appliquée par défaut 🙂