- Proteus: Manual para simulação do Arduino
- Simulação do Arduino no Proteus: Display LCD
- Simulação do Arduino no Proteus: Servo Motor e Displays LCD com o controlador PCD8544
Dando continuidade à sequência apresentada no artigo passado sobre a implementação e simulação de projetos utilizando Arduino UNO e o Proteus/ISIS, apresentamos neste artigo um material exclusivo para ensinar como manipular displays LCD que utilizam o controlador HD44780 (que são os mais básicos e bastante utilizados), bem como realizar simulações com estes elementos. Neste artigo será apresentado como desenvolver um menu completo utilizando o display LCD, além da simulação do circuito.
Objetivo
Este projeto consiste em criar uma aplicação utilizando todo o conhecimento adquirido tanto neste material como no apresentado anteriormente. Aqui será desenvolvido um sistema composto por um display LCD, três leds que serão acionados utilizando PWM (simulando um led RGB), um led com comportamento on/off, quatro botões para a manipulação do menu e dos leds além de um potenciômetro para realizar o ajuste fino do PWM. E você poderá baixar o manual completo no final do artigo.
Hardware – Conectando o Display LCD no Arduino
A seguir são apresentados os componentes utilizados no Proteus, assim como o esquema de ligação dos mesmos.
Componentes necessários
Diagrama esquemático das ligações
Desenvolvimento do código – Controlando o Display LCD com Arduino
No decorrer desta seção serão explicados todos os passos utilizados para o desenvolvimento do projeto, isto é, o código será comentado de modo que todo conhecimento aplicado seja facilmente compreendido.
Primeiramente deve-se incluir a biblioteca LiquidCrystal.h para que seja possível fazer a interação do Arduino UNO com o display utilizado de maneira mais simples.
1 |
#include <LiquidCrystal.h> |
Em seguida utiliza-se a diretiva #define para associarmos os elementos aos seus respectivos pinos. Neste momento os leds vermelho (R), verde (G), azul (B) e amarelo (LED_AM) estão relacionados com os pinos 1, 10, 11 e 13. Os botões BOT1/2/3/4 estão ligados aos pinos 0, 1, 7 e 8, respectivamente, e o potenciômetro está conectado ao pino de entrada analógica A0.
1 2 3 4 5 6 7 8 9 |
#define R 9 #define G 10 #define B 11 #define LED_AM 13 #define BOT1 0 #define BOT2 1 #define BOT3 7 #define BOT4 8 #define POT 0 |
Em seguida é declarada uma grande quantidade de variáveis, sendo assim, estas foram separadas em blocos para facilitar o entendimento sobre a área de atuação das mesmas. A primeira variável é a lcd já discutida neste material. Posteriormente tem-se o primeiro bloco de variáveis, estas são responsáveis pelo funcionamento dos quatro botões existentes no hardware da simulação. Observe que os botões 1 e 2 possuem tratamento em seu acionamento, enquanto os botões 3 e 4 não necessitam deste artifício (se o leitor tive alguma dúvida sobre a implementação do tratamento em botões, favor consultar o item 2.1 do manual disponibilizado na primeira parte).
O segundo bloco possui as variáveis linhaATUAL, linhaANTERIOR e linhaANTERIORaux, responsáveis pelo controle das linhas do menu criado. Já o terceiro bloco é representado pelas variáveis que armazenarão os valores para serem utilizados no acionamento dos leds R, G e B através do PWM e pela variável para realizar o acionamento ON/OFF do led amarelo.
O último bloco é responsável pela interação do potenciômetro com os leds R, G e B. Este possui como função realizar um ajuste fino no valor que será utilizado no PWM para acionar os leds citados.
Além dos blocos comentados anteriormente, existe ainda a variável contador utilizada em estruturas de repetição e os caracteres especiais que farão parte do menu representado no display.
1 |
LiquidCrystal lcd(12, 6, 5, 4, 3, 2); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
bool estadoatualBOT1 = 0; bool estadoantBOT1 = 0; bool leituraBOT1 = 0; long ultDebounceBOT1 = 0; bool estadoatualBOT2 = 0; bool estadoantBOT2 = 0; bool leituraBOT2 = 0; long ultDebounceBOT2 = 0; long tempoDeb = 50; bool leituraBOT3; bool leituraBOT4; |
1 2 3 4 5 |
int linhaATUAL = 1; int linhaANTERIOR = 1; int linhaANTERIORaux = 1; |
1 2 3 4 5 6 7 8 9 10 11 |
int estadoR; int estadoRant; int estadoG; int estadoGant; int estadoB; int estadoBant; bool estadoAM; bool estadoAMant; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int medidaPOT; int ajustefinoR; int ajustefinoRant = 0; int ajustefinoG; int ajustefinoGant = 0; int ajustefinoB; int ajustefinoBant = 0; int contador; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
byte numero0[8] = { //caracter 1 B00000, B00000, B00000, B11111, B00000, B00000, B00000, B00000 }; byte numero1[8] = { //caracter 2 B11000, B11100, B11110, B11111, B11110, B11100, B11000, B11111 }; byte numero2[8] = { //caracter 3 B00000, B00000, B00000, B00000, B00000, B00000, B00000, B00000 }; byte numero3[8] = { //caracter 4 B00100, B00100, B00100, B00100, B11111, B01110, B00100, B00000 }; byte numero4[8] = { //caracter 5 B00100, B01110, B11111, B00100, B00100, B00100, B00100, B00000 }; |
Caracteres especiais gerados com o código acima:
Dentro da função setup() serão definidos os modos de operação dos pinos utilizados, onde os pinos em que estão conectados os 5 leds são definidos como saídas e os pinos em que se encontram os botões são definidos como entradas. Neste momento também são criados os caracteres especiais através da função createChar(), inicializada a interface entre Arduino UNO e display, além da utilização da função cabeçalho, responsável por escrever a parte de cima (duas linhas superiores) do display.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void setup() { pinMode(R,OUTPUT); pinMode(G,OUTPUT); pinMode(B,OUTPUT); pinMode(LED_AM,OUTPUT); pinMode(BOT1,INPUT); pinMode(BOT2,INPUT); pinMode(BOT3,INPUT); pinMode(BOT4,INPUT); lcd.begin(20,4); lcd.createChar(0, numero0); lcd.createChar(1, numero1); lcd.createChar(2, numero2); lcd.createChar(3, numero3); lcd.createChar(4, numero4); cabecalho(); } |
Na função loop() são chamadas quatro funções que proporcionam o funcionamento propriamente dito do programa. As funções verificarBOT1/BOT2/BOT3() são responsáveis por analisar os três botões e tomar as providências necessárias, enquanto a função verificarLINHA() realiza o ajuste do cursor de seleção de acordo com a linha selecionada e organiza as linhas da forma correta para serem representadas no display.
1 2 3 4 5 6 7 8 |
void loop() { verificarBOT1(); verificarBOT2(); verificarBOT3(); verificarLINHA(); } |
Como as funções mostradas a seguir são grandes e complexas, será explicado neste momento apenas as partes importantes e necessárias para o entendimento do funcionamento do programa.
No começo do programa, a primeira função que deve ser analisada é a função verificarBOT1(). Primeiramente nesta função verifica-se o estado do botão 1, caso este tenha sido pressionado a variável linhaANTERIOR recebe o valor da variável linhaATUAL e esta última é incrementada. O mesmo acontece com a função verificarBOT2(), porém a variável linhaATUAL é decrementada neste caso.
Supondo que por enquanto não ocorra um acionamento do botão 3, o programa passará para a função verificarLINHA(). Nesta função, o programa escreve as duas linhas que aparecerão por vez no display, juntamente com o cursor de seleção destas (por isso é importante levar em conta as duas variáveis, linhaANTERIOR e linhaATUAL, para o posicionamento correto do cursor). Repare que dentro das funções verificarBOT1/2() existem condições que limitam o valor destas variáveis no intervalo de 1 a 4.
As linhas fornecem as possibilidades de atuar sobre os seguintes elementos:
- Linha 1 → R
- Linha 2 → G
- Linha 3 → B
- Linha 4 → LED_AM
Supondo agora que o botão 1 foi apertado uma vez (linhaATUAL = 2 e linhaANTERIOR = 1). Quando o botão 3 for pressionado, serão verificadas várias estruturas condicionais if() e uma delas detectará que a linha atual é a número 2, sendo assim, o sistema entrará em um outro patamar onde é possível realizar o acionamento do led G utilizando PWM.
Neste momento o display passa a sinalizar que a componente verde do led RGB foi escolhida para ser alterada. Para isto existe a esturura do/while(), que realizará primeiramente uma sentença e enquanto uma determinada condição não é atingida, esta sentença é repetida indefinidamente. A sentença em questão consiste em verificar o potenciômetro e adicionar até 25 unidades na variável estadoG (responsável por conter o valor que será utilizado para acionar o led). Observe que dentro desta mesma sentença, as funções verificarBOT1/BOT2() são chamadas novamente.
Fazendo uma nova observação na função verificarBOT1() pode-se ver que existe uma estrutura condicional if() que verifica a hipótese de que o botão 3 tenha sido apertado, e posteriormente encontra-se uma estrutura switch/case onde é determinada em qual linha o programa estava quando o botão 3 foi pressionado. Como a variável linhaATUAL possui o valor 2, logo, ao ser pressionar o botão 1, a variável estadoG será decrementada em 25 unidades. O mesmo acontece com o botão 2, porém a diferença é que a variável estadoG será incrementada do mesmo valor. Este ciclo vai se repetindo até que o botão 4 seja pressionado (condição de parada da estrutura do/while()), onde o display volta a apresentar a tela onde estava o sistema no momento em que o botão 3 foi pressionado.
A única exceção acontece quando linhaATUAL = 4, pois neste caso temos o acionamento on/off do led amarelo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void linha1(){ lcd.print("R"); } void linha2(){ lcd.print("G"); } void linha3(){ lcd.print("B"); } void linha4(){ lcd.print("LED_AM"); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
void verificarBOT1(){ leituraBOT1 = digitalRead(BOT1); if (leituraBOT1 != estadoantBOT1) { ultDebounceBOT1 = millis(); } if ((millis() - ultDebounceBOT1) > tempoDeb) { if (leituraBOT1 != estadoatualBOT1) { estadoatualBOT1 = leituraBOT1; if (estadoatualBOT1 == 1) { linhaANTERIORaux = linhaANTERIOR; linhaANTERIOR = linhaATUAL; linhaATUAL = linhaATUAL + 1; if(linhaATUAL > 4){ linhaATUAL = 4; } if(leituraBOT3 == 1){ switch(linhaANTERIOR){ case 1: estadoR = estadoR - 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 2: estadoG = estadoG - 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 3: estadoB = estadoB - 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 4: estadoAM = 1; digitalWrite(LED_AM,HIGH); linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; } } } } } estadoantBOT1 = leituraBOT1; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
void verificarBOT2(){ leituraBOT2 = digitalRead(BOT2); if (leituraBOT2 != estadoantBOT2) { ultDebounceBOT2 = millis(); } if ((millis() - ultDebounceBOT2) > tempoDeb) { if (leituraBOT2 != estadoatualBOT2) { estadoatualBOT2 = leituraBOT2; if (estadoatualBOT2 == 1) { linhaANTERIORaux = linhaANTERIOR; linhaANTERIOR = linhaATUAL; linhaATUAL = linhaATUAL - 1; if(linhaATUAL < 1){ linhaATUAL = 1; } if(leituraBOT3 == 1){ switch (linhaANTERIOR){ case 1: estadoR = estadoR + 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 2: estadoG = estadoG + 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 3: estadoB = estadoB + 25; linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; case 4: estadoAM = 0; digitalWrite(LED_AM,LOW); linhaATUAL = linhaANTERIOR; linhaANTERIOR = linhaANTERIORaux; break; } } } } } estadoantBOT2 = leituraBOT2; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
void verificarBOT3(){ leituraBOT3 = digitalRead(BOT3); if (leituraBOT3 == 1) { if(linhaATUAL == 1){ lcd.setCursor(0,2); lcd.print("COMP VERMELHA"); //display 1 lcd.setCursor(0,3); lcd.print("PWM:"); lcd.print(estadoR); do{ verificarPOT(); ajustefinoR = medidaPOT; if(ajustefinoR != ajustefinoRant){ estadoR = estadoR - ajustefinoRant + ajustefinoR; if(estadoR >= 255){ estadoR = 255; } if(estadoR <= 0){ estadoR = 0; } analogWrite(R,(estadoR - ajustefinoRant + ajustefinoR)); ajustefinoRant = ajustefinoR; } if(estadoR != estadoRant){ for( contador = 4; contador <7; contador++){ lcd.setCursor(contador,3); lcd.write(2); } if(estadoR >= 255){ estadoR = 255; } if(estadoR <= 0){ estadoR = 0; } lcd.setCursor(4,3); lcd.print(estadoR); analogWrite(R,estadoR); estadoRant = estadoR; } verificarBOT1(); verificarBOT2(); verificarBOT4(); } while(leituraBOT4 == 0); lcd.clear(); cabecalho(); } if(linhaATUAL == 2){ lcd.setCursor(0,2); lcd.print("COMP VERDE"); //display 2 lcd.setCursor(0,3); lcd.print("PWM:"); lcd.print(estadoG); do{ verificarPOT(); ajustefinoG = medidaPOT; if(ajustefinoG != ajustefinoGant){ estadoG = estadoG - ajustefinoGant + ajustefinoG; if(estadoG >= 255){ estadoG = 255; } if(estadoG <= 0){ estadoG = 0; } analogWrite(G,(estadoG - ajustefinoGant + ajustefinoG)); ajustefinoGant = ajustefinoG; } if(estadoG != estadoGant){ for( contador = 4; contador <7; contador++){ lcd.setCursor(contador,3); lcd.write(2); } if(estadoG >= 255){ estadoG = 255; } if(estadoG <= 0){ estadoG = 0; } lcd.setCursor(4,3); lcd.print(estadoG); analogWrite(G,estadoG); estadoGant = estadoG; } verificarBOT1(); verificarBOT2(); verificarBOT4(); } while(leituraBOT4 == 0); lcd.clear(); cabecalho(); } if(linhaATUAL == 3){ lcd.setCursor(0,2); lcd.print("COMP AZUL"); //display 3 lcd.setCursor(0,3); lcd.print("PWM:"); for(contador = 4; contador < 7; contador++){ lcd.setCursor(contador,3); lcd.write(2); } lcd.setCursor(4,3); lcd.print(estadoB); do{ verificarPOT(); ajustefinoB = medidaPOT; if(ajustefinoB != ajustefinoBant){ estadoB = estadoB - ajustefinoBant + ajustefinoB; if(estadoB >= 255){ estadoB = 255; } if(estadoB <= 0){ estadoB = 0; } analogWrite(B,(estadoB - ajustefinoBant + ajustefinoB)); ajustefinoBant = ajustefinoB; } if(estadoB != estadoBant){ for( contador = 4; contador <7; contador++){ lcd.setCursor(contador,3); lcd.write(2); } if(estadoB >= 255){ estadoB = 255; } if(estadoB <= 0){ estadoB = 0; } lcd.setCursor(4,3); lcd.print(estadoB); analogWrite(B,estadoB); estadoBant = estadoB; } verificarBOT1(); verificarBOT2(); verificarBOT4(); } while(leituraBOT4 == 0); lcd.clear(); cabecalho(); } if(linhaATUAL == 4){ lcd.setCursor(0,2); lcd.print("LED AMARELO"); //display 4 lcd.setCursor(0,3); if(estadoAM == 0){ lcd.print("DESLIGADO"); } else{ lcd.print("LIGADO "); } do{ if(estadoAM != estadoAMant){ lcd.setCursor(0,3); if(estadoAM == 0){ lcd.print("DESLIGADO"); } else{ lcd.print("LIGADO "); } estadoAMant = estadoAM; } verificarBOT1(); verificarBOT2(); verificarBOT4(); } while(leituraBOT4 == 0); lcd.clear(); cabecalho(); } } } |
1 2 3 4 5 |
void verificarBOT4(){ leituraBOT4 = digitalRead(BOT4); } . |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
void verificarLINHA(){ if(linhaATUAL == 1){ lcd.setCursor(0,2); lcd.write(1); lcd.setCursor(0,3); // linha 1 lcd.write(2); lcd.setCursor(1,2); linha1(); lcd.setCursor(1,3); linha2(); lcd.setCursor(19,2); lcd.write(2); lcd.setCursor(19,3); lcd.write(3); } if(linhaATUAL == 2){ if(linhaANTERIOR == 1){ lcd.setCursor(0,3); lcd.write(1); lcd.setCursor(0,2); lcd.write(2); lcd.setCursor(1,2); //linha 2 linha1(); lcd.setCursor(1,3); linha2(); lcd.setCursor(19,2); lcd.write(4); lcd.setCursor(19,3); lcd.write(3); } if(linhaANTERIOR == 3){ lcd.setCursor(0,2); lcd.write(1); lcd.setCursor(0,3); lcd.write(2); lcd.setCursor(1,2); linha2(); lcd.setCursor(1,3); linha3(); for (contador = 0; contador <5; contador++){ lcd.write(2); } lcd.setCursor(19,2); lcd.write(4); lcd.setCursor(19,3); lcd.write(3); } } if(linhaATUAL == 3){ if(linhaANTERIOR == 2){ lcd.setCursor(0,3); lcd.write(1); lcd.setCursor(0,2); lcd.write(2); lcd.setCursor(1,2); linha2(); lcd.setCursor(1,3); //linha 3 linha3(); lcd.setCursor(19,2); lcd.write(4); lcd.setCursor(19,3); lcd.write(3); } if(linhaANTERIOR == 4){ lcd.setCursor(0,2); lcd.write(1); lcd.setCursor(0,3); lcd.write(2); lcd.setCursor(1,2); linha3(); lcd.setCursor(1,3); linha4(); lcd.setCursor(19,2); lcd.write(4); lcd.setCursor(19,3); lcd.write(3); } } if(linhaATUAL == 4){ lcd.setCursor(0,3); lcd.write(1); lcd.setCursor(0,2); lcd.write(2); lcd.setCursor(1,2); linha3(); lcd.setCursor(1,3); linha4(); //linha 4 lcd.setCursor(19,2); lcd.write(4); lcd.setCursor(19,3); lcd.write(2); } } |
A seguir são apresentadas as telas conforme código acima:
1 2 3 4 5 6 7 8 9 10 11 12 |
void cabecalho(){ lcd.setCursor(6,0); lcd.print("ITEM 1.7"); lcd.setCursor(0,1); for( contador = 0; contador < 20; contador++){ lcd.write((byte)0); } } |
Além deste exemplo de aplicação, estão presentes no manual completo os seguintes tópicos:
- IMPRESSÃO DE CARACTERES NO DISPLAY;
- DESLOCAMENTO LATERAL DE CARACTERES;
- IMPLEMENTAÇÃO DE UM DISPLAY INFORMATIVO;
- IMPLEMENTAÇÃO DE UM SEMÁFORO INFORMATIVO;
- DADO DE 6 FACES NO DISPLAY LCD;
- BARRA DE PROGRESSO NO DISPLAY LCD.
Conclusão
De acordo com o exemplo dado anteriormente, pode-se perceber a grande importância dos displays LCD em proporcionar a interação do usuário com os sistemas de diversas maneiras, seja através de um modo mais informativo ou até mesmo de um maneira que estes possibilitem o controle do próprio sistema, utilizando menus de seleção como as principais estruturas de interação. Além disso conclui-se que a simulação deste componente no Proteus/ISIS funciona de maneira praticamente idêntica ao mesmo quando utilizado fisicamente, servindo portanto como uma forma bastante satisfatória tanto para o aprendizado da manipulação dos displays quanto para testes.
Convidamos o leitor a baixar este manual através do link disponibilizado abaixo. Esperamos que este documento possa contribuir com o seu conhecimento e tenha bons estudos! Pedimos encarecidamente que nos envie dúvidas, sugestões, críticas e caso tenha gostado deste.
Acredito que esteja faltando a função verificarPOT(). Ou pelo menos não encontrei 😉
Depois só pus a função deste modo…
void verificarPOT(){
medidaPOT = analogRead(A0);
}
Fora isso, excelente publicação!