Publicidade:

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


quarta-feira, 22 de julho de 2015

Arduino - Voltímetro com Display de 7 Segmentos

No post anterior (aqui) falei sobre como mostrar números com casas decimais em displays de 7 segmentos. Aproveitando esse mesmo exemplo, fiz um voltímetro, onde é possível ler a tensão de uma fonte externa e mostrar seu valor nos displays.

Vídeo:



código-fonte:

/************************************************************************************************************
**********************************display de 7 segmentos*****************************************************
************************************************************************************************************/
const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos 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) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_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_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_voltage(int pin){
  return (analogRead(pin) * aRef) / 1023.0 * ( (r1+r2)/r2 );
}

void show_voltage(){
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    tensao = get_voltage(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 voltimetro*************************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************setup/loop*****************************************************************
************************************************************************************************************/
void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}  

void loop() {
  show_voltage();
}

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




Referência: http://br-arduino.org/2015/06/voltimetro-com-arduino-como-montar-programar-e-calibrar.html

terça-feira, 21 de julho de 2015

Arduino - Display de 7 Segmentos com Casas Decimais

Nesse post vou mostrar um exemplos simples e funcional de como mostrar números com casas decimais em displays de 7 segmentos diretamente com o Arduino, sem nenhum drive entre o Arduino e os displays.

O Exemplo mostrado no vídeo possui 4 displays em série, mas nada impede que sejam adicionados outros, bastando apenas modificar no código, a constante d7seg_pin_enable, onde deve ser informado os pinos a mais utilizados. No exemplo, foram utilizados os pinos, 2, 3, 4 e 5. E fisicamente, devem ser incluídos os componentes (displays, transistores e resistores) adicionais no circuito, seguindo o esquema abaixo.

Da mesma maneira que é possível incluir mais, pode-se também utilizar esse código, com menos displays, 3, 2 ou 1.

Os dígitos do display (números de zero a nove) são definidos através da constante d7seg_digits, no qual foram necessários apenas 10 bytes, sendo um byte para cada dígito que pode ser representado no em cada um dos displays.

Como um byte tem oito bits, foi possível definir um bit para cada segmento do display. Em termos de economia de memória essa é uma excelente tática, ao contrário de vários outros exemplos que podem ser encontrados na internet, os quais muitas vezes, não possuem os devidos cuidados na hora de definir os tipos corretos de dados para representar cada um dos dígitos. O único inconveniente ao utilizar bytes para representar dígitos, é a "dificuldade" que o programador menos experiente pode sentir ao tentar ler um determinado bit (segmento) na hora de ligar/desligar o display. O segredo para isso, é utilizar operadores de bitwise, o qual podemos ver nessa parte do código:

d7seg_digits[digit] & (1 << (7-i))

onde o "7-i", indica qual bit do byte que será lido.



No exemplo do vídeo tem um potenciômetro ligado ao pino A0. o valor lido no A0, que por padrão retorna um valor entre 0 e 1023, é convertido pra uma escala que vai entre 0 e 2000. Quando o valor a ser mostrado é maior que 1000, nenhuma casa decimal é mostrada. Já quando o número está entre 100 e 999 o valor a ser mostrado no display terá uma casa decimal, já para valores inferiores a 99, o valor aparece com 2 casas decimais. Isso foi feito pra não "perder" dígitos no caso de valores pequenos tendo uma maior precisão do valor lido em caso de números menores.





const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos 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) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_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_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}

void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}

float               f = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

void loop() {
  
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    f = ((analogRead(A0)) / 1023.0 * 2000.0);
    millis_ref = millis();
  }
  
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : 2) );  //de acordo com o numero, mostra 0, 1 ou 2 casas decimais

}