quinta-feira, fevereiro 02, 2012

Display Gráfico Nokia 5110 - Parte 7: PIC

O microcontrolador PIC tradicional (família 16Fxxx) apresenta várias dificuldades para o uso em conjunto com um display gráfico. Não estou me referindo ao hardware, mas à programação. Em primeiro lugar, estes PICs possuem capacidade baixa de memória, impedindo manter nela uma imagem da tela (como fizemos no Arduino na parte anterior).

Mais preocupante é a questão de armazenar na Flash a matriz de caracteres e outras imagens. A matriz que usamos no Arduino tem 96x5 bytes o que é uma quantidade respeitável para os PICs mais simples. Além disso, os modelos mais tradicionais assumem que a Flash conterá apenas código, não tendo instruções específicas para acessar dados armazenados nela. Vamos usar aqui um PIC mais moderno, o 16F882, e deixar por conta do compilador CSC C o trabalho sujo de armazenar e extrair os dados da Flash.

Hardware

O hardware é bastante simples, principalmente se alimentarmos o PIC com 3V:
Montagem com alimentação de 3V
Circuito para alimentação de 3V
 Se alimentarmos o PIC com 5V, são necessárias conversões de nível para conectar o display.:
Montagem com alimentação de 5V
Circuito para alimentação de 5V
 Nos dois casos estamos usando o oscilador interno do 16F882, o que dispensa o uso de um cristal ou ressonador.

Software

O software a seguir é uma adaptação direta do primeiro exemplo que vimos com o Arduino. As principais mudanças se devem às diferenças entre os compiladores e as bibliotecas. Como não temos a função shiftOut, os sinais de dados e clock são pulsados manualmente.
  1. #include <16f882.h>  
  2.   
  3. #device adc=8  
  4. #use delay(clock=8000000)  
  5. #fuses NOWDT, NOCPD, NOPROTECT  
  6. #fuses MCLR, INTRC_IO  
  7.   
  8. #use fast_io(A)  
  9.   
  10. // Conexões do display ao PIC  
  11. #define PIN_SCE   PIN_A0  
  12. #define PIN_RESET PIN_A1  
  13. #define PIN_DC    PIN_A2  
  14. #define PIN_SDIN  PIN_A3  
  15. #define PIN_SCLK  PIN_A4  
  16.   
  17. // Um LED para debug  
  18. #define PIN_LED   PIN_A5  
  19.   
  20. // Seleção de dado ou comando  
  21. #define LCD_CMD   0  
  22. #define LCD_DAT   1  
  23.   
  24. // Tamanho da tela  
  25. #define LCD_DX    84  
  26. #define LCD_DY    48  
  27.   
  28. static const byte ASCII[][5] =  
  29. {  
  30.  {0x00, 0x00, 0x00, 0x00, 0x00} // 20    
  31. ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !  
  32. ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "  
  33. ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #  
  34. ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $  
  35. // etc  
  36. ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ?  
  37. ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f ?  
  38. };  
  39.   
  40. // Rotinas locais  
  41. void InitHw (void);  
  42. void LcdWrite (byte dc, byte data);  
  43. void LcdInitialise (void);  
  44. void LcdClear (void);  
  45. void LcdPos (byte lin, byte col);  
  46. void LcdWriteChar (char character);  
  47. void LcdWriteString(char *characters, byte lin, byte col);  
  48.   
  49. // Ponto de entrada e loop principal  
  50. void main (void)  
  51. {  
  52.  InitHw ();  
  53.  LcdInitialise ();  
  54.  LcdClear ();  
  55.  LcdPos (2, 3);  
  56.  LcdWriteChar ('D');  
  57.  LcdWriteChar ('Q');  
  58.  LcdWriteChar ('S');  
  59.  LcdWriteChar ('o');  
  60.  LcdWriteChar ('f');  
  61.  LcdWriteChar ('t');  
  62.  output_high (PIN_LED);  
  63.  for (;;)  
  64.   ;  
  65. }  
  66.   
  67. // Iniciação do hardware  
  68. void InitHw (void)  
  69. {  
  70.  // Bota para correr  
  71.  setup_oscillator (OSC_8MHZ);  
  72.   
  73.  // Dispositivos não usados  
  74.     setup_comparator(NC_NC);  
  75.     setup_vref(FALSE);  
  76.     setup_adc(ADC_OFF);  
  77.   
  78.  // E/S  
  79.  set_tris_a (0xC0); // RA0-RA5 output  
  80. }  
  81.   
  82. // Envia um byte para o controlador do display  
  83. // dc:   LCD_CMD ou LCD_DAT  
  84. // data: byte a enviar  
  85. void LcdWrite(byte dc, byte data)  
  86. {  
  87.   byte i;  
  88.   
  89.   if (dc == LCD_CMD)  
  90.  output_low (PIN_DC);  
  91.   else  
  92.  output_high (PIN_DC);  
  93.   output_low (PIN_SCE);  
  94.   delay_us(10);  
  95.   for (i = 0; i < 8; i++)  
  96.   {  
  97.  if (data & 0x80)  
  98.    output_high (PIN_SDIN);  
  99.     else  
  100.    output_low (PIN_SDIN);  
  101.     output_high (PIN_SCLK);  
  102.  delay_us(10);  
  103.     output_low (PIN_SCLK);  
  104.  delay_us(10);  
  105.  data = data << 1;  
  106.   }  
  107.   delay_us(10);  
  108.   output_high (PIN_SCE);  
  109. }  
  110.   
  111. // Iniciação do display  
  112. void LcdInitialise(void)  
  113. {  
  114.    output_low  (PIN_SCLK);  
  115.    output_high (PIN_SCE);  
  116.   
  117.   // executa um reset do controlador  
  118.   output_low (PIN_RESET);  
  119.   delay_us (10);  
  120.   output_high (PIN_RESET);  
  121.     
  122.   // envia os comandos de iniciação  
  123.   LcdWrite( LCD_CMD, 0x21 ); // LCD Extended Commands.  
  124.   LcdWrite( LCD_CMD, 0xaf ); // Set LCD Vop (Contraste)  
  125.   LcdWrite( LCD_CMD, 0x04 ); // Set Temp coefficent  
  126.   LcdWrite( LCD_CMD, 0x14 ); // LCD bias mode 1:48  
  127.   LcdWrite( LCD_CMD, 0x20 ); // LCD Basic Commands.  
  128.   LcdWrite( LCD_CMD, 0x0c ); // LCD no modo normal    
  129. }  
  130.   
  131. // Limpa a tela  
  132. void LcdClear(void)  
  133. {  
  134.   int16 i;  
  135.   
  136.   // posiciona ponteiro no inicio da memória  
  137.   LcdWrite( LCD_CMD, 0x40);  
  138.   LcdWrite( LCD_CMD, 0x80);  
  139.     
  140.   // preenche a memória com zeros  
  141.   for (i = 0; i < ((LCD_DX * LCD_DY) / 8); i++)  
  142.   {  
  143.     LcdWrite(LCD_DAT, 0x00);  
  144.   }  
  145. }  
  146.   
  147. // Posiciona em uma determina linha e coluna alfanuméricas  
  148. void LcdPos(byte lin, byte col)  
  149. {  
  150.   LcdWrite( LCD_CMD, 0x40 + lin);    
  151.   LcdWrite( LCD_CMD, 0x80 + col*7);    
  152. }  
  153.   
  154. // Escreve um caracter na posição atual  
  155. void LcdWriteChar(char character)  
  156. {  
  157.   byte i;  
  158.   
  159.   LcdWrite(LCD_DAT, 0x00);  
  160.   for (i = 0; i < 5; i++)  
  161.   {  
  162.     LcdWrite(LCD_DAT, ASCII[character - 0x20][i]);  
  163.   }  
  164.   LcdWrite(LCD_DAT, 0x00);  
  165. }  
  166.   
  167. // Escreve um string a partir de uma certa  
  168. // linha e coluna  
  169. void LcdWriteString(char *characters, byte lin, byte col)  
  170. {  
  171.   LcdPos (lin, col);    
  172.   while (*characters)  
  173.     LcdWriteChar(*characters++);  
  174. }  

Como de costume, o projeto completo está nos arquivos do blog, no arquivo Nokia5110_PIC.zip.

4 comentários:

Unknown disse...

Esses mesmos arquivos e bibliotecas funcionam com o 18F4550?

Daniel Quadros disse...

Nivaldo,

Não tenho experiência com o PIC18, mas acho que serão precisos ajustes, principalmente no InitHw(). Além disso, costuma ter uma diferença grande entre os compiladores C para PIC.

Unknown disse...

boa noite, no pic 16f877a é a mesma configuração?

Daniel Quadros disse...

Renato,

Em uma olhada rápida no datasheet do 16F877 reparei que ele não tem oscilador interno, portanto será necessário acrescentar componentes para gera o clock (por exemplo um cristal de 8MHz). Acho que o resto (inclusive o acesso ao gerador de caracteres na Flash) devem funcionar.