Dessiner la fractale de Mandelbrot avec une carte Arduino / Genuino

Par contre, faut pas être pressé ...

Image d'entête

par skywodd | | Licence (voir pied de page)

Catégories : Tutoriels Arduino Projets | Mots clefs : Arduino Genuino LCD Mandelbrot Fractale MI0283QT2


Dans ce tutoriel, nous allons dessiner la fractale de Mandelbrot sur un écran LCD couleur au moyen d'une carte Arduino / Genuino. En bonus, je vous propose d'étudier ensemble une variante du code utilisant des calculs à virgule fixe.

Sommaire

Bonjour à toutes et à tous !

Fractale de Mandelbrot

Fractale de Mandelbrot

Vous avez sûrement déjà vu une fractale sur le fond d'écran d'un collègue, sur internet ou dans des expositions d'images. Les images de fractales sont très particulières, avec un effet répétitif qui accroche l'oeil et hypnotise celui qui les regarde. Personnellement, je suis un grand fan de fractales. Je ne rate jamais une occasion de faire des fonds d'écrans avec celles-ci.

Les fractales sont des formes générées au moyen d'équations mathématiques. Les fractales les plus connues sont les ensembles de Mandelbrot et de Julia. Il en existe bien d'autres, toutes plus impressionnantes les unes que les autres.

Dans cet article on va voir ensemble comment dessiner la fractale de Mandelbrot (ma préférée) avec une carte Arduino.

L'ensemble de Mandelbrot

L'ensemble de Mandelbrot est un ensemble mathématique très intéressant, qui fournit une fractale très sympathique à l'oeil et de formes variées quand on zoome dans celle-ci.

Mon niveau en mathématique étant relativement bas, surtout quand il s'agit de calculs avec des nombres complexes, je serais bien incapable de vous faire une explication simplifiée du calcul derrière l'ensemble de Mandelbrot. Je vais donc me contenter d'expliquer ce que je faire : le code.

PS Si un lecteur se sent d'écrire une explication sur le sujet dans les commentaires, qu'il n'hésite pas ;)

Le montage de démonstration

Pour visualiser le résultat du code du chapitre suivant, il va falloir utiliser un écran LCD couleur. J'ai choisi d'utiliser un écran MI0283QT2 avec un support d'adaptation, disponible chez Watterott.

Si vous avez un écran d'une autre référence sous la main, vous pourrez l'utiliser, mais il faudra reprendre la partie affichage du code en fonction de votre écran ;)

N.B. J'utilise la version 1 de l'adaptateur avec une mSD-Shield pour bénéficier d'une vitesse d'affichage maximum. La nouvelle version embarque son propre microcontrôleur pour l'affichage. C'est très pratique pour faire des formes géométriques (carrés, triangle, etc.), mais pour afficher des pixels un à un, c'est loin d'être idéal.

Photographie du matériel nécessaire à la réalisation du projet "Arduino Mandelbrot"

Le matériel nécessaire

En résumé, pour cet article, il va falloir :

  • Une carte Arduino UNO (et son câble USB),

  • Une shield mSD-Shield (v1 ou v2, disponible chez Watterott),

  • Un écran LCD MI0283QT2 avec son adaptateur (v1, là aussi disponible chez Watterott).

Le montage se résume à empiler les cartes les une sur les autres dans le bon sens ;)

Le code

Le code ci-dessous permet d'afficher la fractale de Mandelbrot à une position précise dans l'espace de Mandelbrot (-2.5 ~ 1.0 en X et -1.2 ~ 1.2 en Y) avec une profondeur de zoom arbitraire.

Il existe divers algorithmes de calcul en fonction du résultat souhait. La plupart des algorithmes disponibles sur le web permettent de générer une image de taille fixe (pas forcément aux bonnes proportions) ou une image de taille variable aux bonnes proportions.

Pour mon code, j'ai choisi d'utiliser un mix des deux algorithmes disponibles sur le web : une taille d'image fixe en sortie et avec les bonnes proportions. L'algorithme que j'ai choisi prend en argument un point central et un zoom. Il dessine ensuite chaque pixel autour de ce point pour obtenir la taille d'image désirée.

PS L'algorithme en question était disponible sur StackOverflow mais impossible de retrouver le lien de la source :(

1
2
3
4
5
6
/* Includes */
#include <stdint.h>           // Pour les types d'entier de taille fixe
#include <SPI.h>              // Pour la communication avec l'écran
#include <digitalWriteFast.h> // Pour éviter une erreur de compilation
#include <GraphicsLib.h>      // Pour les fonctions de dessin
#include <MI0283QT2.h>        // Pour l'écran lui-même

Le code commence par l'inclusion d'une montagne de bibliothèques de codes pour l'écran LCD.

PS Toutes ces bibliothèques sont disponibles sur le GitHub de Watterott. L'installation est un peu complexe vu le nombre de bibliothèques et l'ancienneté de celles-ci. Si vous avez un écran LCD couleur plus récent, n'hésitez pas à donner la référence en commentaire ;)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Defines */
const uint16_t MAX_ITERATION = 142; // Nombre de couleurs
const uint16_t SCREEN_WIDTH  = 320; // 
const uint16_t SCREEN_HEIGHT = 240; // Taille de l'écran

/**
 * Tableaux de couleurs (142 couleurs + noir, tiré d'un nuancier trouvé sur le web)
 */
const uint16_t COLOR_TABLE[143] = { // HTML color
  0xf7df, 0xff5a, 0x07ff, 0x7ffa, 0xf7ff, 0xf7bb, 0xff38, 0xff59, 0x001f, 0x895c, 
  0xa145, 0xddd0, 0x5cf4, 0x7fe0, 0xd343, 0xfbea, 0x64bd, 0xffdb, 0xd8a7, 0x7ff, 
  0x0011, 0x0451, 0xbc21, 0xad55, 0x0320, 0xbdad, 0x8811, 0x5345, 0xfc60, 0x9999, 
  0x8800, 0xecaf, 0x8df1, 0x49f1, 0x2a69, 0x067a, 0x901a, 0xf8b2, 0x05ff, 0x6b4d, 
  0x1c9f, 0xd48e, 0xb104, 0xffde, 0x2444, 0xf81f, 0xdefb, 0xffdf, 0xfea0, 0xdd24, 
  0x8410, 0x0400, 0xafe5, 0xf7fe, 0xfb56, 0xcaeb, 0x4810, 0xfffe, 0xf731, 0xe73f, 
  0xff9e, 0x7fe0, 0xffd9, 0xaedc, 0xf410, 0xe7ff, 0xffda, 0xd69a, 0x9772, 0xfdb8, 
  0xfd0f, 0x2595, 0x867f, 0x839f, 0x7453, 0xb63b, 0xfffc, 0x07e0, 0x3666, 0xff9c, 
  0xf81f, 0x8000, 0x6675, 0x0019, 0xbaba, 0x939b, 0x3d8e, 0x7b5d, 0x07d3, 0x4e99, 
  0xc0b0, 0x18ce, 0xf7ff, 0xff3c, 0xff36, 0xfef5, 0x0010, 0xffbc, 0x8400, 0x6c64, 
  0xfd20, 0xfa20, 0xdb9a, 0xef55, 0x9fd3, 0xaf7d, 0xdb92, 0xff7a, 0xfed7, 0xcc27, 
  0xfe19, 0xdd1b, 0xb71c, 0x8010, 0xf800, 0xbc71, 0x435c, 0x8a22, 0xfc0e, 0xf52c, 
  0x2c4a, 0xffbd, 0xa285, 0xc618, 0x867d, 0x6ad9, 0x7412, 0xffdf, 0x07ef, 0x4416, 
  0xd5b1, 0x0410, 0xddfb, 0xfb08, 0x471a, 0xec1d, 0xd112, 0xf6f6, 0xffff, 0xf7be, 
  0xffe0, 0x9e66, 0x0000
};

Le code continue ensuite avec plusieurs constantes permettant de définir la taille de l'écran, le nombre de couleurs et les couleurs en question (format RGB565 pour l'écran que j'utilise).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* Déclaration de l'objet lcd pour l'affichage */
MI0283QT2 lcd;

/* Fonction setup */
void setup() {

  /* Initialise l'écran LCD */
  lcd.begin(2); // Initialisation de l'écran lcd
  lcd.led(30); // Backligth à 30%
  //lcd.fillScreen(RGB(0, 0, 0)); // Écran noir
  lcd.setOrientation(270);
}

La fonction setup() commence par la déclaration et l'initialisation de l'écran LCD. J'ai été obligé de modifier l'orientation de l'écran de 270° pour que l'affichage soit dans le bon sens.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* Fonction loop() */
void loop() {

  /* Point de départ de la fenêtre de dessin dans le plan réel / complexe de mandelbrot */
  static float start_x = -0.75;
  static float start_y = 0.0;
  static float zoom = 0.8;

  /* Dessine la fractale */
  draw_mandelbrot(start_x, start_y, zoom);

  /* Effet de zoom très sommaire */
  zoom += 0.1;
}

La fonction loop() permet de faire l'affichage en partant du point (-0.75, 0) avec un zoom de 0.8 et en augmentant le zoom de 0.1 après chaque affichage.

L'affichage d'une image est très lent, plusieurs minutes (voir chapitre en fin d'article). Il n'y a donc pas besoin d'ajouter de délai supplémentaire ;)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void draw_mandelbrot(const float start_x, const float start_y, const float zoom) {

  /* Commence l'affichage */
  lcd.setArea(0, 0, SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1);
  lcd.drawStart();

  /* Pour chaque pixel en X */
  for(uint16_t x = 0; x < SCREEN_WIDTH; ++x) {
    float p_r = 1.5 * (x - SCREEN_WIDTH / 2.0) / (0.5 * zoom * SCREEN_WIDTH) + start_x;

    /* Pour chaque pixel en Y */
    for(uint16_t y = 0; y < SCREEN_HEIGHT; ++y) {
      float p_i = (y - SCREEN_HEIGHT / 2.0) / (0.5 * zoom * SCREEN_HEIGHT) + start_y;
      float new_r = 0.0, new_i = 0.0, old_r = 0.0, old_i = 0.0;
      uint16_t i = 0;

      /* Magie noir mathématique (merci Wikipedia) */
      while ((new_r * new_r + new_i * new_i) < 4.0 && i < MAX_ITERATION) {
        old_r = new_r;
        old_i = new_i;
        new_r = old_r * old_r - old_i * old_i + p_r;
        new_i = 2.0 * old_r * old_i + p_i;
        ++i;
      }

      /* Affiche le pixel */
      lcd.draw(COLOR_TABLE[i]);
    }
  }

  /* Termine l'affichage */
  lcd.drawStop();
}

La fonction draw_mandelbrot() permet de calculer et d'afficher une portion de l'ensemble de Mandelbrot en fonction du point et du zoom fourni en paramètres.

La fonction est découpée en six parties :

  • début de l'affichage (initialisation du mode dessin de l'écran LCD),

  • traitement de chaque pixel en X,

  • traitement de chaque pixel en Y,

  • calcul mathématique pour le pixel en cours,

  • affichage du pixel en cours,

  • fin de l'affichage.

La coloration de la fractale est réalisée au moyen du temps d'échappement de l'algorithme. L'idée est de faire boucler l'algorithme autant de fois qu'il y a de couleurs, jusqu'à ce que le résultat soit trouvé ou que l'algorithme s'arrête de lui-même. Le nombre de fois que l'algorithme s'exécute correspondant à la couleur finale.

Le code complet avec commentaires :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
/*
 * Générateur de fractale de Mandelbrot sur plateforme Arduino
 */

/* Includes */
#include <stdint.h>           // Pour les types d'entier de taille fixe
#include <SPI.h>              // Pour la communication avec l'écran
#include <digitalWriteFast.h> // Pour éviter une erreur de compilation
#include <GraphicsLib.h>      // Pour les fonctions de dessin
#include <MI0283QT2.h>        // Pour l'écran lui-même

/* Defines */
const uint16_t MAX_ITERATION = 142; // Nombre de couleurs
const uint16_t SCREEN_WIDTH  = 320; // 
const uint16_t SCREEN_HEIGHT = 240; // Taille de l'écran

/**
 * Tableaux de couleurs (142 couleurs + noir, tiré d'un nuancier trouvé sur le web)
 */
const uint16_t COLOR_TABLE[143] = { // HTML color
  0xf7df, 0xff5a, 0x07ff, 0x7ffa, 0xf7ff, 0xf7bb, 0xff38, 0xff59, 0x001f, 0x895c, 
  0xa145, 0xddd0, 0x5cf4, 0x7fe0, 0xd343, 0xfbea, 0x64bd, 0xffdb, 0xd8a7, 0x7ff, 
  0x0011, 0x0451, 0xbc21, 0xad55, 0x0320, 0xbdad, 0x8811, 0x5345, 0xfc60, 0x9999, 
  0x8800, 0xecaf, 0x8df1, 0x49f1, 0x2a69, 0x067a, 0x901a, 0xf8b2, 0x05ff, 0x6b4d, 
  0x1c9f, 0xd48e, 0xb104, 0xffde, 0x2444, 0xf81f, 0xdefb, 0xffdf, 0xfea0, 0xdd24, 
  0x8410, 0x0400, 0xafe5, 0xf7fe, 0xfb56, 0xcaeb, 0x4810, 0xfffe, 0xf731, 0xe73f, 
  0xff9e, 0x7fe0, 0xffd9, 0xaedc, 0xf410, 0xe7ff, 0xffda, 0xd69a, 0x9772, 0xfdb8, 
  0xfd0f, 0x2595, 0x867f, 0x839f, 0x7453, 0xb63b, 0xfffc, 0x07e0, 0x3666, 0xff9c, 
  0xf81f, 0x8000, 0x6675, 0x0019, 0xbaba, 0x939b, 0x3d8e, 0x7b5d, 0x07d3, 0x4e99, 
  0xc0b0, 0x18ce, 0xf7ff, 0xff3c, 0xff36, 0xfef5, 0x0010, 0xffbc, 0x8400, 0x6c64, 
  0xfd20, 0xfa20, 0xdb9a, 0xef55, 0x9fd3, 0xaf7d, 0xdb92, 0xff7a, 0xfed7, 0xcc27, 
  0xfe19, 0xdd1b, 0xb71c, 0x8010, 0xf800, 0xbc71, 0x435c, 0x8a22, 0xfc0e, 0xf52c, 
  0x2c4a, 0xffbd, 0xa285, 0xc618, 0x867d, 0x6ad9, 0x7412, 0xffdf, 0x07ef, 0x4416, 
  0xd5b1, 0x0410, 0xddfb, 0xfb08, 0x471a, 0xec1d, 0xd112, 0xf6f6, 0xffff, 0xf7be, 
  0xffe0, 0x9e66, 0x0000
};

/* Déclaration de l'objet lcd pour l'affichage */
MI0283QT2 lcd;

/* Fonction setup */
void setup() {

  /* Initialise l'écran LCD */
  lcd.begin(2); // Initialisation de l'écran lcd
  lcd.led(30); // Backligth à 30%
  //lcd.fillScreen(RGB(0, 0, 0)); // Écran noir
  lcd.setOrientation(270);
}

/* Fonction loop() */
void loop() {

  /* Point de départ de la fenêtre de dessin dans le plan réel / complexe de mandelbrot */
  static float start_x = -0.75;
  static float start_y = 0.0;
  static float zoom = 0.8;

  /* Dessine la fractale */
  draw_mandelbrot(start_x, start_y, zoom);

  /* Effet de zoom très sommaire et pas du tout proportionné */
  zoom += 0.1;
}

/** 
 * Dessine une fractale de Mandelbrot
 */
void draw_mandelbrot(const float start_x, const float start_y, const float zoom) {

  /* Commence l'affichage */
  lcd.setArea(0, 0, SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1);
  lcd.drawStart();

  /* Pour chaque pixel en X */
  for(uint16_t x = 0; x < SCREEN_WIDTH; ++x) {
    float p_r = 1.5 * (x - SCREEN_WIDTH / 2.0) / (0.5 * zoom * SCREEN_WIDTH) + start_x;

    /* Pour chaque pixel en Y */
    for(uint16_t y = 0; y < SCREEN_HEIGHT; ++y) {
      float p_i = (y - SCREEN_HEIGHT / 2.0) / (0.5 * zoom * SCREEN_HEIGHT) + start_y;
      float new_r = 0.0, new_i = 0.0, old_r = 0.0, old_i = 0.0;
      uint16_t i = 0;

      /* Magie noir mathématique (merci Wikipedia) */
      while ((new_r * new_r + new_i * new_i) < 4.0 && i < MAX_ITERATION) {
        old_r = new_r;
        old_i = new_i;
        new_r = old_r * old_r - old_i * old_i + p_r;
        new_i = 2.0 * old_r * old_i + p_i;
        ++i;
      }

      /* Affiche le pixel */
      lcd.draw(COLOR_TABLE[i]);
    }
  }

  /* Termine l'affichage */
  lcd.drawStop();
}

L'extrait de code ci-dessus est disponible en téléchargement sur cette page (le lien de téléchargement en .zip contient le projet Arduino prêt à l'emploi).

Bonus : Le code avec des calculs à virgule fixe

Les calculs avec des nombres à virgule flottante (float) sont très lents sur des petits microcontrôleurs comme ceux des cartes Arduino, car ceux-ci ne disposent pas matériellement du nécessaire pour manipuler ce genre de nombre.

Quand un microcontrôleur ne dispose pas de FPU ("Floating Point Unit", aka "unité de calcul en nombre flottant"), il est nécessaire de réaliser les opérations mathématiques avec du code, ajouté par le compilateur lors de la compilation. Bien évidemment, tout ce code caché prend du temps pour s'exécuter.

Une astuce vielle comme le monde pour pallier à ce manque est le calcul avec des nombres à virgule fixe. L'idée est simple : prendre le nombre à virgule flottante, le multiplier par un coefficient fixe (une puissance de deux en général) et faire tous les calculs avec des nombres entiers.

Pour comprendre le principe, imaginons que l'on souhaite additionner 1.5 et 2.5. On va prendre un coefficient de 10 (soit une précision 0.1 en base 10, en informatique on préférerait une puissance de deux pour simplifier les opérations de calcul). Soit 1.5 * 10 = 15, 2.5 * 10 = 25. Or, 15 + 25 = 40, divisé par 10 cela donne bien 4.0. En suivant ce principe, on peut faire n'importe quel calcul avec des nombres à virgule, sans utiliser le moindre nombre flottant.

PS Dans les faits, on est limité par la valeur maximum que peut stocker un nombre entier.

Les éléments de base en calcul à virgule fixe sont les suivants :

  • conversion d'un nombre flottant vers un nombre à virgule fixe : flottant * coefficient,

  • conversion d'un nombre à virgule fixe vers un nombre flottant : nombre / coefficient,

  • additions de deux nombres à virgule fixe : nombre + nombre,

  • soustraction de deux nombres à virgule fixe : nombre - nombre,

  • multiplication de deux nombres à virgule fixe : (nombre * nombre) / coefficient,

  • division de deux nombres à virgule fixe : (nombre * coefficient) / nombre.

N.B. Lors d'une multiplication ou d'une division, la valeur intermédiaire doit être deux fois plus grande en taille binaire que le résultat. La multiplication de deux nombres à virgule fixe de 16 bits donne un chiffre sur 32 bits, qui est ensuite ramené à 16 bits.

Le code avec commentaires :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
 * Générateur de fractale de Mandelbrot sur plateforme Arduino (calculs à virgule fixe)
 */

/* Includes */
#include <stdint.h>           // Pour les types d'entier de taille fixe
#include <SPI.h>              // Pour la communication avec l'écran
#include <digitalWriteFast.h> // Pour éviter une erreur de compilation
#include <GraphicsLib.h>      // Pour les fonctions de dessin
#include <MI0283QT2.h>        // Pour l'écran lui-même

/* Utilitaire de calculs à virgule fixe */
#define decimal int16_t
#define decimal_2x int32_t
#define MID_FP_POINT (8)
#define floatToDecimal(f) (f * ((decimal_2x) 1 << MID_FP_POINT))
#define addDecimal(a, b) ((decimal) a + (decimal) b)
#define substractDecimal(a, b) ((decimal) a - (decimal) b)
#define multiplyDecimal(a, b) (((decimal_2x) a * b) >> MID_FP_POINT)
#define divideDecimal(a, b) (((decimal_2x) a << MID_FP_POINT) / b)

/* Defines */
const uint16_t MAX_ITERATION = 142; // Nombre de couleurs
const uint16_t SCREEN_WIDTH  = 320; // 
const uint16_t SCREEN_HEIGHT = 240; // Taille de l'écran

/**
 * Tableaux de couleurs (142 couleurs + noir, tiré d'un nuancier trouvé sur le web)
 */
const uint16_t COLOR_TABLE[143] = { // HTML color
  0xf7df, 0xff5a, 0x07ff, 0x7ffa, 0xf7ff, 0xf7bb, 0xff38, 0xff59, 0x001f, 0x895c, 
  0xa145, 0xddd0, 0x5cf4, 0x7fe0, 0xd343, 0xfbea, 0x64bd, 0xffdb, 0xd8a7, 0x7ff, 
  0x0011, 0x0451, 0xbc21, 0xad55, 0x0320, 0xbdad, 0x8811, 0x5345, 0xfc60, 0x9999, 
  0x8800, 0xecaf, 0x8df1, 0x49f1, 0x2a69, 0x067a, 0x901a, 0xf8b2, 0x05ff, 0x6b4d, 
  0x1c9f, 0xd48e, 0xb104, 0xffde, 0x2444, 0xf81f, 0xdefb, 0xffdf, 0xfea0, 0xdd24, 
  0x8410, 0x0400, 0xafe5, 0xf7fe, 0xfb56, 0xcaeb, 0x4810, 0xfffe, 0xf731, 0xe73f, 
  0xff9e, 0x7fe0, 0xffd9, 0xaedc, 0xf410, 0xe7ff, 0xffda, 0xd69a, 0x9772, 0xfdb8, 
  0xfd0f, 0x2595, 0x867f, 0x839f, 0x7453, 0xb63b, 0xfffc, 0x07e0, 0x3666, 0xff9c, 
  0xf81f, 0x8000, 0x6675, 0x0019, 0xbaba, 0x939b, 0x3d8e, 0x7b5d, 0x07d3, 0x4e99, 
  0xc0b0, 0x18ce, 0xf7ff, 0xff3c, 0xff36, 0xfef5, 0x0010, 0xffbc, 0x8400, 0x6c64, 
  0xfd20, 0xfa20, 0xdb9a, 0xef55, 0x9fd3, 0xaf7d, 0xdb92, 0xff7a, 0xfed7, 0xcc27, 
  0xfe19, 0xdd1b, 0xb71c, 0x8010, 0xf800, 0xbc71, 0x435c, 0x8a22, 0xfc0e, 0xf52c, 
  0x2c4a, 0xffbd, 0xa285, 0xc618, 0x867d, 0x6ad9, 0x7412, 0xffdf, 0x07ef, 0x4416, 
  0xd5b1, 0x0410, 0xddfb, 0xfb08, 0x471a, 0xec1d, 0xd112, 0xf6f6, 0xffff, 0xf7be, 
  0xffe0, 0x9e66, 0x0000
};

/* Déclaration de l'objet lcd pour l'affichage */
MI0283QT2 lcd;

/* Fonction setup */
void setup() {

  /* Initialise l'écran LCD */
  lcd.begin(2); // Initialisation de l'écran lcd
  lcd.led(30); // Backligth à 30%
  //lcd.fillScreen(RGB(0, 0, 0)); // Écran noir
  lcd.setOrientation(270);
}

/* Fonction loop() */
void loop() {

  /* Point de départ de la fenêtre de dessin dans le plan réel / complexe de mandelbrot */
  static decimal start_x = floatToDecimal(-0.75);
  static decimal start_y = floatToDecimal(0.0);
  static decimal zoom = floatToDecimal(0.8);

  /* Dessine la fractale */
  draw_mandelbrot(start_x, start_y, zoom);

  /* Effet de zoom très sommaire et pas du tout proportionné */
  zoom += floatToDecimal(0.1);
}

/** 
 * Dessine une fractale de Mandelbrot
 */
void draw_mandelbrot(const decimal start_x, const decimal start_y, const decimal zoom) {

  /* Commence l'affichage */
  lcd.setArea(0, 0, SCREEN_HEIGHT - 1, SCREEN_WIDTH - 1);
  lcd.drawStart();

  /* Pour chaque pixel en X */
  for(uint16_t x = 0; x < SCREEN_WIDTH; ++x) {
    // decimal p_r = 1.5 * (x - SCREEN_WIDTH / 2.0) / (0.5 * zoom * SCREEN_WIDTH) + start_x;
    decimal p_r = addDecimal(divideDecimal(multiplyDecimal(floatToDecimal(1.5), substractDecimal(x, divideDecimal(SCREEN_WIDTH, floatToDecimal(2.0)))), 
      multiplyDecimal(multiplyDecimal(floatToDecimal(0.5), zoom), SCREEN_WIDTH)), start_x);

    /* Pour chaque pixel en Y */
    for(uint16_t y = 0; y < SCREEN_HEIGHT; ++y) {
      // decimal p_i = (y - SCREEN_HEIGHT / 2.0) / (0.5 * zoom * SCREEN_HEIGHT) + start_y;
      decimal p_i = addDecimal(divideDecimal(substractDecimal(y, divideDecimal(SCREEN_HEIGHT, floatToDecimal(2.0))), 
        multiplyDecimal(multiplyDecimal(floatToDecimal(0.5), zoom), SCREEN_HEIGHT)), start_y);
      
      decimal new_r = floatToDecimal(0.0), new_i = floatToDecimal(0.0);
      decimal old_r = floatToDecimal(0.0), old_i = floatToDecimal(0.0);
      uint16_t i = 0;

      /* Magie noir mathématique (merci Wikipedia) */
      // while ((new_r * new_r + new_i * new_i) < 4.0 && i < MAX_ITERATION) {
      while (addDecimal(multiplyDecimal(new_r, new_r), multiplyDecimal(new_i, new_i)) < floatToDecimal(4.0) && i < MAX_ITERATION) {
        old_r = new_r;
        old_i = new_i;
        // new_r = old_r * old_r - old_i * old_i + p_r;
        new_r = addDecimal(substractDecimal(multiplyDecimal(old_r, old_r), multiplyDecimal(old_i, old_i)), p_r);
        // new_i = 2.0 * old_r * old_i + p_i;
        new_i = addDecimal(multiplyDecimal(multiplyDecimal(floatToDecimal(2.0), old_r), old_i), p_i);
        ++i;
      }

      /* Affiche le pixel */
      lcd.draw(COLOR_TABLE[i]);
    }
  }

  /* Termine l'affichage */
  lcd.drawStop();
}

L'extrait de code ci-dessus est disponible en téléchargement sur cette page (le lien de téléchargement en .zip contient le projet Arduino prêt à l'emploi).

Le résultat

Le résultat du code du projet "Arduino Mandelbrot"

Le résultat

En exécutant le code sur la carte Arduino, on se retrouve avec la fractale de Mandelbrot en plein écran (pas de zoom) et centrée par défaut. La résolution de l'écran étant de seulement 320 x 240 pixels, le résultat n'est pas en "haute définition", je vous l'accorde ;)

Pour les amateurs de benchmark, le code utilisant des nombres à virgule flottante met 2 minutes 28 secondes pour afficher la fractale. Le code utilisant des nombres à virgule fixe met seulement 56 secondes, soit 2.6 fois moins de temps.

Conclusion

Ce tutoriel est désormais terminé.

Si ce tutoriel vous a plu, n'hésitez pas à le commenter sur le forum, à le diffuser sur les réseaux sociaux et à soutenir le site si cela vous fait plaisir.