Então pessoal, quando comecei a desenvolver soluções envolvendo sistemas embarcados, enfrentei muitas dificuldades para utilizar os recursos que a Raspberry Pi possui, trabalhando com códigos de baixo nível que permitem um maior controle sobre suas funcionalidades computacionais, como por exemplo a linguagem C.
Dessa forma, pensei em utilizar este aspecto como motivação para produzir este artigo, detalhando todos os pontos em uma configuração dos pinos de GPIO da Raspberry Pi, como o Sistema Operacional se porta perante estas configurações, para que seu desenvolvimento tenha um salto de qualidade e que essas dificuldades impostas ao se trabalhar com um sistema embarcado utilizando linguagens de baixo nível sejam solucionadas.
Relato também que este tema já foi abordado nos artigos Raspberry PI – GPIO input com Python e Raspberry PI – GPIO output com Python, mas a linguagem utilizada em ambos foi Python. Portanto, a construção de um material contendo este assunto é muito importante, visto que podemos efetuar um comparativo entre os benefícios que o desenvolvimento de aplicações envolvendo Python ou C podem proporcionar respeitando as características de projeto, mas este assunto fica para um próximo artigo.
Antes de começarmos a desenvolver os códigos, primeiramente precisamos instalar a imagem do S.O operacional desejado. Para executar este procedimento, disponibilizamos em nosso banco de artigos o material Como instalar o Raspbian na Raspberry? que pode ser utilizado como consulta visto que o S.O presente neste projeto foi o Raspbian.
(Nota: Encaminhamos também para consulta o material postado nesta página que ilustra o procedimento de como o cartão deverá ser gravado).
Caso não conheça a Raspberry Pi, recomendamos ler o artigo Raspberry Pi. Se já possui um conhecimento desta plataforma mas não é muito familiarizado com Linux, o artigo Raspberry Pi e o Linux irá proporcionar uma excelente base sobre os dois assuntos.
Configurações do ambiente
- Board: Raspberry Pi 3 model B
- Distribuição: Raspbian GNU/Linux – Jessie (2016-09-23)
- Kernel 4.4.26-v7+
- gcc (Raspbian 4.9.2-10) 4.9.2
GPIO
GPIO (General Purpose Input/Output) é o termo utilizado para o controle de todo pino que possui as funcionalidades gerais de entrada e saída e não possui atribuições dedicadas, ou seja, todo pino que possibilita a configuração como entrada ou saída pode ser classificado como pino de GPIO desde que não possua características funcionais dedicadas.
Estes pinos podem ser configurados como saída (estado lógico alto – 1 ou baixo – 0) ou como entrada utilizado na leitura de push buttons por exemplo.
Controlando os pinos de GPIO
Primeiramente, para controlar os pinos de GPIO de qualquer microcontrolador, precisamos necessariamente saber qual é o seu impacto quando tentamos controlar o mesmo na presença de um Sistema Operacional portado no processador do dispositivo.
Em processadores de alto desempenho, que possuem um sistema operacional, esta ação de controle é submetida ao kernel, ou seja, o kernel que se comunica diretamente com o hardware e os softwares do espaço de usuário possuem apenas um subconjunto de instruções de máquina. Dessa forma, para que um programa que está no espaço de usuário poder ter acesso ao hardware, é necessário que o kernel permita essas operações.
Quando estamos tratando das configurações dos pinos de GPIO da Raspberry Pi, onde o programa do espaço de usuário necessita da permissão de controle dos pinos de GPIO, existem várias bibliotecas que permitem esse controle. Neste documento iremos utilizar a sysfs, que proporciona um controle maior relacionado à manipulação dos arquivos presentes na estrutura de diretórios do S.O.
SYSFS
O Sysfs possui a capacidade de permitir ao código do kernel a exportação das informações necessárias para o controle dos periféricos do espaço de núcleo ao espaço do usuário em um sistema de arquivos em memória, que nos possibilita a manipulação do hardware no espaço do usuário mesmo com a presença do sistema operacional. Esta ação simplesmente nos proporciona uma interatividade entre nossa aplicação e o hardware, trabalhando apenas com arquivos, links simbólicos e diretórios.
Dessa forma, este recurso torna o processo de controle amigável ao desenvolvedor, visto que para o controle dos periféricos é necessário trabalhar apenas com arquivos e suas derivações em conjunto com uma linguagem que possua essas propriedades de manipulação de arquivos.
Estrutura de diretórios do SYSFS
Sysfs é uma simples coleção de diretórios, arquivos e links simbólicos, que permitem a navegação e manipulação utilizando simples comandos shell.
A Figura 1 ilustra a estrutura dos subdiretórios presentes no sysfs:
Adicionalmente, encaminhamos abaixo, a critério introdutório, os diretórios em conjunto com suas especificações técnicas. Como nosso foco está direcionado apenas com as funcionalidades ligadas ao diretório /sys/class/gpio, qualquer dúvida a respeito deverá ser consultada nesta página.
- block: Este diretório possui subdiretórios para cada dispositivo do sistema. Em cada diretório, são descritos os atributos que descrevem as características dos dispositivos.
- bus: Possui um layout com os vários tipos de barramento do kernel presentes no processador. Cada subdiretório que representa a forma de barramento utilizada, possui dois subdiretórios:
- devices
- Conjunto de links simbólicos para cada device encontrado no sistema.
- drivers
- Possui os drivers para cada dispositivo presente no barramento utilizado.
- devices
- class: Diretório que contém as representações de cada dispositivo conhecidas pelo kernel.
- devices: Contém um sistema de arquivos que representa uma árvore de dispositivos.
- firmware: Possui as interfaces de visualização e manipulação de atributos e objetos específicos do firmware.
- module: É o diretório que contém os subdiretórios para cada módulo do kernel.
Diretório /sys/class/gpio/
Este diretório possui três tipos de entrada:
- Interfaces de controle usadas para permitir ao espaço de usuário o controle do pino de GPIO;
- GPIOs;
- Instâncias de Controle do GPIO.
Visando ilustrar como é disposta a estrutura de diretório após a exportação do controle do GPIO para o espaço de usuário, considere a Figura 2.
Inicialmente, para que possamos controlar um pino de GPIO, necessariamente precisamos fazer com que este controle feito no espaço de núcleo seja exportado para o espaço de usuário. Esta interface de controle irá permitir que um programa situado no espaço de usuário solicite ao kernel o controle do GPIO e de suas propriedades:
- export: O programa no espaço de usuário solicita ao kernel o controle do GPIO no espaço de usuário.
- unexport: Reverte as ações efetuadas pelo export.
Após a exportação do pino, precisamos definir como o mesmo deverá trabalhar, se será como OUTPUT ou INPUT através do arquivo direction. Através do arquivo value, é possível realizar a leitura ou escrita no pino de acordo com as configurações definidas no método direction. O arquivo edge nos permite verificar se a tensão sofreu uma borda de subida, descida ou ambas, já o método active_low, permite a inversão dos níveis lógicos de leitura e/ou gravação.
Código
Como mencionado anteriormente, todo o código foi desenvolvido em C e devido à sua extensão, ficou inviável a disponibilização do mesmo no corpo deste artigo.
Sendo assim, iremos citar os principais pontos necessários para configuração de um pino GPIO utilizando a biblioteca sysfs, desde o export do pino até o unexport, onde o controle do pino é liberado do espaço de usuário.
Por fim, disponibilizamos os arquivos fonte no github caso queiram utilizar o código desenvolvido para efetuar testes.
Exportando o pino
Antes mesmo de realizar as configurações dos pinos como entrada/saída, é necessário solicitar ao kernel a permissão de controle do pino desejado a nível de usuário, ou seja, é exportado o controle do pino para o espaço de usuário do sistema.
Após a exportação ser concluída, os pinos estarão disponíveis para serem utilizados e configurados.
Segue abaixo o exemplo de código desenvolvido em C que realiza a exportação do pino:
bool export_gpio(int pin)
{
arquive = open ("/sys/class/gpio/export", O_WRONLY);
if (arquive==-1)
{
printf("Arquivo abriu incorretamente\n");
return false;
}
snprintf(buffer, 3, "%d", pin);
if(write(arquive, buffer, 3) == -1)
{
close(arquive);
return false;
}
close(arquive);
return true;
}
Configurando o pino como INPUT ou OUTPUT
Após a exportação do pino para o controle no espaço do usuário, o pino está pronto para ser configurado. Dessa forma, é necessário informar que tipo de configuração o pino deverá trabalhar, ou seja, se sua direção deverá ser configurada como INPUT ou OUTPUT.
Para ilustrar este processo, o código abaixo detalha a configuração da direção do pino:
bool direction_gpio(int pin, int direction)
{
arquive=0;
snprintf(path, 35, "/sys/class/gpio/gpio%d/direction", pin);
arquive = open (path, O_WRONLY);
if (arquive==-1)
{
return false;
}
snprintf(buffer, 3, "%d", pin);
if (write( arquive, ((direction == INPUT)?"in":"out"), 3 )==-1)
{
close(arquive);
return false;
}
close(arquive);
return true;
}
Efetuando a leitura
Quando o pino é configurado como INPUT, é possível realizar a leitura de valores 0V – baixo e 3,3V – alto de acordo com a configuração utilizada. Neste artigo utilizamos resistores de Pull-Up na entrada da porta, ou seja, sempre quando o botão estiver pressionado, o sinal baixo será injetado no pino, caso contrário permanecerá em estado alto.
Este processo de leitura está ilustrado no código abaixo:
int value_in_gpio(int pin, int value)
{
arquive=0;
char retorno[3];
snprintf(path, 35, "/sys/class/gpio/gpio%d/value", pin);
arquive = open(path, O_RDONLY);
//printf("Descritor do arquivo: %d \n", arquive);
if (arquive == -1)
{
return false;
}
if (read(arquive, retorno, 3) == -1)
{
close(arquive);
return false;
}
close(arquive);
printf("Valor do pino: %c \n", retorno[0]);
return atoi(retorno);
}
Escrita no pino
Quando o pino está configurado como OUTPUT, é possível efetuar a escrita dos valores zero e um na porta de acordo com a funcionalidade desejada, ou a atuação sobre determinada aquisição de sinal.
Este processo de escrita no pino, está ilustrado no código abaixo:
bool value_gpio(int pin, int value)
{
arquive=0;
snprintf(path, 35, "/sys/class/gpio/gpio%d/value", pin);
arquive = open(path, O_WRONLY);
if (arquive == -1)
{
return false;
}
if (write (arquive, ((value == HIGH)?"1":"0"), 1) == -1)
{
close(arquive);
return false;
}
close(arquive);
return true;
}
Unexport do pino
Por fim, após a desutilização do pino exportado, visando evitar que o mesmo esteja consumindo recursos da placa e do S.O, sempre é necessário que um unexport seja aplicado.
Para ilustrar esta ação, encaminhamos o código abaixo:
bool unexport_gpio(int pin)
{
arquive = open ("/sys/class/gpio/unexport", O_WRONLY);
if (arquive==-1)
{
printf("Arquivo abriu incorretamente\n");
return false;
}
if(write(arquive, buffer, 3) == -1)
{
close(arquive);
return false;
}
return true;
}
Esquema de ligação
Neste tópico, será descrito os esquemas de ligação dos circuitos utilizados para simular o código desenvolvido e serão divididos em duas partes, a parte que representa a configuração do pino de GPIO como OUTPUT e a outra como INPUT.
Ligação do pino de GPIO24 como OUTPUT:
Neste cenário, foi utilizado um resistor de 680 Ohms, com o objetivo de limitar a corrente em aproximadamente 20 mA para proteção do led.
Ligação do pino de GPIO24 como INPUT:
Neste cenário, foi adotado um resistor de PULL-UP de 560 Ohms com o objetivo de limitar a corrente no pino em no máximo 6mA.
Com a utilização desta configuração, quando o botão estiver pressionado, o GPIO24 receberá um sinal comum do GND (0 V – baixo), caso contrário (botão não pressionado), o pino receberá o sinal do VCC (3,3 V – alto).
Operação equivalente no terminal
Com o objetivo de ilustrar todas as operações descritas anteriormente de uma forma mais palpável, desde o export do pino até o seu unexport, a Figura 5 exemplifica todos as ações realizadas através de comandos shell:
- O GPIO24 é exportado para o controle no espaço de usuário.
- A direção do GPIO24 é configurada como saída.
- É escrito no pino de GPIO24 os valores alto e baixo exemplificando um led alternando seu status de aceso para apagado.
- Listagem da estrutura de diretórios presentes no controle de GPIO, com o intuito de ilustrar que o pino GPIO24 ainda encontra-se disponível para uso.
- O GPIO24 é liberado após a utilização do pino, ou seja, o unexport é aplicado ao mesmo.
- Após a liberação do pino, são listados os arquivos visando exemplificar que o pino de GPIO24 não está mais disponível para uso.
Código disponível no github
Informamos que os códigos completos para a configuração dos pinos de GPIO como INPUT/OUTPUT estão disponíveis em leal-freitas para utilização.
Relatamos também que este código foi homologado na Raspberry Pi 3 model B, portanto as macros de definição dos pinos de GPIO precisam ser configuradas de acordo com as características do modelo da placa utilizado.
Conclusão
Com o desenvolvimento deste artigo, ficou claro o quão poderosa é a biblioteca sysfs, que nos permite exportar informações do espaço de núcleo ao espaço do usuário aliada a qualquer linguagem que possuem as propriedades de manipulação dos arquivos, permitindo assim a interação entre o software/firmware presente no espaço do usuário e o hardware de uma forma amigável visto que trabalhar com arquivos, links simbólicos e diretórios são bem acessíveis para os desenvolvedores.
Também ficou evidente com este trabalho que ao utilizar linguagens de baixo nível, as mesmas nos permitem e proporcionam um maior controle sobre os periféricos evitando assim, dependências de funções terceiras.
Aprenda Mais
PWM na Raspberry Pi com Python
RFID com Raspberry Pi e Python
Enviando temperatura dos núcleos da Raspberry Pi 3 para o ThingSpeak em C
Referências
GPIO Sysfs Interface for Userspace
GPIO with sysfs on a Raspberry Pi
GPIO: RASPBERRY PI MODELS A AND B












Testei exportar a gpio usando o fopen e não foi possível, mesmo como root.
Tem alguma limitacao?
Show de bola!!!! Parabéns pelo artigo!!
Ciro, muito obrigado!
André, parabéns pelo artigo!
Fabio, muito obrigado!