[Conseil] Optimisation de Fonction perso drawFramBitmap

Edit : Voir le 4 éme poste pour la fonction en question

Bonjours,

Voici mon problème :

j’ai acheté une Fam de 32KByte et je voudrais y stoker des images au format char hex
via un code puis les récupérer dans une variable volatile pour les afficher a l’écran et ainsi avoir un gain de place conséquent je début en arduino et la je vous avoue que je suis un peux perdu avec cette fameuse Fram.

Ici le code actuel pour afficher les images qui fonctionne mais je n’y comprend rien a cette Fram :

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_FRAM_I2C.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);



static const unsigned char PROGMEM eeg1_bmp[] =
{0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x00, 0xE0,
0x00, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0xEE, 0xE0, 0x0E, 0xEE, 0x00,
0xEE, 0xE0, 0x0E, 0xEE, 0x00, 0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0x00,
0xE0, 0x0E, 0xEE, 0x00, 0x00, 0xE0, 0x0E, 0xEE, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEE, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0x00, 0x00, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00,
0xEE, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE,
0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x0E,
0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0E, 0xE0, 0x00, 0xEE, 0xE0, 0x0E, 0xE0, 0x00, 0xEE, 0xE0, 0x0E, 0xE0, 0x00, 0xEE, 0xE0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x00, 0xEE, 0x00, 0x00, 0xEE, 0x00, 0xEE, 0x00, 0x00, 0xEE,
0x00, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0xEE, 0xEE, 0xE0, 0x0E, 0xEE, 0xEE,
0xEE, 0xE0, 0x0E, 0xEE, 0xEE, 0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

static const unsigned char PROGMEM eeg2_bmp[] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E,
0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x00, 0xE0, 0x00,
0xEE, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xEE, 0x00, 0xEE,
0xE0, 0x0E, 0xEE, 0x00, 0xEE, 0xE0, 0x0E, 0xEE, 0x00, 0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0E, 0xEE, 0x00, 0x00, 0xE0, 0x0E, 0xEE, 0x00, 0x00, 0xE0, 0x0E, 0xEE, 0x00, 0x00, 0xE0, 0x00,
0x00, 0x00, 0x00, 0x00, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0,
0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE,
0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xEE, 0x00,
0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0xE0, 0x00, 0x00, 0x0E, 0xEE, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0xEE, 0xE0, 0x0E, 0xE0, 0x00, 0xEE, 0xE0, 0x0E, 0xE0,
0x00, 0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
}

void loop() {
  // put your main code here, to run repeatedly:
  
  display.clearDisplay();
  display.drawBitmap(44, 15, eeg1_bmp, 40, 48, 1);
  display.display();
  
  delay(500);
  
  display.clearDisplay();
  display.drawBitmap(44, 15, eeg2_bmp, 40, 48, 1);
  display.display();
  
  delay(500);

}

Et ici le code exemple de la Fram :

#include <Wire.h>
#include "Adafruit_FRAM_I2C.h"

/* Example code for the Adafruit I2C FRAM breakout */

/* Connect SCL    to analog 5
   Connect SDA    to analog 4
   Connect VDD    to 5.0V DC
   Connect GROUND to common ground */
   
Adafruit_FRAM_I2C fram     = Adafruit_FRAM_I2C();
uint16_t          framAddr = 0;

void setup(void) {
  Serial.begin(9600);
  
  if (fram.begin()) {  // you can stick the new i2c addr in here, e.g. begin(0x51);
    Serial.println("Found I2C FRAM");
  } else {
    Serial.println("No I2C FRAM found ... check your connections\r\n");
    while (1);
  }
  
  // Read the first byte
  uint8_t test = fram.read8(0x0);
   Serial.print("Restarted "); Serial.print(test); Serial.println(" times");
  // Test write ++
   fram.write8(0x0, test+1);
   
  // dump the entire 32K of memory!
  uint8_t value;
  for (uint16_t a = 0; a < 32768; a++) {
    value = fram.read8(a);
    if ((a % 32) == 0) {
      Serial.print("\n 0x"); Serial.print(a, HEX); Serial.print(": ");
    }
    Serial.print("0x"); 
    if (value < 0x1) 
      Serial.print('0');
    Serial.print(value, HEX); Serial.print(" ");
  }
}

void loop(void) {

}

Les librairie sont ici

Merci beaucoup, des explications serais aussi les bienvenues si vous arrivez a me répondre :slight_smile:

Il faudrait re-écrire la méthode drawBitmap, ou plutôt écrire une méthode supplémentaire drawBitmapFRAM qui travaillerait à partir de données stockées dans la FRAM au lieu d’aller les chercher en mémoire programme.

Je suis pas dans la mouise :D Merci même si je pense aitre incapable de le faire avec mes connaissances. Si quelqu'un d'autre a une solution simple de mise en œuvre je prend aussi ;P

Bon au final j’ai tenté de modifier la fonction de base avec des conversions et autres pour au final me rendre compte que se n’était pas possible et que je devais re-codé complétement une fonction se qui m’a pris deux jours mais j’y suis arrivé :stuck_out_tongue: elle fonctionne et j’aimerais votre aide pour l’optimiser.

La voici :

void drawFramBitmap(int x, int y, int framD, int framF, int w, int h) {
  
  int pBit = 0, numOct = 0, ligne = 0;

  for (int i = framD; i < framF; i++)
  {
    //Recupèrer l'adresse mémoire i dans la FRAM
    byte value = fram.read8(i);
   
    //On continue tant que l'on a pas parcouru 8bit 
    for (int a = 8; a > 0; a--)
    {
      //Vérifie si le bit "a" du String corespond a 1
      if (bitRead(value, a) == 1){
        //Si oui on dessine le pixel pBit étant la plasse du pixel dans le bit et ligne la position sur l'axe y
        display.drawPixel(x+(numOct*8)+pBit, y+ligne, WHITE);
      }
      //Incrémante le numéro du bit a visualiser
      pBit++;  
    }
    //Incrémante le nombre d'octé déja traité
    numOct++;
    //A chaque sortie de boucle on change d'octé on remet pBit=0
    pBit=0;    
    //Si le nombre d'octé traité est suppèrieur ou égal au nombre d'octé par ligne.
    if (numOct >=(w+7)/8)
    {
      //On passe à la ligne suivante
      ligne++;
      //On commence une nouvelle ligne on reset le nombre d'octé traité
      numOct = 0;
    }
  }
}

des idées des conseils pour son optimisation ?

Bonjour

Inutile de transformer ton byte en String "binaire" pour ensuite en extraire chaque "bit"... Tu peux directement lire les bits du byte, ce sera nettement plus rapide. Il y a la fonction bitRead (en fait c'est une macro) pour t'aider.

Voici un exemple: http://codepad.org/v4pXgdcz

Merci beaucoup je modifie dans la journée si tu as d'autres idées je prend :D

Un bit = un point de l'afficheur. Les données sont stockées et accédées par octets. Est-ce que la taille de l'image est quelconque ou bien est-elle tout le temps un multiple de 8?

guix: Bonjour

Inutile de transformer ton byte en String "binaire" pour ensuite en extraire chaque "bit"... Tu peux directement lire les bits du byte, ce sera nettement plus rapide. Il y a la fonction bitRead (en fait c'est une macro) pour t'aider.

Voici un exemple: http://codepad.org/v4pXgdcz

Voila la fonction est modifié elle fonctionne bien :) merci. j'ai édité le poste avec la fonction.

fdufnews: Un bit = un point de l'afficheur. Les données sont stockées et accédées par octets. Est-ce que la taille de l'image est quelconque ou bien est-elle tout le temps un multiple de 8?

J'aimerais bien que la taille de l'image soit quelconque car pour le moment j'ajoute des pixel avant de la stocker pour qu'elle soit un multiple de 8. je tente de modifier la fonction pour traité ligne par ligne en fonction de la logeur de l'image.

j’ai modifier le code il marche toujours pour les multiples de 8 mais toujours pas pour les autres je ne voi pas ou est mon erreur
:confused:

void drawFramBitmap(int x, int y, int framD, int framF, int w, int h) {
  
  int pBit = 0, numBit = 0, ligne = 0;

  for (int i = framD; i < framF; i++)
  {
    //Recupèrer l'adresse mémoire i dans la FRAM
    byte value = fram.read8(i);
   
    //On continue tant que l'on a pas parcouru 8bit 
    for (int a = 8; a > 0; a--)
    {
      //Si le nombre de bit traité est suppèrieur ou égal au nombre de pixel par ligne.
       if (numBit == w)
       {
         //On passe à la ligne suivante
         ligne++;
         //On commence une nouvelle ligne on reset le nombre de bit traité
         numBit = 0;
        }
      //Vérifie si le bit "a" du String corespond a 1
      if (bitRead(value, a) == 1){
        //Si oui on dessine le pixel numBit étant la plasse du pixel dans la ligne et ligne la position sur l'axe y
        display.drawPixel(x+numBit, y+ligne, WHITE);
      }
      //Incrémante le nombre de bit déja traité
      numBit++;
    }
  }
}

De rien ;)

Essaie d'insérer un break; en dessous de numBit = 0;. Le break te permet de sortir du loop dans lequel il est inséré.

guix: De rien ;)

Essaie d'insérer un break; en dessous de numBit = 0;. Le break te permet de sortir du loop dans lequel il est inséré.

Nop le break; n'y change rien je ne veux pas sortir de la boucle avant la fin du traitement du byte je veux changer de ligne lorsque j'ai traité un nombre de bit égal au X (en pixel) de l'image. et que sa marche pour une image aux dimensions multiple de 8 ou non (9x9pixel ou 16x16pixel) sans distinction.

j'ai passé ma journée sur le code sans résultat. j'affiche super bien un image multiple de 8 mais un charabia pour les autres :/ sans doute une erreur toute simple mais je sèche. Je soupçonne ma boucle de traitement des bytes.

(dans le doute j'ai quand même testé le break; sans résultat :P)

Ok je vois...

Tiens encore un petit exemple qui devrais t'aider: http://codepad.org/RsTbLKDg

Edit: un petit bug corrigé (j'avais écrit 98 au lieu de 99 ;))

Edit: que penserais-tu d'écrire les dimensions de l'image dans un "header", dans ta FRAM, juste avant les données de l'image elle-même ? Cela t'éviterais de devoir écrire les dimensions de l'image manuellement, tu n'aurais plus besoin des paramètres w et h ;)

En fait tu pourrais réduire le nombre de paramètres comme cela:

void drawFramBitmap(int x, int y, int fram_idx)

;)

guix: Ok je vois...

Tiens encore un petit exemple qui devrais t'aider: http://codepad.org/RsTbLKDg

Edit: un petit bug corrigé (j'avais écrit 98 au lieu de 99 ;))

Edit: que penserais-tu d'écrire les dimensions de l'image dans un "header", dans ta FRAM, juste avant les données de l'image elle-même ? Cela t'éviterais de devoir écrire les dimensions de l'image manuellement, tu n'aurais plus besoin des paramètres w et h ;)

En fait tu pourrais réduire le nombre de paramètres comme cela:

void drawFramBitmap(int x, int y, int fram_idx)

;)

je suis sans doute complètement largué xD quand je reprend les principe de ton code je retombe trait pour trait sur les même syndrome même si le code n'est pas exactement le même :/ je l'applique surement mal. Et je n'arrive pas a appliqué ton code tel quelle donc j'utilise juste les même principes sans pouvoir les vérifié :/ toutes mes excuse je suis un petit nouveau le peux que je connais me viens du site du zer0 :D

Je pense qu'il serait plus facile de faire 2 boucles imbriquées qui compteraient les points et les lignes. Tous les 8 points tu lis un nouvel octet en FRAM A la fin d'une ligne, que le nombre de bit soit 8 ou moins tu forces la lecture d'un octet ainsi le problème des images de taille quelconque est réglé.

fdufnews: Je pense qu'il serait plus facile de faire 2 boucles imbriquées qui conteraient les points et les lignes. Tous les 8 points tu lis un nouvel octet en FRAM A la fin d'une ligne, que le nombre de bit soit 8 ou moins tu forces la lecture d'un octet ainsi le problème des images de taille quelconque est réglé.

je testerais ta méthode demain pour l'heur je sature :P je vous tien au courant.

Merci beaucoup a vous deux

J'ai un peu modifié mon exemple, peut-être que cela t'aidera à comprendre? Sinon, poste ton code j'y jetterais un oeil.

http://codepad.org/23kXgdje

Partant de la fonction drawBitmap présente dans la librairie GFX que tu utilises la modification est plutôt légère.

void Adafruit_GFX::drawBitmap(int16_t x, int16_t y,
			      const uint8_t *bitmap, int16_t w, int16_t h,
			      uint16_t color) {

  int16_t i, j, byteWidth = (w + 7) / 8;

  for(j=0; j<h; j++) {
    for(i=0; i<w; i++ ) {
      if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
	drawPixel(x+i, y+j, color);
      }
    }
  }
}

Il suffit de remplacer pgm_read_byte par fram.read
Au passage je fais une optimisation (qui je pense n’a pas de conséquence) je ne lis l’octet en mémoire que tous les 8 points est non pas systématiquement comme cela est fait dans la lib GFX.

void drawFRAMBitmap(int16_t x, int16_t y,
			      const uint16_t adFRAM, int16_t w, int16_t h,
			      uint16_t color) {

  int16_t i, j;  // compteurs lignes et points
  uint16_t ad = adFRAM;  // compteur d'adresse en FRAM
  uint8_t 8pix;  // mémorise la valeurs lue en FRAM

  for(j=0; j<h; j++) {
    for(i=0; i<w; i++ ) {
	  if ((i & 7)==0){  // si on est sur le premier point d'un groupe de huit
		8pix = fram.read8(ad++); // on lit un octet en FRAM et on avance l'adresse
	  }
          if(8pix & (128 >> (i & 7))) {  // si le point est positionné on l'affiche
		display.drawPixel(x+i, y+j, color);
      }
    }
  }
}

N’ayant rien sous la main pour tester, le code est à vérifier avec différentes tailles d’images et pour des images dessinées en diverses positions sur l’écran.

En clonant la fonction de la librairie GFX on retrouve le même comportement qu’avec la fonction originale et donc le reste du code ne doit pas changer.

Bon visiblement c’est le logiciel que j’utilise pour convertir les images qui n’accepte pas les non multiple de 8 … j’ai donc codé une image manuellement.

Merci pour ton code.

Ton code étant bien plus optimisé sera utilisé en lieu et place du mien , mais le problème subsiste sur ton code comme sur le mien les images non multiple de 8 sont mal dessiné chaque lignes se retrouve décalé de 1 pixel

Image de 15x15

000011101110111
000011101110111
000011101110111
000000000000000
111000001110111
111000001110111
111000001110111
000000000000000
111011101110111
111011101110111
111011101110111
000000000000000
111011100000000
111011100000000

Je converti donc mon image en hexa pour l’envoyer dans ma mémoire FRAM

00001110    0x0E
11101110    0xEE
00011101    0x1D
11011100    0xDC
00111011    0x3B
10111000    0xB8
00000000    0x00
00001110    0x0E
00001110    0x0E
11111100    0xFC
00011101    0x1D
11111000    0xF8
00111011    0x3B
10000000    0x80
00000000    0x00
11101110    0xEE
11101111    0xEF
11011101    0xDD
11011111    0xDF
10111011    0xBB
10111000    0xB8
00000000    0x00
00001110    0x0E
11100000    0xE0
00011101    0x1D
11000000    0xC0
00111011    0x3B
10000000    0x80
0       0x00

Mon image prend 29byte dans ma FRAM elle débute a 0 (0x00) et fini a 29(0x1D)

J’utilise donc ton code J’appel la fonction :

drawFRAMBitmap(30, 15, 0, 15, 15, 1);

Image en sortie : (les pixel ne sont plus aligner)

000011101110111 
000111011101110 
001110111011100 
000000000000111 
000011101111110 
000111011111100 
001110111000000 
000000001110111 
111011111101110 
110111111011101 
101110000000000 
000011101110000 
000111011100000 
001110111000000 
000000001111000

Je cherche actuellement une solution si tu la trouve avant moi merci :) si non bah merci dans tout les cas tu est d'une grande aide.

A titre informatif le logiciel de conversion de bitmap et LCD assistant softwaretware avec en option : - Byte orientations : Horizontal - Pixel/byte : 8

la sortie pour la même image :

{
0x0E, 0x0E, 0x0E, 0x00, 0xE0, 0xE0, 0xE0, 0x00, 0xEE, 0xEE, 0xEE, 0x00, 0xEE, 0xEE, 0xEE, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

... mon code ne pouvais avidement pas m'afficher une image correcte :/

Le code que je t'ai donné fonctionne comme celui de la librairie GFX. Les données sont alignées sur des octets. Si la taille d'une ligne n'est pas un multiple de 8 les derniers bits sont ignorés. Une ligne commence toujours sur un nouvel octet. Donc

000011101110111
000011101110111
000011101110111
000000000000000
111000001110111
111000001110111
111000001110111
000000000000000
111011101110111
111011101110111
111011101110111
000000000000000
111011100000000
111011100000000

se décompose comme ça

00001110 11101110
00001110 11101110
00001110 11101110
00000000 00000000
11100000 11101110
11100000 11101110
11100000 11101110
00000000 00000000
11101110 11101110
11101110 11101110
11101110 11101110
00000000 00000000
11101110 00000000
11101110 00000000

ce qui au final donne

0x0E    0xEE
0x0E    0xEE
0x0E    0xEE
0x00    0x00
0xE0    0xEE
0xE0    0xEE
0xE0    0xEE
0x00    0x00
0xEE    0xEE
0xEE    0xEE
0xEE    0xEE
0x00    0x00
0xEE    0x00
0xEE    0x00

Cela gâche un peu de mémoire mais accélère un peu la fonction. Pour travailler comme tu le voulais, il faudrait dans ces lignes (et seulement celles-ci)

      if ((i & 7)==0){  // si on est sur le premier point d'un groupe de huit
        8pix = fram.read8(ad++); // on lit un octet en FRAM et on avance l'adresse
      }
          if(8pix & (128 >> (i & 7))) {  // si le point est positionné on l'affiche

remplacer i par un compteur de 0 à 7 indépendant qui serait mis à zéro au début de la fonction et qui s'incrémenterait à chaque point.

Merci beaucoup du coup mon code fonctionné xD je n'avais juste pas compris la décomposition de l'image. Voila pourquoi les image multiple de 8 converti via le logiciel fonctionné avec mon code et pas avec les autres codée moi même :( je me sent tellement idiot je vous ai fait perdre du temps :/

Merci beaucoup j'en aurais beaucoup appris.

et elle est beaucoup plus optimisée aussi :D