Site icon Embarcados – Sua fonte de informações sobre Sistemas Embarcados

Usando GPIO e PWM no Linux Embarcado

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!