{{https://bvdp.inetdoc.net/files/iut/tp_pic/warning.jpeg}} A FAIRE A LA FIN DE CHAQUE SEANCE POUR COPIER VOS FICHIERS SUR UN DISQUE PARTAGE (ET PERMETTRE LA RECUPERATION DES FICHIERS SI UN MEMBRE DU BINOME EST ABSENT) copier coller dans une console: rsync -av --delete ~/workspace_ac6 /mnt/etu/s4 et completer la ligne en appuyant 3 fois sur la touche Tabulation, puis entrée. ====== Mise en place du projet architecture pour le TNS sur plateforme Nucléo ====== Outils utilisés (gratuits et sans restrictions d'utilisation): * STM32CubeMx ([[http://www.st.com/en/development-tools/stm32cubemx.html]]): Outil permettant de créer le squelette initial du projet et de configurer les périphériques utilisés. Cet outil est installé dans /opt/STM32/STM32CubeMX et peut etre lancé en tapant dans une console: /opt/STM32/STM32CubeMX/STM32CubeMX * SystemWorkbench ([[http://www.openstm32.org/System+Workbench+for+STM32]]) : Outil intégré d'édition, compilation, mise au point de programmes pour la plateforme STM32. Cet outil est installé dans /opt/STM32/SystemWorkbench et peut etre lancé en tapant dans une console: /opt/STM32/SystemWorkbench/eclipse Les développements se feront sur une carte NUCLEO-STM32F334R8 [[http://www.st.com/en/evaluation-tools/nucleo-f334r8.html]]. {{ ::carte_stm32f334r8.png?600 |}} Cette carte dispose d'un micro-controlleur STM32F334R8 [[http://www.st.com/en/microcontrollers/stm32f334r8.html]] associé à : * Des connecteurs compatibles Arduino * Des connecteurs compatibles Morpho * Une interface de mise au point (debug) USB sécable * Une led pilotable logiciellement * Un bouton lisible logiciellement * Un oscillateur externe 32kHz La fréquence maxi du microcontroleur est de 72Mhz (par horloge externe). Par contre l'oscillateur interne est à 8Mhz et peut être multipliée par un facteur 8 (*16/2) maximum, ce qui fournit une fréquence de 64Mhz. Les connecteurs sont liés au micro-controlleur selon le schéma suivant : {{ ::nucleo-f334_pinout.png?800 |}} ===== Architecture fonctionnelle du programme ===== Le programme à mettre en place doit permettre de : * Capturer un signal analogique à intervalle maitrisé * Filtrer le signal analogique (filtre FIR/IIR mis en oeuvre sous Qt) * Restituer le signal analogique au meme intervalle que la capture {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} **Question 1 : Lister les périphériques nécessaires à la bonne exécution du programme et dessiner un schéma de principe du programme (chaine de traitement avec noms des périphériques) ** ===== Mise en place du projet initial===== Dans cette partie, nous allons utiliser l'application STM32CubeMX pour configurer graphiquement/textuellement le micro-controlleur et les périphériques nécessaires. Une fois la configuration complète, nous allons générer le code correspondant sous la forme d'un projet exploitable par l'outil SystemWorkbench. A effectuer : Lancer l'outil STM32CubeMx en tapant dans une console, la commande suivante (copier/coller en sélectionnant le texte de la commande et en utilisant le clic central de la souris dans la console): /opt/STM32/STM32CubeMX/STM32CubeMX Une fois lancé vous verrez apparaître la page principale du logiciel {{ ::homescreen.png?600 |}} ====== Configuration du répertoire de firmware ====== Avant de pouvoir commencer à utiliser le logiciel, il est nécessaire de configurer le chemin menant aux fichiers de base pour chaque famille de processeur (pilotes de périphériques, codes exemples ...). Dans le cas d'une installation sur une machine personnelle, ce répertoire sera créé et initialisé lors de la première génération de code. Dans le cas de la manipulation en salle de TP, le répertoire existe dans /opt/STM32/STM32Cube/Repository Pour configurer l'utilisation de ce répertoire, il faut ouvrir : Help -> Updater Settings ... et modifier le chemin intitulé "Repository Folder" en "/opt/STM32/STM32Cube/Repository" (en copiant collant du caractère / au caractère y inclus) {{ ::modify_respository.png?600 |}} puis cliquer sur Ok. ====== Sélection de la carte cible ====== Vous pouvez maintenant cliquer sur "New Project". Après une courte attente, vous verrez apparaitre l'écran de sélection du micro-controleur cible. {{ ::mcu_selector.png?600 |}} Dans notre cas, le project cible une carte de développement reconnue par l'outil, nous pouvons donc directement cliquer sur l'onglet "Board Selector". {{ ::board_selector.png?600 |}} puis sélectionner "Nucleo64" dans le choix "Type of Board" et "NUCLEO-F334R8" dans la liste de cartes sur la droite suivi de "Ok". Le message indiquant "read only" est normal. ====== Sélection des périphériques et configuration des entrées/sorties ====== Après le chargement du projet (cliquer sur "Ok" en cas de message d'avertissement) vous devriez avoir à l'écran le panneau suivant : {{ ::configuration_io_0.png?600 |}} Ce panneau permet de sélectionner les périphériques utilisés par votre application et de les associer si nécessaires aux entrées/sorties du micro-controlleur. Dans le cas de notre carte de développement, certaines entrées/sorties sont déjà associées : * B1 : qui est connectée au bouton poussoir bleu de la carte * LD2 : qui est connectée à une led verte sur la carte * USARTX/RX : qui sont connectées au convertisseur USB/série de la carte * RCC_X : qui sont connectées aux oscillateurs de la carte * SWO/TCK/TMS : qui sont connectées à l'interface de mise au point (debug) Il nous faut donc ajouter l'instanciation des périphériques listés dans la première partie : * Un périphérique ADC pour capturer des échantillons * Un périphérique DAC pour générer des échantillons * Un périphérique Timer pour cadencer l'acquisition et la restitution * Un périphérique UART pour permettre la communication depuis le PC avec le programme (optionnel) Les cartes d'acquistion/restitution étant déjà fabriquées nous devons prendre en compte les contraintes suivantes : * La broche cablée sur la carte d'acquisition est reliée au canal 1 de l'ADC 1 * La broche cablée sur la carte de restitution est reliée au canal 1 du DAC 2 * L'UART cablé au convertisseur USB série de la carte est l'UART 2 Activation de l'ADC1, canal 1, sélectionner Peripherals->ADC1->IN1->IN1 Single-ended: {{ ::adc1_select.png?600 |}} Activation du DAC2, canal 1, sélectionner Peripherals->DAC2->OUT1 Configuration: DAC Output switch Enable: {{ ::dac2_select.png?600 |}} Activation du Timer 1, sélectionner Peripherals->TIM1->Clock Source->Internal Clock: {{ :tim1_select.png?600 |}} Activation de la liaison série, sélectionner Peripherals->USART2->Mode->Asynchronous: {{ ::serial_select.png?600 |}} ===== Configuration des périphériques : ===== Une fois l'activation des périphériques effectuée, il est nécessaire de les configurer. L'outil STM32CubeMX permet de configurer chaque périphérique en mode texte. L'accès au menu de configuration se fait en cliquant sur l'onglet "Configuration" qui ouvre le panneau suivant. {{ ::periph_config.png?600 |}} Dans ce panneau, l'utilisateur a accès aux différents périphériques listés dans les catégories : Connectivity, Analog, System, Control. Dans notre cas, il est nécessaire de configurer: * l'ADC pour la conversion sur la canal 1 avec un déclenchement en logiciel * le timer pour la génération d'une interruption toutes les N µs * le NVIC (Nested Vectored Interrupt controller) pour la prise en charge de l'interruption timer * le DAC * la liaison série en 115200bauds ==== Configuration de la liaison série ==== La liaison série de l'UART2 du micro-controlleur est connectée à un convertisseur USB/série présent sur l'interface de mise au point de la carte (partie sécable). Il faut donc cliquer sur le bouton "USART2" pour accéder au menu de configuration. Dans ce menu, il faut ensuite, configurer l'UART pour 115200bauds 8bits de donnée, pas de parité. Cliquer ensuite sur OK. {{ ::config_serial.png?600 |}} ==== Configuration du Timer 1 ==== Cliquer sur l'onglet Clock Configuration pour visualiser les différentes horloges disponibles. Repérer l'horloge utilisée pour cadencer le TIMER1. La configuration du Timer 1 se fait en cliquant sur le bouton TIM1 dans le bloc "Control". La configuration du Timer est plus fastidieuse que la configuration de la liaison série. Il faut donc consulter la documentation suivante : [[http://www.st.com/content/ccc/resource/technical/document/application_note/54/0f/67/eb/47/34/45/40/DM00042534.pdf/files/DM00042534.pdf/jcr:content/translations/en.DM00042534.pdf]] La configuration de la périodicité du Timer se fait au travers des éléments suivants : * Prescaler : facteur de division de l'horloge de cadencement du comptage/décomptage du timer (fréquence de comptage = (fréquence horloge)/(prescaler + 1)) * Direction de comptage ("Counter mode") : configure le timer en compteur ou en décompteur * Valeur de rechargement ("Counter period") : configure la valeur de départ du compteur. Cette valeur est rechargée quand le compteur atteint la valeur maximum en cas de comptage ou la valeur minimum (0) en cas de décomptage. * Compteur de répétition ("Repetition Counter") : permet de configurer le nombre de cycle de comptage/décomptage complets avant de générer un évènement de mise à jour {{ ::timer_base_config.png?600 |}} auto-reload preload ? up ? Dans notre cas, nous voulons générer des évènements à la fréquence de 48kHz. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} **Question 2 : Calculer la valeur des différents champs et les compléter dans l'outils.** Solution: sur http://www.st.com/content/ccc/resource/technical/document/application_note/54/0f/67/eb/47/34/45/40/DM00042534.pdf/files/DM00042534.pdf/jcr:content/translations/en.DM00042534.pdf p7: on voit que TIM1 sur STM332F334 est Advanced p8: advanced sur 16 bits, up/down p6: Advanced timers: these timers have the most features. In addition to general purpose functions, they include several features related to motor control and digital power conversion applications: three complement ary signals with deadtime insertion, emergency shut-down input. Donc ça fait au moins aussi bien que le general purpose p9: choix de la source d'horloge Internal clock The timer is clocked by default by the internal clock provided from the RCC. To select this clock source, the TIMx_SMCR->SMS (if present) bits should be reset. p10: Calcul des valeurs The update event period is calculated as follows: Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1)) Where: TIM_CLK = timer clock input PSC = 16-bit prescaler register ARR = 16/32-bit Autoreload register RCR = 16-bit repetition counter exemple de la doc: TIM_CLK = 72 MHz Prescaler = 1 Auto reload = 65535 No repetition counter RCR = 0 Update_event = 72*(10^6)/((1 + 1)*(65535 + 1)*(1)) Update_event = 549.3 Hz En fait l'horloge d'entrée qui cadence TIM1 est à 64Mhz (voir l'onglet Clock Configuration dans l'appli) Pour les 48Khz demandés sur le sujet: il faut un facteur de division de 64.10^6 / 48.10^3 = 1333.3333 je peux régler Prescaler = 0 Counter mode = Down Counter Period = round(1333.3333)-1 = 1332 Internal Clock Division = No Division Repetition Counter RCR = 0 Auto reload preload = Enable laisser les autres options inchangées Une fois le timer configuré, il faut activer la génération d'une interruption à la fréquence configurée. Pour cela il faut cliquer sur l'onglet "NVIC Settings" et activer l'interruption "TIM1 update". {{ ::timer_interrupt_enable.png?600 |}} Cliquer sur OK. ==== Configuration de l'ADC1 ==== La configuration de l'ADC 1 se fait en cliquant sur le bouton ADC1 dans le bloc "Analog". La configuration de base dans l'outil est suffisante pour notre utilisation. [[http://www.st.com/content/ccc/resource/technical/document/application_note/c4/63/a9/f4/ae/f2/48/5d/CD00258017.pdf/files/CD00258017.pdf/jcr:content/translations/en.CD00258017.pdf]] {{ ::adc_configuration.png?600 |}} Dans cette configuration, l'ADC est configuré pour un déclenchement par logiciel et une seule conversion par déclenchement. Le canal converti est le canal 1 avec un temps de conversion de 1.5 cycles de l'horloge ADC. ==== Configuration du DAC2 ==== La configuration du DAC2 se fait en cliquant sur le bouton DAC2 dans le bloc "Analog". La configuration de base dans l'outil est suffisante pour notre utilisation. ==== Configuration du gestionnaire d'interruptions ==== La configuration du gestionnaire d'interruptions se fait en cliquant sur le bouton NVIC dans le bloc "System". La configuration de base dans l'outil est suffisante pour notre utilisation, il faut juste ajouter la génération du code d'initialisation dans l'onglet "Code generation". {{ ::nvic_init.png?600 |}} ==== Génération du squelette de code ==== Une fois tous les périphériques configurés, vous pouvez générer un squelette de projet en cliquant sur le bouton "generate code" {{ ::generate_code.png?64 |}} A la première génération de code, il vous faut compléter les attributs de génération de code (chemin, outils de développement ...). Dans le cadre de ce TP il vous utiliser le dossier : /home/IUT//workspace_ac6 Par exemple, si votre login est abc123b, vous devez régler: Project Name: nucleo_tns Project Location: /home/IUT/abc123b/workspace_ac6 Toolchain/IDE: SW4STM32 {{ ::config_gen_code.png?600 |}} Une fois le code généré, vous pouvez fermer l'outil. Au cas ou vous auriez besoin de modifier certains éléments de configuration après la première génération de code, vous pouvez ouvrir le projet (*.ioc) qui se trouve dans le répertoire où le code a été généré. ==== Utilisation de l'outils SystemWorkbench ==== Afin de lancer l'IDE SystemWorkbench, il vous faut lancer dans une console : /opt/STM32/SystemWorkbench/eclipse A l'ouverture le logiciel vous propose de charger un espace de travail (workspace). Il vous faut alors saisir (ou naviguer) vers le répertoire workspace_ac6 de votre répertoire utilisateur (/home/IUT//workspace_ac6). Une fois l'outil lancé : * Cliquer sur File -> Import * Choisir General -> Existing Projects Into Workspace * Naviguer vers le répertoire du code précédement généré et cliquer sur Finish {{ ::import_project.png?600 |}} {{ :import_generated_project.png?600 |}} Une fois le projet importé, il apparait dans l'arboresence de projets, à droite de la fenetre. Si nécessaire, fermer la fenêtre indiquant "Welcome..." {{ ::imported_project.png?600 |}} ==== Analyse du code généré ==== La première étape de génération de projet a mis en place, de manière automatique, une arborescence de projet. Le seul fichier à modifier dans la suite sera le fichier main.c dans le répertiore "Src". **Attention : Le code à saisir dans ce fichier ne doit l'etre que entre deux balises "USER CODE BEGIN" et "USER CODE END"** /* USER CODE BEGIN N */ // VOTRE CODE /* USER CODE END N * dans le cas contraire, votre code sera "ecrasé" lors des prochaines génération de code. {{https://bvdp.inetdoc.net/files/iut/tp_tns/TODO.jpg}} **Lire le code généré et faire le lien avec la configuration saisie dans l'outil STM32CubeMX.** === Compléter le code ... === Le code initial configure tous les périphériques, mais : * Ne démarre pas le TIMER 1 * Ne démarre pas le DAC2 * Ne définit pas la fonction de gestion de l'interruption timer pour l'évènement update Il faut donc démarrer le TIMER 1 HAL_TIM_Base_Start_IT(&htim1); et démarrer le DAC 2 : HAL_DAC_Start(&hdac2, DAC2_CHANNEL_1); dans la fonction principale (main) avant la boucle de tache de fond (avant le while(1)). Il faut ensuite définir la fonction : void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) qui sera appelée périodiquement. Les fonctions nécessaires à l'implémentation du comportement demandé sont : HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); // Lance la conversion HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout); //Attend le résultat de la conversion, le deuxième paramètre permet de limiter le temps d'attente par une valeur 32bits en millisecondes. HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc); // Stoppe la conversion uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc); //Lit le résultat de la conversion HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data); //Ecrit une valeur "Data" sur canal "Channel" du DAC "hdac" avec l'alignement "Alignement" Pour obtenir de la documentation sur ces fonctions, copiez/collez les prototypes dans votre code et faites CTRL+Clic gauche sur le nom de la fonction. Les valeurs possibles du paramètre de retour "HAL_StatusTypeDef" sont : * HAL_OK * HAL_ERROR * HAL_BUSY * HAL_TIMEOUT Pour l'utilisation du DAC, le paramètre effectif "Channel" prendra, dans notre cas, la valeur : DAC2_CHANNEL_1 et l'alignement sera mis à zéro. === Mise au point === Une fois le code saisi. Vous pouvez le compiler en cliquant sur le bouton {{ ::build.png?64 |}} Si le code est compilé sans erreur, vous pourrez ensuite le mettre au point en faisant : * Clique-droit sur le nom du projet * Debug As -> Ac6 STM32 Le logiciel va vous proposer d'ouvrir la "perspective" de Debug (agencement des fenetres facilitant le Debug), répondre "ok". Par défaut le programme est arreté sur la première ligne de la fonction main, vous pouvez ensuite lancer l'exécution en appuyant sur F8 ou faire du pas à pas en utilisant F5 et F6.