quinta-feira, março 08, 2018

Franzininho: Termômetro

Neste projeto vamos usar o Franzininho para apresentar a temperatura atual em um display LCD.


Medição da Temperatura

Vou usar o sensor LM35, que eu já descrevi aqui no blog. Este sensor fornece uma tensão de saída igual à temperatura dividida por 100 (quando medida em Volts). Por exemplo, se a temperatura for 25 graus, a saída do LM35 será 0,25V. Neste projeto vamos considerar apenas temperaturas positivas até 99 graus centígrados, o que significa uma tensão de até 0,99V.

A medida desta tensão será feita com o ADC do ATtiny85. Para maior precisão, vamos usar a referência interna de 1.1V. Desta forma a temperatura (em graus) será (teoricamente) igual à leitura do ADC multiplicada por 110 e dividida por 1024.

Um último cuidado é ligar em um pino do Franzininho que não tenha nada ligado. O que nos leva a usar a entrada AN1.

Display LCD

Neste projeto vou usar um display do Nokia 5110, que vimos com muitos detalhes aqui, aqui, aqui e aqui (entre outros posts). Neste caso temos duas mudanças importantes:
  • O modelo que eu vou usar tem uma placa azul, com a indicação 3v-5v. Este modelo permite ligar direto a 5V. O modelo que usei antes (placa vermelha) usa 3.3V, exigindo redução das tensões nas ligações.
  • Devido ao número limitado de pinos de saída do Franzininho, o pino RST do display será ligado à alimentação através de um resistor de 15K. Desta forma o reset será feito apenas quando o circuito for alimentado, o Franzininho não poderá forçar um reset.
Vamos aproveitar que o display é gráfico e apresentar dígitos grandes.

Um aviso: estes displays são meio temperamentais. Existem vários relatos de problemas com contraste e enfrentei alguns comportamentos estranhos com ele neste projeto.

Circuito

Software

O software foi feito a partir dos meus exemplos anteriores com o display 5110. A principal mudança é a escrita no display, que foi trocada por uma rotina específica para o relógio. Esta rotina permite escrever uma caracter (dígito, vírgula, grau ou C) em uma de seis posições (correspondendo a 99,9oC).

Na leitura do sensor é feita um média de 16 leituras. Um detalhe interessante é que a referência interna é extremamente estável (varia pouco com tempo e temperatura), mas não muito precisa (pode variar entre 1,0 e 1,2). No código abaixo o valor da referência foi "calibrado" usando um multímetro externo para ler a tensão no LM35.

  1. /* 
  2.  * Termômetro 
  3.  * DQ - 01/03/18 
  4.  */  
  5.   
  6. // Conexões do display ao Franzininho  
  7. #define PIN_SCE   0  
  8. #define PIN_DC    4  
  9. #define PIN_SDIN  1  
  10. #define PIN_SCLK  3  
  11.   
  12. // Conexão do sensor (LM35)  
  13. #define PIN_SENSOR A1  
  14.   
  15. // Seleção de dado ou comando  
  16. #define LCD_CMD   LOW  
  17. #define LCD_DAT   HIGH  
  18.   
  19. // Tamanho da tela  
  20. #define LCD_DX    84  
  21. #define LCD_DY    48  
  22.   
  23. // Envia um byte para o controlador do display  
  24. // dc:   LCD_CMD ou LCD_DAT  
  25. // data: byte a enviar  
  26. void LcdWrite(byte dc, byte data)  
  27. {  
  28.   digitalWrite(PIN_DC, dc);  
  29.   digitalWrite(PIN_SCE, LOW);  
  30.   shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);  
  31.   digitalWrite(PIN_SCE, HIGH);  
  32. }  
  33.   
  34. // Iniciação do display  
  35. void LcdInitialise(void)  
  36. {  
  37.   // define a direção dos pinos de E/S  
  38.   pinMode(PIN_SCE, OUTPUT);  
  39.   pinMode(PIN_DC, OUTPUT);  
  40.   pinMode(PIN_SDIN, OUTPUT);  
  41.   pinMode(PIN_SCLK, OUTPUT);  
  42.     
  43.   // envia os comandos de iniciação  
  44.   LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands.  
  45.   LcdWrite( LCD_CMD, 0xB0 ); // Set LCD Vop (Contraste)  
  46.   LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent  
  47.   LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48  
  48.   LcdWrite( LCD_CMD, 0x20 ); // LCD Basic Commands.  
  49.   LcdWrite( LCD_CMD, 0x0c ); // LCD no modo normal    
  50. }  
  51.   
  52. // Limpa a tela  
  53. void LcdClear(void)  
  54. {  
  55.   // posiciona ponteiro no inicio da memória  
  56.   LcdWrite( LCD_CMD, 0x40);    
  57.   LcdWrite( LCD_CMD, 0x80);  
  58.     
  59.   // preenche a memória com zeros  
  60.   for (int index = 0; index < LCD_DX * LCD_DY / 8; index++)  
  61.   {  
  62.     LcdWrite(LCD_DAT, 0x00);  
  63.   }  
  64. }  
  65.   
  66. // Posiciona em uma determina linha e coluna alfanuméricas  
  67. void LcdPos(int lin, int col)  
  68. {  
  69.   LcdWrite( LCD_CMD, 0x40 + lin);    
  70.   LcdWrite( LCD_CMD, 0x80 + col*7);    
  71. }  
  72.   
  73. // Tabelas para desenhar os caracteres  
  74.   
  75. static const int offset_dig[] = {0, 14, 28, 36, 50, 58 };  
  76. static const int larg_dig[] = {14, 14, 8, 14, 8, 14 };  
  77. static const byte gc_dig[][30] =  
  78. {  
  79.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  80.     0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,  
  81.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 0  
  82.   { 0x00, 0x00, 0x0C, 0x06, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,  
  83.     0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,  
  84.     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, // 1  
  85.   { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  86.     0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00,  
  87.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }, // 2  
  88.   { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  89.     0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF,  
  90.     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 3  
  91.   { 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,  
  92.     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF,  
  93.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03 }, // 4  
  94.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,  
  95.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  96.     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 5  
  97.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,  
  98.     0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  99.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 6  
  100.   { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF,  
  101.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,  
  102.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03 }, //7  
  103.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  104.     0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  105.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 8  
  106.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFC,  
  107.     0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xFF,  
  108.     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00 }, // 9  
  109.   { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x60, 0x1F,   
  110.     0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
  111.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },  // virgula  
  112.   { 0x1E, 0x21, 0x21, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
  113.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
  114.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },  // grau  
  115.   { 0xFC, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,  
  116.     0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
  117.     0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }  // C  
  118. };  
  119.   
  120. // Escreve um dígito na tela  
  121. // dig: 0 a 9, 10 (virgula), 11 (grau), 12 (C)  
  122. // pos: 0 a 5  
  123. void LcdWriteDig(int dig, int pos)  
  124. {  
  125.   byte *gc = gc_dig[dig];  
  126.   int larg = larg_dig[pos] - 4;  
  127.   
  128.   for (byte bloco = 1; bloco <= 3; bloco++)  
  129.   {  
  130.     LcdWrite(LCD_CMD, 0x40 + bloco);    
  131.     LcdWrite(LCD_CMD, 0x80 + offset_dig[pos]);    
  132.     LcdWrite(LCD_DAT, 0x00);  
  133.     LcdWrite(LCD_DAT, 0x00);  
  134.     for (int index = 0; index < larg; index++)  
  135.     {  
  136.       LcdWrite(LCD_DAT, *gc++);  
  137.     }  
  138.     LcdWrite(LCD_DAT, 0x00);  
  139.     LcdWrite(LCD_DAT, 0x00);  
  140.   }  
  141. }  
  142.   
  143. //=====================================  
  144. // Pontos de entrada padrão do Arduino  
  145. //=====================================  
  146.   
  147. void setup(void)  
  148. {  
  149.   LcdInitialise();  
  150.   LcdClear();  
  151.   LcdWriteDig (0, 0);  
  152.   LcdWriteDig (0, 1);  
  153.   LcdWriteDig (10, 2);  
  154.   LcdWriteDig (0, 3);  
  155.   LcdWriteDig (11, 4);  
  156.   LcdWriteDig (12, 5);  
  157.   
  158.   analogReference(INTERNAL);  
  159.   analogRead(PIN_SENSOR);  
  160. }  
  161.   
  162. void loop(void)  
  163. {  
  164.   // Faz 16 leituras para maior precisão  
  165.   int vSensor = 0;  
  166.   for (int i = 0; i < 16; i++)  
  167.   {  
  168.     vSensor += analogRead(PIN_SENSOR);  
  169.   }  
  170.   vSensor = vSensor >> 2;  
  171.   
  172.   // 0,01V = 1 grau Celsius  
  173.   // Referência interna pode variar entre 1.0 e 1.2 V  
  174.   // Valor abaixo baseado em medida feita com multímetro  
  175.   long temp = (vSensor*1016L)/(4096L);  
  176.   
  177.   // Mostra a temperatura  
  178.   LcdWriteDig ((int)(temp/100), 0);  
  179.   LcdWriteDig ((int)((temp/10)%10), 1);  
  180.   LcdWriteDig ((int)(temp%10), 3);  
  181.   
  182.   // Tempo entre atualizações  
  183.   delay(1000);  
  184. }  

Nenhum comentário: