Este artigo tem como objetivo mostrar a configuração e uso das funções GPIO e PWM no Linux Embarcado. Ambos são funções amplamente utilizadas em projetos de eletrônica, tradicionalmente em sistemas microcontrolados – e, portanto, aprender como fazer uso delas em sistemas Linux embarcado é muito interessante.
Acessando GPIO através de Linux embarcado
Os pinos de entrada e saída de uso geral, do inglês General Purpose Input/Output – GPIO, são portas utilizadas para prover uma interface entre variados periféricos e o microcontrolador/microprocessador. Leitura de botões e sensores digitais (bit banging), o controle de um atuador e até mesmo o uso para debug são exemplos de utilização desse recurso. Este artigo tem como objetivo apresentar o uso da GPIO do módulo Colibri iMX6 com a placa de desenvolvimento Aster Carrier Board, ambos da Toradex.
Antes de iniciarmos a configuração de I/O, é necessário conhecer os pinos disponíveis para manipulação. Embora a escolha dos pinos seja similar para diferentes plataformas embarcadas disponíveis no mercado, cada uma delas tem suas particularidades. O módulo Colibri iMX6, escolhido para emprego neste artigo, tem prontamente disponível 32 GPIOs, e na placa Aster Carrier Board, 8 pinos de GPIO já estão presentes no conector padrão Arduino. Alguns outros pinos estão disponíveis no header padrão Raspberry Pi, e suas funções podem ser encontradas com mais detalhes no datasheet da Aster.
É importante descrever como é formatado o mapeamento e nomenclatura dos pinos de GPIO do módulo iMX6. A maioria dos pinos no SoC podem ser configurados com diferentes funções (multiplexados), e uma delas é GPIO.
O padrão SODIMM 200 possui duzentos pinos para conexão e muitas das vezes o número do pino no conector não corresponde ao número usado para GPIO. Como o processador i.MX6 possui controladores que podem atuar em até 32 GPIOs, a expressão abaixo descreve a correspondência entre os valores do controlador, GPIO correspondente e a o valor numérico simbólico.
32 x (controller – 1) + gpio
Exemplo: GPIO1_IO00 = 0, GPIO2_IO04 = 36
O kernel do Linux utiliza somente representação numérica para o seu subsistema de GPIO e também na interface sysfs, e por isso a fórmula acima é importante. A tabela das funções padrão e alternativas dos 200 pinos do módulo podem ser encontrados no seu datasheet.
Com relação à Aster, neste artigo serão utilizados os pinos do header Arduino. Vamos focar nos conectores X17 e X18, que possuem pinos de GPIO e PWM por padrão. O modo como são configurados está apresentado nas tabelas a seguir:
Conector X17:
Conector X18:
A maneira mais simples de acesso a pinos de I/O no Linux é feita usando o sysfs, através de arquivos exportados no diretório /sys/class/gpio/. Para acessar um pino de I/O é necessário primeiramente exportá-lo para torná-lo controlável. Depois é preciso configurar a direção do pino no arquivo /direction e por último o estado do pino no arquivo /value.
Por exemplo, para utilizar o GPIO 8 como saída em nível alto deve-se fazer:
# echo 8 > /sys/class/gpio/export # echo out > /sys/class/gpio/gpio8/direction # echo 1 > /sys/class/gpio/gpio8/value
De forma semelhante, para utlizar o GPIO 7 como pino de entrada e realizar sua leitura, deve-se fazer:
# echo 7 > /sys/class/gpio/export # echo in > /sys/class/gpio/gpio7/direction # cat /sys/class/gpio/gpio7/value 0
Para exemplificar o uso de GPIO foram escritos dois códigos simples em linguagem C que manipulam os GPIOs como nos comando anteriores.
#include <stdio.h>
int main(){
// Unexport pin 8 and export pin 8
FILE *fun, *fex, *fd, *fv;
fun = fopen("/sys/class/gpio/unexport","w");
fputs("8",fun);
fclose(fun);
fex = fopen("/sys/class/gpio/export","w");
fputs("8",fex);
fclose(fex);
//GPIO como saida
fd = fopen("/sys/class/gpio/gpio8/direction", "w");
fputs("out",fd);
fclose(fd);
//GPIO nível alto
fv = fopen("/sys/class/gpio/gpio8/value", "w");
fputs("1", fv);
fclose(fv);
return (0);
}
E para leitura do pino:
#include <stdio.h>
int main(){
// Unexport pin 7 and export pin 7
FILE *fun, *fex, *fd, *fv;
fun = fopen("/sys/class/gpio/unexport", "w");
fputs("7",fun);
fclose(fun);
fex = fopen("/sys/class/gpio/export", "w");
fputs("7",fex);
fclose(fex);
//GPIO como entrada
fd = fopen("/sys/class/gpio/gpio7/direction", "w");
fputs("in",fd);
fclose(fd);
fv = fopen("/sys/class/gpio/gpio7/value", "r");
int a;
fscanf(fv,"%d", &a);
printf("\nValor GPIO 7: %d\n", a);
fclose(fv);
}
E a seguir um simples blink led em Python:
#!/usr/bin/env python
import os
import time
os.system("echo 8 > /sys/class/gpio/unexport")
os.system("echo 8 > /sys/class/gpio/export")
os.system("echo out > /sys/class/gpio/gpio8/direction")
count = 0
while count < 4:
print "______LED is ON_____"
os.system("echo 1 > /sys/class/gpio/gpio8/value")
time.sleep(1)
print "_____LED is off ____"
os.system("echo 0 > /sys/class/gpio/gpio8/value")
time.sleep(1)
count = count +1
Para ilustrar o uso de GPIO, o vídeo abaixo demonstra o controle liga/desliga de um LED:
Configurando sinal PWM em Linux embarcado
A técnica de PWM é empregada em diversas áreas da eletrônica, talvez a mais comum seja a utilização em fontes chaveadas mas também pode ser utilizada para controle de velocidade de motores, controle de luminosidade, controle de servo motores e diversas outras aplicações. PWM significa “Pulse Width Modulation” ou Modulação de Largura de Pulso, ou seja, através da largura do pulso de uma onda quadrada é possível o controle de potência ou velocidade.
Nesta seção do artigo iremos utilizar o módulo Colibri iMX6 com a Aster Carrier Board da empresa Toradex. Para utilizar o pinos de PWM do módúlo Colibri iMX6 usa-se o mesmo princípio aplicado na seção de GPIO, a manipualção de arquivos.
Módulo Colibri iMX6:
|
Toradex Name |
NXP/Freescale Name |
sysfs path |
Note |
|
PWM_A |
PWM3 |
/sys/class/backlight/backlight.15/ |
Used for backlight control |
|
PWM_B |
PWM1 |
/sys/class/pwm/pwmchip0/ |
– |
|
PWM_C |
PWM4 |
/sys/class/pwm/pwmchip3/ |
– |
|
PWM_D |
PWM2 |
/sys/class/pwm/pwmchip1/ |
– |
Para este artigo vamos utilizar o PWM_B, então primeiro deve-se exportar o PWM dentro do diretório /sys/class/pwm/pwmchip0, configurar o período e duty cycle em nanosegundos, e por fim habilitar a saída.
O exemplo em Shell Script abaixo apresenta a configuração de um sinal PWM de 1kHz a 25% de duty cycle.
$ cd /sys/class/pwm/pwmchip1 $ echo 0 > export $ echo 1000000 > pwm0/period $ echo 250000 > pwm0/duty_cycle $ echo 1 > pwm0/enable
O exemplo em C a seguir executa a mesma configuração de PWM:
#include <stdio.h>
int main(){
FILE *fex, *fp, *fdc, *fh;
// Export
fex = fopen("/sys/class/pwm/pwmchip0/export", "w");
fputs("0",fex);
fclose(fex);
//Periodo
fp = fopen("/sys/class/pwm/pwmchip0/pwm0/period", "w");
fputs("1000000", fp);
fclose(fp);
//Duty cycle
fdc = fopen("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", "w");
fputs("250000", fdc);
fclose(fdc);
//Habilitar
fh = fopen("/sys/class/pwm/pwmchip0/pwm0/enable","w");
fputs("1",fh);
fclose(fh);
return(0);
}
Obtendo como resultado o sinal PWM apresentado nas imagens seguintes:
Para ilustrar o uso do PWM, o vídeo abaixo apresenta o controle de luminosidade de um LED:
E com isso chegamos ao final deste artigo, aplicando as informações que recebemos para demonstrar na prática seu funcionamento. Até logo!









