Capturando imagens do módulo Ov7670 com microcontrolador ARM Esp8266-01

Introdução

      Ov7670 é um módulo de câmera paralela de 8 bits com sensor CMOS capaz de gerar imagens na resolução VGA (640×480) a 30 fps (frames por segundo). Diferente das câmeras comuns de vídeo composto, este módulo oferece uma boa interface de comunicação, pois disponibiliza através de seus pinos, todos os sinais de sincronismo e os dados da imagem, o que o torna ideal para uso com projetos microcontrolados.
      Neste artigo iremos aprender a configurar o módulo Ov7670 para trabalhar na resolução QQVGA (160×120), que permitirá capturar e armazenar uma imagem na memória RAM interna de um microcontrolador ARM Cortex-M3, que em seguida será transferida para o computador para visualização em formato bitmap.
      Não iremos explorar muito os detalhes teóricos sobre o processamento digital de imagens (PDI), o objetivo maior é entender como funciona e como programar o módulo Ov7670.

O módulo de câmera Ov7670

      Existem diferentes versões desse módulo, sendo a inicial fabricada pela OmniVision dispondo apenas do sensor CMOS e seu conector, conforme a figura 1, e versões mais recentes que inclui na própria placa do sensor, um buffer do tipo FIFO (First In, First Out) o AL422B, e um oscilador de 12MHz, figura 2, e outras de tamanho reduzido e com recursos de autofoco, figura 3.

Vista Frontal
Figura 1
Figura 2
Figura 3

      Das diferentes versões, iremos utilizar neste primeiro artigo desta série, a versão da figura 1, que inclusive é a mais difícil de se trabalhar e, portanto, a mais indicada para o aprendizado. As versões das figuras 2 e 3, permite que a imagem seja armazenada no buffer AL422B de forma automática, e assim o microcontrolador só precisa ler os dados da saída do AL422B a qualquer momento, o que dá uma “folga” ao microcontrolador para executar outras tarefas, como por exemplo: armazenar as imagens lidas em um cartão de memória.

Principais características do Ov7670

  • Alta sensibilidade em operação de baixa luminosidade
  • Opera com baixa voltagem para aplicações embarcadas 3.3v
  • Interface de comunicação padrão SCCB compatível com i2c
  • Suporte aos formatos de saída RGB (GRB 4:2:2, RGB565/555/444), YUV (4:2:2) e YCbCr (4:2:2)
  • Controle automático de ganho (AGC), de exposição (AEC) e balanço automático de branco (AWB)
  • Controle de qualidade de imagem incluindo cor, saturação, gama, brilho e anti-florescente
  • Correção de lente
  • 30 fps – frames por segundo
 

Resolução do Ov7670

      A resolução define a qualidade de uma imagem, portanto quanto maior a resolução, mais rica a imagem é em detalhes, e também influencia diretamente no tamanho da mesma.
      A resolução é medida em pixel por polegada (ppi), o pixel por sua vez é uma unidade que os computadores usam para representar cada minúsculo ponto de uma imagem do mundo real. 
      Algumas resoluções conhecidas que o Ov7670 suporta são:
  •  VGA (640 x 480 pixels)
  • QVGA (320 x 240 pixels)
  • CIF (352 x 240 pixels)
  • QCIF (176 x 144 pixels)
  • QQVGA(160 x 120 pixels)
  • Manual (Possibilita customizar um tamanho diferente de resolução)
 

Formatos de saída suportados pelo Ov7670

  • Monocromático
  • RGB (GRB 4:2:2, RGB565/555/444)
  • YUV (4:2:2)
  • YCbCr (4:2:2)
 

      Podemos entender o formato de saída como sendo, a forma como um pixel é representado, e também a forma como devemos armazená-lo em memória computacional, pois, ele define a quantidade e a ordem dos bits que são usados para representar cada intensidade de luz e cores de uma imagem.
      O formato escolhido para nosso projeto foi o RGB565, esse formato é muito utilizado pela maioria dos dispositivos, tanto de captura (câmeras), como de visualização (displays) de imagens.
      No RGB565 as cores são representadas utilizando apenas as três cores primárias, vermelho (R), verde (G) e azul (B), assim cada pixel de uma imagem é armazenado com essas três intensidades de cores.
      Esse formato utiliza 5 bits para armazenar a cor vermelha, 6 bits para a cor verde e 5 bits para a cor azul, dando um total de 2 bytes para representar cada pixel conforme mostra a figura 4.

Figura 4 – O formato RGB565
 
      Na seção Firmware, entraremos em detalhes mais práticos de como trabalhar com esse formato, como ler um pixel, qual o momento exato da leitura e como armazená-lo na memória do nosso microcontrolador escolhido.

Quantidade de memória necessária para armazenar uma imagem
 
      De posse de algumas informações, já podemos calcular, o quanto de memória será necessário para armazenar uma imagem gerada pelo nosso módulo de câmera Ov7670.
      Como vamos utilizar a resolução QQVGA, que possui as dimensões 160×120, ou seja, cada frame (imagem) é composto de 160 linhas por 120 colunas, dando um total de 19200 pixels, aproximadamente 0.019 Megapixels. Deu pra perceber que nessa resolução estamos muito abaixo de 1 Megapixel?
      Como o nosso formato de saída será o RGB565, temos que 1 pixel é armazenado em 2 bytes. Logo o tamanho de uma foto QQVGA em memória será de:
 
      1 Pixel ———— 2 bytes
      19200 ———— 38400 bytes
 
      Convertendo para Kilobytes (Kibibytes), o tamanho de nossa imagem é 37,5 KB.
      Logo, nosso microcontrolador deve ter um espaço de memória RAM livre, de aproximadamente 37,5KB para que isso seja possível. O microcontrolador escolhido para este projeto possui memória RAM suficiente para tal propósito.
 
Hardware
 
      O microcontrolador que iremos utilizar é um ARM Cortex-M3 de 32bits, modelo LPC1769 da NXP. Iremos utilizá-lo através de uma plataforma de desenvolvimento conhecida como LPCXpresso, que possui gravador e depurador, e ainda conta com uma IDE gratuita baseada no poderoso Eclipse IDE.
 
 Figura 5 – Plataforma LPCXpresso
 
      Com esta placa o trabalho se torna bem mais rápido do que confeccionar uma PCI exclusiva para o projeto, sendo ideal para protótipos como este.
 
Pinagem do módulo Ov7670
 
 
      Os pinos do Ov7670 estão bem definidos na placa através de um conector tipo header 9×2, como mostra a figura 6, a descrição de cada pino encontra-se na tabela abaixo.
 
 Tabela 1 – Descrição dos pinos do módulo
 

Algumas observações importantes podem ser feitas aqui:

 
  1. O barramento de dados é de apenas 8 bits, e o formato escolhido RGB565 utiliza 16 bits, logo, será necessário fazer duas leituras para obter um pixel completo.
  2. Para a entrada de clock XCLK iremos usar a saída de clock do próprio microcontrolador, por ser mais fácil do que adquirir um oscilador para isso.
  3. É através da interface SCCB ou I2C que será feita a configuração da câmera, como: mudar resolução, formato de saída, ajustar brilho, contraste e etc.
  4. O pino PWDN possui um resistor interno de pull-down que garante o funcionamento normal do módulo, portanto não precisamos usar este pino.
  5. O reset através do pino RESET será usado para garantir que o módulo tenha uma inicialização correta, e após isso será usado também o reset via software.
 
Esquema elétrico do projeto
 
      A figura 7 mostra o esquema de ligação entre o módulo Ov7670 e a placa LPCXpresso, assim como o conversor USB/Serial da FTDI, que deverá estar com a alimentação selecionada em 3,3v para ajustar os níveis de tensão entre as placas.
 

Figura 7 – Esquema elétrico do projeto

Software

      Neste primeiro artigo não iremos desenvolver nenhum software específico para a obtenção da imagem no PC, ao invés disso, vamos utilizar algumas ferramentas gratuitas onde você deverá baixar e instalar em seu computador. O uso dessas ferramentas será mostrado através de um vídeo na sessão de resultados.

Figura 8 – Arquitetura do sistema

      A figura 8 mostra a arquitetura do sistema e o fluxo completo do processo de captura, onde através do software HTerm, iremos capturar uma foto do Ov7670 e armazená-lo em disco no formato RGB565 puro, ou seja, as 160 linhas e 120 colunas, em seguida, através do software RGB565ToBitmap iremos inserir um header do tipo bitmap que nos permitirá ver a imagem em algum visualizador do Windows, completando assim o sucesso do nosso projeto.

Baixe as ferramentas através dos links:

 
      Para baixar o LPCXpresso IDE é necessário criar uma conta no site da NXP, e com a versão gratuita conseguimos compilar códigos para o ARM de até 256KB de memória de programa, o que é suficiente para este e outros projetos. Sobre detalhes de como compilar e gravar o código no microcontrolador, será mostrado em um artigo específico, porém no vídeo de demonstração será apresentado também essa etapa, desde a importação do código fonte, até a gravação e teste.

Firmware

      Antes de começar a programar o módulo Ov7670, é necessário conhecer algumas etapas fundamentais para a captura da imagem, isso inclui conhecer sobre os sinais de sincronismo, HREF, PCLK, VSYNC e XCLK baseando-se no datasheet do módulo que pode ser baixado aqui.

Os sinais de sincronismo

      Quando o Ov7670 é alimentado, o mesmo começa a produzir frames em seu barramento de dados, e ao mesmo tempo conduz seus sinais de sincronismo, que devem ser interpretados de forma a identificar o início e o fim de cada linha da imagem. Na figura 9 temos todos esses sinais agrupados para melhor entendimento, e juntamente com a explicação será mostrado partes do código fonte a seguir.

Figura 9 – Sinais de sincronismo do módulo

      Quando o sinal de sincronismo vertical VSYNC vai a nível baixo, isso indica o início de uma imagem (frame), o microcontrolador deve agora, aguardar o sinal de sincronismo horizontal HREF ir a nível alto, o que indica o início de uma linha da imagem, e para capturar cada pixel da linha, deve-se aguardar o sinal de pixel clock PCLK ir a nível alto, e é nesse momento que deve ser feito a leitura do barramento de dados, armazenando os bytes em um buffer na RAM. O processamento total de um frame é dado quando o sinal de VSYNC vai a nível alto, e a quantidade de linhas varia de acordo com a resolução e o formato de saída escolhido. Para nosso projeto, devemos obter 120 linhas e para cada linha devemos ler 320 bytes, dois bytes por pixel devido o formato de saída escolhido rgb565. Conforme mostrado no final da figura 9, esses bytes podem ser armazenados em buffers diferentes, conforme o trecho de código abaixo.

      O código acima executa a captura da imagem fazendo polling nos sinais de sincronismo, isto pode deixar o projeto um pouco inconsistente, devido o processador ficar “preso” nessa tarefa até que se conclua a captura. Nos próximos projetos iremos utilizar interrupção para monitorar esses sinais, deixando assim o programa mais livre de travamentos.

Configuração correta do oscilador e do fps

<<< POST EM ANDAMENTO >>>

Resultados obtidos

Conclusão

Referencias

 

Downloads

8 thoughts on “Capturando imagens do módulo Ov7670 com microcontrolador ARM Esp8266-01

  1. Oi, muito útil a informação.
    Eu tenho uma curiosidade, é sobre o caso da figura 3, essa camera já tem o buffer incluso e os dados são armazenados automaticamente né?, mas como que se faz a leitura desses dados depois de terem sido armazenados?. Outra curiosidade é se a câmera está atualizando em todo momento os dados da imagem no buffer?. Ou há um jeito de só tirar uma foto (armazenar um frame para depois processar os dados da imagem)?.

    Agradeço muito desde já pela informação.

    Abraço

    1. Olá, sim o buffer é preenchido automaticamente pelos sinais de sincronismo da câmera, então um microcontrolador deve habilitar a escrita dele e quando estiver cheio, desabilita a escrita e pode efetuar e leitura da imagem contida no buffer, fazer qualquer processamento (salvar em um sd-card etc) e em seguida habilitar para capturar outro frame.
      A camêra sempre está produzindo frames em seu barramento de dados, conduzidos pelos seus sinais de sincronismo.

  2. Oi,
    Uma consulta, qual seria a configuração dos registros para usar a camera em resolução VGA, rgb565 e pclk 24 mhz.

    Agradeço desde já pela informação.

  3. Parabéns pelo trabalho. Espero que você possa fazer a continuação pois realmente está bem explicado, desde a diferença entre as câmeras até como a sincronização deve ser executada. Gostaria de saber como os dados devem ser passados via serial para o Hterm? Seria simplesmente enviar os dois buffers em sequência ou é necessário acrescentar algum cabeçalho ou mudança nos dados para que ele entenda que é uma imagem?

    1. Olá, nesse caso basta enviar na mesma sequência que foram lidos, ou seja:
      UartSend(qqvgaframe1[i]);
      UartSend(qqvgaframe2[i]);

      Mas ao receber isso no PC, ele vai estar em RGB565 então é necessário converter para bitmap, jpeg ou png.

  4. Parabéns pelo artigo. Está muito bem explicado. Gostaria de saber quais os registradores eu preciso configurar para saber se a câmera está configurada como QQVGA e também como são passados os dados para serial?

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *