Interface de tela LCD 16×2 com Raspberry Pi Pico

Tempo de leitura: 9 minutes

Visão geral

Neste tutorial, aprenderemos como fazer a interface do display LCD 16 × 2 com o Raspberry Pi Pico. Um LCD é um módulo de display eletrônico que usa tecnologia de cristal líquido para produzir uma imagem visível. O display LCD 16 × 2 é um módulo muito básico, comumente usado em aplicações de circuitos eletrônicos. O 16×2 traduz 16 caracteres por linha em 2 dessas linhas. Neste LCD, cada caractere é exibido em uma matriz de 5×8 pixels.

Raspberry Pi Pico, que foi lançado há alguns meses, requer algum display LCD em aplicações de engenharia. Aqui, veremos os detalhes e recursos do visor LCD. O display LCD é baseado no driver HD44780. Existem versões I2C e versões não I2C do display LCD. Tomaremos esses dois LCDs como exemplo e como interface com o Raspberry Pi Pico. Vamos escrever o código MicroPython para fazer a interface do display LCD 16×2 com o Raspberry Pi Pico.

 

Lista de Materiais

Você precisa adquirir os seguintes componentes para construir este projeto.

ComponentesDescriçãoQuantidade
1Raspberry Pi PicoRPI Pico Board RP20401
2LCD Display16×2 I2C LCD Display1
3ProtoBoardProtoBoard1

 

Breve descrição do display LCD 16X2

O LCD 16×2 tem esse nome porque tem 16 colunas e 2 linhas. Portanto, terá (16×2 = 32) 32 caracteres no total e cada personagem será composto de 5×8 Pixel Dots. Um único personagem com todos os seus pixels é mostrado na imagem abaixo.

Cada personagem tem (5×8 = 40) 40 Pixels e para 32 Personagens teremos (32×40) 1280 Pixels. Além disso, o LCD também deve ser instruído sobre a posição dos pixels. Portanto, será uma tarefa complicada lidar com tudo com a ajuda de um microcontrolador. Portanto, o LCD usa uma interface IC como HD44780. Este IC é montado na parte traseira do Módulo LCD. Você pode verificar a HD44780 Datasheet para mais informações.

A função deste IC é obter os comandos e dados do MCU e processá-los para exibir informações significativas na tela LCD. A tensão de operação do LCD é de 4,7V a 5,3V e o consumo de corrente é de 1mA sem luz de fundo. Pode funcionar no modo de 8 bits e 4 bits Ele também pode exibir quaisquer caracteres gerados de forma personalizada. Esses LCDs estão disponíveis com luz de fundo verde e azul.

Existem dois pinos de seção em todo o módulo LCD 16×2. Alguns deles são pinos de dados e alguns são pinos de comando. De alguma forma, cada pino tem uma função no controle de um único pixel na tela.

 

Display LCD 16X2 I2C

Este display incorpora uma interface I2C que requer apenas 2 pinos em um microcontrolador para a interface. A interface I2C é uma placa filha conectada à parte traseira do módulo LCD. O endereço I2C para esses monitores é 0x3F ou 0x27.

O adaptador possui um chip de expansão de E/S de 8 bits PCF8574. Este chip converte os dados I2C de um microcontrolador em dados paralelos exigidos pelo display LCD. Há um pequeno trimpot para fazer ajustes finos no contraste da tela. Além disso, há um jumper na placa que fornece energia para a luz de fundo.

 

Interface de tela LCD 16×2 com Raspberry Pi Pico

Agora, vamos fazer a interface do display LCD 16×2 com o Raspberry Pi Pico. Você pode usar o Fritzing Software para desenhar o esquema. Você pode montar o circuito na placa de ensaio conforme mostrado na imagem abaixo.

Conecte os pinos 1, 5 e 16 do LCD ao GND do Raspberry Pi Pico. Da mesma forma, conecte os pinos 2 e 15 do LCD ao pino 5V, ou seja, Vbus do Raspberry Pi Pico. Conecte os pinos 4, 6, 11, 12, 13, 14 da tela LCD ao pino Raspberry Pi Pico GP16, GP17, GP18, GP19, GP20, GP21.

Para ajustar o contraste do LCD, conecte um potenciômetro de 10K no pino 3 do display LCD.

 

Código Fonte / Programa

Raspberry Pi Pico oferece suporte ao programa MicroPython para interface de display LCD 16 × 2. Você pode usar Thonny IDE ou uPyCraft IDE para executar o código MicroPython.

Aqui está um código completo para fazer a interface do LCD HD44780 16X2 com o Raspberry Pi Pico. Copie o código para o IDE e salve-o com o nome main.py.

import machine
import utime
 
rs = machine.Pin(16,machine.Pin.OUT)
e = machine.Pin(17,machine.Pin.OUT)
d4 = machine.Pin(18,machine.Pin.OUT)
d5 = machine.Pin(19,machine.Pin.OUT)
d6 = machine.Pin(20,machine.Pin.OUT)
d7 = machine.Pin(21,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))

Portanto, assim que você executar o código, o visor LCD começará a mostrar o Hello World! mensagem no display LCD.

 

Interface de tela LCD I2C 16×2 com Raspberry Pi Pico

O código acima é válido para display LCD 16×2 sem qualquer módulo I2C. O módulo PCF8574 I2C ou SMBus simplifica a conexão acima. Em vez de usar tantos fios, você pode usar este Módulo IC e converter as linhas de saída de dados apenas para 2 pinos.

Conseqüentemente, o display LCD se torna um display I2C com um endereço I2C de 0x27. A conexão entre o Raspberry Pi Pico e o I2C LCD é muito simples, conforme mostrado no esquema abaixo.

Conecte o VCC do LCD e o pino GND ao Raspberry Pi Pico 5V e o pino GND respectivamente. Conecte o pino SDA e SCL do LCD ao Raspberry Pi Pico GP8 e GP9 respectivamente.

 

Código Fonte / Programa

O programa desta seção está dividido em 3 partes. Precisamos escrever algum código de driver, bem como código de protocolo I2C para esta parte.

Copie todo esse código e salve todos os 3 códigos no Raspberry Pi Pico Board.

lcd_api.py

import time
 
class LcdApi:
 
    LCD_CLR         = 0x01 # DB0: limpar exibição
    LCD_HOME        = 0x02 # DB1: retornar à posição inicial 

    LCD_ENTRY_MODE  = 0x04 # DB2: definir modo de entrada
    LCD_ENTRY_INC   = 0x02 # --DB1: incremento
    LCD_ENTRY_SHIFT = 0x01 # --DB0: shift
 
    LCD_ON_CTRL     = 0x08 # DB3: ligue LCD / cursor
    LCD_ON_DISPLAY  = 0x04 # --DB2: liga a tela
    LCD_ON_CURSOR   = 0x02 # --DB1: liga o cursor
    LCD_ON_BLINK    = 0x01 # --DB0: cursor piscando
 
    LCD_MOVE        = 0x10 # DB4: mover cursor / exibir
    LCD_MOVE_DISP   = 0x08 # --DB3: mover a tela (0-> mover o cursor)
    LCD_MOVE_RIGHT  = 0x04 # --DB2: mover para a direita (0-> esquerda)
 
    LCD_FUNCTION        = 0x20 # DB5: conjunto de funções
    LCD_FUNCTION_8BIT   = 0x10 # --DB4: definir modo 8BIT (0-> modo 4BIT)
    LCD_FUNCTION_2LINES = 0x08 # --DB3: duas linhas (0-> uma linha)
    LCD_FUNCTION_10DOTS = 0x04 # --DB2: fonte 5x10 (0-> fonte 5x7)
    LCD_FUNCTION_RESET  = 0x30 # Consulte a seção "Inicializando por instrução"
 
    LCD_CGRAM       = 0x40 # DB6: definir o endereço CG RAM
    LCD_DDRAM       = 0x80 # DB7: definir endereço DD RAM
 
    LCD_RS_CMD = 0
    LCD_RS_DATA = 1
 
    LCD_RW_WRITE = 0
    LCD_RW_READ = 1
 
    def __init__(self, num_lines, num_columns):
        self.num_lines = num_lines
        if self.num_lines > 4:
            self.num_lines = 4
        self.num_columns = num_columns
        if self.num_columns > 40:
            self.num_columns = 40
        self.cursor_x = 0
        self.cursor_y = 0
        self.implied_newline = False
        self.backlight = True
        self.display_off()
        self.backlight_on()
        self.clear()
        self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
        self.hide_cursor()
        self.display_on()
 
    def clear(self):
        """Limpa o visor LCD e move o cursor para o canto superior esquerdo
         canto.
        """
        self.hal_write_command(self.LCD_CLR)
        self.hal_write_command(self.LCD_HOME)
        self.cursor_x = 0
        self.cursor_y = 0
 
    def show_cursor(self):
        """Torna o cursor visível."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR)
 
    def hide_cursor(self):
        """Faz com que o cursor fique oculto."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
 
    def blink_cursor_on(self):
        """Liga o cursor e o faz piscar."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR | self.LCD_ON_BLINK)
 
    def blink_cursor_off(self):
        """Liga o cursor e não pisca (ou seja, fica sólido)."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
                               self.LCD_ON_CURSOR)
 
    def display_on(self):
        """Liga (ou seja, desobstrui) o LCD."""
        self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
 
    def display_off(self):
        """Desliga (ou seja, apaga) o LCD."""
        self.hal_write_command(self.LCD_ON_CTRL)
 
    def backlight_on(self):
        """Liga a luz de fundo.
         Este não é realmente um comando LCD, mas alguns módulos têm luz de fundo
         controles, então isso permite que o hal passe pelo comando.
        """
        self.backlight = True
        self.hal_backlight_on()
 
    def backlight_off(self):
        """Desliga a luz de fundo.
         Este não é realmente um comando LCD, mas alguns módulos têm luz de fundo
         controles, então isso permite que o hal passe pelo comando.
        """
        self.backlight = False
        self.hal_backlight_off()
 
    def move_to(self, cursor_x, cursor_y):
        """Move a posição do cursor para a posição indicada. O cursor
         a posição é baseada em zero (ou seja, cursor_x == 0 indica a primeira coluna).
        """
        self.cursor_x = cursor_x
        self.cursor_y = cursor_y
        addr = cursor_x & 0x3f
        if cursor_y & 1:
            addr += 0x40    # Lines 1 & 3 add 0x40
        if cursor_y & 2:    # Lines 2 & 3 add number of columns
            addr += self.num_columns
        self.hal_write_command(self.LCD_DDRAM | addr)
 
    def putchar(self, char):
        """Grava o caractere indicado no LCD no cursor atual
         posição e avança o cursor uma posição.
        """
        if char == '\n':
            if self.implied_newline:
                # self.implied_newline means we advanced due to a wraparound,
                # so if we get a newline right after that we ignore it.
                pass
            else:
                self.cursor_x = self.num_columns
        else:
            self.hal_write_data(ord(char))
            self.cursor_x += 1
        if self.cursor_x >= self.num_columns:
            self.cursor_x = 0
            self.cursor_y += 1
            self.implied_newline = (char != '\n')
        if self.cursor_y >= self.num_lines:
            self.cursor_y = 0
        self.move_to(self.cursor_x, self.cursor_y)
 
    def putstr(self, string):
        """Escreva a string indicada no LCD no cursor atual
         posição e avança a posição do cursor apropriadamente.
        """
        for char in string:
            self.putchar(char)
 
    def custom_char(self, location, charmap):
        """Escreva um personagem para um dos 8 locais CGRAM, disponíveis
         como chr(0) a chr(7).
        """
        location &= 0x7
        self.hal_write_command(self.LCD_CGRAM | (location << 3))
        self.hal_sleep_us(40)
        for i in range(8):
            self.hal_write_data(charmap[i])
            self.hal_sleep_us(40)
        self.move_to(self.cursor_x, self.cursor_y)
 
    def hal_backlight_on(self):
        """Permite que a camada hal ligue a luz de fundo.
         Se desejado, uma classe HAL derivada implementará esta função.
        """
        pass
 
    def hal_backlight_off(self):
        """Permite que a camada hal desligue a luz de fundo.
         Se desejado, uma classe HAL derivada implementará esta função.
        """
        pass
 
    def hal_write_command(self, cmd):
        """Escreva um comando no LCD.
         Espera-se que uma classe derivada HAL implemente este
         função.
        """
        raise NotImplementedError
 
    def hal_write_data(self, data):
        """Grave dados no LCD.
         Espera-se que uma classe derivada HAL implemente este
         função.
        """
        raise NotImplementedError
 
    def hal_sleep_us(self, usecs):
        """Dormir por algum tempo (dado em microssegundos). """
        time.sleep_us(usecs)

pico_i2c_lcd.py

from lcd_api import LcdApi
from machine import I2C
from time import sleep_ms
 
DEFAULT_I2C_ADDR = 0x27
 
# Define mudanças ou máscaras para as várias linhas de LCD conectadas ao PCF8574
 
MASK_RS = 0x01
MASK_RW = 0x02
MASK_E = 0x04
SHIFT_BACKLIGHT = 3
SHIFT_DATA = 4
 
 
class I2cLcd(LcdApi):
    """Implementa um lcd baseado em caracteres conectado via PCF8574 no i2c."""
 
    def __init__(self, i2c, i2c_addr, num_lines, num_columns):
        self.i2c = i2c
        self.i2c_addr = i2c_addr
        self.i2c.writeto(self.i2c_addr, bytearray([0]))
        sleep_ms(20)   # Dê tempo ao LCD para ligar
        # Enviar reset 3 vezes
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(5)    #precisa atrasar pelo menos 4,1 mseg
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        # Coloque o LCD no modo de 4 bits
        self.hal_write_init_nibble(self.LCD_FUNCTION)
        sleep_ms(1)
        LcdApi.__init__(self, num_lines, num_columns)
        cmd = self.LCD_FUNCTION
        if num_lines > 1:
            cmd |= self.LCD_FUNCTION_2LINES
        self.hal_write_command(cmd)
 
    def hal_write_init_nibble(self, nibble):
        """Grava um nibble de inicialização no LCD.
         Esta função específica é usada apenas durante a inicialização.
        """
        byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
        self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytearray([byte]))
 
    def hal_backlight_on(self):
        """Permite que a camada hal ligue a luz de fundo."""
        self.i2c.writeto(self.i2c_addr, bytearray([1 << SHIFT_BACKLIGHT]))
 
    def hal_backlight_off(self):
        """Permite que a camada hal desligue a luz de fundo."""
        self.i2c.writeto(self.i2c_addr, bytearray([0]))
 
    def hal_write_command(self, cmd):
        """Grava um comando no LCD.
         Os dados estão travados na borda descendente de E.
        """
        byte = ((self.backlight << SHIFT_BACKLIGHT) | (((cmd >> 4) & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytearray([byte]))
        byte = ((self.backlight << SHIFT_BACKLIGHT) | ((cmd & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytearray([byte]))
        if cmd <= 3:
            # Os comandos home e clear requerem um atraso de pior caso de 4,1 mseg
            sleep_ms(5)
 
    def hal_write_data(self, data):
        """Grave dados no LCD."""
        byte = (MASK_RS | (self.backlight << SHIFT_BACKLIGHT) | (((data >> 4) & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytearray([byte]))
        byte = (MASK_RS | (self.backlight << SHIFT_BACKLIGHT) | ((data & 0x0f) << SHIFT_DATA))
        self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
        self.i2c.writeto(self.i2c_addr, bytearray([byte]))

main.py

from pico_i2c_lcd import I2cLcd
from machine import I2C
from machine import Pin
import utime as time
 
 
i2c = I2C(id=1,scl=Pin(9),sda=Pin(8),freq=100000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
 
while True:
      lcd.move_to(2,0)
      lcd.putstr('Hello world')

Depois de executar todos esses códigos no Raspberry Pi Pico, o LCD começará a exibir a mensagem Hello World.