Table des matières

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

Les informations nécessaires au lancement de l'outil SystemWorkbench utilisé pour ce tp sont disponibles sur 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:

  1. lancer eclipse via la console en tapant: /opt/STM32/SystemWorkbench/eclipse
  2. Si une fenêtre “Select a workspace” apparaît, saisir le nom de dossier: /home/IUT/<login étudiant>/workspace_ac6/ et cocher “Use this as the default…”
  3. fermer l'onglet “Welcome” en cliquant sur la croix.
  4. cliquer à gauche sur l'icone C/C++
  5. cliquer droit dans project Explorer→Import, puis General→Existing Projects into Workspace
  6. cliquer sur Next
  7. cocher Select archive file, puis cliquer sur Browse
  8. choisir /home/IUT/<login étudiant>/workspace_ac6/nucleo_tns_etu2018.zip
  9. cliquer sur Finish

Activation de la sauvegarde automatique des fichiers à chaque compilation

  1. Cliquer sur Window→Preferences.
  2. Dans la colonne de gauche, cliquer sur la petite flèche à gauche de “General” puis sur “Workspace” dans la liste qui apparaît.
  3. A droite, cocher “Save automatically before build”.
  4. 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:

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:

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):

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):

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:

Brancher la carte Nucleo au port USB du PC.

Pour activer le mode Debug :

  1. Clique-droit sur le nom du projet
  2. 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.

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

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.

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

————————————————————————————

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:

sin_tabbed.c
 #include <math.h>
 #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.

aide.txt
  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<nbEch; k++) signal[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é:

call_sin_tabbed.c
  //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 :

  1. Piloter la sortie connectée à la broche PB11 à l'état haut (déjà mis en place dans l'exercice précédent)
  2. Déterminer la valeur de l'échantillon actuel du signal sinusoïdal à 480Hz
  3. Mettre à jour le numéro de l'échantillon pour l'interruption suivante (en veillant à rester dans les bornes autorisées)
  4. Ecrire l'échantillon sur le DAC
  5. 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.

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

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 :

  1. Piloter la sortie connectée à la broche PB11 à l'état haut (déjà mis en place dans l'exercice précédent)
  2. Piloter la sortie connectée à la broche PB12 à l'état haut
  3. Lancer une conversion sur l'ADC
  4. Attendre la fin de la conversion (HAL_ADC_PollForConversion doit retourner HAL_OK)
  5. Récupérer le résultat de la conversion
  6. Stopper le convertisseur
  7. Piloter la sortie connectée à la broche PB12 à l'état bas
  8. Ecrire l'échantillon converti sur le DAC (recopie le signal d'entrée sur la sortie)
  9. 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 :

  1. 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).
  2. vérifiez les caractéristiques du signal avec l'oscilloscope
  3. branchez la sortie du générateur de signaux sur l'entrée A0 de la carte d'adaptation LINE_IN
  4. 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 img_20170327_130710.jpg

  1. Vérifiez la recopie correcte du signal d'entrée sur la sortie (au facteur d'amplification/atténuation près).
  2. Mesurez ensuite et reportez sur le compte rendu :
    1. sur la broche PB12, mesurez le temps temps nécessaire à l'ADC pour effectuer une conversion.
    2. sur la broche PB11, mesurez le temps total d'exécution de la fonction d'interruption.
  3. Conclure sur le temps disponible pour effectuer le calcul de l'équation de récurrence.

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

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

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-)