Publicidade:

Mostrando postagens com marcador Eletrônica. Mostrar todas as postagens
Mostrando postagens com marcador Eletrônica. Mostrar todas as postagens

segunda-feira, 5 de outubro de 2015

Arduino - Max7219 e 74HC595 Juntos + Attiny85

Nesse Artigo vou explicar um pouco dos detalhes de como implementei o código para poder controlar o Max7219 junto com o 74HC595, os quais mostrei dois vídeos na página do facebook.



74hc595 e Max7219 juntos no mesmo barramento.



Agora rodando em um attiny85.74hc595 e Max7219 juntos no mesmo barramento.






Apesar de a ideia não ser entrar em detalhes do que são e como funcionam Registradores de Deslocamento, vou deixar aqui um trecho do que a wikipedia diz sobre eles:

Em eletrônica digital um registrador de deslocamento é um conjunto de registradores configurados em um arranjo linear de tal forma que a informação é deslocada pelo circuito conforme o mesmo é ativado.
Os registradores de deslocamento podem possuir uma combinação de entradas e saídas seriais e paralelas, incluindo as configurações entrada serial, saída paralela (SIPO) e entrada paralela, saída serial (PISO). Existem outra configurações possuindo ambas as entradas serial e paralela e outra com saídas serial paralela. Existem também registradores de deslocamento bi-direcionais, os quais permitem que se varie a direção do deslocamento da informação. As entradas e saídas seriais de um registrador podem ser conectadas juntas, de modo a formar um registrador de deslocamento circular. Poderiam também ser desenvolvidos registradores de deslocamento multi-dimensionais, os quais podem realizar processamentos mais complexos. Mais informações, aqui.

Dois exemplos de Registradores de Deslocamento são o Max7219 e o 74HC595, cada um com uma finalidade diferente. Enquanto o 595 é usado apenas para expandir saídas, o Max7219 tem o objetivo de servir como driver para display de 7 segmentos e matriz de Leds 8x8. Apesar de terem suas diferenças, a comunicação entre os dois é basicamente a mesma, com a pequena diferença de que o 595 possui 8 bits de Saída, enquanto que o Max7219 possui 16.

Também não vou entrar em maiores detalhes de como cada um funciona, mas pra quem quiser mais informações, seguem os links para os datasheets:

https://www.sparkfun.com/datasheets/Components/General/COM-09622-MAX7219-MAX7221.pdf
http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf

Aqui no blog mesmo, há várias postagens sobre o 74HC595: http://fabianoallex.blogspot.com.br/search?q=74HC595

A grande vantagem dos Registradores de Deslocamento é que eles podem ser ligados em cascata e sem um limite de quantidade. Ambos possuem 3 pinos de comunicação, clock e data, além do Load no max7219 e o seu equivalente Latch para o 74HC595.

Fisicamente não há grandes dificuldades na comunicação entre eles, uma estudada sobre como são as ligações de ambos dará pra entender (instintivamente) como liga-los juntos. Mas no caso específico que mostro aqui, há algumas regras. Foi necessário ligar primeiramente os Max7219, e por último os 595, nunca intercalando um com o outro.

Programação


A parte que deu mais trabalho foi a parte de programação, já que a ideia foi reutilizar as classes já existentes para controle de ambos, no caso do Max 7219 a biblioteca LedControl, e do 74HC595, utilizar uma classe que já disponibilizei aqui no blog ( veja ). Eu poderia criar uma classe do zero e recriar todas as funcionalidades nessa única classe, mas achei que seria retrabalho, então a ideia foi fazer algo que unisse a funcionalidade das duas.

As explicações dadas daqui pra baixo irão exigir um conhecimento básico de conceitos de Orientação a Objetos. Já fiz um vídeo falando algumas coisas sobre o assunto: http://fabianoallex.blogspot.com.br/2014/09/arduino-dicas-de-programacao-04.html

Ambas as classes agora precisam funcionar de maneira unificada. Quando os dados de qualquer uma das duas for atualizado, será necessário envia-los para todos os CIs que estão ligados em cascata e não apenas a que foi alterada. Pra isso foi necessário criar uma terceira classe que controlasse o envio dos dados para os registradores, a qual chamei de ShiftRegisterController.

A finalidade dessa classe é gerenciar o envio dos dados para os CIs. Porém ainda há um outro detalhe, foi necessário criar uma outra classe que fosse a classe-base para LedControl e para a classe Expansor74HC595, de modo que as duas herdem as mesmas funcionalidades básicas.


class ShiftRegisterController; //prototipo

class ShiftRegister{
  protected:
    ShiftRegisterController * _controller;
  public:
    virtual void shiftOutRegister() ;
};

class ShiftRegisterController {
  private:
    int             _pin_latch;  // ou load no max7219
    ShiftRegister * _exp_595;
    ShiftRegister * _exp_lc;
  public:
    ShiftRegisterController(int pin_latch)      { 
      _pin_latch = pin_latch; 
      pinMode(_pin_latch,OUTPUT);
      digitalWrite(_pin_latch, HIGH);
    }  
    
    void setLedControl(ShiftRegister * lc)      { _exp_lc  = lc;          }
    void setExp74HC595(ShiftRegister * exp_595) { _exp_595 = exp_595;     }
    
    void shiftOutRegisters() {
      if (_exp_595 && _exp_lc){
        digitalWrite(_pin_latch, LOW); 
        _exp_595->shiftOutRegister();
        _exp_lc ->shiftOutRegister();
        digitalWrite(_pin_latch, HIGH);
      }
    } 
};


Além da inclusão dessas novas classes, foram feitas alterações nas definições das classes já existentes, indicando que as mesmas, agora serão herdadas de ShiftRegister:

class Expansor74HC595 : public ShiftRegister {...};
class LedControl : public ShiftRegister {...};

Podemos ver que há um atributo chamado _pin_latch na classe ShiftRegisterController, que anteriormente ficava nas classes LedControl e Expansor74HC595, pois é através desse pino que todos os CIs são "notificados" que as alterações estão prontas e devem ser aplicadas as suas respectivas saídas, logo, as duas classes citadas não controlam mais quando os dados são finalizados, ficando essa função para a classe  ShiftRegisterController.

Observação: Pra não ter que fazer essas alterações diretamente na biblioteca LedControl, eu copiei todo o conteúdo da classe dentro da própria sketch e fiz as alterações necessárias, como pode ser visto no código completo mais abaixo. Em outro artigo eu já mostrei como fazer isso, incluindo algumas melhorias na classe LedControl, a quais explico nesse artigo: http://fabianoallex.blogspot.com.br/2015/09/arduino-alteracoes-na-biblioteca.html

O código abaixo mostra como devem ser declarados os objetos, que são 3.

int const PIN_DATA = 10;
int const PIN_CLK = 9;
int const PIN_LATCH = 8;
int const QUANTIDADE_MAX7219 = 2;
int const QUANTIDADE_74HC595 = 1;

ShiftRegisterController exp_src = ShiftRegisterController(PIN_LATCH);
LedControl      exp_lc  = LedControl     (PIN_DATA,PIN_CLK,QUANTIDADE_MAX7219, &exp_src);
Expansor74HC595 exp_595 = Expansor74HC595(PIN_DATA,PIN_CLK,QUANTIDADE_74HC595 , &exp_src);

Primeiramente foi necessário criar o objeto controlador e posteriormente criar os outros dois Objetos, um LedControl e outro Expansor74HC595. Veja que o primeiro objeto recebeu como parâmetro o PIN_LATCH, enquanto que as demais classes não recebem mais esse parâmetro, recebendo apenas o pino de clock e o pino de data, porém recebem como parâmetro o endereço do objeto controlador.

Código-Fonte - Arduino Uno:

/*
Criado em 04/10/2015
 Por: 
   Fabiano A. Arndt 
   http://www.youtube.com/fabianoallex
   http://fabianoallex.blogspot.com.br
   fabianoallex@gmail.com
*/
/*
 *    LedControl.h / LedControl.cpp - A library for controling Leds with a MAX7219/MAX7221
 *    Copyright (c) 2007 Eberhard Fahle
 * 
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 * 
 *    This permission notice shall be included in all copies or 
 *    substantial portions of the Software.
 * 
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */
/*************************************************************************************************************
*******************************CLASSES ShiftRegister E ShiftRegisterController********************************
**************************************************************************************************************/

class ShiftRegisterController; //prototipo

class ShiftRegister{
  protected:
    ShiftRegisterController * _controller;
  public:
    virtual void shiftOutRegister() ;
};

class ShiftRegisterController {
  private:
    int             _pin_latch;
    ShiftRegister * _exp_595;
    ShiftRegister * _exp_lc;
  public:
    ShiftRegisterController(int pin_latch)      { 
      _pin_latch = pin_latch; 
      pinMode(_pin_latch,OUTPUT);
      digitalWrite(_pin_latch, HIGH);
    }  
    
    void setLedControl(ShiftRegister * lc)      { _exp_lc  = lc;          }
    void setExp74HC595(ShiftRegister * exp_595) { _exp_595 = exp_595;     }
    
    void shiftOutRegisters() {
      if (_exp_595 && _exp_lc){
        digitalWrite(_pin_latch, LOW); 
        _exp_595->shiftOutRegister();
        _exp_lc ->shiftOutRegister();
        digitalWrite(_pin_latch, HIGH);
      }
    } 
};

/*************************************************************************************************************
***************************FIM CLASSES ShiftRegister E ShiftRegisterController********************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************EXPANSOR 74HC595***************************************************************
**************************************************************************************************************/
class Expansor74HC595 : public ShiftRegister {
  private:
    int  _pin_clock;
    int  _pin_data;
    int  _num_cis;
    byte* _pins;
    int  _auto_send;
  public:
    Expansor74HC595(int pin_data, int pin_clock, int num_cis,  ShiftRegisterController * controller){
      _pin_clock  = pin_clock;
      _pin_data   = pin_data;
      _num_cis    = num_cis;
      _auto_send  = true;
      _pins       = new byte[_num_cis];
      _controller = controller;
      
      _controller->setExp74HC595(this);
 
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_data, OUTPUT);
      //clear();
    };
   
    void startWrite() { _auto_send = false;  };
     
    void clear() { 
      for (int i=0; i<_num_cis; i++){  _pins[i] = 0b00000000; }
      send();  
    };
   
    int read(int pin)  { 
      if (pin >= _num_cis * 8) {return LOW;} 
      int pos = pin / 8;
      pin = pin % 8;
      return (_pins[pos] & (1 << pin)) != 0;
    };
   
    byte readByte(int num_ci) { 
      return _pins[num_ci];
    };
    
    void shiftOutRegister(){  for(int i=_num_cis-1; i>=0; i--) {  shiftOut(_pin_data, _pin_clock, MSBFIRST, readByte(i) );  }  };
    
    void send(){
      _controller->shiftOutRegisters();
      _auto_send = true;
    };
     
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      if (first == MSBFIRST){
        byte reversed;
        for(int i=0;i<8;i++){ reversed |= ((b>>i) & 0b1)<<(7-i); }
        b = reversed;
      }
      _pins[num_ci] = b; 
      if (_auto_send) { send(); } 
    };
     
    void write(int pin, int value) {
      if (pin >= _num_cis * 8) { return; }
       
      int pos = pin / 8;
      pin     = pin % 8;
 
      if (value){
        _pins[pos] |= (1 << pin);  //set a bit HIGH
      } else {
        _pins[pos] &= ~(1 << pin); //set a bit LOW
      }
 
      if (_auto_send) { send(); }
    };
};

/*************************************************************************************************************
*******************************FIM 74HC595********************************************************************
**************************************************************************************************************/



/*************************************************************************************************************
*******************************LEDCONTROL ALTERADA************************************************************
**************************************************************************************************************/
 
//the opcodes for the MAX7221 and MAX7219
#define OP_DECODEMODE  9
#define OP_INTENSITY   10
#define OP_SCANLIMIT   11
#define OP_SHUTDOWN    12
#define OP_DISPLAYTEST 15
 
class LedControl : public ShiftRegister {
  private :
    byte spidata[16];
    byte * status;
    int SPI_MOSI;
    int SPI_CLK;
    int maxDevices;
    int _auto_send;
     
    void spiTransfer(int addr, volatile byte opcode, volatile byte data) {
      int offset   = addr*2;
      int maxbytes = maxDevices*2;
      for(int i=0;i<maxbytes;i++)  { spidata[i]=(byte)0; }
      spidata[offset+1] = opcode;
      spidata[offset]   = data;
      
      _controller->shiftOutRegisters();
      
    }
  public:
    void shiftOutRegister(){ for(int i=maxDevices*2;i>0;i--) { shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); } };
  
    LedControl(int dataPin, int clkPin, int numDevices,  ShiftRegisterController * controller) {
      _auto_send  = true;
      SPI_MOSI    = dataPin;
      SPI_CLK     = clkPin;
      maxDevices  = numDevices;
      
      _controller = controller;
      _controller->setLedControl(this);
       
      pinMode(SPI_MOSI, OUTPUT);
      pinMode(SPI_CLK,  OUTPUT);
       
      status      = new byte[maxDevices * 8]; //instancia o array de acordo com a quantia de displays usados
      for(int i=0;i<maxDevices * 8 ;i++) { status[i]=0x00; }
      
    }
    
    void begin(){
      for(int i=0;i<maxDevices;i++) {
        spiTransfer(i, OP_DISPLAYTEST,0);
        setScanLimit(i, 7);               //scanlimit is set to max on startup
        spiTransfer(i, OP_DECODEMODE,0);  //decode is done in source
        clearDisplay(i);
        shutdown(i,true);                 //we go into shutdown-mode on startup
      }
    }
     
    void startWrite() {  _auto_send = false;  };
     
    void send() {
      for (int j=0; j<maxDevices; j++) {
        int offset = j*8;
        for(int i=0;i<8;i++) { spiTransfer(j, i+1, status[offset+i]); }
      }
      _auto_send = true;
    }
     
    int getDeviceCount(){ return maxDevices; }
     
    void shutdown(int addr, bool b){
      if(addr<0 || addr>=maxDevices) return;
      spiTransfer(addr, OP_SHUTDOWN, b ? 0 : 1);
    }
     
    void setScanLimit(int addr, int limit){
      if(addr<0 || addr>=maxDevices) return;
      if(limit>=0 && limit<8) spiTransfer(addr, OP_SCANLIMIT,limit);
    }
     
    void setIntensity(int addr, int intensity) {
      if(addr<0 || addr>=maxDevices)   {  return;                                    }
      if(intensity>=0 && intensity<16) {  spiTransfer(addr, OP_INTENSITY, intensity); }
    }
     
    void clearDisplay(int addr){
      if(addr<0 || addr>=maxDevices) return;
       
      int offset = addr*8;
      for(int i=0;i<8;i++) {
        status[offset+i] = 0;
        if (_auto_send) { spiTransfer(addr, i+1, status[offset+i]); }
      }
    }
     
    void setLed(int addr, int row, int column, boolean state) {
      if(addr<0 || addr>=maxDevices)             { return; }
      if(row<0 || row>7 || column<0 || column>7) { return; }
       
      int offset = addr*8;
      byte val = B10000000 >> column;
       
      if(state) { status[offset+row] = status[offset+row] | val; }
      else {
        val=~val;
        status[offset+row] = status[offset+row]&val;
      }
       
      if (_auto_send) { spiTransfer(addr, row+1, status[offset+row]); }
    }
     
    void setRow(int addr, int row, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(row<0 || row>7) return;
      int offset = addr*8;
      status[offset+row] = value;
      if (_auto_send) {
        spiTransfer(addr, row+1, status[offset+row]);
      }
    }
     
    void setColumn(int addr, int col, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(col<0 || col>7)             return;
       
      byte val;
      for(int row=0; row<8; row++) {
        val=value >> (7-row);
        val=val & 0x01;
        setLed(addr,row,col,val);
      }
    }
};
/*************************************************************************************************************
*******************************FIM LEDCONTROL ALTERADA********************************************************
**************************************************************************************************************/
 

/*
 pin 10 is connected to the DataIn 
 pin 9 is connected to the CLK 
 pin 8 is connected to LOAD 
 */
 
int const PIN_DATA = 10;
int const PIN_CLK = 9;
int const PIN_LATCH = 8;
int const QUANTIDADE_MAX7219 = 2;
int const QUANTIDADE_74HC595 = 1;
 
ShiftRegisterController exp_src = ShiftRegisterController(PIN_LATCH);
LedControl      exp_lc  = LedControl     (PIN_DATA,PIN_CLK,QUANTIDADE_MAX7219, &exp_src);
Expansor74HC595 exp_595 = Expansor74HC595(PIN_DATA,PIN_CLK,QUANTIDADE_74HC595 , &exp_src);
const int LINHAS  = 8;
const int COLUNAS = 16;
 
int cont=0;
 
 
void update_displays() {
  exp_lc.startWrite();
   
  for (int lin=0; lin<8; lin++) {
    for (int col=0; col<16; col++) {
      for (int i=0; i<2; i++) {
        int l = lin;
        int c = col - (i*8); 
         
        if (l>=0 && l<=7 && c>=0 && c<=7) {
          exp_lc.setLed(i, l, c, (l+c+cont)%2 );
        }
      }
    }
  }
   
  cont++;
  exp_lc.send();
}
  
void setup() {
  //Serial.begin(9600);
  
  exp_lc.begin();
  exp_lc.shutdown(0,false);
  exp_lc.setIntensity(0,8);
  exp_lc.clearDisplay(0);  
  exp_lc.shutdown(1,false);
  exp_lc.setIntensity(1,8);
  exp_lc.clearDisplay(1);  
}
 
void loop() {
  
  for (byte b=0; b<=255; b++) {    //i=0-> B00000000   i=255-> B11111111
    exp_595.startWrite();
    exp_595.writeByte(0, b, LSBFIRST);
    exp_595.send();
  
    update_displays();
    
    delay(500);
  }
}


Código-fonte Attiny85:

/*
Criado em 04/10/2015
 Por: 
   Fabiano A. Arndt 
   http://www.youtube.com/fabianoallex
   http://fabianoallex.blogspot.com.br
   fabianoallex@gmail.com
*/ 
/*
 *    LedControl.h / LedControl.cpp - A library for controling Leds with a MAX7219/MAX7221
 *    Copyright (c) 2007 Eberhard Fahle
 * 
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 * 
 *    This permission notice shall be included in all copies or 
 *    substantial portions of the Software.
 * 
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */
/*************************************************************************************************************
*******************************CLASSES ShiftRegister E ShiftRegisterController********************************
**************************************************************************************************************/
class ShiftRegisterController; //prototipo

class ShiftRegister{
  protected:
    ShiftRegisterController * _controller;
  public:
    virtual void shiftOutRegister() ;
};

class ShiftRegisterController {
  private:
    int             _pin_latch;
    ShiftRegister * _exp_595;
    ShiftRegister * _exp_lc;
  public:
    ShiftRegisterController(int pin_latch)      { 
      _pin_latch = pin_latch; 
      pinMode(_pin_latch,OUTPUT);
      digitalWrite(_pin_latch, HIGH);
    }  
    
    void setLedControl(ShiftRegister * lc)      { _exp_lc  = lc;          }
    void setExp74HC595(ShiftRegister * exp_595) { _exp_595 = exp_595;     }
    
    void shiftOutRegisters() {
      if (_exp_595 && _exp_lc){
        digitalWrite(_pin_latch, LOW); 
        _exp_595->shiftOutRegister();
        _exp_lc ->shiftOutRegister();
        digitalWrite(_pin_latch, HIGH);
      }
    } 
};


/*************************************************************************************************************
***************************FIM CLASSES ShiftRegister E ShiftRegisterController********************************
**************************************************************************************************************/


/*************************************************************************************************************
*******************************EXPANSOR 74HC595***************************************************************
**************************************************************************************************************/
class Expansor74HC595 : public ShiftRegister {
  private:
    int  _pin_clock;
    int  _pin_data;
    int  _num_cis;
    byte* _pins;
    int  _auto_send;
  public:
    Expansor74HC595(int pin_data, int pin_clock, int num_cis,  ShiftRegisterController * controller){
      _pin_clock  = pin_clock;
      _pin_data   = pin_data;
      _num_cis    = num_cis;
      _auto_send  = true;
      _pins       = new byte[_num_cis];
      _controller = controller;
      
      _controller->setExp74HC595(this);
 
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_data, OUTPUT);
      //clear();
    };
   
    void startWrite() { _auto_send = false;  };
     
    void clear() { 
      for (int i=0; i<_num_cis; i++){  _pins[i] = 0b00000000; }
      send();  
    };
   
    int read(int pin)  { 
      if (pin >= _num_cis * 8) {return LOW;} 
      int pos = pin / 8;
      pin = pin % 8;
      return (_pins[pos] & (1 << pin)) != 0;
    };
   
    byte readByte(int num_ci) { return _pins[num_ci];  };
    
    void shiftOutRegister(){  for(int i=_num_cis-1; i>=0; i--) {  shiftOut(_pin_data, _pin_clock, MSBFIRST, readByte(i) );  }  };
    
    void send(){
      _controller->shiftOutRegisters();
      _auto_send = true;
    };
     
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      if (first == MSBFIRST){
        byte reversed;
        for(int i=0;i<8;i++){ reversed |= ((b>>i) & 0b1)<<(7-i); }
        b = reversed;
      }
      _pins[num_ci] = b; 
      if (_auto_send) { send(); } 
    };
     
    void write(int pin, int value) {
      if (pin >= _num_cis * 8) { return; }
       
      int pos = pin / 8;
      pin     = pin % 8;
 
      if (value){
        _pins[pos] |= (1 << pin);  //set a bit HIGH
      } else {
        _pins[pos] &= ~(1 << pin); //set a bit LOW
      }
 
      if (_auto_send) { send(); }
    };
};

/*************************************************************************************************************
*******************************FIM 74HC595********************************************************************
**************************************************************************************************************/



/*************************************************************************************************************
*******************************LEDCONTROL ALTERADA************************************************************
**************************************************************************************************************/
 
//the opcodes for the MAX7221 and MAX7219
#define OP_DECODEMODE  9
#define OP_INTENSITY   10
#define OP_SCANLIMIT   11
#define OP_SHUTDOWN    12
#define OP_DISPLAYTEST 15
 
class LedControl : public ShiftRegister {
  private :
    byte spidata[16];
    byte * status;
    int SPI_MOSI;
    int SPI_CLK;
    int maxDevices;
    int _auto_send;
     
    void spiTransfer(int addr, volatile byte opcode, volatile byte data) {
      int offset   = addr*2;
      int maxbytes = maxDevices*2;
      for(int i=0;i<maxbytes;i++)  { spidata[i]=(byte)0; }
      spidata[offset+1] = opcode;
      spidata[offset]   = data;
      
      _controller->shiftOutRegisters();
      
    }
  public:
    void shiftOutRegister(){ for(int i=maxDevices*2;i>0;i--) { shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); } };
  
    LedControl(int dataPin, int clkPin, int numDevices,  ShiftRegisterController * controller) {
      _auto_send  = true;
      SPI_MOSI    = dataPin;
      SPI_CLK     = clkPin;
      maxDevices  = numDevices;
      
      _controller = controller;
      _controller->setLedControl(this);
       
      pinMode(SPI_MOSI, OUTPUT);
      pinMode(SPI_CLK,  OUTPUT);
       
      status      = new byte[maxDevices * 8]; //instancia o array de acordo com a quantia de displays usados
      for(int i=0;i<maxDevices * 8 ;i++) { status[i]=0x00; }
      
    }
    
    void begin(){
      for(int i=0;i<maxDevices;i++) {
        spiTransfer(i, OP_DISPLAYTEST,0);
        setScanLimit(i, 7);               //scanlimit is set to max on startup
        spiTransfer(i, OP_DECODEMODE,0);  //decode is done in source
        clearDisplay(i);
        shutdown(i,true);                 //we go into shutdown-mode on startup
      }
    }
     
    void startWrite() {  _auto_send = false;  };
     
    void send() {
      for (int j=0; j<maxDevices; j++) {
        int offset = j*8;
        for(int i=0;i<8;i++) { spiTransfer(j, i+1, status[offset+i]); }
      }
      _auto_send = true;
    }
     
    int getDeviceCount(){ return maxDevices; }
     
    void shutdown(int addr, bool b){
      if(addr<0 || addr>=maxDevices) return;
      spiTransfer(addr, OP_SHUTDOWN, b ? 0 : 1);
    }
     
    void setScanLimit(int addr, int limit){
      if(addr<0 || addr>=maxDevices) return;
      if(limit>=0 && limit<8) spiTransfer(addr, OP_SCANLIMIT,limit);
    }
     
    void setIntensity(int addr, int intensity) {
      if(addr<0 || addr>=maxDevices)   {  return;                                    }
      if(intensity>=0 && intensity<16) {  spiTransfer(addr, OP_INTENSITY, intensity); }
    }
     
    void clearDisplay(int addr){
      if(addr<0 || addr>=maxDevices) return;
       
      int offset = addr*8;
      for(int i=0;i<8;i++) {
        status[offset+i] = 0;
        if (_auto_send) { spiTransfer(addr, i+1, status[offset+i]); }
      }
    }
     
    void setLed(int addr, int row, int column, boolean state) {
      if(addr<0 || addr>=maxDevices)             { return; }
      if(row<0 || row>7 || column<0 || column>7) { return; }
       
      int offset = addr*8;
      byte val = B10000000 >> column;
       
      if(state) { status[offset+row] = status[offset+row] | val; }
      else {
        val=~val;
        status[offset+row] = status[offset+row]&val;
      }
       
      if (_auto_send) { spiTransfer(addr, row+1, status[offset+row]); }
    }
     
    void setRow(int addr, int row, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(row<0 || row>7) return;
      int offset = addr*8;
      status[offset+row] = value;
      if (_auto_send) {
        spiTransfer(addr, row+1, status[offset+row]);
      }
    }
     
    void setColumn(int addr, int col, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(col<0 || col>7)             return;
       
      byte val;
      for(int row=0; row<8; row++) {
        val=value >> (7-row);
        val=val & 0x01;
        setLed(addr,row,col,val);
      }
    }
};
/*************************************************************************************************************
*******************************FIM LEDCONTROL ALTERADA********************************************************
**************************************************************************************************************/
  
 
/*
 pin 2 is connected to the DataIn 
 pin 1 is connected to the CLK 
 pin 0 is connected to LOAD 
 */
 

int const PIN_DATA = 2;
int const PIN_CLK = 1;
int const PIN_LATCH = 0;
int const QUANTIDADE_MAX7219 = 2;
int const QUANTIDADE_74HC595 = 1;
 
ShiftRegisterController exp_src = ShiftRegisterController(PIN_LATCH);
LedControl      exp_lc  = LedControl     (PIN_DATA,PIN_CLK,QUANTIDADE_MAX7219, &exp_src);
Expansor74HC595 exp_595 = Expansor74HC595(PIN_DATA,PIN_CLK,QUANTIDADE_74HC595, &exp_src);

 
const int LINHAS  = 8;
const int COLUNAS = 16;
 
int cont=0;
 
 
void update_displays() {
  exp_lc.startWrite();
   
  for (int lin=0; lin<8; lin++) {
    for (int col=0; col<16; col++) {
      for (int i=0; i<2; i++) {
        int l = lin;
        int c = col - (i*8); 
         
        if (l>=0 && l<=7 && c>=0 && c<=7) {
          exp_lc.setLed(i, l, c, (l+c+cont)%2 );
        }
      }
    }
  }
   
  cont++;
  exp_lc.send();
}
 
 
void setup() {
  //Serial.begin(9600);
  exp_lc.begin();
  exp_lc.shutdown(0,false);
  exp_lc.setIntensity(0,8);
  exp_lc.clearDisplay(0);  
  exp_lc.shutdown(1,false);
  exp_lc.setIntensity(1,8);
  exp_lc.clearDisplay(1);  
}
 
void loop() {
  for (byte b=0; b<=255; b++) {                    //i=0-> B00000000   i=255-> B11111111
    exp_595.startWrite();
    exp_595.writeByte(0, b, LSBFIRST);
    exp_595.send();
 
    update_displays();
    
    delay(500);
  }  
}





segunda-feira, 28 de setembro de 2015

Arduino - Hack da parte frontal de uma impressora HP Deskjet F4280




Nesse fim de semana peguei uma impressora velha, uma HP Deskjet F4280 e tentei ver o que dava pra fazer com o display. Analisando os componentes que haviam na placa, identifiquei 3 registradores de deslocamento 595 - clique para ver o datasheet - (expansores de saídas) e 1 165 - clique para ver o datasheet - (expansor de entrada). Essa placa se comunicava com outra placa, através de um cabo flat através de 7 vias. Com ajuda de um multímetro identifiquei onde cada uma das vias estava chegando no circuito:
  • 1 - pino 11 do 595 (SH_CP) e pino 2 do 165 (CLOCK) 
  • 2 - gnd
  • 3 - pino 9 do 165 (QH)
  • 4 - pino 14 do 595 (DS)
  • 5 - vcc
  • 6 - pino 12 do 595 (ST_CP) e pino 1 do 165 (LOAD)
  • 7 - botão on/off - ligado diretamente

Com os pinos identificados, cortei com um tesoura um das extremidades do cabo flat e separei cada uma das trilhas. Com as pontas separadas, queimei o plástico que fica em volta do cabo flat para que apenas a trilha ficasse exposta, depois com um bombril removi os resíduos de plástico queimado dos terminais. Depois soldei essas trilhas em uma pequena placa, de modo que fosse possível conecta-las com o arduino através de jumpers. 


--vou colocar uma foto de como foi feito


conforme as trilhas identificadas e numeradas acima, fiz a seguinte ligações no arduino:

  • 1  --> arduino : pino 8
  • 2  --> arduino : gnd
  • 3  --> arduino : pino 2
  • 4  --> arduino : pino 9
  • 5  --> arduino : vcc
  • 6  --> arduino : pino 10
  • 7  --> arduino : não utilizado nesse exemplo. mas poderia ser ligado como qualquer pino e configura-lo como entrada


O próximo passo foi identificar como cada botão e cada led estavam sendo controlados. Os botões são controlados pelo único 165, e ao lado de cada botão (na cor preta) mostro qual o bit que é usado para representá-lo. Os outros 3 595 estão ligados em cascata, e têm suas saídas ligadas nos leds conforme numerados abaixo. O primeiro 595 está na cor Azul, o segundo na cor Vermelha e o terceira na Cor Verde.



Com tudo identificado, o próximo passo foi fazer a programação, pra isso aproveitei uma classe que eu já havia criado pra se comunicar com os registradores 595 e 165 juntos (veja o artigo aqui). Porém, o esquema desse display, não está exatamente da forma que mostrei no artigo citado, mas ainda assim deu pra aproveitar a classe, apenas fazendo algumas modificações. A principal delas, foi que quando é enviado HIGH para acionar um led, os leds são apagados.

Código-fonte:

/*
Criado em 27/09/2015
 Por: 
   Fabiano A. Arndt 
   http://www.youtube.com/fabianoallex
   http://fabianoallex.blogspot.com.br
   fabianoallex@gmail.com
*/
 
/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     INICIO*********************************
*********************************************************************************************/
 
class Expansor74HC595_74HC165 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    byte* _pins_out;
    byte* _pins_in;
    int _num_cis_out;
    int _num_cis_in;
  public:
    Expansor74HC595_74HC165(int pin_clock, int pin_latch, int pin_data, int num_cis_out, int num_cis_in){
      _pin_clock = pin_clock;
      _pin_latch = pin_latch;
      _pin_data  = pin_data;
       
      _num_cis_out = num_cis_out;
      _num_cis_in  = num_cis_in;
       
      _pins_out    = new byte[num_cis_out];
      _pins_in     = new byte[num_cis_in];
       
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,OUTPUT);
       
      clear();
    }
    
    void clear(){
      for (int i=0; i<_num_cis_out; i++){ _pins_out[i] = B11111111; }
      update();
    }
    
    void update(){
      digitalWrite(_pin_clock, LOW); 
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
       
      for(int i=max(_num_cis_in, _num_cis_out) * 8 - 1; i>=0;  i-- ) {   //max -->o for vai até o que tiver mais, ou entradas, ou saidas
        int pos = i / 8;
        int pin = 7-(i % 8);  
         
        if (i < _num_cis_in * 8){
          pinMode(2, INPUT);
         
          if ( digitalRead(2) ){
            _pins_in[pos] |= (1 << pin);  //set a bit HIGH
          } else { 
            _pins_in[pos] &= ~(1 << pin); //set a bit LOW
          }
        }
         
        if (i < _num_cis_out * 8){
          pinMode(_pin_data, OUTPUT);
          digitalWrite(_pin_data,   (_pins_out[pos] & (1 << pin)) != 0   );
        }        
        digitalWrite(_pin_clock, HIGH); 
        digitalWrite(_pin_clock, LOW); 
      }      
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
      //pinMode(_pin_data, INPUT);   //removido
    }
     
    int read(int  pin){
      int pos = pin / 8;
      pin     = 7-(pin % 8);  
       
      if (pos > _num_cis_out) {
        pos = pos - _num_cis_out;
        return (  (_pins_in[pos] & (1 << pin)) != 0   );
      } else {
        return (  (_pins_out[pos] & (1 << pin)) != 0   );
      }
    }
     
    byte readByte(int num_ci) { 
      if (num_ci >= _num_cis_out) {
        num_ci = num_ci - _num_cis_out;
        return _pins_in[num_ci];
      } else {
        return _pins_out[num_ci];
      }
    }
     
    void write(int pin, int value){
      if (pin >= _num_cis_out*8) { return; }
       
      int pos = pin / 8;  //pos -> indica qual ci será atualizado.
      pin     = 7-(pin % 8);
  
      if (pos > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      } else {
        if (value){
          _pins_out[pos] |= (1 << pin);  //set a bit HIGH
        } else {
          _pins_out[pos] &= ~(1 << pin); //set a bit LOW
        }
      }
    }
     
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      if (num_ci > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      }
       
      if (first == LSBFIRST) {
        byte r=0;
        for(int i=0;i<8;i++) {
          r |= ((b>>i) & 0b1)<<(7-i);
        }
        b = r;
      }
        
      _pins_out[num_ci] = b; 
    } ;
};
 
/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     FIM ***********************************
*********************************************************************************************/
 
 
const int PIN_CLOCK    = 8; 
const int PIN_LATCH    = 10; 
const int PIN_DATA_595 = 9;
const int PIN_DATA_165 = 2;
 
  
Expansor74HC595_74HC165 * exp1;  //controla as saídas
Expansor74HC595_74HC165 * exp2;  //controla as entradas
   
void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA_595, 3, 0);
  exp2   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA_165, 0, 3);
   
   
  exp1->writeByte(0, B11111111);
  exp1->writeByte(1, B11111111);
  exp1->writeByte(2, B11111111);
   
  Serial.begin(9600);
}


byte digitos[] = {
  B01000000,
  B01011110,
  B00100100,
  B00001100,
  B00011010,
  B10001000,
  B10000000,
  B01011100,
  B00000000,
  B00001000,
  B00010000,
  B10000010,
  B11100000,
  B00000110,
  B10100000,
  B10110000
};

byte leds[] = {20, 21, 22, 23, 4, 19, 18, 17, 16, 5};

unsigned int dig=0;
unsigned int opt=0;
unsigned int led=0;

void loop() {
  
  
  exp2->update(); //primeiro verifica o estado dos botões
  
  
  
  if (!exp2->read(5+16)) { exp1->write(15, !exp1->read(15)); }
  if (!exp2->read(2+16)) { exp1->write(6,  !exp1->read(6)); }
  
  
  if (!exp2->read(0+16)) { dig++; }
  if (!exp2->read(4+16)) { dig--; }
  byte temp = exp1->read(15);
  exp1->writeByte(1, digitos[dig%16]);
  exp1->write(15, temp);
  
  
  if (!exp2->read(6+16)) { opt++; }
  exp1->write(1, opt%3+1==1);
  exp1->write(2, opt%3+1==2);
  exp1->write(3, opt%3+1==3);
  
  
  if (!exp2->read(3+16)) { 
    for (int i=0; i<10; i++){
      exp1->write(leds[i], led%10!=i);
    }
    led++;
  }
  
  
  
  exp1->update();  //agora envia o estado dos leds
  
  
    
  Serial.println(exp1->readByte(0), BIN);  //LE O PRIMEIRO CI (595)
  Serial.println(exp1->readByte(1), BIN);  //LE O SEGUNDO CI (595)
  Serial.println(exp1->readByte(2), BIN);  //LE O TERCEIRO CI (165)
  Serial.println(exp2->readByte(2), BIN);  //LE O QUARTO CI (165)
  Serial.println("");
   
  delay(100);
  
  
}



sexta-feira, 25 de setembro de 2015

Arduino - Array de bits

Como sabemos arrays são estruturas de dados utilizados para armazenar dados de um mesmo tipo em sequência. A sintaxe utilizada para a declaração de um array é a seguinte:

type nome_do_array [ tamanho ];

por exemplo,

int valores[10] = {1, 3, 2, 5, 6, 4, 8, 9, 7, 0};

É muito comum termos a necessidade de utilizarmos arrays para representar conjunto de dados como os listados acima.

Os tipos de dados a serem armazenados podem ser qualquer tipo de dado aceito em C.

boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
string - char array
String - object


Cada um dos tipos de dados tem um determinado tamanho que terá quer ser reservado na memória ao ser declarado um array. Um int, por exemplo, ocupa dois bytes de memória, um char ocupa um byte, um long ocupa 4 bytes.

Mas se tratando de programação para micro controladores, algo importante na hora de declarar um array, é escolhermos o tipo de dados mais adequado para armazenarmos os dados pretendidos. Por exemplo, se eu pretendo ter um array de números inteiros, mas os valores armazenados não ultrapassam o valor de 255, poderia utilizar um tipo de dados que não ocupasse mais de um byte, como word, ou byte. Caso contrário, teria que ser utilizado outro tipo, como int, ou ainda long. A escolha de cada um desses tipos de dados resulta em maiores ou menores quantidades de memória utilizadas. Em aplicações pequenas, talvez isso não faça diferença, mas algumas situações acaba fazendo.

Uma situação especial, e bastante comum, é necessitarmos armazenar arrays com informações binárias, ou seja, valor do tipo 1 ou 0. ou true ou false, ou HIGH ou LOW. enfim, apenas dois valores distintos podem ser armazenados em cada posição.

Um exemplo comum, seria por exemplo criar um array pra controlar o estado de um conjunto de lâmpadas, onde elas podem estar ou ligadas ou desligadas.

Vamos supor que fossem 20 lampadas.

Uma maneira fácil seria criar um array assim:

int lampadas[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

Mas será que essa é a melhor forma de declarar esse array?

Como sabemos, int ocupa dois bytes. Multiplicado por 20, totalizaria 40 bytes.

Outra opção seria mudar int, para byte:

byte lampadas[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

O que totalizaria 20 bytes. ou seja a metade.

Mas e se quisermos diminuir ainda mais? Nesse caso teríamos que ter um tipo de dados que ocupasse menos de um byte, mas isso infelizmente a linguagem C não nos oferece, mas ainda assim há um jeito de diminuirmos.

Como sabemos, 1 byte é formado por 8 bits. E cada bit pode assumir apenas dois estados, ou seja, 1 ou 0. Uma saída então, seria utilizar 1 byte, pra controlar 8 lâmpadas. Com isso precisaríamos ter 20 / 8 (2.5 --> 3) bytes para representar os estados das 20 lâmpadas. ou seja, algo assim:

byte lampadas[3];  //24 bits disponíveis

O que totalizaria apenas 3 bytes de memória para gerenciar o estado de 20 lâmpadas.

Sem dúvida essa é uma estratégia bem útil quando temos limitações de memória importante, o que é bem comum quando se trata de micro controladores.

Mas como tudo tem seu preço, teremos agora que criarmos meios de alterarmos bit a bit de cada um dos bytes e para isso precisaremos utilizar operadores de bitwise.

Já fiz um vídeo explicando como funcionam operadores de bitwise, quem não conhece, vale muito a pena aprender.



Pra facilitar o uso desses recursos, criei uma classe que encapsula toda essa parte "complicada", a qual está abaixo:



class BitArray{
  private:
    int _num_bits;   //quantidade de bits a serem gerenciados
    int _num_bytes;  //quantidade de bytes utilizados para armazenar os bits a serem gerenciados
    byte * _bytes;   //array de bytes onde estaram armazenados os bits
  public:
    BitArray(int num_bits){
      _num_bits  = num_bits;
      _num_bytes = _num_bits/8 + (_num_bits%8 ? 1 : 0) + 1;
      _bytes = (byte *)(malloc( _num_bytes * sizeof(byte) ) );
    }
    
    void write(int index, byte value) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      _bytes[ index/8 + (index%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value) {
      for(int j=0; j<_num_bytes;j++) { _bytes[j] = value ? B11111111 : B00000000;  } 
    }
    
    int read(int index) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      return (b & (1 << bit)) != 0;
    }

   ~BitArray(){ free( _bytes ); }

};


Com isso, agora não iremos ter um array, mas sim um objeto, o qual poderá ser utilizado da seguinte maneira:


BitArray lampadas(20); //instancia do objeto com 20 bits

E para alterarmos uma das lâmpadas, fazemos da seguinte maneira:

lampadas.write(0, HIGH);  //liga a primeira lampada
lampadas.write(1, HIGH);  //liga a segunda lampada

if ( lampadas.read(0) ) { Serial.println("a primeira lampada está ligada."); } //verifica o estado da lampada.



Com isso conseguimos ter o menor consumo de memória, sem termos grandes dificuldades na parte de programação.


versão bidimensional :


class BitArray2D {
  private:
    unsigned int _rows;
    unsigned int _columns;
    unsigned int _cols_array; //pra cada 8 colunas, 1 byte é usado 
    byte**       _bits;
  public:
    BitArray2D(unsigned int rows, unsigned int columns){
      _rows       = rows;
      _columns    = columns;
      _cols_array = columns/8 + (_columns%8 ? 1 : 0) + 1; //divide por 8 o número de colunas
      _bits = (byte **)malloc(_rows * sizeof(byte *));
      for(int i=0;i<_rows;i++){ _bits[i] = (byte *)malloc(  _cols_array  *  sizeof(byte)); } //cria varios arrays
      clear();
    }
    
    unsigned int rows(){ return _rows; }
    unsigned int columns(){ return _columns; }
    
    void clear() { 
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) { _bits[i][j] = B00000000; }       
      }   
    }
  
    void write(unsigned int row, unsigned int column, int value){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      
      _bits[row][ column/8 + (column%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value){
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) {      
          _bits[i][j] = value ? B11111111 : B00000000;     
        }       
      }  
    }
    
    int read(unsigned int row, unsigned int column){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      return (b & (1 << bit)) != 0;
    }
    
    void toggle(unsigned int row, unsigned int column){ write(row, column, !read(row, column)); }
    void toggle(){ for(int i=0;i<_rows;i++){      for(int j=0; j<_columns;j++) {      toggle(i,j);   }   }   }
};


Arduino - Alterações na biblioteca LedControl

Algumas alterações feitas na biblioteca LedControl.

Quando usamos repetidamente o método setLed da biblioteca LedControl, percebemos um atraso na atualização dos displays (como pode ser visto no vídeo abaixo). Isso acontece, pois a cada chamada de setLed, é feito o envio do status de todos os leds do display, inclusive, os que não tiveram alterações, o que compromete muito a eficiência do código.

Para melhorar a performance da biblioteca foi incluída uma nova funcionalidade, a qual permite atualizar os status dos leds sem enviar os dados para os registradores de deslocamento (Max7219), ou seja, as alterações são mantidas na memória, e depois do processamento terminado, os dados são, então, enviados todos de uma única vez aos registradores. Essa funcionalidade se chama "Auto Send". 

Por default a biblioteca funciona da mesma forma que é originalmente, com o auto send igual a true, ou seja, cada chamada a clear(), setLed(), setRow() ou setColumn() continua enviando os dados aos registradores. Mas caso o programador queira fazer um conjunto de alterações nos leds e só enviar as informações para os registradores após todas as alterações estarem prontas, deve-se então, usar o método startWrite().

Depois de chamado startWrite(), os dados serão alterados mas apenas na memória e só serão enviados aos registradores através de um outro método que foi criado. O método send().

No código-fonte a baixo, vemos o uso desses dois métodos, na função update_display. Como pode ser visto, antes de iniciar o processamento dos dados, é feita uma chamada a startWrite(), e ao final uma chamada a send().


void update_displays() {
  lc.startWrite();
  
  for (int lin=0; lin<8; lin++) {
    for (int col=0; col<16; col++) {
      for (int i=0; i<2; i++) {
        int l = lin;
        int c = col - (i*8); 
        
        if (l>=0 && l<=7 && c>=0 && c<=7) {
          lc.setLed(i, l, c, (l+c+cont)%2 );
        }
      }
    }
  }
  
  cont++;
  lc.send();
}





Observações: No código abaixo, foram removidas as funcionalidades voltadas ao uso de displays de 7 segmentos, pois não serão utilizados nos meu projetos futuros, mas nada impede que sejam inclusos novamente.

Código-fonte:

/*************************************************************************************************************
*******************************LEDCONTROL ALTERADA************************************************************
**************************************************************************************************************/

//the opcodes for the MAX7221 and MAX7219
#define OP_DECODEMODE  9
#define OP_INTENSITY   10
#define OP_SCANLIMIT   11
#define OP_SHUTDOWN    12
#define OP_DISPLAYTEST 15

class LedControl {
  private :
    byte spidata[16];
    byte * status;
    int SPI_MOSI;
    int SPI_CLK;
    int SPI_CS;
    int maxDevices;
    int _auto_send;
    
    void spiTransfer(int addr, volatile byte opcode, volatile byte data) {
      int offset   = addr*2;
      int maxbytes = maxDevices*2;
      for(int i=0;i<maxbytes;i++)  { spidata[i]=(byte)0; }
      spidata[offset+1] = opcode;
      spidata[offset]   = data;
      
      digitalWrite(SPI_CS,LOW);
      for(int i=maxbytes;i>0;i--) { shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); }
      digitalWrite(SPI_CS,HIGH);
    }
      
  public:
    LedControl(int dataPin, int clkPin, int csPin, int numDevices) {
      _auto_send  = true;
      SPI_MOSI    = dataPin;
      SPI_CLK     = clkPin;
      SPI_CS      = csPin;
      maxDevices  = numDevices;
      
      pinMode(SPI_MOSI, OUTPUT);
      pinMode(SPI_CLK,  OUTPUT);
      pinMode(SPI_CS,   OUTPUT);
      digitalWrite(SPI_CS, HIGH);
      
      status      = new byte[maxDevices * 8]; //instancia o array de acordo com a quantia de displays usados
      for(int i=0;i<maxDevices * 8 ;i++) { status[i]=0x00; }
      
      for(int i=0;i<maxDevices;i++) {
        spiTransfer(i, OP_DISPLAYTEST,0);
        setScanLimit(i, 7);               //scanlimit is set to max on startup
        spiTransfer(i, OP_DECODEMODE,0);  //decode is done in source
        clearDisplay(i);
        shutdown(i,true);                 //we go into shutdown-mode on startup
      }
    }
    
    void startWrite() {  _auto_send = false;  };
    
    void send() {
      for (int j=0; j<maxDevices; j++) {
        int offset = j*8;
        for(int i=0;i<8;i++) { spiTransfer(j, i+1, status[offset+i]); }
      }
      _auto_send = true;
    }
    
    int getDeviceCount(){ return maxDevices; }
    
    void shutdown(int addr, bool b){
      if(addr<0 || addr>=maxDevices) return;
      spiTransfer(addr, OP_SHUTDOWN, b ? 0 : 1);
    }
    
    void setScanLimit(int addr, int limit){
      if(addr<0 || addr>=maxDevices) return;
      if(limit>=0 && limit<8) spiTransfer(addr, OP_SCANLIMIT,limit);
    }
    
    void setIntensity(int addr, int intensity) {
      if(addr<0 || addr>=maxDevices)   {  return;                                    }
      if(intensity>=0 && intensity<16) {  spiTransfer(addr, OP_INTENSITY, intensity); }
    }
    
    void clearDisplay(int addr){
      if(addr<0 || addr>=maxDevices) return;
      
      int offset = addr*8;
      for(int i=0;i<8;i++) {
        status[offset+i] = 0;
        if (_auto_send) { spiTransfer(addr, i+1, status[offset+i]); }
      }
    }
    
    void setLed(int addr, int row, int column, boolean state) {
      if(addr<0 || addr>=maxDevices)             { return; }
      if(row<0 || row>7 || column<0 || column>7) { return; }
      
      int offset = addr*8;
      byte val = B10000000 >> column;
      
      if(state) { status[offset+row] = status[offset+row] | val; }
      else {
        val=~val;
        status[offset+row] = status[offset+row]&val;
      }
      
      if (_auto_send) { spiTransfer(addr, row+1, status[offset+row]); }
    }
    
    void setRow(int addr, int row, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(row<0 || row>7) return;
      int offset = addr*8;
      status[offset+row] = value;
      if (_auto_send) {
        spiTransfer(addr, row+1, status[offset+row]);
      }
    }
    
    void setColumn(int addr, int col, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(col<0 || col>7)             return;
      
      byte val;
      for(int row=0; row<8; row++) {
        val=value >> (7-row);
        val=val & 0x01;
        setLed(addr,row,col,val);
      }
    }
};
/*************************************************************************************************************
*******************************FIM LEDCONTROL ALTERADA********************************************************
**************************************************************************************************************/



/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */
LedControl lc=LedControl(4,6,5,2);

const int LINHAS  = 8;
const int COLUNAS = 16;

int cont=0;


void update_displays() {
  lc.startWrite();
  
  for (int lin=0; lin<8; lin++) {
    for (int col=0; col<16; col++) {
      for (int i=0; i<2; i++) {
        int l = lin;
        int c = col - (i*8); 
        
        if (l>=0 && l<=7 && c>=0 && c<=7) {
          lc.setLed(i, l, c, (l+c+cont)%2 );
        }
      }
    }
  }
  
  cont++;
  lc.send();
}


void setup() {
  lc.shutdown(0,false);
  lc.setIntensity(0,8);
  lc.clearDisplay(0);  
  lc.shutdown(1,false);
  lc.setIntensity(1,8);
  lc.clearDisplay(1);  
  

  
  Serial.begin(9600);
}

void loop() {
  update_displays();
  
  for(int i=0;i<LINHAS;i++){ 
    for(int j=0;j<COLUNAS;j++){ 
      Serial.print( (i+j)%2 );
      Serial.print(" ");
    } 
    Serial.println(" ");
  }
  Serial.println(" ");
  delay(2000);
  
}

terça-feira, 22 de setembro de 2015

Arduino - Dicas de programação 08 - int x unsigned int

Arduino é uma plataforma de prototipação que fez sucesso devido a facilidade com que junta eletrônica e programação e apresenta aos interessados nessas áreas. Tanto pessoas com experiência como sem experiências podem começar a implementar seus projetos sem ter que se preocupar muito com os fundamentos dessas duas áreas de conhecimento. Claro que isso é bom, pois democratiza o conhecimento e permite com que ideias saiam do papel e se tornem projetos, sejam simples ou mais complexos, mas isso não significa que não devemos nos aprofundar nos conhecimentos dessas duas áreas de conhecimento.

A IDE padrão do Arduino, utiliza C/C++, e o que eu quero mostrar aqui são algumas pegadinhas em que as vezes programadores menos experientes podem cair.

C/C++ é uma linguagem Fortemente Tipada. Isso significa que sempre que formos declarar uma variável, precisamos definir a que tipo de dados ela pertence.

Os principais tipos de variáveis são:

void
boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
string - char array
String - object
array

Minha intenção não é apresentar e explicar os tipos de dados utilizados para programar o Arduino, mas mostrar alguns detalhes que devemos prestar atenção na hora de programar.

Se olharmos os tipos acimas, veremos que alguns deles, como int, char e long, possuem o tipo unsigned, por exemplo, tem int, e tem unsigned int.

Mas qual a diferença entre esses dois tipos? Para exemplificar vamos pegar os tipos int e unsigned int. Tanto um como outro irá reservar 16 bits de memória para cada variável declarada. A diferença é que int armazena números que vão de  -32.768 a 32.767, ou seja, aceita números negativos. Enquanto que unsigned int, armazena números de 0 a 65.535.

Agora vamos imaginar o seguinte, iremos declarar duas variáveis, uma variável i do tipo int, e uma variável u do tipo unsigned int e inicializamos cada uma com o valor 0.

Se agora fizermos um if verificando se i é igual a u, obviamente o if será verdadeiro, pois ambas possuem valor 0.

Agora supomos que o valor das duas variáveis sejam decrementadas em 1 (i-- e u--). Vamos analisar então o que acontece com cada uma das variáveis.

Se i era igual a 0, agora ela será igual a -1. Já no caso de u, é diferente, pois u não pode armazenar valores negativos. Nesse caso, u-- var resultar no seguinte valor: 65.535. Isso mesmo. O valor assumido é o último valor dos possíveis valores para o tipo unsigned int. Isso sempre acontece quando extrapolamos os valores aceitos por esses tipos de dados, seja no início ou no fim.

Pois bem, considerando então que i é igual a -1 e u igual a 65.535, o que aconteceria se novamente verificássemos se i é igual a u. Apesar de não ser tão óbvio agora, a comparação continuaria resultado verdadeiro. Sim. -1 seria igual a 65.535.

O que acontece aqui é o seguinte: Quando uma comparação entre dois tipos é feita, é preciso que o compilador converta os tipos de dados para um mesmo tipo, e nessa conversão, os dados resultantes da conversão dos tipos de dados vão ser os mesmos, ou seja iguais. O que, dependendo da lógica que está sendo programada, pode levar a erros. E o programador menos experiente pode ficar doidinho tentando entender o que está acontecendo e não conseguir achar o problema.

Sketch com o exemplo acima


int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    Serial.println("diferentes");
  }
  
  i--;
  u--;

  
  delay(5000);
  
  Serial.println("");
  Serial.println("");
}





Vamos imaginar agora um exemplo parecido, mas ao invés de decrementarmos as duas variaveis, vamos decrementar apenas i e iremos verificar se i é maior, menor ou igual a u.

int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    
    if (i > u) {
      Serial.println("i maior que u");
    } else {
      Serial.println("u maior que i");
    }
    
  }
  
  i--; //apenas i é decrementado

  
  delay(5000);
  
  Serial.println("");
  Serial.println("");
}





Veja que mesmo i sendo negativo, o programa entende que i é maior que zero. Isso acontece pelo mesmo motivo mostrado anteriormente. O compilador precisa converteri, que é int, em unsigned int. pois só pode comparar variáveis do mesmo tipo, e nessa conversão de um tipo para outro, o valor  que era -1, passa a ser 65.535, ou seja, após a conversão, i de fato passa a ser maior que u.

Conclusão

Sempre que for declarar o tipo de dado, pense exatamente o que aquele dado vai representar, se de fato pode ou não haver valores negativos. E se haver, pense nos problemas que podem ocorrer. E procure não misturar os tipos de dados na hora de fazer comparações ou outras operações entre eles. E o principal: Sempre teste seu código com todas as possibilidades de valores.


sexta-feira, 18 de setembro de 2015

Arduino - Pai Nerd

Olá pessoal, ontem postei na página do facebook,  o vídeo abaixo, mostrando um projetinho que fiz pra comemorar o aniversário de um ano do meu filho.


Pai Nerd. Hoje meu filho, o Nicolas, faz 1 ano, e claro que tem Arduino pra comemorar :D

Pra quem estiver interessado, vou deixar o código-fonte, mas já aviso, que foi algo rápido, sem muito planejamento, e que com certeza dá pra ser melhorado.

Uma das melhorias que poderiam ser feitas, é substituir os delays, pelos millis. Tenho um artigo falando sobre como fazer isso. veja aqui.

Outro detalhe é que utilizo uma classe que criei pra gerar números aleatórios não repetidos. Quem quiser saber mais detalhes sobre essa classe, veja o artigo, clicando aqui.

valeu!


código-fonte:



#include "LedControl.h"
 
/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/
   
class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _tam;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _tam = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
       
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(_tam * sizeof(int)); }  
      for (int i=0; i<_tam; i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
         
      //embaralha a lista
      for (int i=0; i<_tam; i++) {  
        int r = random(0, _tam);     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
       
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= _tam ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
       
       
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/
 
 
 
/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */
LedControl   lc(4,6,5, 2); //2 max7219
UniqueRandom ur(64); //declaracao do objeto unique random
 
/* we always wait a bit between updates of the display */
unsigned long delaytime=500;
 
void setup() {
  int seed = 0;
     
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
     
  lc.shutdown(0,false);
  lc.setIntensity(0,1);
  lc.clearDisplay(0);
    
  lc.shutdown(1,false);
  lc.setIntensity(1,1);
  lc.clearDisplay(1);
   
  int lin = 0;
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B01110000);
  lc.setColumn(1, lin++, B00111000);
  lc.setColumn(1, lin++, B00011100);
  lc.setColumn(1, lin++, B00001110);
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B11111111);
   
  delay(500);
}
 
byte leds_0[8] = {
  B00111000, 
  B00111000, 
  B00011000, 
  B00011000, 
  B00011000, 
  B00011000, 
  B01111110, 
  B01111110
};
 
byte leds_1[8] = {
  B11000011, 
  B11100011, 
  B11110011, 
  B11111011, 
  B11011111, 
  B11001111, 
  B11000111, 
  B11000011
};
 
void loop() { 
 
  int aleatorio = random(0, 9);
  //aleatorio = 8;
  Serial.println(aleatorio);
   
  if (aleatorio == 0) {
    //linhas de cima para baixo
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) { lc.setRow(0,row, leds_0[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 1) {
    //linhas de baixo para cima
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=7;row>=0;row--) { lc.setRow(0,row, leds_0[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 2) {
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 3) {  
    //esquerda para a direita
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 4) {
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=0;col<8;col++){  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 5) {  
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=7;col>=0;col--){  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 6) {    
    //------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
     
  }
   
  if (aleatorio == 7) {  
    //--------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 8) {  
    //--------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
     
    for(int i=0;i<64;i++) {  
      int r = ur.next();
      int l = r / 8;
      int c = r % 8;
      int val = (leds_0[l] & (1 << (7-c) )) != 0 ;
        
      if ( val ) { delay(100); }
      lc.setLed(0, l, c, val );
    }
     
    ur.randomize();
    delay(delaytime*5);
  }
 
 
 
 
 
 
 
 
 
 
 
 
  aleatorio = random(0, 9);
   
  if (aleatorio == 0) {
    //linhas de cima para baixo
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) { lc.setRow(1,row, leds_1[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 1) {
    //linhas de baixo para cima
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=7;row>=0;row--) { lc.setRow(1,row, leds_1[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 2) {
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 3) {  
    //esquerda para a direita
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 4) {
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=0;col<8;col++){  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 5) {  
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=7;col>=0;col--){  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 6) {    
    //------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
     
  }
   
  if (aleatorio == 7) {  
    //--------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 8) {  
    //--------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
     
    for(int i=0;i<64;i++) {  
      int r = ur.next();
      int l = r / 8;
      int c = r % 8;
      int val = (leds_1[l] & (1 << (7-c) )) != 0 ;
        
      if ( val ) { delay(100); }
      lc.setLed(1, l, c, val );
    }
     
    ur.randomize();
    delay(delaytime*5);
  }
 
 
 
 
   
 
}


segunda-feira, 14 de setembro de 2015

Arduino - Números aleatórios (repetidos e não repetidos)

No Arduino temos duas funções que nos permitem gerar números aleatórios.

RandomSeed() e Random().

Na verdade os números aleatórios gerados em conjunto pelas funções acimas não são inteiramente aleatórios, eles são na verdade pseudo aleatórios, o que significa dizer, que parecem números aleatórios, mas não são. Na prática, os números aleatórios gerados pelas funções acima, são pré determinados, dependendo do valor passado para RandomSeed(valor).

Normalmente RandomSeed é chamado no início da execução do programa e inicializado com um valor qualquer, porém se na inicialização de RandomSeed for passado sempre o mesmo valor, a sequencia de números aleatórios gerados será sempre a mesma.

Se a intenção for gerar sequencia de números diferentes a cada inicialização de randomSeed, deve-se então, ser passado valores diferentes a cada inicialização.

Existem algumas alternativas no Arduino para conseguirmos números (seed: que significa semente) diferentes a cada vez que se deseja inicializar o RandomSeed. A mais utilizada é utilizar o valor lido de uma porta analógica não conectada, que por não estar conectada, acaba retornando valores diferentes. Nos casos em que os números aleatórios são gerados a partir de uma ação externa do arduino, como o pressionar de um botão, a leitura de um valor ou qualquer outra ação, o valor a ser utilizado para inicializar o RandomSeed, pode ser baseado no valor de millis() do arduino, que é o tempo em millissegundos já passados desde que o arduino foi ligado. Outra possibilidade seria ainda gravar na eeprom o ultimo utilizado para inicializar, e na próxima vez que o arduino inicializar o RandomSeed, o valor gravado na eeprom ser incrementado.

Como vimos, então, randomSeed é utilizado para gerar uma sequencia aleatória de números. Agora vamos ver como funciona a função random(), que é a função que retorna o número aleatório, quando desejamos um.

random() pode ser chamada com dois, ou com um parâmetros. Quando chamada com dois, significa que queremos números aleatórios entre o primeiro e o ultimo valor, por exemplo, random(10, 20) irá retornar somentes os seguintes valores {10,11,12,13,14,15,16,17,18 e 19}. Percebam que 20 não será retornado.

Quando chamada com um único parâmetro, como por exemplo, random(300), significa que irá retornar qualquer valor entre 0 e 299.

abaixo tem um exemplo retirado da página do Arduino:

https://www.arduino.cc/en/Reference/Random

long randNumber; 
void setup() { 
  Serial.begin(9600); 
  // if analog input pin 0 is unconnected, random analog 
  // noise will cause the call to randomSeed() to generate 
  // different seed numbers each time the sketch runs. 
  // randomSeed() will then shuffle the random function. 

  randomSeed(analogRead(0)); 
} 

void loop() { 
  // print a random number from 0 to 299 
  randNumber = random(300); 
  Serial.println(randNumber); 

  // print a random number from 10 to 19 
  randNumber = random(10, 20); 
  Serial.println(randNumber); 
  delay(50); 
}



Aleatórios não repetidos


Quando projetamos um programa que necessita de valores aleatórios, há basicamente duas situações diferentes, que são os casos onde os números sorteados podem se repetir, e outras onde os números não podem se repetir.

Num bingo por exemplo, os números sorteados nunca se repetem, pois a cada valor sorteado o mesmo é removido do globo, enquanto que numa roleta de cassino os valores sorteados podem ser repetidos.

Para conseguirmos gerar números não repetidos, é necessário gerarmos uma lista de números, embaralharmos os mesmos (muda-los de posição de modo aleatório), e posteriormente lermos os números sequencialmente do início da lista até o fim. Assim garantimos que se tenhamos 10 números na lista não iremos ter sorteios repetidos nos primeiros 10 números sorteados.

Para facilitar esse processo, criei uma classe que faz esse trabalho.


/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/

class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
    
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
      
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
    
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
    
    int size() { return _size; }
    
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/


/**********************************************************************************
************************************SETUP / LOOP***********************************
**********************************************************************************/

UniqueRandom ur(10, 30); //declaracao do objeto unique random

void setup() {
  Serial.begin(9600);
  
  int seed = 0;
  
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
  
  Serial.print("Seed: ");
  Serial.println(seed);
  
}

void loop() {
    
  Serial.println("");
  Serial.print( "Iniciando com " );
  Serial.print( ur.size() );
  Serial.print( " itens: " );
    
  ur.randomize();  //gera os numeros aleatoriamente
    
  for (int i=0; i<ur.size(); i++){
    Serial.print( ur.next() );
    Serial.print( " " );
  }
    
  delay(100);
}

/**********************************************************************************
************************************FIM SETUP / LOOP*******************************
**********************************************************************************/




Exemplo de uso com matriz de led 8x8



#include "LedControl.h"

/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/

class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
    
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
      
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
    
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
    
    int size() { return _size; }
    
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/

/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */

LedControl lc=LedControl(4,6,5,1); //1 max7219
UniqueRandom ur(64); //declaracao do objeto unique random

/* we always wait a bit between updates of the display */
unsigned long delaytime=500;

void setup() {
  int seed = 0;
  
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
  
  lc.shutdown(0,false);
  lc.setIntensity(0,8);
  lc.clearDisplay(0);
}

void loop() { 
  lc.clearDisplay(0);
  delay(delaytime/2);
  
  ur.randomize();
    
  for(int i=0; i<64; i++) {  
    int r = ur.next();
      
    int l = r / 8;
    int c = r % 8;
     
    delay(100);
    lc.setLed(0, l, c, HIGH );
  }
  
  
  ur.randomize();
    
  for(int i=0; i<64; i++) {  
    int r = ur.next();
      
    int l = r / 8;
    int c = r % 8;
     
    delay(100);
    lc.setLed(0, l, c, LOW );
  }
  
  delay(delaytime*2);
}

Atualização 25/11/2015 - Números do tipo double ou float (com casas decimais)

Essa semana me pediram como fazer para gerar números aleatórios com números números do tipo double ou float, ou seja, com casas decimais. Na verdade a ideia é até bem simples, vamos supor que você queira números aleatórios entre 5.58 e 9.44... considerando os números com até duas casas decimais nesse intervalo.

Nesse caso basta multiplicar o 5.58 por 100 (ou 10² ou pow(10, 2)  ) o que daria um número inteiro igual a 558. Aplicando o mesmo ao 9.44 teremos 944. Se fosse com 3 casas decimais, multiplicaríamos o número por 1000 (ou 10³ ou pow(10,3) ).

Tendo convertido os dois números para inteiros, bastaria agora utilizar a função random normalmente, porém o valor retornado precisa ser divido pelo valor multiplicado anteriormente para que o mesmo volte à mesma faixa utilizada inicialmente, tendo o cuidado de armazenar o resultado em um campo do tipo double, para que as casas decimais não sejam perdidas.

Para facilitar o uso, criei um exemplo com uma função que chamei de randomDouble, onde são passados 3 valores: o valor mínimo, o valor máximo, e a quantidade de casas decimais a ser considerada:



Código:

double randomDouble(double min, double max, int numCasas){
  long _min = min * pow(10, numCasas) + 0.1;  //0.1--> para corrigir erro da funcao pow. funciona até 4 casas. mais que isso da erro no calculo.
  long _max = max * pow(10, numCasas) + 0.1;
  
  return (double) random(_min, _max) / pow(10, numCasas) ; 
}

void setup() { 
  Serial.begin(9600); 
  randomSeed(analogRead(0)); 
} 

void loop() { 
  double randNumber = randomDouble(10.20, 11.05, 2);
  Serial.println(randNumber); 
  
  delay(500); 
}