Partage
  • Partager sur Facebook
  • Partager sur Twitter

Ecran LCD et PCF8574

Challenge Reverse Enginerring

Sujet résolu
    7 avril 2021 à 23:24:04

    Bonjour à tous,

    Voilà je fais un challenge pour le plaisir et voici ce que j'ai comme information :

    Suite à mes recherches je sais qu'en hardware j'ai un PCF8574 relié à un écran LCD 16x2.

    J'ai donc commencé à câbler un circuit (qui fonctionne) sur cette base :

    Avec le programme suivant :

    // Arduino with PCF8574T I2C LCD example
     
    #include <Wire.h>                  // Include Wire library (required for I2C devices)
    #include <LiquidCrystal_I2C.h>     // Include LiquidCrystal_I2C library 
     
    LiquidCrystal_I2C lcd(0x27, 16, 2);  // Configure LiquidCrystal_I2C library with 0x27 address, 16 columns and 2 rows
     
    void setup() {
     
      lcd.init();                        // Initialize I2C LCD module
     
      lcd.backlight();                   // Turn backlight ON
     
      lcd.setCursor(0, 0);               // Go to column 0, row 0
      lcd.print("Hello, world!");
      lcd.setCursor(0, 1);               // Go to column 0, row 1
      lcd.print("Arduino I2C LCD");
     
    }
     
    void loop() {
     
    }

    En m'inspirant de ce tuto : https://simple-circuit.com/arduino-i2c-lcd-pcf8574/

    Maintenant ce que j'ai à ma disposition est une suite de donnée transféré en I2C :

    Si je regarde à l'oscilloscope avec un I2C debugguer j'ai les séquences suivantes :

    J'aimerais maintenant envoyer MES données I2C brut ci dessous pour voir ce que m'affiche l'écran : Voici la forme des données en question disponible dans un fichier csv qui fait plus de 8000 lignes que je ne peux donc pas mettre en entier ici:

    Time [s],Packet ID,Address,Data,Read/Write,ACK/NAK
    0.448499000000000,0,0x4E,0x08,Write,ACK
    0.448728500000000,1,0x4E,0x0C,Write,ACK
    0.448958500000000,2,0x4E,0x08,Write,ACK
    0.449248000000000,3,0x4E,0x18,Write,ACK
    0.449478000000000,4,0x4E,0x1C,Write,ACK
    0.449707500000000,5,0x4E,0x18,Write,ACK
    0.452084500000000,6,0x4E,0x88,Write,ACK
    0.452314500000000,7,0x4E,0x8C,Write,ACK
    0.452544000000000,8,0x4E,0x88,Write,ACK
    0.452823500000000,9,0x4E,0x08,Write,ACK


    J'aimerais renvoyer ces données BRUT dans le module PCF8574 : mais je ne vois pas comment faire cela, sachant que si je fais un Wire.write(0x08) par exemple 8000 fois je dépasse la capacité en mémoire de la puce ?

    Ou suis-je condamné à écrire le code sous raspberry par exemple avec le même hardware derrière?

    Merci d'avance.

    Kasimashi.



    -
    Edité par Kasimashi 8 avril 2021 à 0:12:09

    • Partager sur Facebook
    • Partager sur Twitter
      9 avril 2021 à 8:29:41

      L'arduino UNO dispose de 32Ko de mémoire flash donc théoriquement, cela devrait être assez pour mettre les 8000 octets de ton fichier CSV, en considérant que l'adresse reste la même à chaque fois et qu'il n'y a qu'un seul octet à écrire à chaque fois.

      Sinon, cela va ajouter encore quelques octets supplémentaire mais ça passe.

      Il te faut déclarer un tableau dans PROGMEM puis utiliser pgm_read_byte_near pour aller lire ce tableau car si tu ne le fait pas, il va se retrouver en mémoire RAM, limitée à 2 Ko.

      #include <Arduino.h>
      #include <avr/pgmspace.h>
      
      const PROGMEM  uint8_t datamem[]  = { 0x08, 0x0C, 0x08, ... }
      uint16_t datasize = 8000;
      
      void setup() {
      
      	Serial.begin(9600);
      	
      	Wire.begin();
      	
      	for ( uint16_t i = 0 ; i < datasize i++ ) {
      		Wire.beginTransmission();
      		Wire.write(0x4E)
      		Wire.write(pgm_read_byte_near(datamem+i);
      		Wire.endTransmission();
      	}
      	
      }



      -
      Edité par lorrio 9 avril 2021 à 8:30:19

      • Partager sur Facebook
      • Partager sur Twitter
        9 avril 2021 à 14:58:52

        Bonjour Lorrio,

        Merci beaucoup pour ta réponse !

        Je n'ai pas trop compris pourquoi le code déborde en RAM? Mon buffer est trop grand donc?

        Et en quoi le fait d'utiliser la bibliothèque PDG règle le problème de ce fait ? :p

        Je me suis permis de réécrire ton code un peu il est disponible dans ce répertoire : https://github.com/Kasimashi/Systemes-embarques/blob/master/BASIC_HARDWARE_SIMULATION/PROTEUS_ISIS/Challenge%20PCF8574/solution.ino

        (Avec les vraies données).

        En sortie de simulation dans Proteus je n'observe rien malgré les changements de signaux sur les bornes D4 à D7 , E , RS , etc ...

        Cependant ceci peut s'expliquer par le message de warning suivant :

        Simulation is not running in real time due to excessive CPU load.
        

        J'ai donc décidé de faire ça en réel : (je n'ai pas mis les résistances de pull up sur le SCL et SDA : à vrai dire je ne sais pas trop à quoi elle servent)

        Et voici la sortie que j'obtient : L'adresse de mon slave est 0x27.

        Je ne vois pas trop ou est le problème ?

        Je sais que je dois obtenir un message du style : ENTER PASSWORD ********

        Voici le datasheet du LCD : https://www.openhacks.com/uploadsproductos/eone-1602a1.pdf que j'essai de comprendre.

        D'après je j'ai compris dans le message I2C on doit avoir 1- La commande et 2- Le data c'est bien ça?

        Merci d'avance.

        Cordialement.

        Kasimashi.

        -
        Edité par Kasimashi 9 avril 2021 à 15:08:29

        • Partager sur Facebook
        • Partager sur Twitter
          9 avril 2021 à 19:38:29

          L'arduino dispose de 2 types de mémoires :

          - la mémoire FLASH, qui sert à contenir le programme

          - la mémoire RAM, qui sert à contenir les variables

          Chaque a ses avantages et ses inconvénients.

          La mémoire flash a l'avantage de ne pas s'effacer lorsque l'arduino est hors tension (heureusement d'ailleurs, car sinon, on perdrait le programme a chaque fois) mais a le gros inconvénient de ne pas pouvoir être modifiée à la volée (il faut d'abord l'effacer, ce qui prend du temps, avant de pouvoir à nouveau écrire dessus).

          A l'inverse, la mémoire RAM est effacée si l'arduino n'est pas alimenté mais elle est ultra rapide et accessible à n'importe quel moment, sans avoir besoin de l'effacer au préalable pour écrire dedans, ce qui la rend parfaite pour stocker des variables.

          Par défaut, toutes les variables se trouvent dans la mémoire RAM, qui est limitée à 2 Ko, ce qui est problématique car ton tableau est plus gros que ça.

          Tu peux spécifier au compilateur de placer des variables dans la mémoire Flash avec la commande PROGMEM  et ainsi bénéficier des 32 Ko mais il y a la contrainte que cette variable ne sera pas modifiable et accessible uniquement à travers des fonctions pgm_read_.

          ----------

          Les périphériques d'un bus I2C sont des périphériques en OpenDrain, c'est à dire qu'ils sont capable d'écrire un '0' mais pas d'écrire un '1'.

          Le gros avantage, c'est qu'il n'est pas possible d'endommager le bus lors des conflits dans le cas où un périphérique écrirait un '1' tandis que l'autre écrit un '0' puisque personne n'écrit de '1'.

          Le '1' provient de la résistance de pull-up qui sert à ramener le bus à l'état logique HIGH lorsque aucun périphérique n'écrit de '0'.

          Si tu ne mets pas cette résistance, ton bus I2C ne fonctionnera pas.

          Bref, il est impératif d'avoir une résistance de pull-up de 2.2k sur SCL et SDA pour que le bus fonctionne (j'insiste sur le 2.2k car si tu mets n'importe quoi comme valeur, ça ne marchera pas non plus).


          • Partager sur Facebook
          • Partager sur Twitter
            10 avril 2021 à 1:23:26

            Merci pour les explications Lorrio : je prend note pour plus tard donc ;) Je me demande juste si c'est pareil pour le SPI?

            J'ai refais mes tests avec la résistances de pull up : verdict : Toujours le même affichage :/

            Cordialement.

            Kasimashi

            Edit : j'ai refais le test mais cette fois sous Raspberry ;) (j'aurais pu le faire aussi avec Arduino) mais maintenant ça fonctionne :

            Le problème était dans la mise en place d'un delay entre deux data, peut être étaient t-elle trop proches ?

            Voici mon code source :

            # -*- coding: utf-8 -*-
            # Original code found at:
            # https://gist.github.com/DenisFromHR/cc863375a6e19dce359d
            
            """
            Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
            Made available under GNU GENERAL PUBLIC LICENSE
            
            # Modified Python I2C library for Raspberry Pi
            # as found on http://www.recantha.co.uk/blog/?p=4849
            # Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
            # added bits and pieces from various sources
            # By DenisFromHR (Denis Pleic)
            # 2015-02-10, ver 0.1
            
            """
            
            # i2c bus (0 -- original Pi, 1 -- Rev 2 Pi)
            I2CBUS = 1
            
            # LCD Address
            ADDRESS = 0x27
            
            import smbus
            from time import sleep
            
            class i2c_device:
               def __init__(self, addr, port=I2CBUS):
                  self.addr = addr
                  self.bus = smbus.SMBus(port)
            
            # Write a single command
               def write_cmd(self, cmd):
                  self.bus.write_byte(self.addr, cmd)
                  sleep(0.0001)
            
            # Write a command and argument
               def write_cmd_arg(self, cmd, data):
                  self.bus.write_byte_data(self.addr, cmd, data)
                  sleep(0.1951)
            
            # Write a block of data
               def write_block_data(self, cmd, data):
                  self.bus.write_block_data(self.addr, cmd, data)
                  sleep(0.0001)
            
            # Read a single byte
               def read(self):
                  return self.bus.read_byte(self.addr)
            
            # Read
               def read_data(self, cmd):
                  return self.bus.read_byte_data(self.addr, cmd)
            
            # Read a block of data
               def read_block_data(self, cmd):
                  return self.bus.read_block_data(self.addr, cmd)
            
            
            # commands
            LCD_CLEARDISPLAY = 0x01
            LCD_RETURNHOME = 0x02
            LCD_ENTRYMODESET = 0x04
            LCD_DISPLAYCONTROL = 0x08
            LCD_CURSORSHIFT = 0x10
            LCD_FUNCTIONSET = 0x20
            LCD_SETCGRAMADDR = 0x40
            LCD_SETDDRAMADDR = 0x80
            
            # flags for display entry mode
            LCD_ENTRYRIGHT = 0x00
            LCD_ENTRYLEFT = 0x02
            LCD_ENTRYSHIFTINCREMENT = 0x01
            LCD_ENTRYSHIFTDECREMENT = 0x00
            
            # flags for display on/off control
            LCD_DISPLAYON = 0x04
            LCD_DISPLAYOFF = 0x00
            LCD_CURSORON = 0x02
            LCD_CURSOROFF = 0x00
            LCD_BLINKON = 0x01
            LCD_BLINKOFF = 0x00
            
            # flags for display/cursor shift
            LCD_DISPLAYMOVE = 0x08
            LCD_CURSORMOVE = 0x00
            LCD_MOVERIGHT = 0x04
            LCD_MOVELEFT = 0x00
            
            # flags for function set
            LCD_8BITMODE = 0x10
            LCD_4BITMODE = 0x00
            LCD_2LINE = 0x08
            LCD_1LINE = 0x00
            LCD_5x10DOTS = 0x04
            LCD_5x8DOTS = 0x00
            
            # flags for backlight control
            LCD_BACKLIGHT = 0x08
            LCD_NOBACKLIGHT = 0x00
            
            En = 0b00000100 # Enable bit
            Rw = 0b00000010 # Read/Write bit
            Rs = 0b00000001 # Register select bit
            
            class lcd:
               #initializes objects and lcd
               def __init__(self):
                  self.lcd_device = i2c_device(ADDRESS)
            
                  self.lcd_write(0x03)
                  self.lcd_write(0x03)
                  self.lcd_write(0x03)
                  self.lcd_write(0x02)
            
                  self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
                  self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
                  self.lcd_write(LCD_CLEARDISPLAY)
                  self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
                  sleep(0.2)
            
            
               # clocks EN to latch command
               def lcd_strobe(self, data):
                  self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
                  sleep(.0005)
                  self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
                  sleep(.0001)
            
               def lcd_write_four_bits(self, data):
                  self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
                  self.lcd_strobe(data)
            
               # write a command to lcd
               def lcd_write(self, cmd, mode=0):
                  self.lcd_write_four_bits(mode | (cmd & 0xF0))
                  self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))
            
               # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
               # works!
               def lcd_write_char(self, charvalue, mode=1):
                  self.lcd_write_four_bits(mode | (charvalue & 0xF0))
                  self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))
              
               # put string function with optional char positioning
               def lcd_display_string(self, string, line=1, pos=0):
                if line == 1:
                  pos_new = pos
                elif line == 2:
                  pos_new = 0x40 + pos
                elif line == 3:
                  pos_new = 0x14 + pos
                elif line == 4:
                  pos_new = 0x54 + pos
            
                self.lcd_write(0x80 + pos_new)
            
                for char in string:
                  self.lcd_write(ord(char), Rs)
            
               # clear lcd and set to home
               def lcd_clear(self):
                  self.lcd_write(LCD_CLEARDISPLAY)
                  self.lcd_write(LCD_RETURNHOME)
            
               # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
               def backlight(self, state): # for state, 1 = on, 0 = off
                  if state == 1:
                     self.lcd_device.write_cmd(LCD_BACKLIGHT)
                  elif state == 0:
                     self.lcd_device.write_cmd(LCD_NOBACKLIGHT)
            
               # add custom characters (0 - 7)
               def lcd_load_custom_chars(self, fontdata):
                  self.lcd_write(0x40);
                  for char in fontdata:
                     for line in char:
                        self.lcd_write_char(line)        

            et

            import I2C_LCD_driver
            
            I2C = I2C_LCD_driver.i2c_device(0x27)
            
            bytes = [
            0x08,
            0x0C,
            0x08,
            0x18,
            ...
            0x4D,
            0x49]
            
            for i in range(len(bytes)):
            	I2C.write_cmd_arg(0x08,bytes[i])

            Voilà le résultat de mettre en sortie les données brutes : https://dms.licdn.com/playlist/C4D05AQHlgTnqnMfgPA/mp4-720p-30fp-crf28/0/1618013222925?e=1618102800&v=beta&t=J-CtPTw_LztQBENNaxc5RIS7XkL87-Kygq9gKHviddM

            -
            Edité par Kasimashi 10 avril 2021 à 2:15:35

            • Partager sur Facebook
            • Partager sur Twitter
              11 avril 2021 à 11:04:26

              Le bus SPÏ n'est pas en Open Drain donc il n'y a pas de résistance de pull-up à mettre.

              Pour ce qui est des delay, la fonction sleep sur Raspberry-Pi prend accepte les nombres flottants en paramètre (nombres à virgule) avec une valeur en secondes.

              Ainsi, faire un sleep(0.0001) revient à attendre 0.0001 secondes, soit 0.1ms ou encore 100us.

              Pour ce qui est de Arduino, la fonction delay ne prend en paramètre que des nombres entiers (sans virgule) dont la valeur est en milli secondes.

              Donc si tu demandes à faire un delay(0.1), ce 0.1 va être arrondi au nombre entier inférieur, soit 0, et il n'y aura pas de delay.

              Si tu veux faire une pause d'un temps inférieur à la milli seconde, il te faut utiliser la fonction delayMicros(x) qui prend en paramètre un nombre entier (sans virgule) avec une unité en micro secondes.

              Bref, un sleep(0.0001) sur Rasperry-Pi se traduit par un delayMicros(100) sur Arduino.

              -
              Edité par lorrio 11 avril 2021 à 11:05:44

              • Partager sur Facebook
              • Partager sur Twitter
                12 avril 2021 à 14:30:47

                Merci Lorrio pour ta réponse ! :)

                Et pour tout ces détails !

                Tout compris ! :)

                Bonne journée à toi!

                Kasimashi

                • Partager sur Facebook
                • Partager sur Twitter

                Ecran LCD et PCF8574

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown