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