7 Comentários

Utilizando o MPU-6050 com device driver e device tree na Raspberry Pi Zero W

Introdução

Todos os sistemas embarcados, sem exceção, lidam com algum tipo de periférico: sensores, atuadores, displays e por aí vai. Logo, os sistemas embarcados devem, obrigatoriamente, contar com drivers para estes dispositivos, ou seja, eles dispõe de programas capazes de comunicar uma aplicação com um hardware / periférico específico, seja utilizando uma interface de comunicação ou na base de oscilações de GPIO mesmo.
Com Linux embarcado, isso não é diferente, sendo disponíveis drivers tanto em Kernel Space quanto User Space. Este artigo mostra como trabalhar com sensores utilizando drivers pré-disponíveis em Kernel Space, utilizando como plataforma de hardware uma popular Raspberry Pi Zero W e como dispositivo / sensor o MPU-6050, um IMU contendo acelerômetro (3 eixos), giroscópio (3 eixos) e sensor de temperatura.

Kernel Space x User Space - uma breve explicação

Em termos de Linux embarcado, existem dois grandes “ambientes”: Kernel Space e User Space. Em poucas palavras, no User Space estão os serviços, aplicações e processos de usuário. Já no Kernel Space, está o Kernel propriamente dito e os drivers.

Para acessar recursos de hardware (memórias, periféricos, etc.), aplicações no User Space fazem uso das syscalls (open, write, read, close). Dessa forma, os direitos de acesso aos recursos do sistema fica do lado do Kernel, que através das syscalls interage com o User Space.

Pelo fato dos drivers ficarem em Kernel Space, estes tem acesso direto ao hardware, levando a uma performance muito superior se comparado a aplicações em User Space que se comunicam com periféricos utilizando syscalls.

Device Trees e Linux Device Drivers - uma breve explicação

Pela razão do desempenho de algumas coisas (principalmente acesso à hardware) ser melhor em Kernel Space, acesso irrestrito a recursos computacionais do sistema e também visando maior padronização de drivers, o Kernel conta com um grande conjunto já pré-estabelecido (e você pode criar o seu também, se desejar ou precisar) de drivers de dispositivos, chamados de Linux Device Drivers. Isso significa dizer que, muito provavelmente, um sensor ou periférico que você precisa integrar em um projeto que utiliza Linux embarcado já possui um device driver feito, mantido pelos desenvolvedores do Kernel e exaustivamente testado e depurado. Do ponto de vista da forma como se manifesta no Linux, um device driver compilado é disponível no Kernel na forma de Kernel Modules (arquivos de extensão .ko).

Além disso, para um dispositivo físico ser associado corretamente a um device driver, existe os Device Trees. Resumidamente, device trees são arquivos que são capazes de descrever o hardware sob qual o Kernel irá executar, informando ao Kernel quais dispositivos existem e onde (em que barramento, interface ou GPIOs) estão presentes. Ainda, um Device Tree informa, através de uma tag chamada compatible, qual device driver é capaz de lidar com cada dispositivo, fazendo assim a “amarração” entre os device drivers e os dispositivos físicos.

Logo, o Kernel Linux é capaz de exportar o acesso ao dispositivo na forma de arquivos (via sysfs) ,com base na descrição de hardware do Device Tree e no device driver referente ao dispositivo. Este par de requisitos tornam possível se acessar vários dispositivos de quaisquer aplicações fazendo simples leituras de arquivos.

Como tais arquivos são visíveis e acessíveis no User Space, programas e serviços em User Space podem acessar, com grande performance, dispositivos cujo os drivers estão rodando em Kernel Space, unindo-se assim o melhor dos dois mundos: desempenho do Kernel Space com a segurança do User Space.

Você pode estar se perguntando agora: mas porque usarei um Linux Device Driver, Device Tree e tudo mais se existem bibliotecas por aí para vários sensores, instaláveis facilmente no User Space? Bom, as principais razões para se utilizar os sensores e demais periféricos com os device drivers são:

  1. Performance: a performance (velocidade de leitura, por exemplo) de um sensor com uma biblioteca em User Space é muito inferior se comparado ao que um Device Driver pode oferecer. A depender da aplicação, o uso de uma biblioteca em User Space pode ser inviável.
  2. Padronização e confiabilidade: os device drivers são mantidos junto com o Kernel pelos desenvolvedores de Kernel, logo são exaustivamente utilizados, testados e depurados nas mais variadas aplicações e cenários diferentes. Dessa forma, a chance de haver um bug em um device driver é mínima.
  3. Aplicação final e com código-fonte mais elegante: uma vez que toda a parte baixo nível de acesso ao dispositivo está em Kernel Space, basta uma aplicação ou serviço no User Space ler um ou alguns arquivos para se obter uma leitura de um sensor, por exemplo. Dessa forma, o código-fonte fica muito mais “clean” para se ler um dispositivo, deixando o código final da aplicação mais enxuto.
  4. Menor número de dependências: uma vez que os device drivers são mantidos junto com o Kernel e gerenciados em execução pelo Kernel (sem precisar instalar nenhuma biblioteca a mais, desde que presentes no sistema Linux em questão, claro), o número de dependências de uma solução final é muito menor. Isso deixa a etapa de setup de um dispositivo em campo ou em fabricação muito mais rápida e simples.

Device Tree Overlays

Um Device Tree pode se manifestar basicamente de duas formas:

  1. Device Tree único, compilados junto com o Kernel: toda distribuição que faz uso de Device Trees possui ao menos um Device Tree “pai”, que descreve o hardware básico da plataforma a qual executa. Este Device Tree normalmente é compilado junto com o Kernel Linux da plataforma e é carregado pelo Kernel, de forma obrigatória.
  2. Device Tree Overlay: são arquivos de device tree compilados (blobs, na forma de arquivos .dtbo) que podem ser carregados dinamicamente durante o uso do sistema operacional. Dessa forma, o device tree “pai” é modificado dinamicamente, dando grande flexibilidade para o desenvolvedor carregá-los somente quando necessário.

Embora seja plenamente possível se fazer tudo (indicar todos os dispositivos que vai se trabalhar) com um único Device Tree “pai” / base, isso não é uma prática muito recomendada, pois acarreta em Devices Trees demasiadamente grandes, complexos e o tempo de boot pode ser longo (uma vez que, no boot, o Kernel deverá carregar todos os device drivers necessários). Dessa forma, uma boa prática é ter um Device Tree base e o restante dos periféricos opcionais ser carregado dinamicamente conforme a necessidade. Além disso, fica mais fácil e rápido ao desenvolvedor adicionar ou remover dispositivos, uma vez que não precisaria recompilar o Kernel (ou seu Device Tree) toda vez que um dispositivo novo precisasse acessado pelo sistema operacional.

Carregando um dispositivo dinamicamente: verificação do Device driver (MPU-6050)

Considerando que está sendo utilizada uma distro padrão (Raspberry Pi OS, por exemplo) para a Raspberry Pi Zero W, antes de se carregar um dispositivo dinamicamente através de um Device Tree Overlay é preciso verificar se o device driver deste dispositivo está disponível.

Para isso, primeiro você deve saber a versão do Kernel Linux que a Raspberry Pi Zero W está usando, uma vez que é possível no Linux se utilizar vários Kernel Linux (um por boot / um de cada vez). Isso pode ser feito com o comando abaixo:

Uma vez descoberta a versão do Kernel Linux utilizado, procure pelo nome do dispositivo na pasta de device drivers, localizada em /lib/modules/VERSAO_DO_KERNEL/kernel/drivers, sendo VERSAO_DO_KERNEL a versão que você utiliza. Por exemplo, na minha Raspberry Pi Zero W, a versão do Kernel que está sendo usada é a 4.19.97+, conforme mostra a figura 1.

Figura 1 - versão de Kernel e diretório de device drivers

Como exemplo, vamos supor que deverá ser utilizado um MPU-6050 (IMU com acelerômetro e giroscópio de 3 eixos e sensor de temperatura). Procurando neste diretório por arquivos que tenham no nome a keyword “mpu”, descobrimos que o device driver do MPU-6050 está localizado em /lib/modules/4.19.97+/kernel/drivers/iio/imu/inv_mpu6050/inv-mpu6050.ko, conforme mostra a figura 2.

Figura 2 - localização do device driver do MPU-6050

Caso você não encontre o device driver do dispositivo que deseja, há duas possibilidades: ou o dispositivo não possui Device Driver implementado ainda ou este existe e não foi compilado para a versão de Kernel que você está utilizando. Uma solução seria compilar novamente (e atualizar na Raspberry Pi Zero W) o Kernel adicionado o Kernel Module do dispositivo em questão na compilação ou, ainda, somente recompilar os Kernel Modules desejados e fazer atualização na Raspberry Pi Zero W. Por ser um processo longo e que foge um pouco do propósito deste artigo, indico a seguinte referência para tal: https://www.raspberrypi.org/documentation/linux/kernel/building.md

Carregando um dispositivo dinamicamente: verificação do Device Tree Overlay (MPU-6050)

Uma vez existente o device driver do dispositivo desejado, é preciso verificar se existe um Device Tree Overlay para o mesmo. Na Raspberry Pi Zero W, os Device Tree Overlays disponíveis ficam em /boot/overlays. Logo, novamente supondo que vamos utilizar o MPU-6050, busca-se por um arquivo que tenha “mpu” no nome. Conforme mostra a figura 3, o Device Tree Overlay para o MPU-6050 existe e tem nome mpu6050.dtbo.

Figura 3 - Device Tree Overlay do MPU-6050

Desta forma, está presente tudo que é necessário para utilizar o MPU-6050 na forma de um overlay: device driver e Device Tree Overlay para o dispositivo.

Montagem do circuito

Para conseguir ler o MPU-6050, antes de tudo este deve ser ligado à Raspberry Pi Zero W.
Para isso, utilize o circuito esquemático da figura 4.

Carregando o Device Tree Overlay - MPU-6050

Na Raspberry Pi, um Device Tree Overlay pode ser carregado de duas formas:

  1. Utilizando-se o comando dtoverlay (https://www.raspberrypi.org/documentation/configuration/device-tree.md) no terminal, o que exige, obrigatoriamente, ser executado como superusuário (sudo).
  2. Utilizando-se dtoverlay automaticamente no boot, através de uma chamada no arquivo /boot/config.txt.

Este artigo considerará a forma 2, por ser mais simples de se utilizar, mais simples de se escrever e, ainda, ser automaticamente carregado no boot. Apesar de não se carregar muitos drivers no boot ser uma das vantagens do Device Tree Overlay (conforme dito anteriormente), utilizar isto desta forma deixa possível se utilizar um Device Tree “pai” / base mais simples (com o básico) e, todo o restante, claramente identificável por chamadas de dtoverlay em /boot/config.txt. Logo, se o tempo de boot não for um problema tão grande, essa forma de trabalhar pode ser mais organizada e clara.

As chamadas de dtoverlay seguem esta documentação: https://raw.githubusercontent.com/raspberrypi/firmware/master/boot/overlays/README . Nela, estão descritos todos os dispositivos que podem ser suportados via Device Tree Overlay, assim como mostra como fazer suas chamadas com dtoverlay e quais parâmetros cada dispositivo aceita.

Voltando ao dispositivo MPU-6050, a documentação dele informa o seguinte:

Ou seja, ele deve ser chamado como mpu6050 e aceita somente um parâmetro chamado interrupt, que por default é no GPIO 4. Este parâmetro corresponde a dizer em qual GPIO é ligado o pino INT do sensor, o qual serve para informar que há medições prontas do IMU para leitura.

Por razões de simplificação e visando maior didática, vou omitir o uso deste parâmetro. desta forma, adicionando a seguinte linha ao final do arquivo /boot/config.txt (atenção: é preciso acessar este arquivo como superusuário).
Dica: para evitar problemas com formato de arquivo e terminadores de linha, utilize um editor da própria Raspberry Pi Zero W, como o nano por exemplo.

O Kernel “saberá” que há um MPU-6050 na I²C (neste caso, na I²C-1, a única acessível pelo usuário / programador) e exportará os arquivos via sysfs para leitura deste sensor.

Salve o arquivo /boot/config.txt e reinicie a Raspberry Pi com o seguinte comando:

Lendo o dispositivo MPU-6050

Após o reboot, o seu dispositivo terá o device driver e Device Tree automaticamente carregados e estará pronto para uso. Pelo MPU-6050 se tratar de um dispositivo do tipo iio, este estará disponível em /sys/bus/iio/devices/. Observe a figura 5, onde são mostrados os arquivos exportados via sysfs e a leitura, via arquivo do sysfs da aceleração no eixo X.

Figura 5 - arquivos para acesso ao MPU-6050 exportados via sysfs e leitura da aceleração do eixo X

Dessa forma, qualquer programa ou serviço em User Space que desejar ler informações do MPU-6050, basta fazer leituras nos arquivos disponíveis em /sys/bus/iio/devices/iio:device0.

Conclusão

O uso de device drivers e Device Tree Overlays no Linux embarcado é algo que leva a grandes vantagens, sendo dentre elas as principais: desempenho, confiabilidade e código-fonte “clean” de uma aplicação que faz uso de um dispositivo lido desta forma.


Para tal, tomando a Raspberry Pi Zero W como exemplo, é preciso verificar a existência do device driver (na forma de Kernel Modules, extensão .ko) e do Device Tree Overlay (extensão .dtbo) e, se ambos estiverem disponíveis. fazer o carregamento destes com o comando dtoverlay, seja via linha de comando ou no boot (em /boot/config.txt).

Sem licença Creative Commons

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

Linux Embarcado » Utilizando o MPU-6050 com device driver e device tree na Raspberry Pi Zero W
Comentários:
Notificações
Notificar
guest
7 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Marcelo Campos Silva
Marcelo
17/08/2020 13:31

Artigo muito bom!
Respondeu algumas dúvidas mais básicas sobre o OS que eu tive.
Vocês podem fazer um passo a passo de como fazer um device driver de um gpio para ligar e desligar um led? Seja usando os drivers do gpio em si ou mesmo acessando registradores?

Tiago Henrique Macedo
15/08/2020 00:37

Oi Pedro, obrigado pela iniciativa de estar sempre compartilhando algo bacana com a comunidade! Acho a Raspberry Pi Zero um produto incrível se considerarmos o preço oficial de apenas $5...

Ronaldo Nunez
Ronaldo Nunez
13/08/2020 15:07

Excelente artigo, Pedro. Vou reproduzir os passos esse fim de semana.

Talvez você goste:

Nenhum resultado encontrado.

Séries

Menu

WEBINAR
 

Soluções inteligentes para acionamento de MOSFETs/IGBTs com família STDRIVE

Data: 08/10 às 15:00h - Apoio: STMicroelectronics
 
INSCREVA-SE AGORA »



 
close-link