Raspberry Pi PICO LCD (não I2C)

Tempo de leitura: 9 minutes

Se você comprou o livro (Get started with MicroPython on Raspberry Pi Pico), notará que realmente não há muita informação sobre como dirigir LCDs. Na verdade, o livro se concentra apenas no LCD sparkfun I2C. Portanto, neste instrutivo, gostaria de ver como conectar e dirigir um LCD padrão usando o Pico.

Suprimentos

  • Raspberry Pi Pico executando MicroPython.
  • LCD padrão por exemplo 20*4 ou 16*2
  • Fio para conectar.
  • ProtoBoard

 

Binários, uns e zeros.

Existem apenas 10 tipos de pessoas no mundo – aquelas que entendem de binário e aquelas que não entendem.

Então, vamos dar uma olhada no LCD. A maioria dos LCDs simples são controlados por um controlador Hitachi HD44780 LCD e possuem as seguintes conexões.

  1. Terra
  2. VCC +3,3 a + 5V (típico)
  3. Ajuste de contraste (VO) Esta é uma entrada analógica.
  4. Register Select (RS). RS = 0: Comando, RS = 1: Dados
  5. Leitura/Escrita (R/W). R/W = 0: Gravação, R/W = 1: Leitura, Não usado conectado ao aterramento.
  6. Relógio (habilitar). Borda de queda acionada
  7. Bit 0 (não usado em operação de 4 bits)
  8. Bit 1 (não usado em operação de 4 bits)
  9. Bit 2 (não usado em operação de 4 bits)
  10. Bit 3 (não usado em operação de 4 bits)
  11. Bit 4
  12. Bit 5
  13. Bit 6
  14. Bit 7
  15. Ânodo de luz de fundo (+) (se aplicável)
  16. Cátodo de retroiluminação (-) (se aplicável)

Isso pode parecer um monte de conexões, mas você precisa de apenas 4 bits de dados e os pinos E e RS. Não esquecendo o poder.

 

Vamos começar em um Arduino.

Portanto, pode parecer um passo estranho, mas o IDE do Arduino é tão simples de usar e as bibliotecas tornam a programação muito fácil.

#include <Time.h>
#include 

const int rs = 3, en = 2, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup()
{
  lcd.begin(20, 4);
  lcd.print("Hello");
}

void loop() {
}

No entanto, a biblioteca, neste caso, não me ajuda a entender o que está acontecendo em segundo plano, então decidi escrever meu próprio programa. (uma vez que eu provei que o LCD funcionava) A página wiki foi realmente útil para esta parte. Ele explica que o LCD precisa ser inicializado, pois pode estar em um dos três estados na inicialização e, neste ponto, vale dizer que o Register Select (RS) é o pino que é usado para informar ao LCD se os dados são um comando ou personagem.

Portanto, para começar, você precisa limpar o pino RS (definido como zero). Em seguida, você liga / desliga as 4 linhas de dados para o código necessário e, em seguida, clica os dados usando o pino de ativação. Os dados que você precisa enviar são os seguintes.

  • 0b0011 (D7-D4) Conjunto de funções com interface de 8 bits.
  • 0b0011 (D7-D4) Conjunto de funções com interface de 8 bits.
  • 0b0011 (D7-D4) Conjunto de funções com interface de 8 bits.
  • 0b0010 (D7-D4) Conjunto de funções com interface de 4 bits.

Depois de enviar esses 4 comandos, você pode enviar números binários de 8 bits que precisam ser enviados em dois nibbles de 4 bits. Certos atrasos devem ser mantidos de acordo com a página wiki, mas isso não é difícil de conseguir. Eu escrevi uma função para pulsar o pino de habilitação e uma função para enviar 4 bits para o LCD e depois outra para enviar 8 bits para o LCD (em dois nibbles)

#include <Time.h>

const int e = 2, rs = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
int BinNum;

void setup()
{
  pinMode(e, OUTPUT);
  pinMode(rs, OUTPUT);
  pinMode(d4, OUTPUT);
  pinMode(d5, OUTPUT);
  pinMode(d6, OUTPUT);
  pinMode(d7, OUTPUT);
  setUpLCD();
  digitalWrite(rs, HIGH);
  delay(5);
  send2LCD8(0b01001000);//H
  send2LCD8(0b01100101);//e
  send2LCD8(0b01101100);//l
  send2LCD8(0b01101100);//l
  send2LCD8(0b01101111);//o
}

void loop(){
}

void setUpLCD()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD4(0b0011);//send 8bit operation command
  send2LCD4(0b0011);//send 8bit operation command
  send2LCD4(0b0011);//send 8bit operation command
  send2LCD4(0b0010);//send 4bit operation command
  send2LCD8(0b00101000);//Functin set, 8 bit
  send2LCD8(0b00001100);//Display ON, Cursor OFF, Cursor NOT Blinking
  send2LCD8(0b00000110);//Entry mode, incremental,
  send2LCD8(0b00000001);//clear screen
}
void pulseE()
{
  digitalWrite(e, HIGH);
  delay(2);
  digitalWrite(e, LOW);
  delay(5);
}
void send2LCD8(int BinNum)
{
  digitalWrite(d4, (BinNum & 0b00010000) >> 4);
  digitalWrite(d5, (BinNum & 0b00100000) >> 5);
  digitalWrite(d6, (BinNum & 0b01000000) >> 6);
  digitalWrite(d7, (BinNum & 0b10000000) >> 7);
  pulseE();
  digitalWrite(d4, (BinNum & 0b00000001) >> 0);
  digitalWrite(d5, (BinNum & 0b00000010) >> 1);
  digitalWrite(d6, (BinNum & 0b00000100) >> 2);
  digitalWrite(d7, (BinNum & 0b00001000) >> 3);
  pulseE();
}
void send2LCD4(int BinNum)
{
  digitalWrite(d4, (BinNum & 0b00000001) >> 0);
  digitalWrite(d5, (BinNum & 0b00000010) >> 1);
  digitalWrite(d6, (BinNum & 0b00000100) >> 2);
  digitalWrite(d7, (BinNum & 0b00001000) >> 3);
  pulseE(); 
}

 

Programa Arduino com todos os bits!

Portanto, antes de mudar para o Pico, adicionei mais alguns bits ao programa Arduino. Se você olhar a página do Wiki, verá que existem alguns comandos, então adicionei mais algumas funções para testá-los. Isso incluiu limpar a tela, retornar para casa e mover o cursor para a esquerda ou para a direita. E, por último, criei uma função com que permitisse definir a linha e a posição em que você queria que o texto começasse.

void whichLinePos(int number, int pos)
{
  int b = 0;
  if (number == 1){
    b = 0;
  }
  if (number == 2){
    b = 40;
  }
  if (number == 3){
    b = 20;
  }
  if (number == 4){
    b = 60;
  }
  returnHome();
  for (int x = 0; x < b+(pos-1); x++)
  {
    moveCursorR();
  }
}

void returnHome()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD8(0b00000010);
  digitalWrite(rs, HIGH);
  delay(5);
}
void clearScreen()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD8(0b00000001);
  digitalWrite(rs, HIGH);
  delay(5);
}
void cursorOff()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD8(0b00001100);
  digitalWrite(rs, HIGH);
  delay(5);
}
void moveCursorR()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD8(0b00010100);
  digitalWrite(rs, HIGH);
  delay(5);
}
void moveCursorL()
{
  digitalWrite(rs, LOW);
  delay(5);
  send2LCD8(0b00010000);
  digitalWrite(rs, HIGH);
  delay(5);
}

 

MicroPython no Pico

Agora que eu tinha um bom entendimento dos comandos do LCD, era hora de traduzir o programa para MicroPython. Felizmente, existem muitas semelhanças, então não foi tão difícil de fazer e muito rapidamente eu escrevi o programa inteiro. MAS funcionou? Inicialmente nada aconteceu e eu pensei que isso fosse devido aos níveis lógicos de 3,3 voltagem vindos do Pico, então eu rapidamente adicionei conversores de nível lógico em todos os pinos, o LCD tinha que ser alimentado por 5 Volts. No entanto, isso não funcionou e o LCD ainda não fez nada.

Depois de um café rápido, dei uma olhada no código novamente e percebi que havia cometido um erro! quando eu envio o número binário para a função send2LCD4, ele usa um “&” para isolar o bit necessário, em seguida, o arquivo gira o bit necessário para colocá-lo no LSB e então define o pino necessário alto ou baixo, dependendo do valor. Eu tinha usado a palavra “e” (a afirmação um e as duas afirmações corretas) e não o “&” (bit a bit e). De qualquer forma, uma vez corrigido, o LCD ganhou vida!

Eu então me perguntei se eu precisava dos conversores de nível lógico. então eu os removi e tentei novamente e sim o LCD ainda funciona em níveis lógicos de 3,3V. (o LCD ainda precisava de 5 volts de energia)

De qualquer forma, o programa abaixo é o simples Hello World! Programa LCD.

import machine
import utime

rs = machine.Pin(0,machine.Pin.OUT)
e = machine.Pin(1,machine.Pin.OUT)
d4 = machine.Pin(2,machine.Pin.OUT)
d5 = machine.Pin(3,machine.Pin.OUT)
d6 = machine.Pin(4,machine.Pin.OUT)
d7 = machine.Pin(5,machine.Pin.OUT)

def pulseE():
    e.value(1)
    utime.sleep_us(40)
    e.value(0)
    utime.sleep_us(40)
def send2LCD4(BinNum):
    d4.value((BinNum & 0b00000001) >>0)
    d5.value((BinNum & 0b00000010) >>1)
    d6.value((BinNum & 0b00000100) >>2)
    d7.value((BinNum & 0b00001000) >>3)
    pulseE()
def send2LCD8(BinNum):
    d4.value((BinNum & 0b00010000) >>4)
    d5.value((BinNum & 0b00100000) >>5)
    d6.value((BinNum & 0b01000000) >>6)
    d7.value((BinNum & 0b10000000) >>7)
    pulseE()
    d4.value((BinNum & 0b00000001) >>0)
    d5.value((BinNum & 0b00000010) >>1)
    d6.value((BinNum & 0b00000100) >>2)
    d7.value((BinNum & 0b00001000) >>3)
    pulseE()
def setUpLCD():
    rs.value(0)
    send2LCD4(0b0011)#8 bit
    send2LCD4(0b0011)#8 bit
    send2LCD4(0b0011)#8 bit
    send2LCD4(0b0010)#4 bit
    send2LCD8(0b00101000)#4 bit,2 lines?,5*8 bots
    send2LCD8(0b00001100)#lcd on, blink off, cursor off.
    send2LCD8(0b00000110)#increment cursor, no display shift
    send2LCD8(0b00000001)#clear screen
    utime.sleep_ms(2)#clear screen needs a long delay

setUpLCD()
rs.value(1)
for x in 'Hello World!':
    send2LCD8(ord(x))

Programa completo com funções

O programa completo está abaixo e dá uma boa indicação do que é possível. No entanto, você precisa ter cuidado e alguns comandos afetarão outras opções. Por exemplo

O controle liga/desliga da tela também tem a configuração para ligar / desligar o cursor e o caractere de posição do cursor intermitente.

Reduzi a velocidade da exibição para que você possa ver o texto escrito na tela.

  1. Cursor de piscar desligado, texto escrito da esquerda para a direita (normal).
  2. Cursor de piscar desligado, texto escrito da direita para a esquerda (para trás).
  3. Erro corrigido com cursor e piscar.
  4. Cursor de piscar desligado, texto escrito da esquerda para a direita (normal).
  5. Cursor de piscar desligado, texto escrito da esquerda para a direita (normal).
  6. O texto da tela se move um caractere para a direita.
  7. O texto da tela se move um caractere para a esquerda.
  8. Tela em branco.
  9. A tela retorna.
  10. Limpe a tela e comece novamente.
import machine
import utime

rs = machine.Pin(0,machine.Pin.OUT)
e = machine.Pin(1,machine.Pin.OUT)
d4 = machine.Pin(2,machine.Pin.OUT)
d5 = machine.Pin(3,machine.Pin.OUT)
d6 = machine.Pin(4,machine.Pin.OUT)
d7 = machine.Pin(5,machine.Pin.OUT)

def pulseE():
    e.value(1)
    delayShort()
    e.value(0)
    delayShort()
    
def delayShort():
    utime.sleep_us(40)
    
def delay():
    utime.sleep_ms(2)
    
def delayBig():
    utime.sleep(0.3)

def send2LCD4(BinNum):
    d4.value((BinNum & 0b00000001) >>0)
    d5.value((BinNum & 0b00000010) >>1)
    d6.value((BinNum & 0b00000100) >>2)
    d7.value((BinNum & 0b00001000) >>3)
    pulseE()

def send2LCD8(BinNum):
    d4.value((BinNum & 0b00010000) >>4)
    d5.value((BinNum & 0b00100000) >>5)
    d6.value((BinNum & 0b01000000) >>6)
    d7.value((BinNum & 0b10000000) >>7)
    pulseE()
    d4.value((BinNum & 0b00000001) >>0)
    d5.value((BinNum & 0b00000010) >>1)
    d6.value((BinNum & 0b00000100) >>2)
    d7.value((BinNum & 0b00001000) >>3)
    pulseE()
    
def whichLinePos(line, pos):
    b = 0
    if (line == 1):
        b = 0
    if (line == 2):
        b = 40
    if (line == 3):
        b = 20
    if (line == 4):
        b = 60
    cursorHome()
    for x in range(0,b+pos):
        moveCursorR()

def clearDisplay():#blanks the LCD, needs a long delay.
    rs.value(0)
    send2LCD8(0b00000001)
    rs.value(1)
    delay()        
def cursorHome():#returns the cursor to home, needs a long delay.
    rs.value(0)
    send2LCD8(0b00000010)
    rs.value(1)
    delay()
def cursorMoveForward():
    rs.value(0)
    send2LCD8(0b00000110)
    rs.value(1)
def cursorMoveBack():
    rs.value(0)
    send2LCD8(0b00000100)
    rs.value(1)
def moveCursorR():#write text from left to right
    rs.value(0)
    send2LCD8(0b00010100)
    rs.value(1)
def moveCursorL():#write text from right to left (backwards)
    rs.value(0)
    send2LCD8(0b00010000)
    rs.value(1)
def cursorOff():
    rs.value(0)
    send2LCD8(0b00001100)
    rs.value(1)
def cursorOn():
    rs.value(0)
    send2LCD8(0b00001110)
    rs.value(1)
def blinkOn():
    rs.value(0)
    send2LCD8(0b00001111)
    rs.value(1)
def blinkOff():
    rs.value(0)
    send2LCD8(0b00001100)
    rs.value(1)
def displayShiftR():#move all caractors one space right
    rs.value(0)
    send2LCD8(0b00011100)
    rs.value(1)
def displayShiftL():#move all caractors one space left
    rs.value(0)
    send2LCD8(0b00011000)
    rs.value(1)
def displayOff():
    rs.value(0)
    send2LCD8(0b00001000)
    rs.value(1)
def displayOn():
    rs.value(0)
    send2LCD8(0b00001100)
    rs.value(1)
    
def setUpLCD():
    rs.value(0)
    send2LCD4(0b0011)
    send2LCD4(0b0011)
    send2LCD4(0b0011)
    send2LCD4(0b0010)
    send2LCD8(0b00101000)
    send2LCD8(0b00001100)
    send2LCD8(0b00000110)
    send2LCD8(0b00000001)
    rs.value(1)

setUpLCD()
while True:
    whichLinePos(1,3)
    for x in 'Instructables':
        send2LCD8(ord(x))
        delayBig()
    whichLinePos(2,18)
    moveCursorL()
    cursorMoveBack()
    for x in 'a no LCD a gnisU':
        send2LCD8(ord(x))
        delayBig()
    whichLinePos(3,1)
    moveCursorR()
    cursorMoveForward()
    whichLinePos(2,9)
    cursorOn()
    blinkOn()
    for x in ' LCD':
        send2LCD8(ord(x))
        delayBig()
        delayBig()
    blinkOff()
    
    whichLinePos(3,1)
    for x in 'Raspberry Pi PICO!':
        send2LCD8(ord(x))
        delayBig()
    whichLinePos(4,5)
    for x in '(NOT I2C!)':
        send2LCD8(ord(x))
    utime.sleep(2)
    displayShiftR()
    utime.sleep(2)
    displayShiftL()
    utime.sleep(2)
    displayOff()
    utime.sleep(2)
    displayOn()
    utime.sleep(2)
    clearDisplay()

Outro Exemplo com  uma lib

from machine import Pin
from time import sleep
import utime

# LCD Code
class LCD:
    """
     RPI PICO   LCD Pins on most Hitachi HD44780 controlled LCDs
        GND     1 - VSS Ground
       VBUS     2 - VDD / VCC +3.3 to +5V (typical)
         -      3 - VO - Contrast adjust - Use 10K Pot OR 2K ohm resistor to GND
        GP0     4 - RS - Register Select. RS=0: Command, RS=1: Data
        GND     5 - Read/Write (R/W). R/W=0: Write, R/W=1: Read, Not used connected to ground.
        GP1     6 - Clock (Enable). Falling edge triggered
         -      7 - Bit 0 (Not used in 4-bit operation)
         -      8 - Bit 1 (Not used in 4-bit operation)
         -      9 - Bit 2 (Not used in 4-bit operation)
         -      10 - Bit 3 (Not used in 4-bit operation)
        GP2     11 - Bit 4
        GP3     12 - Bit 5
        GP4     13 - Bit 6
        GP5     14 - Bit 7
       VBUS     15 - A Backlight Anode (+) (If applicable) 
        GND     16 - K Backlight Cathode (-) (If applicable)
    """

    def __init__(self):
        self.rs = Pin(0,Pin.OUT)
        self.e = Pin(1,Pin.OUT)
        self.d4 = Pin(2,Pin.OUT)
        self.d5 = Pin(3,Pin.OUT)
        self.d6 = Pin(4,Pin.OUT)
        self.d7 = Pin(5,Pin.OUT)


    def scrollText(self, text, line=1, breakChars=" | ", displayWidth=16):
        xindex = 0
        st = text + breakChars + text + breakChars
        scrollSlice = text
        while True:
            if len(text) > displayWidth:
                scrollSlice = st[xindex:displayWidth+xindex]
                xindex += 1
                if xindex > len(st) - displayWidth:
                    xindex = 0
            self.whichLinePos(line, 0)
            self.prt(scrollSlice)
            
            yield None
            
    def prt(self, text):
        for x in text:
            self.send2LCD8(ord(x))

    def delayShort(self):
        utime.sleep_us(40)
        
    def delay(self):
        utime.sleep_ms(2)
        
    def delayBig(self):
        utime.sleep(0.3)
    
    def pulseE(self):
        self.e.value(1)
        self.delayShort()
        self.e.value(0)
        self.delayShort()
                
    def send2LCD4(self, BinNum):
        self.d4.value((BinNum & 0b00000001) >>0)
        self.d5.value((BinNum & 0b00000010) >>1)
        self.d6.value((BinNum & 0b00000100) >>2)
        self.d7.value((BinNum & 0b00001000) >>3)
        self.pulseE()
        
    def send2LCD8(self, BinNum):
        self.d4.value((BinNum & 0b00010000) >>4)
        self.d5.value((BinNum & 0b00100000) >>5)
        self.d6.value((BinNum & 0b01000000) >>6)
        self.d7.value((BinNum & 0b10000000) >>7)
        self.pulseE()
        self.d4.value((BinNum & 0b00000001) >>0)
        self.d5.value((BinNum & 0b00000010) >>1)
        self.d6.value((BinNum & 0b00000100) >>2)
        self.d7.value((BinNum & 0b00001000) >>3)
        self.pulseE()

    def whichLinePos(self, line, pos):
        b = 0
        if (line == 1):
            b = 0
        if (line == 2):
            b = 40
        if (line == 3):
            b = 20
        if (line == 4):
            b = 60
        self.cursorHome()
        for x in range(0,b+pos):
            self.moveCursorR()

    def clearDisplay(self):#blanks the LCD, needs a long delay.
        self.rs.value(0)
        self.send2LCD8(0b00000001)
        self.rs.value(1)
        self.delay()        
    def cursorHome(self):#returns the cursor to home, needs a long delay.
        self.rs.value(0)
        self.send2LCD8(0b00000010)
        self.rs.value(1)
        self.delay()
    def cursorMoveForward(self):
        self.rs.value(0)
        self.send2LCD8(0b00000110)
        self.rs.value(1)
    def cursorMoveBack(self):
        self.rs.value(0)
        self.send2LCD8(0b00000100)
        self.rs.value(1)
    def moveCursorR(self):#write text from left to right
        self.rs.value(0)
        self.send2LCD8(0b00010100)
        self.rs.value(1)
    def moveCursorL(self):#write text from right to left (backwards)
        self.rs.value(0)
        self.send2LCD8(0b00010000)
        self.rs.value(1)
    def cursorOff(self):
        self.rs.value(0)
        self.send2LCD8(0b00001100)
        self.rs.value(1)
    def cursorOn(self):
        self.rs.value(0)
        self.send2LCD8(0b00001110)
        self.rs.value(1)
    def blinkOn(self):
        self.rs.value(0)
        self.send2LCD8(0b00001111)
        self.rs.value(1)
    def blinkOff(self):
        self.rs.value(0)
        self.send2LCD8(0b00001100)
        self.rs.value(1)
    def displayShiftR(self):#move all caractors one space right
        self.rs.value(0)
        self.send2LCD8(0b00011100)
        self.rs.value(1)
    def displayShiftL(self):#move all caractors one space left
        self.rs.value(0)
        self.send2LCD8(0b00011000)
        self.rs.value(1)
    def displayOff(self):
        self.rs.value(0)
        self.send2LCD8(0b00001000)
        self.rs.value(1)
    def displayOn(self):
        self.rs.value(0)
        self.send2LCD8(0b00001100)
        self.rs.value(1)
        
    def setUpLCD(self):
        self.rs.value(0)
        self.send2LCD4(0b0011)
        self.send2LCD4(0b0011)
        self.send2LCD4(0b0011)
        self.send2LCD4(0b0010)
        self.send2LCD8(0b00101000)
        self.send2LCD8(0b00001100)
        self.send2LCD8(0b00000110)
        self.send2LCD8(0b00000001)
        self.rs.value(1)

if __name__ == "__main__":

    l = LCD()
    l.setUpLCD()
    l.clearDisplay()

    line1 = l.scrollText("Hello There", line=1, breakChars="", displayWidth=16)
    line2 = l.scrollText("Do you like my scrolling sign?", line=2, breakChars=" | ", displayWidth=16)

    while True:
        sleep(0.2)
        next(line1)
        next(line2)

    # This code never actually gets here but to clean up the 2 generators
    # Call the following lines:
    line1.close()
    line2.close()

 

Visits: 1 Visits: 1126495