Pular para o conteúdo principal

Usar um display SPI no reComputer J4012 Classic

Introdução​

Este wiki apresenta como conectar e controlar um display SPI no Seeed reComputer J4012 Classic. Ele abrange o fluxo de trabalho básico de uso de um display SPI através do conector de 40 pinos, incluindo a fiação de hardware, configuração da interface SPI, verificação do nó de dispositivo, instalação de dependências, compilação de um demo em C++ e execução de um teste simples de exibição.

Neste guia, um display LCD SPI ST7789 é usado como exemplo. Para outros módulos de display SPI, como ST7735 ou ILI9341, o fluxo de trabalho geral é semelhante, mas os detalhes de fiação, resolução do display, sequência de inicialização e parâmetros do driver podem ser diferentes.

O método deste guia também pode ser usado como referência para outras aplicações de display SPI.

Preparação de hardware​

Hardware necessário​

ItemDescrição
reComputer J4012 ClassicComputador de borda de IA baseado em Jetson
Módulo de display SPIEste guia usa um display LCD SPI ST7789 como exemplo
Fios DupontUsados para conectar o display ao conector de 40 pinos
Display HDMI ou terminal SSHUsado para configurar e testar o dispositivo

Software necessário​

SoftwareDescrição
JetPack / UbuntuSistema operacional em execução no reComputer J4012 Classic
g++Usado para compilar o demo em C++
spidevInterface SPI em espaço de usuário do Linux
sysfs GPIOUsado para controlar pinos GPIO como DC e RES

Conexão de hardware​

Conector de 40 pinos​

O reComputer J4012 Classic fornece um conector de expansão de 40 pinos. Sinais SPI e pinos GPIO podem ser usados através desse conector para conectar pequenos módulos de display.

40-pin header pinout

Figura 1. Pinagem do conector de 40 pinos do reComputer J4012 Classic

Exemplo de fiação de display SPI: ST7789​

Neste guia, um display SPI ST7789 é usado como módulo de display de exemplo. Conecte o display ao conector de 40 pinos de acordo com a tabela abaixo.

Pino ST7789Pino de 40 pinos do J4012 ClassicFunçãoDescrição
GNDPino 6GNDTerra
VCCPino 13.3VEntrada de alimentação para o display
SCLPino 23SPI SCLKSinal de clock SPI
SDAPino 19SPI MOSIDados SPI do J4012 Classic para o display
RESPino 31GPIO / PQ.06Sinal de reset de hardware
DCPino 29GPIO / PQ.05Seleção de dados / comando
CSPino 24SPI CSSeleção de chip SPI
BLKPino 173.3VAlimentação do backlight, sempre ligado

ST7789 SPI display wiring

Figura 2. Fiação entre o reComputer J4012 Classic e o display SPI ST7789

Habilitar interface SPI​

Antes de executar o demo de display, a interface SPI no conector de 40 pinos deve ser habilitada.

Abra a ferramenta de configuração Jetson-IO:

sudo /opt/nvidia/jetson-io/jetson-io.py

Selecione o menu de configuração do conector de 40 pinos.

Jetson-IO main menu

Figura 3. Menu principal do Jetson-IO

Configure header pins manually

Figura 4. Selecione "Configure header pins manually"

Enable spi1 function

Figura 5. Habilite a função spi1 no conector de 40 pinos

Salve a configuração e reinicie o dispositivo:

sudo reboot

Após a reinicialização do dispositivo, carregue o módulo de kernel spidev:

sudo modprobe spidev

Esta etapa garante que o driver SPI em espaço de usuário do Linux esteja disponível antes de verificar ou acessar /dev/spidev*.

Verificar dispositivo SPI​

Após a reinicialização do dispositivo, verifique se o nó de dispositivo SPI foi gerado:

ls /dev/spidev*

Se o SPI estiver habilitado corretamente, você poderá ver uma saída semelhante à seguinte:

/dev/spidev0.0
/dev/spidev0.1

Check SPI device node

Figura 6. Nó de dispositivo SPI gerado com sucesso

Neste guia, o display ST7789 usa os sinais SPI conectados aos Pinos 19, 23 e 24. O código de exemplo usa /dev/spidev0.0 por padrão. Se o seu sistema gerar um nó de dispositivo SPI diferente, modifique o caminho do dispositivo SPI no código.

Instalar dependências​

Atualize a lista de pacotes:

sudo apt update

Instale o compilador C++:

sudo apt install -y g++

Verifique se o nó de dispositivo SPI existe:

ls /dev/spidev*

Controle de GPIO​

Antes de executar o demo de display, exporte os pinos GPIO usados por DC e RES. O demo controla esses dois pinos através da interface sysfs GPIO.

Neste guia:

SinalPino de 40 pinosNome do GPIONúmero do GPIO
DCPino 29PQ.05453
RESPino 31PQ.06454

Ao exportar GPIO através de /sys/class/gpio/export, use o número do GPIO em vez do nome do GPIO. Neste guia, o GPIO 453 corresponde a PQ.05, e o GPIO 454 corresponde a PQ.06:

sudo sh -c 'echo 453 > /sys/class/gpio/export'
sudo sh -c 'echo 454 > /sys/class/gpio/export'

Após a exportação, os nós GPIO correspondentes devem aparecer como PQ.05 e PQ.06. Verifique se os nós GPIO existem:

ls /sys/class/gpio/PQ.05
ls /sys/class/gpio/PQ.06

Executar a demonstração do display ST7789​

Esta seção usa uma demonstração em C++ para verificar se o display SPI ST7789 pode funcionar corretamente no reComputer J4012 Classic.

A demonstração executa as seguintes operações:

  1. Abre o dispositivo SPI /dev/spidev0.0.
  2. Configura o modo SPI, bits por palavra e velocidade SPI.
  3. Controla os pinos DC e RES por meio do sysfs GPIO.
  4. Inicializa o controlador de display ST7789.
  5. Preenche continuamente a tela com diferentes cores RGB565.

A fiação usada nesta demonstração é mostrada abaixo.

SinalPino de 40 viasGPIO / Dispositivo
SPI SCLKPino 23Clock SPI
SPI MOSIPino 19SPI MOSI
SPI CSPino 24SPI CS
RESPino 31/sys/class/gpio/PQ.06
DCPino 29/sys/class/gpio/PQ.05
BLKPino 173,3 V

Crie um arquivo chamado st7789_spi.cpp:

nano st7789_spi.cpp

Adicione o seguinte código de demonstração em C++:

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <cstdint>
#include <cstring>

int spi_fd = -1;
int dc_fd = -1;
int res_fd = -1;

// GPIO paths for reComputer J4012 Classic
// DC -> Pin 29 -> PQ.05
// RES -> Pin 31 -> PQ.06
const char* DC_DIR_PATH = "/sys/class/gpio/PQ.05/direction";
const char* DC_VAL_PATH = "/sys/class/gpio/PQ.05/value";
const char* RES_DIR_PATH = "/sys/class/gpio/PQ.06/direction";
const char* RES_VAL_PATH = "/sys/class/gpio/PQ.06/value";

bool write_file(const char* path, const char* value)
{
int fd = open(path, O_WRONLY);
if (fd < 0) {
std::cerr << "open failed: " << path << std::endl;
return false;
}

write(fd, value, strlen(value));
close(fd);
return true;
}

bool init_gpios()
{
if (!write_file(DC_DIR_PATH, "out")) return false;
if (!write_file(RES_DIR_PATH, "out")) return false;

dc_fd = open(DC_VAL_PATH, O_WRONLY);
res_fd = open(RES_VAL_PATH, O_WRONLY);

if (dc_fd < 0 || res_fd < 0) {
std::cerr << "open gpio value failed" << std::endl;
return false;
}

return true;
}

void gpio_write(int fd, int value)
{
lseek(fd, 0, SEEK_SET);
write(fd, value ? "1" : "0", 1);
}

void WriteCommand(uint8_t cmd)
{
gpio_write(dc_fd, 0);
write(spi_fd, &cmd, 1);
}

void WriteData(uint8_t data)
{
gpio_write(dc_fd, 1);
write(spi_fd, &data, 1);
}

void WriteDataBuf(const uint8_t* data, size_t len)
{
gpio_write(dc_fd, 1);

const size_t CHUNK_SIZE = 4096;
size_t bytes_sent = 0;

while (bytes_sent < len) {
size_t current_chunk = (len - bytes_sent > CHUNK_SIZE) ? CHUNK_SIZE : (len - bytes_sent);

struct spi_ioc_transfer tr;
std::memset(&tr, 0, sizeof(tr));

tr.tx_buf = (unsigned long)(data + bytes_sent);
tr.rx_buf = 0;
tr.len = current_chunk;
tr.speed_hz = 24000000;
tr.bits_per_word = 8;

if (ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr) < 0) {
std::cerr << "SPI chunk transfer failed at " << bytes_sent << std::endl;
return;
}

bytes_sent += current_chunk;
}
}

void ST7789_Reset()
{
gpio_write(res_fd, 0);
usleep(200000);

gpio_write(res_fd, 1);
usleep(200000);
}

void ST7789_Init()
{
ST7789_Reset();

WriteCommand(0x11); // Sleep Out
usleep(120000);

WriteCommand(0x3A); // Pixel Format Set
WriteData(0x05); // RGB565

WriteCommand(0x36); // Memory Access Control
WriteData(0x08); // Default direction

WriteCommand(0x21); // Display Inversion ON

WriteCommand(0x29); // Display ON
usleep(20000);
}

void ST7789_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
WriteCommand(0x2A); // Column Address Set
WriteData(x0 >> 8);
WriteData(x0 & 0xFF);
WriteData(x1 >> 8);
WriteData(x1 & 0xFF);

WriteCommand(0x2B); // Row Address Set
WriteData(y0 >> 8);
WriteData(y0 & 0xFF);
WriteData(y1 >> 8);
WriteData(y1 & 0xFF);

WriteCommand(0x2C); // Memory Write
}

void ST7789_FillColor(uint16_t color)
{
const int width = 240;
const int height = 320;

ST7789_SetWindow(0, 0, width - 1, height - 1);

uint8_t screen_buf[height * width * 2];

uint8_t high = color >> 8;
uint8_t low = color & 0xFF;

for (int i = 0; i < height * width * 2; i += 2) {
screen_buf[i] = high;
screen_buf[i + 1] = low;
}

WriteDataBuf(screen_buf, sizeof(screen_buf));
}

bool init_spi()
{
spi_fd = open("/dev/spidev0.0", O_RDWR);
if (spi_fd < 0) {
std::cerr << "open /dev/spidev0.0 failed" << std::endl;
return false;
}

uint8_t mode = SPI_MODE_0;
uint8_t bits = 8;
uint32_t speed = 24000000;

ioctl(spi_fd, SPI_IOC_WR_MODE, &mode);
ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

return true;
}

uint16_t Rainbow_HSV_To_RGB565(int h)
{
float r = 0, g = 0, b = 0;

int sector = h / 60;
float fractional = (h % 60) / 60.0f;

float x = 1.0f - fractional;
float y = fractional;

switch (sector) {
case 0: r = 1.0f; g = y; b = 0.0f; break;
case 1: r = x; g = 1.0f; b = 0.0f; break;
case 2: r = 0.0f; g = 1.0f; b = y; break;
case 3: r = 0.0f; g = x; b = 1.0f; break;
case 4: r = y; g = 0.0f; b = 1.0f; break;
case 5: r = 1.0f; g = 0.0f; b = x; break;
default: r = 0.0f; g = 0.0f; b = 0.0f; break;
}

uint8_t R8 = static_cast<uint8_t>(r * 255);
uint8_t G8 = static_cast<uint8_t>(g * 255);
uint8_t B8 = static_cast<uint8_t>(b * 255);

return ((R8 >> 3) << 11) | ((G8 >> 2) << 5) | (B8 >> 3);
}

int main()
{
uint16_t color = 0x001F;
int i = 0;

std::cout << "Init SPI..." << std::endl;
if (!init_spi()) return -1;

std::cout << "Init GPIO..." << std::endl;
if (!init_gpios()) return -1;

std::cout << "Init ST7789..." << std::endl;
ST7789_Init();

std::cout << "Fill 240x320 Color..." << std::endl;

while (1) {
color = Rainbow_HSV_To_RGB565(i++ % 360);
ST7789_FillColor(color);
usleep(10000);
}

close(spi_fd);
close(dc_fd);
close(res_fd);

return 0;
}

Compile a demonstração:

g++ st7789_spi.cpp -o st7789_spi

Execute a demonstração:

sudo ./st7789_spi

Se a fiação e a configuração SPI estiverem corretas, o display ST7789 deverá ser atualizado continuamente com cores diferentes.

Resultado do demo do display ST7789

Figura 7. Resultado do demo do display ST7789

Explicação do código​

Inicialização do SPI​

O demo abre /dev/spidev0.0 e configura a interface SPI da seguinte forma.

ParâmetroValor
Dispositivo SPI/dev/spidev0.0
Modo SPISPI_MODE_0
Bits por palavra8
Velocidade SPI24000000 Hz

Se o seu sistema gerar um nó de dispositivo SPI diferente, modifique a seguinte linha no código do demo:

spi_fd = open("/dev/spidev0.0", O_RDWR);

Por exemplo, se o seu nó de dispositivo for /dev/spidev1.0, altere para:

spi_fd = open("/dev/spidev1.0", O_RDWR);

Controle de GPIO​

O demo usa sysfs GPIO para controlar os pinos DC e RES.

SinalPino de 40 pinosCaminho sysfs GPIO
DCPino 29/sys/class/gpio/PQ.05
RESPino 31/sys/class/gpio/PQ.06

O pino DC é usado para alternar entre o modo de comando e o modo de dados. O pino RES é usado para reinicializar o display ST7789.

Inicialização do ST7789​

O demo inicializa o display ST7789 com os seguintes comandos.

ComandoDescrição
0x11Sleep Out
0x3APixel Format Set
0x05Formato RGB565
0x36Memory Access Control
0x21Display Inversion On
0x29Display On

Teste de Preenchimento de Tela​

A demonstração usa o formato RGB565 para preencher a tela inteira. A resolução de exibição usada na demonstração é:

const int width = 240;
const int height = 320;

Se o seu display ST7789 usar uma resolução diferente, modifique esses valores de acordo com o tamanho real da tela.

Transferência de Dados SPI​

O buffer de tela cheia é maior do que uma única pequena transferência SPI. Portanto, a demonstração envia os dados de exibição em blocos:

const size_t CHUNK_SIZE = 4096;

Isso evita limitações de tamanho de transferência e torna a atualização de tela cheia mais estável.

Solução de Problemas​

Nenhum dispositivo /dev/spidev* encontrado​

Possíveis causas:

  1. A interface SPI não está habilitada.
  2. A configuração do Jetson-IO não foi salva.
  3. O dispositivo não foi reiniciado após habilitar o SPI.
  4. A árvore de dispositivo não expõe o nó de dispositivo SPI.
  5. A imagem do sistema não inclui a configuração SPI esperada.

Verificações sugeridas:

ls /dev/spidev*

Se nenhum dispositivo SPI for encontrado, execute o Jetson-IO novamente e verifique se o SPI está habilitado.

Permissão negada ao acessar SPI ou GPIO​

Se você vir um erro de permissão ao executar a demonstração em C++, tente executá-la com sudo:

sudo ./st7789_spi

Você também pode verificar a permissão do dispositivo SPI e dos nós GPIO:

ls -l /dev/spidev*
ls -l /sys/class/gpio/PQ.05/value
ls -l /sys/class/gpio/PQ.06/value

Caminho de GPIO não existe​

Se /sys/class/gpio/PQ.05 ou /sys/class/gpio/PQ.06 não existir, os pinos GPIO podem não ter sido exportados corretamente.

Verifique os nós GPIO disponíveis:

ls /sys/class/gpio/

Em seguida, exporte novamente os pinos GPIO:

sudo sh -c 'echo 453 > /sys/class/gpio/export'
sudo sh -c 'echo 454 > /sys/class/gpio/export'

SPI pode ser aberto, mas o display não mostra nada​

Possíveis causas:

  1. A fiação do display está incorreta.
  2. O caminho do dispositivo SPI está incorreto.
  3. Os pinos CS, DC ou RES não estão conectados corretamente.
  4. O controlador do display não é ST7789.
  5. O display requer uma sequência de inicialização diferente.
  6. O pino do backlight não está alimentado.

Verificações sugeridas:

  1. Confirme que VCC está conectado ao Pino 1.
  2. Confirme que GND está conectado ao Pino 6.
  3. Confirme que BLK está conectado ao Pino 17 e que o backlight está ligado.
  4. Confirme que SCL, SDA e CS estão conectados ao Pino 23, Pino 19 e Pino 24.
  5. Confirme que RES e DC estão conectados ao Pino 31 e Pino 29.
  6. Reduza a velocidade do SPI e teste novamente.
  7. Confirme o modelo do controlador do display no datasheet do módulo de display.

Backlight ligado, mas nenhuma imagem é exibida​

Se o backlight estiver ligado, mas nenhuma imagem for exibida, a fiação de alimentação pode estar correta, mas a comunicação SPI ou a inicialização do display pode estar incorreta.

Verifique:

  1. Se o dispositivo SPI correto está sendo usado.
  2. Se os pinos DC e RES estão configurados corretamente.
  3. Se o driver de display corresponde ao controlador ST7789.
  4. Se a resolução da tela está correta.
  5. Se o módulo de display requer configurações de deslocamento de linha ou coluna.

Cor da imagem está anormal​

Possíveis causas:

  1. Ordem de cores RGB e BGR não corresponde.
  2. Configuração de inversão de display é diferente.
  3. O parâmetro MADCTL não é adequado para o seu painel.
  4. O módulo de display usa uma sequência de inicialização ST7789 ligeiramente diferente.

Soluções sugeridas:

  1. Tente alterar o valor de MADCTL.
  2. Tente ativar ou desativar a inversão de display.
  3. Verifique o datasheet do módulo de display ST7789.
  4. Confirme se o seu módulo usa ordem de cores RGB ou BGR.

Direção da imagem está incorreta​

Se a imagem estiver girada ou espelhada, modifique o parâmetro do comando MADCTL na função de inicialização:

WriteCommand(0x36);
WriteData(0x08);

O valor correto depende da orientação do módulo de display.

Atualização do display está lenta​

Possíveis causas:

  1. A velocidade do clock SPI é muito baixa.
  2. O programa atualiza a tela inteira todas as vezes.
  3. O módulo de display tem desempenho de atualização limitado.
  4. A demonstração em C++ usa um método simples de preenchimento de tela cheia para verificação.

Soluções sugeridas:

  1. Aumente gradualmente a velocidade do clock SPI.
  2. Evite atualizações desnecessárias de tela cheia.
  3. Atualize apenas as áreas alteradas se sua aplicação suportar isso.
  4. Use o teste atual de preenchimento de cor apenas como uma demonstração básica de verificação de hardware.

Resumo​

Neste wiki, apresentamos como conectar um display SPI ao Seeed reComputer J4012 Classic através do conector de 40 pinos. O fluxo de trabalho geral do display SPI inclui fazer a fiação do display, habilitar a interface SPI, verificar o nó de dispositivo SPI, instalar dependências, compilar um demo em C++ e executar um teste de display. O JetPack 5 usa GPIO via sysfs, o JetPack 6 usa libgpiod com configuração manual de pinmux, e o JetPack 7 usa libgpiod com pinos GPIO configurados diretamente na ferramenta Jetson Expansion Header.

O LCD SPI ST7789 foi usado como módulo de display de exemplo neste guia. Para outros displays SPI, o processo geral é semelhante, mas o driver de display, a resolução, a sequência de inicialização e os detalhes de fiação devem ser ajustados de acordo com o módulo de display real.

Recursos​

Suporte Técnico e Discussão de Produtos​

Obrigado por escolher nossos produtos! Estamos aqui para fornecer diferentes tipos de suporte para garantir que sua experiência com nossos produtos seja a mais tranquila possível. Oferecemos vários canais de comunicação para atender a diferentes preferências e necessidades.

Loading Comments...