{{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. =====Objectifs du TP==== * Mettre en oeuvre les périphériques ADC, DAC, Timer * Générer un signal audio sur la sortie DAC de la carte * Capturer un signal audio sur l'entrée ADC de la carte Les informations nécessaires au lancement de l'outil SystemWorkbench utilisé pour ce tp sont disponibles sur [[tns_nucleo|Mise en place du projet architecture pour le TNS sur plateforme Nucléo]] ------------------------------------------------------------------------------------ =====Récupération du Projet C++ de départ===== Fermer les outils eclipse et STM32CubeMX. ===Récupération du projet de base en C++=== Copier/coller dans une console: echo commence mv ~/workspace_ac6 ~/workspace_ac6_etu cd ~/ mkdir -p ~/workspace_ac6 cd ~/workspace_ac6 wget https://bvdp.inetdoc.net/files/iut/tp_tns/nucleo_tns_etu2018.zip echo fini Ensuite: - lancer eclipse via la console en tapant: /opt/STM32/SystemWorkbench/eclipse - Si une fenêtre "Select a workspace" apparaît, saisir le nom de dossier: /home/IUT//workspace_ac6/ et cocher "Use this as the default..." - fermer l'onglet "Welcome" en cliquant sur la croix. - cliquer à gauche sur l'icone C/C++ - cliquer droit dans project Explorer->Import, puis General->Existing Projects into Workspace - cliquer sur Next - cocher Select archive file, puis cliquer sur Browse - choisir /home/IUT//workspace_ac6/nucleo_tns_etu2018.zip - cliquer sur Finish ===Activation de la sauvegarde automatique des fichiers à chaque compilation=== - Cliquer sur Window->Preferences. - Dans la colonne de gauche, cliquer sur la petite flèche à gauche de "General" puis sur "Workspace" dans la liste qui apparaît. - A droite, cocher "Save automatically before build". - Cliquer sur OK. ===Initialisation du système de gestion de version=== Seulement après avoir importé le projet depuis l'archive dans eclipse, saisir **dans une nouvelle console**: echo commence cd ~/workspace_ac6/ rm nucleo_tns_etu2018.zip cd ~/workspace_ac6/nucleo_tns/ git init git add * git commit -m'initial version' gitk echo fini ------------------------------------------------------------------------------------ ===== Exercice 1: Génération de la base de temps ===== Afin de valider le fonctionnement du timer, nous allons utiliser la fonction d'interruption mise en place lors du TD pour générer des signaux périodiques sur des broches (dont on mesurera le signal à l'oscilloscope) et sur une LED (dont on observera le clignotement). L'interruption timer doit survenir à la fréquence de 48kHz (arrondi au plus proche). Pour cela, il faut modifier la fonction **static void MX_TIM1_Init(void)** déjà mise en place par l'outils STM32CubeMX dans le fichier Src/tim.c Vous devrez affecter une bonne valeur à **htim1.Init.Period** pour obtenir la fréquence demandée. Il s'agit de la valeur correspondant au nombre de cycle -1 de décomptage du timer (chargée dans le registre nommé ARR lors du TD précédent), le prédiviseur étant réglé sur un facteur 1. Vous avez effectué un tel calcul lors du TD précédent. Il faut ensuite compléter la fonction d'interruption dans le fichier **Src/main.cpp**: void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ //Ecrire ici votre code exécuté à la fréquence d'interruption } ===Génération du signal sur la broche=== Vous disposez de 2 connecteurs sur la face avant du circuit imprimé de la carte de sortie connectés aux sorties PB11 et PB12 du STM32. Ces broches vont être utilisées pour mesurer à l'aide d'un oscilloscope le temps passé dans différentes parties du programme. Dans un premier temps, vous allez générer un signal sur la broche PB11 qui sera à 1 pendant la durée d'exécution de la fonction d'interruption timer et à 0 le reste du temps. La figure suivante présente les différentes broches des connecteurs de la carte: {{https://bvdp.inetdoc.net/files/iut/tp_tns/tns_line_in.jpg}} Au verso de la carte, les signaux en sortie directe du DAC sont accessibles sur la broches PA6 (et éventuellement PA5 pour un second signal dans le TP suivant): {{https://bvdp.inetdoc.net/files/iut/tp_tns/image_verso.jpg}} Les autres broches des connecteurs sont organisées tel que le montre le schéma suivant (la carte étant vue depuis le haut, composants face à vous): {{ ::nucleo-f334_pinout.png?800 |}} Pour générer le signal demandé, vous utiliserez la fonction **void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)**. La valeur pour l'argument **GPIOx** est **GPIOB**, et pour l'argument **GPIO_Pin**, la valeur est **GPIO_PIN_11**. Le troisième argument peut prendre la valeur **GPIO_PIN_SET** ou **GPIO_PIN_RESET** selon que l'on souhaite activer ou désactiver la sortie. === Compilation et téléchargement sur la cible en mode Debug === Après l'ouverture du projet, il est nécessaire de le compiler une première fois avant de pouvoir le debbuguer. Pour cela, cliquez sur le bouton: {{ ::build.png?64 |}} Brancher la carte Nucleo au port USB du PC. Pour activer le mode Debug : - Clique-droit sur le nom du projet - Choisir Debug As -> Ac6 STM32 Par la suite, pour exécuter votre application, il suffit de cliquer sur Run->Debug (ou raccourci touche F11). Important: Il faut quitter le mode debug avant de pouvoir recompiler et tester à nouveau votre programme, pour cela, cliquer sur Run->Terminate (ou raccourci CTRL+F2). Le logiciel va vous proposer d'ouvrir la "perspective" de Debug (agencement des fenêtres facilitant le Debug), répondre "ok". Par défaut le programme est arrêté 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. Pour positionner un point d'arrêt sur une ligne, il suffit de double cliquer sur son numéro. ===Mesure des signaux à l'oscilloscope=== Une fois le code mis en place et compilé, il vous faut mesurer la fréquence et la durée à l'état haut du signal généré à l'oscilloscope pour valider le bon fonctionnement. Reporter ces valeurs sur le compte rendu. **ATTENTION :le signal généré est haute fréquence (par rapport à la fréquence analogique max de l'oscilloscope), il faut donc désactiver le filtrage HF dans le menu Mode/Coupling.** {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois le fonctionnement validé par un enseignant, mettre à jour le suivi de version en saisissant dans une console: echo commence cd ~/workspace_ac6/nucleo_tns/ git commit -a -m'timer ok' gitk & echo fini ===Pilotage de la LED=== {{https://bvdp.inetdoc.net/files/iut/tp_tns/BONUS.jpg}} Vous devez maintenant faire clignoter la LED rouge présente sur la carte de sortie. Pour cela, à chaque 24000 exécutions de la fonction d'interruption, changer l'état de la broche pilotant la LED. Vous pourrez utiliser la fonction **void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)**. La valeur pour l'argument **GPIOx** est **GPIOB**, et pour l'argument **GPIO_Pin**, la valeur est **GPIO_PIN_13**. Conseil: Implémenter un compteur à l'aide d'une variable **static** définie dans la fonction **void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)**. Vérifier que la LED s'allume bien une fois par seconde. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois le fonctionnement validé par un enseignant, mettre à jour le suivi de version en saisissant dans une console: echo commence cd ~/workspace_ac6/nucleo_tns/ git commit -a -m'led ok' gitk & echo fini Solution: régler dans Src/tim.c htim1.Init.Period = 1332; et void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t cpt = 0; //pour accès plus rapide aux broches //GPIOB->ODR|=GPIO_PIN_11; //GPIOB->ODR&=~GPIO_PIN_11; //pour accès plus lent au broches, en passant par les registres de demande de mise à 1/0 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); if (cpt == 24000) { cpt = 0; HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13); } else cpt++; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); } ------------------------------------------------------------------------------------ ===== Exercice 2: Test du DAC ===== Nous allons utiliser le DAC pour générer un signal sinusoïdal (480Hz). ===Précalcul des échantillons pour la fonction sinus=== Les fonctions de la librairies math (sin, cos, tan ...) ne sont pas utilisables dans la fonction de gestion d'interruption timer du fait de leur lenteur. Il faut donc définir un tableau contenant les échantillons précalculés d'une période de la fonction sinus pour la fréquence demandée. Pour cela, copier dans le fichier main.cpp et compléter le code suivant: #include #define SAMPLING_RATE ( 48000.0f) #define SIN_FREQ ( 480.0f) #define SIN_AMP 1024.0f #define SIN_TAB_SAMPLES_PER_PERIOD ((unsigned int) (SAMPLING_RATE / SIN_FREQ)) //Taille du tableau pour une période du signal sinus uint32_t sin_tabbed[SIN_TAB_SAMPLES_PER_PERIOD]; void init_sin_tabbed(){ unsigned int k ; for(k = 0 ; k < SIN_TAB_SAMPLES_PER_PERIOD; k ++ ){ sin_tabbed[k] = ???; //Compléter ici par le calcul de la valeur des échantillons } } Pour déterminer la valeur des différents échantillons, vous pouvez vous inspirer du code écrit lors du TP4 de TNS qui est rappelé ci-dessous. Les échantillons ainsi générés doivent être compris dans la dynamique du convertisseur DAC (0 < = échantillon < = 4095) et leur valeur moyenne doit être réglée au milieu cette plage. La constante $\pi$ est définie sous le nom **M_PI**. Dans la formule suivante, **Tech** est obtenue en inversant **SAMPLING_RATE**. Ecrivez d'abord l'expression mathématique du signal: Ex : signal(t)=A.sin(2Π.fo.t) + Vmoy Déterminez alors la valeur du signal aux instants d'échantillonnages: Ex : signal(k.Tech)=A.sin(2Π.fo.k.Tech) + Vmoy Créez ensuite un tableau de taille nbEch (qui dépend de la durée voulue. Ex : tmax=duree=kmax.Tech d’où kmax=duree.Fech) par signal et remplissez-le avec les valeurs pour chaque k Ex : nbEch = kmax+1 double signal[nbEch] for(int k=0; k La fonction **void init_sin_tabbed()** est à appeler **une seule fois** au bon endroit dans le code de la fonction **main()**, avant le lancement du timer, afin d'éviter qu'une interruption timer ne survienne avant que le tableau soit initialisé: //Insérez ici l'appel de votre fonction **init_sin_tabbed** HAL_TIM_Base_Start_IT(&htim1); //Déjà mis en place HAL_DAC_Start(&hdac2, DAC2_CHANNEL_1); //Déjà mis en place Ainsi, le tableau **sin_tabbed** contient les échantillons à utiliser dans la fonction d'interruption. ===Exploitation des échantillons dans la fonction d'interruption Timer=== Complétez la fonction d'interruption pour : - Piloter la sortie connectée à la broche PB11 à l'état haut (déjà mis en place dans l'exercice précédent) - Déterminer la valeur de l'échantillon actuel du signal sinusoïdal à 480Hz - Mettre à jour le numéro de l'échantillon pour l'interruption suivante (en veillant à rester dans les bornes autorisées) - Ecrire l'échantillon sur le DAC - Piloter la sortie connectée à la broche PB11 à l'état bas (déjà mis en place dans l'exercice précédent) La fonction à utiliser pour écrire sur la sortie du DAC est: **HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data);**. Cette fonction écrit une valeur "Data" sur canal "Channel" du DAC "hdac" avec l'alignement "Alignement". Utiliser comme valeur de premier argument **DAC_HandleTypeDef* hdac** configuré à **&hdac2**. Pour le canal du DAC utiliser **DAC2_CHANNEL_1** et pour l'alignement **0**. Une fois le code complet, observez le signal généré à l'oscilloscope sur la broche PA6 (indiquée par une étiquette sous la carte Nucléo). Mesurez la valeur moyenne, la fréquence et l'amplitude crête à crête du signal sinusoïdal généré. Mesurez aussi le nouveau temps d'exécution de la fonction d'interruption sur la boche PB11 et reportez ces valeurs sur votre compte rendu. Mesurer la valeur moyenne, la fréquence et l'amplitude crête à crête du signal en sortie du circuit de mise en forme sur la broche A1 de la carte LINE_OUT et en déduire le facteur d'atténuation. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois le fonctionnement validé par un enseignant, mettre à jour le suivi de version en saisissant dans une console: echo commence cd ~/workspace_ac6/nucleo_tns/ git commit -a -m'dac ok' gitk & echo fini Solution: #include #define SAMPLING_RATE ( 48000.0f) #define SIN_FREQ ( 480.0f) #define SIN_AMP 2000.0f #define SIN_TAB_SAMPLES_PER_PERIOD ((unsigned int) (SAMPLING_RATE / SIN_FREQ)) //Taille du tableau pour une période du signal sinus uint32_t sin_tabbed[SIN_TAB_SAMPLES_PER_PERIOD]; void init_sin_tabbed(){ unsigned int k ; for(k = 0 ; k < SIN_TAB_SAMPLES_PER_PERIOD; k ++ ){ sin_tabbed[k] = SIN_AMP*sin( 2*M_PI *SIN_FREQ*k/SAMPLING_RATE)+ 2048; //Compléter ici par le calcul de la valeur des échantillons //signal(k.Tech)=A.sin(2Π.fo.k.Tech) + Vmoy } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t k = 0; HAL_StatusTypeDef ret= HAL_DAC_SetValue(&hdac2,DAC2_CHANNEL_1,0,sin_tabbed[k]); if (k>= SIN_TAB_SAMPLES_PER_PERIOD -1) k=0; else k++; //.... } ------------------------------------------------------------------------------------ ===== Exercice 3: Utilisation de l'ADC ===== Nous allons utiliser l'ADC pour échantillonner un signal d'entrée analogique. Pour vérifier le bon fonctionnement de l'ADC, les échantillons acquis seront recopiés sur le DAC. La broche PB11 est utilisée pour mesurer le temps total passé dans l'interruption tandis que PB12 est maintenant utilisé pour mesurer la durée de la conversion numérique->analogique. Complétez la fonction d'interruption **void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)** pour : - Piloter la sortie connectée à la broche PB11 à l'état haut (déjà mis en place dans l'exercice précédent) - Piloter la sortie connectée à la broche PB12 à l'état haut - Lancer une conversion sur l'ADC - Attendre la fin de la conversion (HAL_ADC_PollForConversion doit retourner HAL_OK) - Récupérer le résultat de la conversion - Stopper le convertisseur - Piloter la sortie connectée à la broche PB12 à l'état bas - Ecrire l'échantillon converti sur le DAC (recopie le signal d'entrée sur la sortie) - Piloter la sortie connectée à la broche PB11 à l'état bas (déjà mis en place dans l'exercice précédent) L'ADC du micro-controleur s'utilise avec les fonctions suivantes : HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); // Lance la conversion HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc); // Stoppe 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. //Vous pouvez le régler à 10 uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc) //Lit le résultat de la conversion Les valeurs possibles du paramètre de retour **HAL_StatusTypeDef** sont : **HAL_OK** , **HAL_ERROR** , **HAL_BUSY** et **HAL_TIMEOUT**. Pour la valeur effective de l'argument **ADC_HandleTypeDef* hadc** des fonctions ADC, vous utiliserez la valeur **&hadc1**, par exemple : **HAL_ADC_Start(&hadc1);** Une fois le code mis en place il vous faut le tester. Pour ce test, la carte Nucléo est associée à deux cartes : LINE_IN, LINE_OUT pour adapter les niveaux et protéger l'entrée ADC du micro-controleur. L'adaptation en entrée applique un gain de 4.54 sur le signal mis en entrée et le centre sur 1.65v. La procédure de test est la suivante : - configurez le canal génération de signal de l'oscilloscope pour une sinusoide à 400Hz centrée sur 0 avec une amplitude réglée pour ne pas saturer l'amplificateur de la carte LINE_IN (vcc=3.3v). - vérifiez les caractéristiques du signal avec l'oscilloscope - branchez la sortie du générateur de signaux sur l'entrée A0 de la carte d'adaptation LINE_IN - branchez l'entrée de l'oscilloscope sur la sortie A1 de la carte d'adaptation LINE_OUT. Il s'agit d'une version mise en forme du signal présent sur PA6. {{:img_20170327_130651.jpg?0x500}} {{:img_20170327_130710.jpg?0x500}} - Vérifiez la recopie correcte du signal d'entrée sur la sortie (au facteur d'amplification/atténuation près). - Mesurez ensuite et reportez sur le compte rendu : - sur la broche PB12, mesurez le temps temps nécessaire à l'ADC pour effectuer une conversion. - sur la broche PB11, mesurez le temps total d'exécution de la fonction d'interruption. - Conclure sur le temps disponible pour effectuer le calcul de l'équation de récurrence. {{https://bvdp.inetdoc.net/files/iut/tp_pic/validation.png}} Une fois le fonctionnement validé par un enseignant, mettre à jour le suivi de version en saisissant dans une console: echo commence cd ~/workspace_ac6/nucleo_tns/ git commit -a -m'adc ok' gitk & echo fini Solution: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); uint32_t ech; HAL_ADC_Start(&hadc1); // Lance la conversion HAL_ADC_PollForConversion(&hadc1, 10); //Attend le résultat de la conversion, le deuxième paramètre permet de limiter le temps d'attente. HAL_ADC_Stop(&hadc1); // Stoppe la conversion ech= HAL_ADC_GetValue(&hadc1); //Lit le résultat de la conversion HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); HAL_StatusTypeDef ret= HAL_DAC_SetValue(&hdac2,DAC2_CHANNEL_1,0,ech); // HAL_StatusTypeDef ret= HAL_DAC_SetValue(&hdac2,DAC2_CHANNEL_1,0,sin_tabbed[k]); ------------------------------------------------------------------------------------ Vous pouvez maintenant passer au TP suivant dans lequel vous allez insérer le filtre dans le programme du microcontroleur: [[tns_nucleo_filtre]] ------------------------------------------------------------------------------------ =====En cas de plantage du debuggueur===== si l'étudiant relance le debuggueur alors qu'il est déjà en route, le processus est détaché d'Eclipse et il ne peut plus être fermé. Pour lister les processus utilisant le fichier .elf: lsof |grep nucleo_tns.elf Pour tuer le processus à partir de son PID: kill -9 3942 il faut aussi tuer tous les process dont le nom commence par arm-none- Pour tuer le processus du debugguer au cas où vous n'auriez pas arreté le mode Debug avant de le relancer, copier coller dans une console: kill -9 $(lsof -t ~/workspace_ac6/nucleo_tns/Debug/nucleo_tns.elf) kill -9 $(lsof -t -c arm-none-) Pour gestion fine de la fréquence d'interruption: cpt_it++; if (cpt_it>=3) { htim1.Init.Period = 1333; cpt_it=0; }else{ htim1.Init.Period = 1332; }