Sensor de proximidade VL6180x: uma implementação OOP com PSoC-4

VL6180x

Neste artigo vamos apresentar um módulo bem interessante da ST que pode ser utilizado para a medição da distância absoluta de objetos e também integra um sensor de luz ambiente.

 

Este módulo utiliza a tecnologia FlightSense®, patenteada pela ST, que mede precisamente o tempo gasto pelo laser do VL6180x ser emitido, refletir no objeto mais próximo e voltar para o elemento foto-sensível presente no sensor (Time-of-Flight).

 

Desenho mecânico do VL6180x
Desenho mecânico do VL6180x. Imagem capturada do documento DocID028640 Rev 1.

 

 

Principais características:

 

Abaixo vou listar as principais características deste módulo:

  • Módulo ótico 3 em 1:
    • Sensor de Proximidade;
    • Sensor de Luz Ambiente (ALS);
    • Fonte de Luz VCSEL.
  • Medição da distância rápida e precisa:
    • Medição da distância variando de 0 a 10cm;
    • Independente da reflectância do objeto;
    • Rejeição da Luz Ambiente;
    • Compensação do cross-talking devido a uma cobertura de vidro.
  • Sensor de Luz Ambiente:
    • Sensibilidade em luz baixa;
    • Saída calibrada em lux;
  • Fácil integração no projeto final pois utiliza barramento I2C e pode utilizar uma única fonte de alimentação.

 

 

Integração do VL6180x em seu projeto de sistema embarcado

 

Neste artigo vamos apresentar como podemos realizar a integração do sensor VL6180x em uma aplicação embarcada. Vamos demonstrar esta implementação utilizando como target o kit CY8CKIT-042 em conjunto com o kit de expansão X-NUCLEO-6180XA1. Essas duas placas possuem um header Arduino e, com um pouco de retrabalho, podemos utilizar essas placas em conjunto no nosso projeto. 

 

O kit CY8CKIT-042 é uma placa de avaliação para PSoC ARM Cortex-M0 da Cypress. É um ótimo kit de avaliação para entrar no mundo dos PSoC da Cypress. Ele é baseado no microcontrolador CY8C4245AXI-483 que dentre as principais características possui, 32 KB de flash, 4 KB de SRAM e alguns periféricos interessantes, como CapSense®, conversores AD, canais de comunicação I2C e UART, timers e etc.

 

Diagrama em blocos da familia PSoC 4200
Diagrama em blocos da familia PSoC 4200. Imagem capturada do documento 01-87197

 

Não vou entrar em muitos detalhes sobre os PSoC pois esses já foram abordados pelo articulista André Curvello em outros artigos presentes aqui no Embarcados.

 

Agora vamos falar um pouco sobre a placa de expansão X-NUCLEO-6180AX1. Ela possui um sensor VL6180x, expansão para três módulos satélite VL6180x (utilizado para gestures), um expansor de IO para controle do display e dos GPIOs das placas satélites.

 

Placa de expansão VL6180 conectada em uma STM32 Nucleo
Placa de expansão VL6180 conectada em uma STM32 Nucleo. Imagem capturada do documento DocID027330

 

Para uma fácil integração do VL6180x com qualquer sistema embarcado, a ST fornece uma API (Application Programming Interface) que permite uma fácil integração do sensor com a plataforma alvo. Esta biblioteca pode ser baixada a partir daqui

 

Alterações no hardware

 

Como nem sempre as configurações de hardware das placas que pretendemos utilizar casam com as nossas demandas de firmware, algumas vezes devemos realizar algumas alterações nas placas envolvidas. Para este projeto, tive que realizar as seguintes na X-NUCLEO-6180AX1 e na CY8CKIT-042:

  • Conexão direta do pino GPIO0 do VL6180x, após o level shifter, para um port do shield Arduino;
  • Remoção dos terminais J3.10 e J3.9 do kit CY8CKIT-042;
  • Conexão direta dos ports P3.0 e P3.1 do kit CY8CKIT-042 nos pinos SCL e SDA da placa de expansão X-NUCLEO-6180AX1.

 

A alteração mais necessária foi a relativa ao GPIO0. Olhando o esquema elétrico simplificado do kit, notei que para controlar o GPIO0 eu deveria controlar o expansor de IO e isso fugiria um pouco do escopo do projeto final.

 

Conexões X-Nucleo-6180AX1
Conexões da X-Nucleo-6180AX1

 

 

Visão geral da biblioteca VL6180X_API

 

Realizada as alterações, vamos para a integração da biblioteca VL6180X_API ao projeto. Após realizar o download e descompactar a biblioteca, reparamos que ela é organizada da seguinte forma:

Organização da API
Organização da API

 

O código fonte da API é estruturada em vários módulos definidos da seguinte forma:

  • Funções de baixo nível: realiza operações unitárias através do acesso aos registradores básicos. As funções de baixo nível são implementadas no diretório core da biblioteca. Essas funções não precisam ser chamadas diretamente pela aplicação e podem ser deixadas somente para um uso avançado da API;
  • Funções de alto nível: Consomem as funções de baixo nível para obter medidas oriundas do VL6180X. Para obter esses resultados, essas funções realizam o polling por novas leituras, em detrimento da operação que pode ser realizada através da espera de um sinal de interrupção. Funções de alto nível também são implementadas no diretório core;
  • Configuração: Este módulo é responsável pela configuração da API para a plataforma alvo. O módulo de configuração está presente no diretório config da API; 
  • Platform: Módulos que dependem do hardware, tais como, I2C, Log, defines e etc.

 

O trabalho inicial é inserir uma pequena camada de abstração de hardware para que a biblioteca se comunique em nossa plataforma alvo. Para isso, vamos habilitar o I2C em nossa placa.

 

 

Integração da biblioteca ao nosso código fonte

 

Como descrito nos artigos do André Curvello, a habilitação de um periférico no PSoC Creator não apresenta muito segredos. Munido do datasheet (pois, como em qualquer outro microcontrolador, o PSoC tem um subset de pinos que podem ser atribuídos a cada periférico) escolhemos o mapeamento de IOs mais apropriado ao suporte de um canal I2C. Nas figuras abaixo mostro como está a configuração do nosso PSoC:

 

Componente SCB (Serial Communication Block) inserido no projeto
Componente SCB (Serial Communication Block) inserido no projeto e configurado como I2C Master com clock de 100 kHz
Mapeamento dos pinos de IO do nosso PSoC.
Mapeamento dos pinos de IO do nosso PSoC. Note que para o projeto que estou trabalhando realizei uma turbinada no microcontrolador e coloquei um CY8C4126AXI, que possue 64 kB de Flash, 8 kB de SRAM e é pino a pino compatível.

 

No projeto que estou trabalhando atualmente há dois módulos VL6180x presentes na placa como podemos ver na partícula de esquemático apresentada abaixo:

 

Partícula do esquemático do meu projeto atual com dois sensores VL6180X.
Partícula do esquemático do meu projeto atual. Note que há dois sensores VL6180X.

 

Vale enfatizar que nesta etapa de prototipagem estou trabalhando somente com um sensor VL6180x e, no desenvolvimento da solução, a minha intenção é que o código final já apresente suporte para N sensores. Esta etapa do desenvolvimento será abordada no decorrer do texto.

 

A próxima tarefa é implementar algumas funções de baixo nível da VL6180X_API para que seja possível habilitar a comunicação I2C. As funções que devemos implementar são as VL6180x_I2CWrite e VL6180x_I2CRead, presentes no módulo vl6180x_i2c.h . A implementação destas funções acabei colocando em um arquivo chamado vl6180x_hal.c :

 

 

Sendo que a definição do tipo VL6180xDev_t  é dada por:

 

 

A ideia do tipo VL6180xDev_t é que ele seja agnóstico de arquitetura, deixando a cargo do implementador que dê a melhor roupagem para a estrutura e que esta consiga descrever melhor a plataforma alvo.

 

Para o projeto que estou trabalhando, tenho como requisito a inserção de dois sensores VL6180x, como foi comentado anteriormente no texto. Para isso, resolvi partir para uma abordagem um pouco mais sofisticada na implementação do módulo de controle do sensor VL6180x. Para isso, redefini a estrutura MyDev_t , inserindo ponteiros para funções de controle de GPIO, inicialização de ISRs, inicialização do periférico e etc. Abaixo segue a nova "roupagem" dada à estrutura MyDev_t:

 

 

Já temos a definição dos objetos básicos de controle do VL6180x, agora vamos ter que definir as estruturas que realizam as tarefas de realizar a leitura dos sensores VL6180x. Para isso criei o módulo OpticalSensorInterface , que poderia, a princípio encapsular as funções de acesso aos sensores presentes na placa.

 

 

O ponto chave aqui é que o campo dev , que representa o dispositivo a ser integrado, pode representar qualquer tipo de sensor ótico. Com esta abstração podemos integrar "facilmente" qualquer outro sensor ótico que tenha características semelhantes. No nosso exemplo, acabei criando um simulacro de sensor ótico, que chamei de vl6180_sim, e que possue somente funções dummy. Temos aqui uma implementação em firmware do paradigma de polimorfismo, que morrerá quando a placa final chegar e não for mais necessário manter esses dois objetos para simular como seria a implementação completa. Na listagem a seguir apresento como é a inicialização das instâncias da estrutura STOpticalSensorIntpresente em nosso projeto:

 

 

A solução de baixo nível para o acesso aos sensores óticos presentes na placa foi implementada no módulo dev_VL6180x.c , ilustrado abaixo:

 

 

Note que a inicialização é realizada através da chamada da rotina vl6180x_dev_init . Internamente, a vl6180x_dev_init consegue resolver qual dispositivo será controlado. Isso porque esta rotina recebe como parâmetro um ponteiro para uma estrutura STOpticalSensorInt que, por sua vez, possui um ponteiro para o dispositivo a ser inicializado. Neste ponto temos uma bela abstração do tipo de dispositivo que está sendo utilizado, podendo ser de qualquer tipo desde que respeite a interface dada pela estrutura MyDev_t .

 

A inicialização do sensor VL6180x é realizada para que ele trabalhe no modo intercalado, onde o sensor inicia a aquisição da luz ambiente e, assim que esta é finalizada, o mesmo já inicia um processo de aquisição do sensor de proximidade. No final de cada passo, ele deveria gerar uma interrupção via GPIO1, porém este evento não está ocorrendo. Por isso que o firmware na versão atual realiza o polling do status do registrador de interrupção através da chamada das funções  VL6180x_AlsGetInterruptStatus e VL6180x_RangeGetInterruptStatus .

 

Para ter acesso às leituras dos sensores, vamos criar um módulo Sens como exemplo:

 

 

Observe que, por exemplo, ao se chamar o método xOpticalSens[i]->init( xOpticalSens[i] ), não estamos nos importando muito com qual tipo de dispositivo está sendo inicializado. O próprio objeto xOpticalSens possui toda a informação necessária para que ele saiba como deve ser inicializado. O mesmo ocorre para todos os outros métodos presentes na estrutura STOpticalSensorInt. Podemos integrar o módulo Sens à rotina main da seguinte forma:

 

 

A abstração fugiu do controle, não é?

 

Bom, realmente é um pouco difícil de visualizar de início, mas assim que vai observando como os objetos estão relacionados dá para se entender o poder da abstração empregada.

 

Para mostrar um resultado prático, abaixo mostro uma saída no terminal gerado pela implementação na qual estou trabalhando.

 

Resultados: Leitura da luz ambiente (ALS) e da proximidade (Range) em uma aplicação de terminal.
Resultados: Leitura da luz ambiente (ALS) e da proximidade (Range) em uma aplicação de terminal.

 

O repositório com a biblioteca utilizada no artigo pode ser encontrado neste github. Para realizar o port para algum outro microcontrolador, é só reescrever as rotinas VL6180x_I2CWrite e VL6180x_I2CRead. Enjoy! 🙂

 

 

Considerações finais

 

Neste artigo realizamos a integração de um sensor ótico VL6180X e, através de uma certa abstração, criamos uma arquitetura que nos possibilita colocar vários sensores óticos pendurados em uma solução de hardware qualquer. A ideia aqui não era a de abordar conceitos de programação orientada a objetos, mas acabamos caindo nesta área, que pode ser um pouco difícil para o desenvolvedor iniciante.

 

Agora, como você faria para implementar uma solução que fosse independente de placa? Se estamos desenvolvendo para uma placa que ainda está em desenvolvimento e precisamos já ir testando algumas coisas, como fazemos para que a transição seja a menos traumática possível?

 

 

Saiba mais sobre os assuntos abordados no artigo:

 

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Rafael Dias
Sou Bacharel em Física formado pelo Instituto de Física da USP, mestre em Engenharia Elétrica, com ênfase em materiais nanoestruturados pela Escola Politécnica da USP e também Técnico em Automação da Manufatura pela Escola SENAI Anchieta. Trabalho com desenvolvimento de software, firmware e me arrisco com eletrônica analógica para instrumentação e controle. Nos tempos livres gosto de dar uma espairecida e pedalar um pouco.

4
Deixe um comentário

avatar
 
1 Comment threads
3 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Rafael DiasRafael DiasAriel F. Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Ariel F.
Visitante
Ariel F.

Olá, onde que posso encontrar este sensor para comprar no Brasil, o Vl6180x?

Rafael Dias
Visitante
Rafael Dias

Ariel,
este módulo eu achei via distribuidor nacional da ST. No caso, foi com o próprio pessoal da ST.

Porém, você pode ecomendar com a filipeflop. A Adafruit é representada pela FilipeFlop aqui e, pelo o que eu me lembro, é possível vc ecomendar com eles.
Bom, se isso não atender, a FilipeFlop tem um módulo da muito semelhante à este, porém de outro fabricante, o https://www.filipeflop.com/produto/sensor-de-gestos-e-rgb-sparkfun/

Rafael Dias
Visitante
Rafael Dias