terça-feira, janeiro 11, 2022

Saída Digital no RP2040 (Raspberry Pi Pico)

A saída digital em microcontroladores parece ser muito simples, principalmente se você está acostumado a programar com a biblioteca Arduino. Com um comando você coloca um pino em nível alto ou baixo. Isso é tudo? Não... vamos ver neste post algumas considerações importantes no caso particular do RP2040 (o microcontrolador usado na Raspberry Pi Pico).

Display de 7 segmentos acionado pela Pi Pico


Um detalhe: o RP2040 possui 36 pinos de E/S de uso geral (GPIO), divididos em dois bancos. Um destes bancos, com 6 pinos, é normalmente destinado à conexão da Flash onde reside o programa. O que vamos falar aqui se refere principalmente ao outro banco (o "User Bank"), onde estão GPIO0 a GPIO29.

A primeira coisa a lembrar é que um pino de microcontrolador pode ter muitas funções. A figura abaixo mostra a estrutura lógica de um pino, indicando como diversas parte do microcontrolador podem ser ligadas a ele:

Fonte: datasheet do RP2040

A saída digital "normal" corresponde ao "GPIO" na figura, que é implementado como SIO (Single-Cycle I/O). Basicamente temos um conjunto de três registradores:

  • GPIO_OUT determina o valor do pino, se a saída estiver habilitada e o pino estiver configurado para GPIO
  • GPIO_OE se o pino estiver configurado para GPIO, controla se a saída está habilitada (1) ou não (0). Quando a saída não está habilitada ela fica num estado de alta impedância (o que normalmente chamamos de "entrada").
  • GPIO_IN indica o nível lógico no pino. Este registrador é atualizado mesmo que a função do pino não seja GPIO.

Este conjunto de registradores é compartilhado pelos dois cores do RP2040. O termo "Single-Cycle" se refere ao fato destes registradores poderem ser acessados de forma atômica pelas instruções SET, CLR e XOR. Isto significa que, atualizando os registradores com estas instruções, não existe problema de concorrência entre os dois cores ou interrupções, é uma operação indivisível. Note que se você ler um registrador, gerar um novo valor e depois escrever (em operações separadas) pode ocorrer de o outro core ou uma interrupção alterar o conteúdo do registrador após a leitura e antes da escrita, gerando problemas.

Voltando à figura lá atrás, repare o bloco "I/O Pad", que representa a interface elétrica entre a lógica interna e o pino propriamente dito:

Fonte: datasheet do RP2040

Este módulo possui várias configurações, também controladas por registradores SIO:

  • Slew Rate determina a rapidez na mudança de nível
  • Drive Strength determina quão "forte" é o sinal gerado
  • Podemos ativar resistores internos de pull-up e pull-down (entre 50 e 80 kOhms)
  • Podemos habilitar ou desabilitar o buffer de entrada
  • Podemos habilitar ou desabilitar a histerese (schmitt trigger) do buffer de entrada. Quando habilitado são usados dois limites de tensão, uma para determinar que o sinal passou do nível alto para baixo e outro para determinar a mudança no sentido contrário. Isso evita oscilações na leitura em caso de variações pequenas.

Os gráficos abaixo mostram o efeito da configuração de drive strength:

Donte: datasheet do RP2040

Ou seja, um drive stength mais alto permite obter uma tensão mais próxima da ideal se for solicitada uma corrente alta.

Independente desta configuração, o datasheet indica uma corrente máxima total de 50mA nos pinos de I/O.

Embora o datasheet informe os endereços e bits dos registradores de controle, a recomendação é utilizar as funções do SDK para isso. O exemplo abaixo corresponde a acender todos os segmentos de um display de 7 segmentos de catodo comum. Tanto os segmentos como o catodo comum estão ligados a pinos de GPIO. Resistores de 1K em cada segmento limitam a corrente em cada segmento em  1,4mA (para o display usado para teste), o catodo comum irá fornecer a soma destas correntes (7 x 1,4 = 9,8mA), por isso foi configurado para uma "força" maior.

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"

int main() {
    gpio_init_mask (0xFF);  // configura os pinos para GPIO
    gpio_set_dir_masked (0xFF, 0xFF);    // configura os pinos como saída
    gpio_set_drive_strength (7, GPIO_DRIVE_STRENGTH_12MA);  // GPIO7 "vitaminado"
    gpio_clr_mask (0x7F);   // nível baixo nos pinos ligados aos segmentos
    gpio_set_mask (0x80);   // nível alto no anodo comum
    while (1)
        ;
    return 0;
}

O código acima usa versões das funções do SDK onde uma máscara de bits seleciona os GPIOs afetados. No meu caso GPIO0 a GPIO6 são os catodos dos segmentos e GPIO7 é o anodo comum.

Detalhes sobre o RP2040 podem ser vistos no datasheet e uma descrição detalhada das funções do SK estão no respectivo manual. Ambos podem ser vistos em:

https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html

Nenhum comentário: