Partage
  • Partager sur Facebook
  • Partager sur Twitter

Hexadécimal et décalage de bits

masquage pour un registre à décalage

Sujet résolu
    18 novembre 2021 à 20:14:37

    Bonjour,

    Je travaille actuellement sur un projet pour lequel je dois chainer un nombre important de registres à décalage (74HC595) afin d'alimenter des LEDs.

    Je souhaite créer une méthode qui puisse tourner avec N registres à décades.

    Lorsque leur nombre est petit cela reste relativement simple. Par exemple avec 2 registres :

    void Register::sendData(byte dataPin, byte clockPin, byte latchPin, uint8_t led)
    {
      int mask = 0x01 << led;
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, ~((mask & (0xFF00)) >> 8));
      shiftOut(dataPin, clockPin, MSBFIRST, ~(mask & 0x00FF));         
      digitalWrite(latchPin, HIGH);
    }

    Et à chaque fois qu'on en ajoute un il faudrait ajouter une ligne. Par exemple pour un troisième :

    shiftOut(m_dataPin, m_clockPin, MSBFIRST, ~((mask & (0xFF0000)) >> 16));

    Ce que je souhaite faire c'est avoir une méthode qui prenne en paramètre le nombre de registres à décalages mis en cascade pour simplifier la chose.

    j'ai essayé la solution suivante mais ça ne fonctionne plus à partir de la 11ème LED (soit la 3ème du second registre) :

    void BitRegister::sendData(uint8_t numberOfRegister, byte dataPin, byte clockPin, byte latchPin, uint8_t led)
    {
      int mask = 0x01 << led;
      int byteMask = 0x00FF;
      
      digitalWrite(latchPin, LOW);
      for(int i = 1; i < numberOfRegister + 1; i++){
        int registerNumber = numberOfRegister - i;
        shiftOut(dataPin, clockPin, MSBFIRST, ~((mask & (byteMask << (2 * registerNumber))) >> (8 * registerNumber)));       
      digitalWrite(latchPin, HIGH);
    }

    Le problème vient à mon sens du décalage de bit :

    (byteMask << (2 * registerNumber))

    Je ne suis pas expert en la matière mais à mon avis on perd les deux bits de gauche lors du décalage.

    Le problème étant que je ne sais pas comment passer de "0x00FF" à "0xFF00" puis "0xFF0000" et ainsi de suite.

    Une idée ?

    Merci à ceux qui prendront le temps de répondre ;) !


    • Partager sur Facebook
    • Partager sur Twitter
      18 novembre 2021 à 21:02:34

      Bonjour,

      J'ai l'impression que ton premier code et ton second ne correspondent pas.
      Il faudrait plus d'explication sur ce que cherches à faire.
      J'ai l'impression que tu veux par exemple (les données sont en binaire):
      si led=0 et numberOfRegister=2 : 11111111 puis 11111110
      si led=1 et numberOfRegister=2 : 11111111 puis 11111101
      si led=13 et numberOfRegister=3 : 11111111 puis 11011111 puis 11111111

      Dans tous les cas tu t'en es douté, on ne peut pas indéfiniment décaler à gauche. Le résultat doit tenir dans un int et ça a ses limites (ça semble être 0xFFFFFFFF pour ton processeur.)

      Si mon exemple est juste tu veux pour le registre i=registerNumber:
      - 11111111 si la led n'est pas dans l'intervalle [i*8,i*8+7]
      - 1111x111 si la led est dans l'intervalle [i*8,i*8+7] avec la position d'un x nul qui serait: led-i*8.
      la valeur a écrire pour le second cas est donc:

      ~( 1 << (led - registerNumber*8) ) & 0xFF

      Ça fait des décalages et des masques moins compliqués.

      • Partager sur Facebook
      • Partager sur Twitter

      En recherche d'emploi.

        18 novembre 2021 à 21:07:41

        Que faut-il envoyer comme valeur pour 5 ou 9 registres ? Et aussi, quel est le type du 4ème paramètre de shiftOut() ?

        Au passage, faire des décalages binaires sur des entiers signés est une très mauvaise idée, le résultat ne sera pas celui attendu. Il faut utiliser le même type que celui attendu par shiftOut().

        • Partager sur Facebook
        • Partager sur Twitter
          19 novembre 2021 à 3:13:05

          Contrairement à ce que j'ai écrit (je l'ai laissé), les décalages ne sont pas circulaires.
          Si je considère que le SN74HC595 est un registtre à 8 bits, on peut simuler un décalage circulaire comme suit:
          unsigned int registre[nombre_registres];
          unsigned int carry = 0;
          for(int i = 0; i < nombre_registres; i++) {
              registre[i] = (registre[i] << decalage) | carry;   // le décalage devrait être inférieur à 8.
              carry = registre[i] >> 8;
              registre[i] &= 0xff;
          }
          Il suffit de transtyper pour envoyer les registres à la fonction.

          >> D'abord, le décalage vers la gauche (ou droite) est circulaire.
          Si tu décales plus que le type choisi, tu auras des problèmes de calcul ...
          décaler de 40 bits sur un unsigned int par exemple.
          Pour faire un masque de N bits tant qu'on est dans les marges (<32 pour uint):
          mask = (1<<N) - 1;   // 2^N -1 donne N bits.;
          Si tu veux les décaler vers le haut:
          mask <<= (32-N);   // pour un uint
          Pour le masque complémentaire:
          mask = ~mask;
          Pour faire un décalage, on fait un peu comme pour les additions, on a une sorte de "carry". Ce carry est la partie haute du précédent registre
          carry = 0;
          for(int i=0; i < nombre_registres; i++) {
              registre[i] <<= decalage;
              newcarry = registre[i] & mask;
              registre[i] ^= newcarry;
              registre[i] |= carry;
              carry = newcarry;
          }

          Si ton dernier carry n'est pas nul, tu auras peut-être besoin d'un registre supplémentaire.

          Si le décalage global est circulaire, on ramène le dernier carry sur le premier registre.
          Reste à savoir dans qquel ordre sont placés les registres. J'ai supposé que le registre 0 était celui le moins significatif.

          -
          Edité par PierrotLeFou 19 novembre 2021 à 4:59:32

          • Partager sur Facebook
          • Partager sur Twitter

          Le Tout est souvent plus grand que la somme de ses parties.

            19 novembre 2021 à 12:03:35

            Merci pour vos réponses.

            @Dalfab : 

            Effectivement c'est exactement ce que tu décris. Vu que je tourne sur des LEDs montées en anode commune je dois envoyer un état logique à zéro pour les allumer. 

            Pour le moment en écrivant en dur dans ma méthode les "0x00FF", "0xFF00", etc... je suis allé jusqu'à six registres et donc "0xFF0000000000". C'est plus par manque de place que je ne suis pour le moment pas allé plus loin. 

            A la fin il me faudra au total 64 registres car je dois piloter 512 LEDs par paquets de 16 LEDs (donc 32 ensembles de 16 LEDs) et je souhaite utiliser un minimum de GPIO. Je ne vais surement pas les mettre tous en cascade car comme tu le dis un int ça a ses limites, mais j'aimerai voir jusqu'où je peux aller avec le micro-contrôleur que j'utilise (SAMD21 de la Carte Arduino Zero).

            En plus de ça, chainer les registres génère de fortes variations d'intensité lumineuse sur les LEDs en fonction du nombre qui sont allumées. Je vais essayer de voir si je ne peux pas trouver un moyen de stabiliser ça...

            Je vais tester le décalage de masque que tu proposes et je te dirai ce que ça donne.

            @jo_link_noir :

            Pour 5 ou 9 registres il faudrait théoriquement appeler la méthode avec 5 ou 9 au paramètre "numberOfRegister". Cependant, comme expliqué dans ma réponse à @Dalfab, je travaille sur 32 ensembles de 16 LEDs et donc mes registres seront toujours en nombres pairs.

            Pour ce qui est de la méthode shiftOut(), il s'agit d'une méthode de la librairie Arduino :

            https://www.arduino.cc/reference/en/language/functions/advanced-io/shiftout/

            https://arduino.stackexchange.com/questions/12285/how-shiftout-function-works-internally-explanation-on-source-code

            Si je comprends bien ce que tu me dis, mes différents masques devraient être des "unsigned int" ?

            @PierrotLeFou :

            Je suis désolé mais je maitrise encore peu les manipulations de bits et j'ai beaucoup de mal à comprendre ce que tu me suggères. 

            Si je saisis bien, le principe de décalage circulaire est de récupérer les bits qui "perdus" lors du décalage et de les réinjecter de l'autre côté afin de ne pas avoir que des 0. C'est bien ça ? Au quel cas je ne comprends pas bien comment l'appliquer dans mon cas ?

            -
            Edité par Fanch JMR 19 novembre 2021 à 14:19:57

            • Partager sur Facebook
            • Partager sur Twitter
              19 novembre 2021 à 15:28:01

              Tu connais l'électronique? Le & correspond au AND et le | correspond au OR.
              Le ^ correspond au XOR et le ~ est un bête inverseur.
              Je ne comprend pas que tu sois limité dans le nombre de registres.
              Si ton arduino supporte les long long, on peut mettre 8 registres dans un tel nombre.
              Pour le décalage global circulaire, oui le dernier carry se retrouve dans le premier registre dont le bas est à 0.

              Tu ferais  registre[0] = registre[0] | carry;   // carry final.
              Moi, je ne suis pas un expert en électronique, mais tes variations de luminosité ne sont-elles pas dues à un voltage trop bas sur ton alimentation?
              Ou c'est ton bloc d'alimentation qui est trop faible.

              -
              Edité par PierrotLeFou 19 novembre 2021 à 15:43:28

              • Partager sur Facebook
              • Partager sur Twitter

              Le Tout est souvent plus grand que la somme de ses parties.

                19 novembre 2021 à 18:48:56

                @Dalfab : 

                Merci, ta solution fonctionne très bien !

                @PierrotLeFou :

                Effectivement je connais les opérateurs logiques pour les opérations sur les bits mais c'est surtout que je n'ai pas encore vraiment l'habitude de les utiliser donc c'est un exercice que je maitrise encore mal.

                Je ne parviens pas encore à visualiser le résultat d'une suite d'opérations complexes de ce genre.

                Pour ce qui est des variations de luminosité je ne pense pas que cela provienne d'un problème d'alimentation. Mon Arduino est alimenté en 5V lors de mes essais.

                • Partager sur Facebook
                • Partager sur Twitter
                  19 novembre 2021 à 19:54:00

                  Une façon de visualiser est peut-être de penser aux registres comme des rectangles ayant 8 cases.
                  Quand tu décales, tu fais passer des bits d'une case dans l'autre et ça peut déborder d'un rectangle dans le suivant.
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le Tout est souvent plus grand que la somme de ses parties.

                    24 novembre 2021 à 10:31:07

                    Du coup, en me basant sur la solution proposée par @Dalfab j'ai pu coder une classe de registre à décalage fonctionnelle mais relativement lente du fait de la quantité importante de "digitalWrite" utilisée par la méthode "shiftOut".

                    j'ai donc créé une nouvelle classe de registre à décalage plus bas niveau en utilisant les méthodes de manipulation de port de l'Arduino Zero.

                    Je vous partage mon code au cas où cela vous intéresse.

                    N'hésitez pas à me faire part de vos remarques.

                    LowBitRegister.h

                    #ifndef LowBitRegister_h
                    #define LowBitRegister_h
                    
                    #include "Arduino.h"
                    
                    class LowBitRegister
                    {
                      public:
                        LowBitRegister(byte dataPin, byte clockPin, byte latchPin, uint8_t registerSize = 1);; //Multiple registers cascade
                        
                        void sendData(uint8_t led);
                        void lowShiftOut(uint8_t bitOrder, uint8_t val);
                    
                      private:
                        byte m_dataPin;
                        byte m_clockPin;
                        byte m_latchPin;
                        uint8_t m_registerSize;
                    };
                    
                    #endif


                    LowBitRegister.cpp

                     #include "LowBitRegister.h"
                    
                    //**************************************************************  
                    
                    LowBitRegister::LowBitRegister(byte dataPin, 
                                                   byte clockPin, 
                                                   byte latchPin,
                                                   uint8_t registerSize)
                    :   m_dataPin(dataPin),
                        m_clockPin(clockPin),
                        m_latchPin(latchPin),
                        m_registerSize(registerSize) //Number of register (if cascade)
                    {
                      REG_PORT_DIR0 |= (1 << m_dataPin) | (1 << m_clockPin) | (1 << m_latchPin); //Set dataPin, clockPin and latchPin to OUTPUT
                    }
                    
                    //************************************************************** 
                    
                    void LowBitRegister::sendData(uint8_t led)
                    {
                      REG_PORT_OUT0 &= ~(1 << m_latchPin); //Set latchPin to LOW
                      
                        for(int i = 1; i < m_registerSize + 1; i++){
                          int registerNumber = m_registerSize - i; //The number of the register we're working on in the loop
                          lowShiftOut(MSBFIRST, ~( 1 << (led - registerNumber*8) ) & 0xFF);
                        }
                      
                      REG_PORT_OUT0 |= (1 << m_latchPin); //Set latchPin to HIGH
                    }
                    
                    void LowBitRegister::lowShiftOut(uint8_t bitOrder, uint8_t val)
                    {
                    for (int i = 0; i < 8; i++) {
                        if (bitOrder == LSBFIRST){
                          if(val & (1 << i))
                            REG_PORT_OUT0 |= (1 << m_dataPin);
                          else
                            REG_PORT_OUT0 &= ~(1 << m_dataPin);
                        }
                        else {
                          if (val & (1 << (7 - i)))
                            REG_PORT_OUT0 |= (1 << m_dataPin);
                          else
                            REG_PORT_OUT0 &= ~(1 << m_dataPin);
                        }
                        REG_PORT_OUT0 |= (1 << m_clockPin); //Set clockPin to HIGH
                        REG_PORT_OUT0 &= ~(1 << m_clockPin); //Set clockPin to LOW     
                      }
                    }




                    -
                    Edité par Fanch JMR 24 novembre 2021 à 10:32:00

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Hexadécimal et décalage de bits

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    • Editeur
                    • Markdown