Publicidade:

Mostrando postagens classificadas por relevância para a consulta 74HC595. Ordenar por data Mostrar todas as postagens
Mostrando postagens classificadas por relevância para a consulta 74HC595. Ordenar por data Mostrar todas as postagens

quinta-feira, 23 de julho de 2015

Arduino - Voltímetro com display de 7 Segmentos + 74hc595

No post anterior (aqui) mostrei como fazer um voltímetro com o Arduino utilizando displays de 7 segmentos. Para isso foi necessário utilizar 12 pinos do Arduino.

Visando reduzir a quantidade de pinos utilizados, alterei o circuito para trabalhar com o 74HC595, que é um expansor de portas bem útil (veja aqui outros exemplos desse CI).

O primeiro 74HC595 foi utilizado para acionar cada um dos 8 segmentos do display, enquanto que o segundo foi utilizado pra controlar qual dos displays será exibido a cada vez. Como no exemplo foi utilizado apenas 4 displays, sobraram 4 pinos do segundo 595, sendo possível expandir esse exemplo para até oito displays.

Para saber como fazer as ligações entre os dois expansores e o Arduino, veja o esquema abaixo.



Ligações entre Arduino / 74HC595

Arduino - pino 08 ---> 74HC595 - DS - DATA PIN        (Ligar apenas no primeiro 595)
Arduino - pino 09 ---> 74HC595 - STCP- LATCH PIN  (Ligar em todos os 595)
Arduino - pino 10 ---> 74HC595 - SHCP- CLOCK PIN (Ligar em todos os 595)

Ligações entre os 74HC595 e os Displays

PRIMEIRO 74HC595 - Q0 --> DP (segmento de ponto decimal)
PRIMEIRO 74HC595 - Q1 --> g
PRIMEIRO 74HC595 - Q2 --> f
PRIMEIRO 74HC595 - Q3 --> e
PRIMEIRO 74HC595 - Q4 --> d
PRIMEIRO 74HC595 - Q5 --> c
PRIMEIRO 74HC595 - Q6 --> b
PRIMEIRO 74HC595 - Q7 --> a

SEGUNDO 74HC595 - Q0 --> primeiro display
SEGUNDO 74HC595 - Q1 --> segundo display
SEGUNDO 74HC595 - Q2 --> terceiro display
SEGUNDO 74HC595 - Q3 --> quarto display
SEGUNDO 74HC595 - Q4..Q7 --> não utilizados (mas poderiam ser utilizados para gerenciar mais displays)

Verifique como ligar os demais pinos no esquema abaixo, ou verifique nesse site aqui.






Código-fonte:

/************************************************************************************************************
**********************************expansor 74hc595***********************************************************
************************************************************************************************************/

class Expansor74HC595 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    int  _num_cis;
    byte* _pins;
    int  _auto_send;
  public:
    Expansor74HC595(int pin_clock, int pin_latch, int pin_data, int num_cis){
      _pin_clock        = pin_clock;
      _pin_latch        = pin_latch;
      _pin_data         = pin_data;
      _num_cis          = num_cis;
      _auto_send        = true;
      _pins             = new byte[_num_cis];
 
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,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 send(){
      digitalWrite(_pin_latch, LOW); 
      for(int i=_num_cis-1; i>=0; i--) {  shiftOut(_pin_data, _pin_clock, MSBFIRST, readByte(i) );  }
      digitalWrite(_pin_latch, HIGH);
      _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(); }
    };
};
  
 
const int PIN_CLOCK = 10;   //SHCP
const int PIN_LATCH = 9;    //STCP
const int PIN_DATA  = 8;    //DS
 
Expansor74HC595 *exp1;

/************************************************************************************************************
**********************************fim expansor 74hc595*******************************************************
************************************************************************************************************/
  
/************************************************************************************************************
**********************************display de 7 segmentos*****************************************************
************************************************************************************************************/
const int  d7seg_595_index        = 0;   //em qual dos 595´s será usado pra armazenar os segmentos a, b, c, d, e, f, g e dp
const int  d7seg_595_pin_enable[] = {8,9,10,11};               //pinos do 595 para habilitar os displays
const byte d7seg_digits[]         = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                     B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  exp1->startWrite();
  exp1->write(d7seg_595_pin_enable[pos], HIGH);
  exp1->writeByte(d7seg_595_index, point ? d7seg_digits[digit] | (1 << 0) : d7seg_digits[digit], LSBFIRST);  // | (1 << 0) --> alterar o bit que representa o dp (ponto) do display. envia para o 595
  exp1->send();
  
  delay(1);   
  
  exp1->write(d7seg_595_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_595_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}
/************************************************************************************************************
**********************************fim display de 7 segmentos*************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************voltimetro*****************************************************************
************************************************************************************************************/
const unsigned long r1   = 1000000;  //resistor de 1M
const unsigned long r2   = 100000;   //resistor de 100K
const unsigned int  aRef = 5;        //referencia de 5v
float               tensao = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

float get_tensao(int pin){
  return (analogRead(pin) * aRef) / 1023.0 * ( (r1+r2)/r2 );
}
/************************************************************************************************************
**********************************fim voltimetro*************************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************setup/loop*****************************************************************
************************************************************************************************************/
void setup(){
  exp1   = new Expansor74HC595(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2);
}  

void loop() {
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    tensao = get_tensao(A0); //calculo da tensão lida
    millis_ref = millis();
  }
  float f = tensao;
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : (f>=10 ? 2 : 3) ) );  //de acordo com o numero, mostra 0, 1, 2 ou 3 casas decimais
}

/************************************************************************************************************
**********************************fim setup/loop*************************************************************
************************************************************************************************************/

Veja também: montagem dos 74hc595


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);
  }  
}





sábado, 6 de junho de 2015

Arduino - Teclado Matricial com 74HC595 e 74HC165

Esse post é uma continuação de alguns outros posts. Então antes de ver esse post, veja os seguintes posts antes:


http://fabianoallex.blogspot.com.br/2015/06/arduino-74hc595-e-74hc165-juntos.html

e

http://fabianoallex.blogspot.com.br/2015/05/arduino-texto-com-teclado-matricial-4x3.html

A ideia aqui é mostrar algo prático para ser usado com o 74HC595 e o 74HC165 juntos. E como eu já tinha feito um post falando de teclados matriciais, resolve utilizá-lo como exemplo.

Vídeo:




código-fonte:

/*
Criado em 06/06/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] = B00000000;
      }
      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(_pin_data, INPUT);
          
          if ( digitalRead(_pin_data) ){
            _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);
    }
    
    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 = 4; 
const int PIN_LATCH = 7; 
const int PIN_DATA  = 12; 

 
Expansor74HC595_74HC165 * exp1;


/****************************************************************
*********funcao pra ler teclado matricial com 595 e 165**********
*****************************************************************/

#define GET_PIN(num_ci, pos)  num_ci*8+pos
#define col1 GET_PIN(3, 7)   //pino do CI 3 (QUARTO CI) 165 - 31
#define col2 GET_PIN(3, 6)   //pino do CI 3 (QUARTO CI) 165 - 30
#define col3 GET_PIN(3, 5)   //pino do CI 3 (QUARTO CI) 165 - 29
#define lin1 GET_PIN(1, 4)   //pino do CI 1 (SEGUNDO CI) 595 - 12
#define lin2 GET_PIN(1, 3)   //pino do CI 1 (SEGUNDO CI) 595 - 11
#define lin3 GET_PIN(1, 2)   //pino do CI 1 (SEGUNDO CI) 595 - 10
#define lin4 GET_PIN(1, 1)   //pino do CI 1 (SEGUNDO CI) 595 - 9
 
 
char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
   
  for (i=0; i<4; i++) {
    exp1->write(lin1, LOW); 
    exp1->write(lin2, LOW);
    exp1->write(lin3, LOW);
    exp1->write(lin4, LOW);
    exp1->write(l[i],HIGH); 
    
    exp1->update();
    exp1->update();
    
    if(exp1->read(col1)) { t = i*3+1; break; }
    if(exp1->read(col2)) { t = i*3+2; break; }
    if(exp1->read(col3)) { t = i*3+3; break; }
  }
   
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
   
  return '\0';
}
  
/****************************************************************
*********fim da funcao pra ler teclado matricial com 595 e 165***
*****************************************************************/
  
  
/****************************************************************
********************************setup e loop**********************
*****************************************************************/

void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2, 2);
  
  Serial.begin(9600);
}
  
unsigned long millis_alt = 0;
  
void loop() {
  if (  (millis() - millis_alt) > 1000  ){
    exp1->write(15, !exp1->read(15));
    exp1->update();
    millis_alt = millis();
  }
  
  if (get_tecla() != '\0'){
    Serial.println( get_tecla() );
  }
}

/****************************************************************
********************************fim setup e loop******************
*****************************************************************/

sexta-feira, 5 de junho de 2015

Arduino - 74HC595 e 74HC165 Juntos

Quando se fala em expansão de portas do Arduino, uma das possibilidades é fazer isso através desses dois CIs, o 74HC595 e o 74HC165. Pra quem ainda não os conhece, são dois CIs que com apenas três pinos do Arduino permitem expandir saídas (595) e entradas (165). Tanto um quanto o outro, permitem que sejam conectados vários CIs em cascata, o que dá a possibilidade de infinitas portas.

Datasheets aqui:

http://www.nxp.com/products/logic/shift_registers/74HC595N.html
http://www.nxp.com/products/logic/shift_registers/74HC165N.html

Pra quem quiser ver um exemplo de como expandir apenas saídas, utilizando apenas 74HC595, criei um classe que permite o uso de maneira bem facilitada, que pode ser visto nesse link aqui.

Nesse post de hoje, vou demonstrar como criar uma Classe no Arduino, que permita nos mesmos 3 pinos ligar 595 e 165 juntos, e serem acessados compartilhando os mesmos 3 pinos, Latch, Clock e Data (sim, o pino Data é o mesmo para entradas e saídas :D ).

Como meu foco é mais disponibilizar o código que facilite a programação, não vou entrar muito nos detalhes da implementação, mas vou deixar aqui o link para o site no qual me baseei para chegar nessa solução:

http://homepages.which.net/~paul.hills/Software/ShiftRegister/ShiftRegisterBody.html

A imagem abaixo foi retirado do site acima, e demonstra como fazer a ligação entre os CIs.


Obs: output enable (que é ligado ao pino 13 do 595) não precisa ser ligado ao Arduino. Os Pinos 13 dos 595s devem ser ligado diretamente ao Ground. Outro detalhes é que o pino identificado como Strobe no esquema acima, no arduino é identificado como Latch.

As principais regras ao ligar são:

 - Primeiro devem ser ligados os 595 e após o último 595, ligar os 165.
 - Não ligar os CIs intercalados.
 - Não há limites para quantidades de cada um, podendo ser apenas um ou outro. Mas para ser usado apenas um dos CIs, recomendo utilizar a classe só para 595, que mostrei acima. Em breve criarei uma classe ser usado apenas por 165.
 - Todas as entradas dos CIs 165 devem obrigatoriamente ser ligadas a resistores PULL DOWN ou PULL UP. Caso alguma entrada fique "em aberto" a entrada ficará flutuando, inclusive interferindo nas demais entradas do CI.

Para testar o circuito acima, montei o mesmo em uma protoboard, mas não liguei todas as saídas e entradas, pois deixaria o circuito muito poluído e com muitas conexões, então preferi focar nas ligações de conexões dos próprios componentes.



Mas para testar as saídas liguei apenas um led na protoboard, onde testo uma saída por vez, como poderá ser visto no vídeo que postarei em breve. Nos Caso das entradas, liguei algumas diretamente ao vcc e outras diretamente ao ground.

Identificação dos pinos

No exemplo acima, foi utilizado 4 cis, dois 595 e dois 165.

Cada um dos pinos é identificado por um número, começando de 0 (zero) e indo até a quantidade de CIs multiplicado por 8 menos 1. No nosso exemplo temos 32 pinos adicionais, sendo os pinos de 0 a 15 os pinos de saídas e os pinos de 16 a 31 os pinos de entradas.

Se for ligado um botão no último pino do ultimo CI 165, faremos a leitura da seguinte maneira:

  int valor = exp->read(31);

Já para leitura de todas as entradas de uma única vez de um determinado CI 165, faremos o seguinte:

  byte valor = exp->readByte(3);  //leitura de todos os bits do quarto CI 

Da mesma maneira que é possível fazer a leitura de um bit por vez ou vários de uma vez, é possível ainda fazer a escrita da mesma forma, utilizando os métodos write() e writeByte().   exemplo:

exp->writeByte(1, B00011001); ou 
exp->writeByte(1, B10011000, LSBFIRST); 

exp->read(9, HIGH); ou
exp->read(9, LOW); 

Tanto os métodos write, writeByte, read e readByte, manipulam apenas os dados que estão em cache na classe. Para que as leituras e escritas tenham efeito é necessário chamar o método update(). Ele é quem envia e recebe os dados para os CIs.

o método update() deve ser chamado antes de serem feitas as leituras das entradas, e após serem feitas as escritas nas saídas.

Vídeo:



Código-Fonte:

/*
Criado em 04/06/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] = B00000000;
      }
      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(_pin_data, INPUT);
          
          if ( digitalRead(_pin_data) ){
            _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);
    }
    
    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 = 4; 
const int PIN_LATCH = 7; 
const int PIN_DATA  = 12; 

 
Expansor74HC595_74HC165 * exp1;
  
void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2, 2);
  
  Serial.begin(9600);
}
  
void loop() {
  exp1->writeByte(0, B00000000);
  exp1->writeByte(0, B11001101, LSBFIRST);
  
  exp1->write(8, HIGH);
  exp1->write(9, HIGH);
  
  exp1->write(10, HIGH);
  exp1->write(11, HIGH);
  
  exp1->write(14, HIGH);
  exp1->write(15, !exp1->read(15));  //blink
  
  
    
  exp1->update();  //AS ESCRITAS DEVEM SER FEITAS ANTES DO UPDATE. E AS LEITURAS APOS O UPDATE
   
  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(exp1->readByte(3), BIN);  //LE O QUARTO CI (165)
  Serial.println("");
  
  delay(1000);
}

terça-feira, 2 de junho de 2015

Arduino - Classe para 74HC595 Corrigida


Nesse post (clique aqui) foi mostrado como criar uma classe pra facilitar o uso do registrador de deslocamento 74HC595 com o Arduino.

Apesar de funcionar perfeitamente, a classe ocupava muita memória para armazenar o estados de todas as saídas.

o código abaixo, foi reescrito de forma a ocupar menos memória e ser mais simples. Alguns métodos foram renomeados.

segue código-fonte:


class Expansor74HC595 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    int  _num_cis;
    byte* _pins;
    int  _auto_send;
  public:
    Expansor74HC595(int pin_clock, int pin_latch, int pin_data, int num_cis){
      _pin_clock        = pin_clock;
      _pin_latch        = pin_latch;
      _pin_data         = pin_data;
      _num_cis          = num_cis;
      _auto_send        = true;
      _pins             = new byte[_num_cis];

      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,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 send(){
      
      digitalWrite(_pin_latch, LOW); 
      for(int i=_num_cis-1; i>=0; i--) {  shiftOut(_pin_data, _pin_clock, MSBFIRST, readByte(i) );  }
      digitalWrite(_pin_latch, HIGH);
      _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(); }
    };
};
 

const int PIN_CLOCK = 2; 
const int PIN_LATCH = 3; 
const int PIN_DATA  = 4; 

Expansor74HC595 *exp1;
 
void setup() {
  exp1   = new Expansor74HC595(PIN_CLOCK, PIN_LATCH, PIN_DATA, 3);
  
  Serial.begin(9600);
}
 
void loop() {
  for (byte b=0; b<=255; b++) {                    //i=0-> B00000000   i=255-> B11111111
    exp1->startWrite();
    exp1->writeByte(0, b);
    exp1->writeByte(1, b, LSBFIRST);
    exp1->write(23, HIGH);
    exp1->send();
  
    delay(500);
  }
  
  exp1->clear();
  delay(500); 
}

sábado, 12 de setembro de 2015

Arduino - DIY - Módulo Gravador Attiny85

Como fazer um módulo gravador para Attiny que encaixa no Arduino.

Download: https://docs.google.com/uc?export=download&id=0B9ZzXhiNwNSrUkVpbXpKYWdCbFU

Para ver o artigo sobre os módulos para 74HC595, clique no link abaixo.

http://fabianoallex.blogspot.com.br/2015/08/arduino-modulo-expansor-74hc595.html


Vídeo:




Fotos:













Veja nesse vídeo como fazer a montagem dos componentes:


quarta-feira, 7 de janeiro de 2015

Arduino - Expansão dinâmica de portas


Vaja como fazer para o Arduino identificar a quantidade de expansores (74hc595, max7219, etc) conectados em série. veja o vídeo.






vídeo anterior sobre o 74HC595:
http://youtu.be/SwQECcYdEkI

Link para o artigo:
http://www.arduinoecia.com.br/2014/04/Painel-de-led-max7219.html


código:



int conta_modulos(int pino, float Rb, float Rp){
  float La = analogRead(pino);
  double N = ( 1 / (  ((1023*Rb)/La) - Rb ) )  / ( 1 / Rp );
  int n = round(N);
    
  Serial.print(N);
  Serial.print(" arredondado: ");
  Serial.println(n);
  
  return n;
}

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

void loop() {
  int q1 = conta_modulos(A0, 1000, 10000);  //resistor gnd->1K, resistor paralelo 10k
  delay(1000);
}


sexta-feira, 2 de janeiro de 2015

Arduino - Classe para 74HC595

No vídeo abaixo mostro como criar uma classe para facilitar o uso do Registrador 74HC595 com o Arduino.








Código:

ATENÇÃO:

o código abaixo foi melhorado, e publicado em um novo post. Clique aqui para o código atualizado.


class Expansor74HC595 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    int  _num_cis;
    int* _pins;
    int  _envio_automatico;
  public:
    Expansor74HC595(int pin_clock, int pin_latch, int pin_data, int num_cis=1);
    void startWrite()         { _envio_automatico = false; };
    int  get_num_portas() { return _num_cis * 8; };
    void limpar() { for (int i=0;i < _num_cis * 8;i++){ _pins[i] = LOW; } enviar();  };
    int  read(int pin)  { if (pin >= get_num_portas()) {return LOW;} return _pins[pin]; };
  
    void enviar();
    void writeByte(int num_ci, byte values, bool q0=true);
    void write(int pin, int value);
};

Expansor74HC595::Expansor74HC595(int pin_clock, int pin_latch, int pin_data, int num_cis){
  _pin_clock        = pin_clock;
  _pin_latch        = pin_latch;
  _pin_data         = pin_data;
  _num_cis          = num_cis;
  _envio_automatico = true;
  _pins             = new int[_num_cis*8];
  
  pinMode(_pin_clock,OUTPUT);
  pinMode(_pin_latch,OUTPUT);
  pinMode(_pin_data, OUTPUT);
  limpar();
}

void Expansor74HC595::writeByte(int num_ci, byte b, bool q0) { 
  for(int i=(num_ci*8-1),j=q0?7:0;  i>=(num_ci-1)*8;  i--, q0?j--:j++) {
    _pins[i]  = (b & (1 << j)) ?  HIGH  :  LOW;
  }     
  
  if (_envio_automatico) { enviar(); }
}

void Expansor74HC595::write(int pin, int value) {
  if (pin >= get_num_portas()) {return;}
  _pins[pin] = value ? HIGH : LOW;
  if (_envio_automatico) { enviar(); }
}

void Expansor74HC595::enviar(){
  digitalWrite(_pin_latch, LOW); 
  
  for (int i=(_num_cis*8-1); i>=0; i-- ) {  
    digitalWrite(_pin_clock, LOW); 
    digitalWrite(_pin_data,  _pins[i]);
    digitalWrite(_pin_clock, HIGH); 
    digitalWrite(_pin_data,  LOW);
  }
  
  digitalWrite(_pin_latch,HIGH);
  
  _envio_automatico = true;
}


/*----------------------*/



const int PIN_CLOCK = 2; 
const int PIN_LATCH = 3; 
const int PIN_DATA  = 4; 

Expansor74HC595 *exp1;
 
void setup() {
  exp1 = new Expansor74HC595(PIN_CLOCK, PIN_LATCH, PIN_DATA, 16);
  
  Serial.begin(9600);
}


void conta_tempo(void(*p)(), String descricao){
  long m1, m2;
  m1 = millis();
  (*p)(); //executa a função
  m2 = millis();
 
  Serial.print(descricao + ": m1= ");
  Serial.print(m1);
  Serial.print(" - m2 = ");
  Serial.print(m2);
  Serial.print(" - dif = ");
  Serial.print(m2 - m1);
  Serial.println(" milissegundos");
}

void enviar(){
  exp1->enviar();
}
 
void loop() {
  
  for (int i=0; i<=255; i++) {                    //i=0-> B00000000   i=255-> B11111111
    exp1->startWrite();
    
    exp1->writeByte(1, i);         //q0 será o menos significativo   
    exp1->writeByte(3, i, false);  //q7 será o menos significativo
    
    exp1->write(8, 1); 
    exp1->write(9, 0);
    exp1->write(10, 1);
    exp1->write(11, 0);

    conta_tempo(enviar, "teste");

    //exp1->enviar();
  
    delay(500);
  
    exp1->startWrite();

    exp1->write(8, 0);
    exp1->write(9, 1);
    exp1->write(10, 0);
    exp1->write(11, 1);

    exp1->enviar();
    
    delay(500);  
    
    exp1->limpar();
    delay(300);    
  }
}

domingo, 7 de junho de 2015

Arduino - Display LCD com 74HC595 e 74HC165

Mais um exemplo do uso dos 74HC595 e 74HC165, agora utilizando um Display LCD.

a montagem do circuito, para expansão de entradas e saídas, pode ser visto nesse link.

http://fabianoallex.blogspot.com.br/2015/06/arduino-74hc595-e-74hc165-juntos.html

a explicação sobre o teclado matricial, pode ser visto aqui

http://fabianoallex.blogspot.com.br/2015/06/arduino-teclado-matricial-com-74hc595-e.html

Display LCD

Para ligar o Display LCD no Arduino, há uma biblioteca específica, que faz a comunicação entre os pinos do Arduino e o Display, porém nesse exemplo, o display não irá se comunicar diretamente com o Arduino, e sim com um dos 595 que são controlados pelo Arduino. Então para conseguir fazer a comunicação entre o Arduino e o Display LCD, foi necessário fazer alterações na biblioteca disponibilizada para tal. Como a biblioteca foi escrita para se comunicar com os pinos do Arduino, foi necessário, substituir todas as chamadas feitas a digitalWrite(pin, value) para exp1->write(pin, value). E todas as referencias a pinMode(pin, mode) foram removidas.

Essas alterações não foram feitas diretamente na biblioteca, o que eu fiz, foi copiar o conteúdo da biblioteca e colar dentro da sketch e fazer as devidas alterações, como pode ser visto no código-fonte abaixo. Ainda não criei uma biblioteca específica para isso, pois ainda não terminei de fazer todos os testes, então por enquanto, é assim mesmo que vai funcionar.

Vídeo




Código-Fonte:


/********************************************************************************************
*******************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] = B00000000;
      }
      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(_pin_data, INPUT);
          
          if ( digitalRead(_pin_data) ){
            _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);
    }
    
    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 ***********************************
*********************************************************************************************/

/********************************************************************************************
*******************ponteiro para o expansor a ser instanciado  INICIO  **********************
*********************************************************************************************/

Expansor74HC595_74HC165 * exp1;

/********************************************************************************************
*******************ponteiro para o expansor a ser instanciado  FIM  *************************
*********************************************************************************************/

/********************************************************************************************
*******************CLASSE LiquidCrystal  INICIO  ********************************************
*********************************************************************************************/

#include <inttypes.h>
#include "Print.h"

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

class LiquidCrystal : public Print {
public:
  LiquidCrystal(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
  LiquidCrystal(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);

  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
    
  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void setRowOffsets(int row1, int row2, int row3, int row4);
  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t); 
  virtual size_t write(uint8_t);
  void command(uint8_t);
  
  using Print::write;
private:
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void write8bits(uint8_t);
  void pulseEnable();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _data_pins[8];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines;
  uint8_t _row_offsets[4];
};



#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Arduino.h"

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set: 
//    DL = 1; 8-bit interface data 
//    N = 0; 1-line display 
//    F = 0; 5x8 dot character font 
// 3. Display on/off control: 
//    D = 0; Display off 
//    C = 0; Cursor off 
//    B = 0; Blinking off 
// 4. Entry mode set: 
//    I/D = 1; Increment by 1 
//    S = 0; No shift 
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LiquidCrystal constructor is called).

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

LiquidCrystal::LiquidCrystal(uint8_t rs,  uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _rw_pin = rw;
  _enable_pin = enable;
  
  _data_pins[0] = d0;
  _data_pins[1] = d1;
  _data_pins[2] = d2;
  _data_pins[3] = d3; 
  _data_pins[4] = d4;
  _data_pins[5] = d5;
  _data_pins[6] = d6;
  _data_pins[7] = d7; 

  //pinMode(_rs_pin, OUTPUT);
  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
  if (_rw_pin != 255) { 
    //pinMode(_rw_pin, OUTPUT);
  }
  //pinMode(_enable_pin, OUTPUT);
  
  if (fourbitmode)
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
  else 
    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
  
  begin(16, 1);  
}

void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
  if (lines > 1) {
    _displayfunction |= LCD_2LINE;
  }
  _numlines = lines;

  setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);  

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
  delayMicroseconds(50000); 
  // Now we pull both RS and R/W low to begin commands
  exp1->write(_rs_pin, LOW);
  exp1->write(_enable_pin, LOW);
  if (_rw_pin != 255) { 
    exp1->write(_rw_pin, LOW);
  }
  exp1->update();
  
  //put the LCD into 4 bit or 8 bit mode
  if (! (_displayfunction & LCD_8BITMODE)) {
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms
    
    // third go!
    write4bits(0x03); 
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02); 
  } else {
    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(150);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);
  }

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction);  

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
  display();

  // clear it off
  clear();

  // Initialize to default text direction (for romance languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

}

void LiquidCrystal::setRowOffsets(int row0, int row1, int row2, int row3)
{
 _row_offsets[0] = row0;
 _row_offsets[1] = row1;
 _row_offsets[2] = row2;
 _row_offsets[3] = row3;
}

/********** high level commands, for the user! */
void LiquidCrystal::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
{
  const size_t max_lines = sizeof(_row_offsets) / sizeof(*_row_offsets);
  if ( row >= max_lines ) {
    row = max_lines - 1;    // we count rows starting w/0
  }
  if ( row >= _numlines ) {
    row = _numlines - 1;    // we count rows starting w/0
  }
  
  command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
}

// Turn the display on/off (quickly)
void LiquidCrystal::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LiquidCrystal::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LiquidCrystal::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LiquidCrystal::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LiquidCrystal::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LiquidCrystal::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LiquidCrystal::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LiquidCrystal::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
  send(value, LOW);
}

inline size_t LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  exp1->write(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) { 
    exp1->write(_rw_pin, LOW);
  }
  
  exp1->update();
  
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value); 
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  exp1->write(_enable_pin, LOW);
  exp1->update();
  delayMicroseconds(1);    
  exp1->write(_enable_pin, HIGH);
  exp1->update();
  delayMicroseconds(1);    // enable pulse must be >450ns
  exp1->write(_enable_pin, LOW);
  exp1->update();
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    //pinMode(_data_pins[i], OUTPUT);
    exp1->write(_data_pins[i], (value >> i) & 0x01);
  }
  exp1->update();

  pulseEnable();
}

void LiquidCrystal::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    //pinMode(_data_pins[i], OUTPUT);
    exp1->write(_data_pins[i], (value >> i) & 0x01);
  }
  exp1->update();
  
  pulseEnable();
}


/********************************************************************************************
*******************CLASSE LiquidCrystal  FIM  ***********************************************
*********************************************************************************************/

const int PIN_CLOCK = 4; 
const int PIN_LATCH = 7; 
const int PIN_DATA  = 12; 



/****************************************************************
*********funcao pra ler teclado matricial com 595 e 165**********
*****************************************************************/

#define GET_PIN(num_ci, pos)  num_ci*8+pos
#define col1 GET_PIN(3, 7)   //pino do CI 3 (QUARTO CI) 165 - 31
#define col2 GET_PIN(3, 6)   //pino do CI 3 (QUARTO CI) 165 - 30
#define col3 GET_PIN(3, 5)   //pino do CI 3 (QUARTO CI) 165 - 29
#define lin1 GET_PIN(1, 4)   //pino do CI 1 (SEGUNDO CI) 595 - 12
#define lin2 GET_PIN(1, 3)   //pino do CI 1 (SEGUNDO CI) 595 - 11
#define lin3 GET_PIN(1, 2)   //pino do CI 1 (SEGUNDO CI) 595 - 10
#define lin4 GET_PIN(1, 1)   //pino do CI 1 (SEGUNDO CI) 595 - 9
 
 
char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
   
  for (i=0; i<4; i++) {
    exp1->write(lin1, LOW); 
    exp1->write(lin2, LOW);
    exp1->write(lin3, LOW);
    exp1->write(lin4, LOW);
    exp1->write(l[i],HIGH); 
    
    exp1->update();
    exp1->update();
    
    if(exp1->read(col1)) { t = i*3+1; break; }
    if(exp1->read(col2)) { t = i*3+2; break; }
    if(exp1->read(col3)) { t = i*3+3; break; }
  }
   
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
   
  return '\0';
}
  
/****************************************************************
*********fim da funcao pra ler teclado matricial com 595 e 165***
*****************************************************************/
  
  
//Criando um objeto da classe LiquidCrystal e 
//inicializando com os pinos da interface.
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);  //esses pinos, são os pinos do 595 e não do arduino
  
  
/****************************************************************
********************************setup e loop**********************
*****************************************************************/

void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2, 2);
  
  lcd.begin(16, 2); 
  
  Serial.begin(9600);
}
  
unsigned long millis_alt = 0;
  
void loop() {
  if (  (millis() - millis_alt) > 1000  ) {
    lcd.setCursor(0,0);
    lcd.print(millis());
    
    exp1->write(15, !exp1->read(15));
    exp1->update();
    millis_alt = millis();
  }
  
  char t = get_tecla();
  if (t != '\0'){
    lcd.setCursor(0,1); 
    lcd.print(t);
  }
}

/****************************************************************
********************************fim setup e loop******************
*****************************************************************/