Tous les articles par admin

Bye-bye Eye-Sat

Je vous partage ce message de Nicolas Verdier, Chef de Projet Nanolab Academy au CNES

"J’aurais aimé vous écrire ce message pour le 4ième anniversaire d’EYESAT en orbite mais la mécanique spatiale en a décidé autrement !

En effet, depuis quelques semaines notre satellite perdait de l’altitude, signe annonciateur d’une rentrée dans l’atmosphère prévue début novembre. Nous avons eu un dernier contact à 350km d’altitude vendredi dernier qui attestait d’un bilan de puissance négatif lié à la conjonction de plusieurs facteurs (consommation accrue liée à l’augmentation du nombre de passages pour mieux prévoir l’orbite, performances de pointage affectées par l’altitude et occurrence d’éclipses)

Eyesat, le premier cubesat du CNES réalisé par des étudiants vit ainsi ses derniers jours. Il nous livre cette ultime image en guise d’adieu !

Cette descente était attendue après 4 ans de vie en orbite et beaucoup de retours d'expérience sur les nouvelles technologies embarquées par ce petit satellite.

Même si nous sommes émus, cette fin est inéluctable et tous les acteurs de ce projet peuvent être fiers du travail accompli.

Plus de 250 étudiants se sont succédés pour concevoir, lancer et opérer EYESAT. Nos partenaires académiques l’ISAE-SUPAERO, l’IUT de Cachan, l’ENAC,… scientifiques le LATMOS et l’IRAP et industriels STEEL, SYRLINKS, SAP, et bien d’autres ont accompagné ce projet mettant toutes leurs compétences au service de sa réussite. La startup U-Space est née de cette aventure et nous a accompagné avec professionnalisme tout au long de ces quatre années pour l’opérer.

Un grand merci aussi pour Alain qui a été l’architecte de cet exploit et qui nous a laissé un bel héritage

EYESAT est une réussite collective, le totem de toute une génération d’étudiants. Il marquera sans conteste les débuts de la grande épopée des nano satellites français !

Merci encore pour vos soutiens et vos encouragements et place maintenant à AEROSAT et à tous les autres nano satellites des CSU qui reposeront sur SEED, la nouvelle plateforme héritée d’EYESAT que le CNES mettra à leur disposition."

Ethernet sur nucleo-F429ZI

Mise en oeuvre sur un microcontrôleur STM32F429ZI (Nucleo-F429ZI) en UDP

Testé avec STM32CubeIDE 1.12.1

Objectif de ce tutoriel :

Etablir une communication ethernet entre un PC et une carte Nucleo-F429ZI en utilisant le protocole UDP.
Une fois la configuration hardware réalisée, nous aborderons deux cas d'utilisations :

  • 1 La carte Nucleo-F429ZI dans le rôle de serveur UDP sur lequel vient se connecter le PC avec l'outil Netcat
  • 2 La carte Nucleo-F429ZI dans le rôle de client UDP qui vient se connecter au serveur Netcat démarré sur le PC préalablement.

Step 0 : configuration de l'Ethernet histoire de communiquer avec le PC et Netcat

Step 1 : configuration des horloges du STM32

Step 1 : configuration du driver LWIP

Le schéma électronique de la nucleo-F429ZI nous renseigne sur le driver hardware (LAN8742), qu'on choisit donc pour notre projet.

Le checksum est calculé par le hardware :

On désactive le DHCP pour fixer une adresse IPV4 de notre choix. Ici 192.168.0.51
Gateway 192.168.0.50 (ce sera l'adresse IP du PC de test).

On alloue 10 KBytes pour la mémoire.

et on n'oublie pas d'activer les "interruptions" ethernet dans la section NVIC de System core dans le Pinout and configuration (IOC).

Serveur UDP

Dans cette partie nous allons traiter de la mise en oeuvre du code en C pour réaliser un serveur UDP sur la carte Nucleo-F429ZI. Il aura l'adresse IP 192.168.0.51 et le PC : 192.168.0.50

Serveur-Step 0 : que faire dans le main ?

On rajoute la bibliothèque udp.h ains

#include "lwip/udp.h"

void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);

void udpServer_init(void);
extern struct netif gnetif; // déjà défini ailleurs mais on en a besoin donc on le met en extern
struct udp_pcb *upcb;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART3_UART_Init();
    MX_LWIP_Init();
    udpServer_init();
    while (1)
    {
        ethernetif_input(&gnetif);
        sys_check_timeouts();
    }
    } //fin main

Serveur-Step 1 : que faire ensuite ?

On rajoute les fonctions udbServer_init et udp_receive_callback


void udpServer_init(void)
{
    // UDP Control Block structure
   struct udp_pcb *upcb;
   err_t err;

   /* 1. Create a new UDP control block  */
   upcb = udp_new();

   /* 2. Bind the upcb to the local port */
   ip_addr_t myIPADDR;
   IP_ADDR4(&myIPADDR, 192, 168, 0, 51);

   err = udp_bind(upcb, &myIPADDR, 7000);  // 7 is the server UDP port

   /* 3. Set a receive callback for the upcb */
   if(err == ERR_OK)
   {
       udp_recv(upcb, udp_receive_callback, NULL);
   }
   else
   {
       udp_remove(upcb);
   }
}

//cette fonction sera exécutée lorsqu'un paquet de données UDP arrivera sur la nucleo-F429ZI
//Elle renvoie le contenu du paquet et un petit Hello xxx From UDP SERVER
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    struct pbuf *txBuf;
    /* Get the IP of the Client */
    char *remoteIP = ipaddr_ntoa(addr);
    char buf[100];

    int len = sprintf (buf,"Hello %s From UDP SERVER\n", (char*)p->payload);
    /* allocate pbuf from RAM*/
    txBuf = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
    /* copy the data into the buffer  */
    pbuf_take(txBuf, buf, len);
    /* Connect to the remote client */
    udp_connect(upcb, addr, port);
    /* Send a Reply to the Client */
    udp_send(upcb, txBuf);
    /* free the UDP connection, so we can accept new clients */
    udp_disconnect(upcb);
    /* Free the p_tx buffer */
    pbuf_free(txBuf);
    /* Free the p buffer */
    pbuf_free(p);
}

Serveur-Step 2 : c'est bien tout ça mais comment je teste ?

Pour les tests, la bonne manière est :
1 - de regarder si la carte répond à un ping
2 - de tester si le serveur répond

ping 192.168.0.51

Si la réponse est du genre :

➜  ozone ping 192.168.0.51  
PING 192.168.0.51 (192.168.0.51) 56(84) bytes of data.  
64 bytes from 192.168.0.51: icmp_seq=1 ttl=255 time=0.580 ms  
64 bytes from 192.168.0.51: icmp_seq=2 ttl=255 time=0.686 ms  
^C  
--- 192.168.0.51 ping statistics ---  
2 packets transmitted, 2 received, 0% packet loss, time 1006ms  
rtt min/avg/max/mdev = 0.580/0.633/0.686/0.053 ms

C'est que ça fonctionne, on peut passer au test du serveur UDP.

Pour ce faire, on utilise l'outils Netcat (nc), cela donne :

➜  ozone nc -u 192.168.0.51 7000

-u : UDP
192.168.0.51 adresse du PC
7000 port utilisé dans notre cas.

Rien ne se passe, c'est normal, il n'y a pas de données échangées pour le moment entre le PC et la nucleo.

On tape Alice dans le terminal et si on voit cela :

➜  ozone nc -u 192.168.0.51 7000  
Alice  
Hello Alice  
From UDP SERVER

C'est gagné !

Client UDP

Dans cette partie nous allons traiter de la mise en oeuvre du code en C pour réaliser un client UDP sur la carte Nucleo-F429ZI. Il aura l'adresse IP 192.168.0.51 et le PC qui sera un serveur aura l'adresse : 192.168.0.50

Client-Step 0 : que faire dans le main ?

On rajoute la bibliothèque udp.h ains

#include "lwip/udp.h"

void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);

void udpClient_connect(void);
static void udpClient_send(void);

extern struct netif gnetif; // déjà défini ailleurs mais on en a besoin donc on le met en extern
struct udp_pcb *upcb;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART3_UART_Init();
    MX_LWIP_Init();
    udpClient_connect();
    while (1)
    {
        ethernetif_input(&gnetif);
        sys_check_timeouts();
    }
    } //fin main

Client-Step 1 : que faire ensuite ?

On rajoute les fonctions udpClient_connect et udp_receive_callback


void udpClient_connect(void)
{
    err_t err;
    /* 1. Create a new UDP control block  */
    upcb = udp_new();

    /* Bind the block to module's IP and port */
    ip_addr_t myIPaddr;
    IP_ADDR4(&myIPaddr, 192, 168, 0, 51);
    udp_bind(upcb, &myIPaddr, 7000);

    /* configure destination IP address and port */
    ip_addr_t DestIPaddr;
    IP_ADDR4(&DestIPaddr, 192, 168, 0, 50);
    err= udp_connect(upcb, &DestIPaddr, 7000);

    if (err == ERR_OK)
    {
        /* 2. Send message to server */
        udpClient_send ();
        /* 3. Set a receive callback for the upcb */
        udp_recv(upcb, udp_receive_callback, NULL);
    }
}

static void udpClient_send(void)
{
  struct pbuf *txBuf;
  char data[100];

  int len = sprintf(data, "sending UDP client message\r\n");

  /* allocate pbuf from pool*/
  txBuf = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);

  if (txBuf != NULL)
  {
    /* copy data to pbuf */
    pbuf_take(txBuf, data, len);
    /* send udp data */
    udp_send(upcb, txBuf);
    /* free pbuf */
    pbuf_free(txBuf);
  }
}

//cette fonction sera exécutée lorsqu'un paquet de données UDP arrivera sur la nucleo-F429ZI
//Elle renvoie le contenu du paquet et un petit Hello xxx From UDP SERVER
void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    struct pbuf *txBuf;
    /* Get the IP of the Client */
    char *remoteIP = ipaddr_ntoa(addr);
    char buf[100];

    int len = sprintf (buf,"Hello %s From Nucleo-F429ZI UDP client\n", (char*)p->payload);
    /* allocate pbuf from RAM*/
    txBuf = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
    /* copy the data into the buffer  */
    pbuf_take(txBuf, buf, len);
    /* Connect to the remote client */
    udp_connect(upcb, addr, port);
    /* Send a Reply to the Client */
    udp_send(upcb, txBuf);
    /* free the UDP connection, so we can accept new clients */
    udp_disconnect(upcb);
    /* Free the p_tx buffer */
    pbuf_free(txBuf);
    /* Free the p buffer */
    pbuf_free(p);
}

Client-Step 2 : c'est bien tout ça mais comment je teste ?

Pour les tests, la bonne manière est :
1 - de regarder si la carte répond à un ping
2 - de tester si le serveur répond

ping 192.168.0.51

Si la réponse est du genre :

➜  ozone ping 192.168.0.51  
PING 192.168.0.51 (192.168.0.51) 56(84) bytes of data.  
64 bytes from 192.168.0.51: icmp_seq=1 ttl=255 time=0.580 ms  
64 bytes from 192.168.0.51: icmp_seq=2 ttl=255 time=0.686 ms  
^C  
--- 192.168.0.51 ping statistics ---  
2 packets transmitted, 2 received, 0% packet loss, time 1006ms  
rtt min/avg/max/mdev = 0.580/0.633/0.686/0.053 ms

C'est que ça fonctionne, on peut passer au test de notre nucleo-F429ZI.

Ensuite on va lance le serveur UDP sur le PC
Pour ce faire, on utilise l'outils Netcat (nc), cela donne :

➜  ozone nc -ul 7000

-u : UDP
-l : listen (le PC est un serveur en écoute sur le port 7000)
7000 port utilisé dans notre cas.

On reset la nucleo
On tape Alice dans le terminal et si on voit cela :

➜  ozone nc -ul 7000  
sending UDP client message

C'est gagné !

Et maintenant si c'est le PC qui envoie des données, cela donne :

➜  ozone nc -ul 7000  
sending UDP client message  
Bob  
Hello Bob  
From Nucleo-F429ZI UDP client

That's all folks!

Mise en œuvre de l’USART avec le DMA

Mise en oeuvre de l'USART avec le DMA

objectif de la manip :

Une chaine de caractère est envoyée automatiquement par l'USART du microcontrôleur. L'envoi est piloté par le contrôleur DMA sans intervention du coeur du microcontrôleur (il déclenche seulement l'envoi et c'est le DMA qui gère le reste).

Matériel requis

  • un ordinateur avec un terminal (putty, termite,...)
  • une carte nucleo-L073RZ ou F446RE ou autre...
  • un câble USB pour programmer et émuler la liaison série.

Configuration de l'USART

USART_Config_Sans_NVIC_params.png
Pas obligé d'activer les interruptions USART en mode circular buffer.

A noter que si on se met en Mode Circular et Increment Address coché pour Memory, il suffit de faire une seule fois l'appel à la fonction HAL_UART_Transmit_DMA(&huart2, testMessage, sizeof(testMessage)) pour que l'envoi se répète inlassablement.

    // Appel de ces deux fonctions dans cet ordre la et pas l'inverse sinon cela ne fonctionne pas
    MX_DMA_Init();
    MX_USART2_UART_Init();
  uint8_t testMessage[]="Message envoye automatiquement grace au controleur DMA\r\n";

    HAL_UART_Transmit_DMA(&huart2, testMessage, sizeof(testMessage));
    while(1)
    {
        //on ne fait rien
    }

En mode normal, cela donne :

Attention, il faut activer les interruptions USART !

while (1)
{
    HAL_UART_Transmit_DMA(&huart2, testMessage, sizeof(testMessage));
    HAL_Delay(1000);
}

Tests

Ouvrir un terminal (putty) et le configurer en 115200 bauds, 8 bits, pas de parité. 1 bit de stop.
Programmer la carte.
Le message doit s'afficher toutes les secondes à l'écran du terminal.

Problèmes fréquents

La fonction d'initialisation du DMA MX_DMA_Init() est appelée avant celle de l'USART MX_USART_init();
Et c'est le drame. Rien ne fonctionne. Bienvenue en STM32zarbie.

  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_DMA_Init();

Mise en œuvre de l’ADC avec le DMA

Mise en oeuvre de l'ADC avec le DMA

Matériel requis

  • un ordinateur avec un terminal (putty, termite,...)
  • une carte nucleo-L073RZ ou STM32F446RE ou autre
  • Un potentiomètre de 10kOhm par exemple relié à PA0 ou la carte IHM (mbed-application-shield)

Création du projet - Côté configuration

Configuration USART

On configure l'USART2 en 115200 baud.

Configuration ADC

Les choses importantes ici sont la résolution (12 bits), on aurait pu prendre moins.
Les paramètre Continous conversion Mode et Discontinuous Conversion Mode peuvent être indifféremment Enable ou Disable.
Le paramètre DMA Continuous Requests peut être aussi indifféremment Enable ou Disable.


Notons que les événements (interrupt) issues de l'ADC ne sont pas autorisées. Ce sera le contrôleur DMA qui se chargera d'informer d'une fin de transfert d'un échantillon vers l'emplacement mémoire où il sera stocké.

Configuration du DMA pour l'ADC

Comme dans cet exemple, on n'échantillonne qu'une seule entrée et on ne veut à l'instant t qu'une seule valeur, on peut être soit en mode Circular soit Normal, on peut cocher Increment Address ou non.
Dans notre cas, on aura une précision sur 12 bits donc on prend des word (16 bits).

Programmation - Côté code donc...

A rebrousse poil, cela donne :

Récupération des échantillons

Dans main.c et dans une section genre / USER CODE BEGIN 4 /

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
    ADC_FLAG=TRUE;
}

Dans la fonction main de main.c :

 if (ADC_FLAG == TRUE)
      {
        ADC_FLAG = FALSE;
        sprintf(message,"%ld\n\r", /*(uint16_t)*/pdata); //on peut caster mais pas obligé
        HAL_UART_Transmit(&huart2, message , sizeof(message), HAL_MAX_DELAY);
        HAL_ADC_Start_DMA(&hadc, &pdata, 1);

      }

On aura évidemment fait auparavant :

uint8_t ADC_FLAG;
#define TRUE 1
#define FALSE 0
uint32_t  pdata = 0; //very important de le mettre en uint32_t
uint8_t message[10]; //ça c'est pour envoyer une chaîne au PC

et in fine dans le main de main.c

uint8_t welcomeMessage[]="Test ADC & DMA sur PA0 8 bits 115200 bauds\r\n";  //un message d'accueil ne fait jamais de mal

  HAL_UART_Transmit(&huart2, welcomeMessage , sizeof(welcomeMessage), HAL_MAX_DELAY); //et on le balance

  HAL_ADC_Start_DMA(&hadc, &pdata, 1); //on lance les hostivités

  while (1)
  {
      if (ADC_FLAG == TRUE) //sur fin de conversion (merci le callback AL_ADC_ConvCpltCallback)
      {
        ADC_FLAG = FALSE;
        sprintf(message,"%ld\n\r", /*(uint16_t)*/pdata); //on peut caster mais pas obligé
        HAL_UART_Transmit(&huart2, message , sizeof(message), HAL_MAX_DELAY);
        HAL_ADC_Start_DMA(&hadc, &pdata, 1); // et rebelote

      }
  }
}

Attention !

Seule cet ordre fonctionne. Si on s'amuse à mettre MX_DMA_Init(); après MX_ADC_Init(); ça déraille complètement, on n'est plus sur 12 bits mais sur 8... Bienvenue en STM32zarbie.

  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_DMA_Init();
  MX_ADC_Init();

void MX_ADC_Init(void) (extrait)

Si on ne veut numériser qu'un seul échantillon sous 8 bits ou plus, alors on peut se mettre en circular buffer ou non.
Avoir à l'esprit que les conversions seront lancées avec la fonction :
HAL_ADC_Start_DMA(&hadc, &pdata, 1);
Cette fonction signifie que le contrôleur DMA va lancer la conversion dont l'échantillon sera stockée dans pdata.

    hadc.Init.ContinuousConvMode = DISABLE;
    hadc.Init.DiscontinuousConvMode = DISABLE;
    hadc.Init.DMAContinuousRequests = ENABLE;

Et quand cela fonctionne : exemple de sortie sur Putty

Mise en œuvre de l’ADC

Mise en oeuvre de l'ADC sans le DMA

Matériel requis

  • un ordinateur avec un terminal (putty, termite,...)
  • une carte nucleo-L073RZ ou STM32F446RE ou autre
  • Un potentiomètre de 10kOhm par exemple relié à PA0 ou la carte IHM (mbed-application-shield)

  • Création du projet - Côté configuration

    Configuration USART

    On configure l'USART2 en 115200 baud.

Configuration ADC

Les choses importantes ici sont la résolution (12 bits), on aurait pu prendre moins.
Les paramètre Continous conversion Mode et Discontinuous Conversion Mode peuvent être indifféremment Enable ou Disable.

Si on met Enable à Conversion Continuous Mode plus besoin de faire à chaque fois un ADC_Start(), on le fait au début une fois pour toute.
Evidemment, si on fait un ADC_Stop(), va falloir remettre un ADC_Start() quand on veut un nouvel échantillon.

Fonction main de main.c :

    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART2_UART_Init();
    MX_ADC_Init();
    HAL_UART_Transmit(&huart2, welcomeMessage , sizeof(welcomeMessage), HAL_MAX_DELAY);

    uint8_t pdata = 0;
    uint8_t message[5];
    HAL_ADC_Start(&hadc);
  while (1)
  {
        HAL_ADC_Start(&hadc);
        HAL_ADC_PollForConversion(&hadc, 1);
        pdata = HAL_ADC_GetValue(&hadc);
        sprintf(message,"%d\r\n",HAL_ADC_GetValue(&hadc));
        HAL_UART_Transmit(&huart2, message , sizeof(message), HAL_MAX_DELAY);
        HAL_ADC_Stop(&hadc);
        HAL_Delay(200);
  }
}

Fonction MX_ADC_Init de main.c :

static void MX_ADC_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  hadc.Instance = ADC1;
  hadc.Init.OversamplingMode = DISABLE;
  hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
  hadc.Init.Resolution = ADC_RESOLUTION_8B;
  hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ContinuousConvMode = ENABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerFrequencyMode = ENABLE;
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure for the selected ADC regular channel to be converted.
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

Mettre en oeuvre d’une LED avec un microcontrôleur STM32L073 (ou autre de chez ST)

Mise en oeuvre d'une led branchée sur la sortie PA5 du STM32L073

Choisir un nom de projet explicite, par exemple :

GPIO_PA5_LD2_SET_RESET_TOGGLE_L073RZ

Et dans le main avant le while (1) :

Allumage de la led

  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);

Extinction de la led

  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

Clignotement de la led

  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

code du de la boucle while du main complet

  while (1)
  {
      //Décommenter la ligne suivante pour allumer la led 2 branchée sur PA5
//    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
      // Décommenter les deux lignes suivantes pour faire clignoter la led branchée sur PA5
      HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
      HAL_Delay(500);
  }

Note :

Si on appelle la fonction HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
comme des bourrins, dans un while(1) sans mettre de délais (c'est pas malin de l'appeler en permanance...) la led ne s'allumera pas. Qu'on se le dise.

Objectif pour un étudiant : le faire en moins de 8 minutes

Utiliser un encodeur dans vos projets

Encodeur rotatif sous STM32

Mise en oeuvre sur un STM32-F429ZI (nucleo-F429ZI)

  • Mise en oeuvre d'un codeur rotatif avec bouton central.

  • Mise en garde : désactiver l'ethernet qui s'il n'est pas configuré fait planter au démarrage.

  • Au niveau du Timer à utiliser
    utiliser un timer qui permet le mode Encoder
    Attention : Sur la nucleo-F429ZI le timer2 ne semble pas bien connecté sur PA0 et PA1 (A vérifier)

  • Timer3 : PC7 et PA6. Testé avec succès.

  • GPIO : s'il n'y a pas de résistances de pull-up, les rajouter en interne.

Step 0 : configuration de l'USART3 histoire de communiquer avec le PC et putty (115200 8N1)

usart3

Step 1 : configuration des horloges du STM32

encoder_clock_config

Step 2 : configuration du Timer 3

tim3_step1

Step 3 : configuration du Timer 3, suite

tim3_step2

Step 4 : et dans le main

// ne pas oublier
#include "stdio.h"
int main(void)
{
    uint16_t EncVal = 0;
    uint8_t msg[]="Encoder test with default settings of project\r\n";

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
    /* Configure the system clock */
    SystemClock_Config();

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART3_UART_Init();
    MX_TIM3_Init();
    uint8_t Uart_Buf[70];
    HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
    HAL_UART_Transmit(&huart3, msg, sizeof(msg), 1000);
  while (1)
  {
        EncVal = (TIM3->CNT)>>2; //selon les valeurs configurées, pas nécessaire de faire une division par 4
        // EncVal = __HAL_TIM_GET_COUNTER(&htim2);
         int len = sprintf(Uart_Buf, "%d\r\n",EncVal);
         HAL_UART_Transmit(&huart3, Uart_Buf, len, 1000);
         HAL_Delay(100);
  }
}

Step 5 : Hardware

encodeur

Bibliographie

https://stm32world.com/wiki/STM32_Rotary_Encoder
https://stm32f4-discovery.net/2014/08/library-26-rotary-encoder-stm32f4/

Nucleo-F429ZI Schematic and pinout

foo@bar:~$ wget https://www.st.com/content/ccc/resource/technical/layouts_and_diagrams/schematic_pack/63/95/5c/0c/d6/af/4b/e2/nucleo_144pins_sch.zip/files/nucleo_144pins_sch.zip/jcr:content/translations/en.nucleo_144pins_sch.zip

Nucleo-F446RE Schematic and pinout

foo@bar:~$ wget https://www.st.com/content/ccc/resource/technical/layouts_and_diagrams/schematic_pack/71/1e/2a/ac/b5/c1/4b/a9/nucleo_64pins_sch.zip/files/nucleo_64pins_sch.zip/jcr:content/translations/en.nucleo_64pins_sch.zip

STM32F429ZI Reference Manual
https://www.st.com/resource/en/reference_manual/rm0090-stm32f405415-stm32f407417-stm32f427437-and-stm32f429439-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

USB

Première image d’Eyesat !!

Voici en avant première une image de la galaxie d'Andromède (M31) que le satellite Eyesat à capturé le 31/12/2019 à 20h38 (UTC). Taille : 1000*1000 pixels.

Et voici une image de référence de la même galaxie :

D'autres images à venir !