Raspberry Pi Pico: Vale a pena aprender?

O Raspberry Pi Pico desde o lançamento vem sendo muito desejado por muitos projetistas, mas será que realmente vale a pena aprender? Neste post você vai aprender tudo sobre ele.

1 Surgimento do Raspberry Pi Pico

Quem acompanha a empresa Raspberry Pi, sabe que todos seus lançamentos tem um preço base para um determinado produto e o propósito disso é democratizar ao máximo o acesso aos produtos da empresa.

Até então, o produto mais barato da empresa era o Raspberry Pi Zero que custava em torno dos $15, com um hardware bem primário não servia para utilizar como um computador desktop, porém era o mais próximo de um computador e um microcontrolador.

Estrutura do Raspberry pi Zero
Fonte: Embarcados

Porém pagar mais de $15 em um Raspberry para usar apenas as GPIO’s é completamente inviável, ainda mais com os preços atuais.

Raspberry Pi Pico

Então em 2021 a Rasp lançou o seu produto mais barato, o Pico, com apenas $4 teria o mais próximo das GPIO’s dos minicomputadores porém em um microcontrolador Cortex M0+.

2 O que é Cortex M0+?

Em algum momento em sua vida você já deve ter escutado sobre a tecnologia ARM, seja em celulares, computadores como o novo Macbook que utiliza um processador com arquitetura ARM ou em microcontroladores.

Estrutura interna dos microcontroladores ARM cortex-M0+

ARM na verdade é uma empresa holding que foi fundada em 1990 como uma join-venture (empresa conjunta por outras empresas) entre a Acorn Computers, Apple Computer e VLSI Technology (como Advanced RISC Machines).

A tecnologia ARM criou um ecossistema em que mais de 52 empresas utilizam essa arquitetura em seus produtos, por exemplo a Broadcorn que fabrica microprocessador que é utilizado no Raspberry Pi e que tem tecnologia ARM.

2.3 Classificação ARM

Segundo uma publicação do site Newnton C Braga existem três classificações para os microcontroladores ARM, sendo eles:

  1. Cortex-M (microcontroller) – Podem ser usados em aplicações típicas de microcontroladores como geladeiras, lavadoras, controles remotos, etc.
  1. Cortex-R (Real Time) – Podem ser usados em reprodutores MP3, controles de motores, robóticas, etc;
  1. Cortex-A (Application) – Podem ser usados em equipamentos que exigem sistemas operacionais como celulares, tablets, computadores, etc.

2.4 Gerações do ARM Cortex M

Assim como a AMD e Intel, os ARM também são divididos por gerações e no caso do Cortex M0+ significa que pertence à primeira geração, esta imagem a seguir que é presente nos datasheets da ARM ajuda a entender as diferenças.

Características que diferenciam as gerações dos microcontroladores ARM cortex-M

De cara já dá para perceber que o Pico utiliza na verdade uma tecnologia “antiga”, mas se pegar documentação da Raspberry, estudar e praticar você terá maiores facilidades para entender outros microcontroladores como o STM32 por exemplo.

STM são os microcontroladores mais usados em produtos, se você pegar um hoverboard vai ter um STM32, eletrônica automotiva é certo ter também, mas aprender STM32 logo de cara pode ser uma tarefa desanimadora se você gosta de ter logo o resultado. 

A comunidade da STM  não é a mesma de um Arduino ou a do Raspberry Pi Pico e além disso, as documentações são bem mais técnicas e não muito amigáveis. Por isso, iniciar com o Pico vai te possibilitar a ter uma curva de aprendizado mais rápida com a arquitetura ARM.

3 Raspberry Pi Pico: Especificações

O microcontrolador RP2040 é dual-core Arm Cotex-M0+ e tem uma RAM interna de 264kB com suporte até 16MB de memória flash externa, porém na placa Rasp Pico, já conta com uma memória flash de 2MB.

Estrutura do Raspberry Pi Pico

Além de tudo isso, o clock o Pico pode chegar até 133MHz, isso é 8.31 vezes mais poderoso que um Arduino UNO com cristal de 16MHz, mas nem tudo trabalha com essa frequência. 

3.1 Clock

O RP2040 tem que ter uma entrada de clock de 6 a 12MHz, na placa Pico já vem com um cristal de quartzo de 12MHz.

Cristal do raspberry pi pico

Por mais que o clock de entrada seja de 6 a 12MHz, o clock de processamento pode chegar até 133MHz graças a uma técnica conhecida como PLL.

Diagrama de blocos da entrada de clock do RP2040

Porém o 133MHz na verdade é válido apenas para processamento, os demais apresentam limites de clock diferentes, veja só. 

Clock máximo de cada periférico do RP2040

3.2 Pinout

Além disso, o RP2040 possui duas 2x UART, 2x I2C, 2x SPI e 30x GPIO’s sendo 3x ADC e todos com suporte a PWM. Isso fica nítido ao visualizar o pinout.

Pinout do Raspberry Pi Pico

Perceba que mais de um GPIO possui o mesmo suporte a um mesmo barramento de comunicação, isso significa que você pode optar pelo GPIO que você pretende usar para determinada função da comunicação.

Isso é muito bom, pois flexibiliza o desenvolvimento dos circuitos, diferentemente de microcontroladores como o ESP32 e o Atmega328p que tem barramento fixo. 

Uma coisa muito importante que você deve se atentar é em relação a tensão, pois no RP2040 a tensão é de 1v8 a 3v3, porém é fixa em 3v3 no Pico. 

3.3 Alimentação

Diferentemente de outras placas embarcadas o Pico não usa um regulador de tensão e sim um conversor DC-DC buck boster.

O conversor foi escolhido pela equipe da Raspberry para que viabilizasse aplicações com bateria, o conversor presente na placa é o  RT6150 e o diagrama da alimentação é este:

3.3.1 Mas o que é um Buck Boster?

Basicamente um Buck Boster é como se fosse um Step up e Step down combinados, de modo que se a tensão for menor que a desejada, ele eleva e se for acima ele reduz. 

Este tipo de conversor acaba sendo muito bom para aplicações que utilizam bateria, pois a tensão mínima de entrada é de 1v8 e a máxima é de 5v5, no caso do RT6150

O bom é que este CI é bem simples de usar e você pode por em seus projetos com outros microcontroladores seguindo o mesmo esquemático abaixo:

Esquemático do buck boster RT150a

3.3.1.1 Observações

Embora o Pico tenha sido pensado para utilizar bateria como alimentação, ele não possui nenhum sistema de proteção de carga,  sobrecarga, sobretensão ou proteção contra descarga excessiva. 

3.3.2 Raspberry Pi Pico com Bateria Li-ion

Por mais que o Rasp Pico utilize um conversor DC-DC para viabilizar o uso de bateria, é necessário ainda assim, implementar um circuito que resolva todos os problemas da observação acima. 

Considerando que sua aplicação com bateria seja algo simples, dá para usar um  TP4056 – Protect, ele tem todas as características de segurança que precisamos. 

Para montar o circuito, vamos analisar novamente o circuito de alimentação, se você observar bem, vai notar que há dois pinos de VCC “Vbus” e “Vsys”.

O Vbus antecede um diodo de proteção, porém se ligarmos a bateria 18650 nele, haverá uma queda de tensão desnecessária, então temos que por em Vsys, dessa forma:

Raspberry Pi Pico e Bateria de Li-ion

4 Primeiros passos

Para que possamos usar o Pico, é necessário prepara e configurar todo nosso ambiente de trabalho. Primeiramente vamos configurar a IDE.

4.1 Instalando e configurando Thonny IDE

A IDE que é utilizada para o Pico é a Thonny pois nesse post só será abordado a programação em Micropython, você até pode programar em C, mas tem tanto material em Micropython para esta placa que talvez nem seja vantajoso manter em C.

Para baixar a IDE, basta clicar aqui, selecionar teu sistema operacional qual e em seguida basta seguir o padrão de qualquer instalação de programa no computador.

Agora, só precisamos configurar a IDE para programar para o Pico, para isso basta por o mouse no menu “Tools” e clicar em “Options”. Em seguida clique em “Interpreter” e escolha o seguinte interpretador:

Selecionando interpretador do Thonny IDE

4.2 Configurar o Pico para uso.

Antes de começarmos a usar o Pico na IDE, é necessário fazer uma pequena configuração na placa. Note que há um botão na placa, você deve manter pressionado e inserir o cabo USB, da seguinte forma:

Como por o RP2040 em modo boot
Fonte: Circuitaria

Após feito a etapa anterior, o computador vai reconhecer como se fosse um “pendrive” e irá abrir o diretório com alguns arquivos. Você deve baixar o arquivo UF2 e colar no diretório aberto do Pico. 

Gravando o arquivo .UF2

Pronto, agora a única configuração a ser feita é configurar a IDE para identificar a porta USB em que o Pico está conectado, para isso basta ir em “Tools” -> “Options” -> “Interpreter” e selecionar a opção “Try detect port automatically” ou selecione manualmente.

4.3 Olá mundo

Após ter feito todas as configurações podemos fazer nosso primeiro “Olá mundo”, neste exemplo vamos apenas imprimir a mensagem “Olá mundo”. Ao decorrer desta postagem, você aprenderá outras funcionalidades.

Se você não desconectou o Pico após ter adicionado o arquivo .UF2, desconecte e conecte novamente para o Rasp sair do modo boot. Em seguida, digite o seguinte código.

Esse pequeno código será o suficiente para fazer o primeiro teste com o Pico. Para executar o código basta clicar no botão de “play” no canto superior esquerdo do Thonny. 

Rodando o primeiro còdigo no Thonny IDE

Após fazer isso, pode aparecer esse pequeno pop up para selecionar onde o código vai ficar salvo, você pode escolher qualquer uma das opções. Prefira salvar no rasp e faça uma cópia no computador.

Caso você queira que um determinado código fique executando mesmo com o Pico desconectado, basta renomear o código para “main.py”.

Salvando còdigo no thonny IDE

Salvar o arquivo não é algo que você precisa fazer em qualquer teste de código, é possível rodar o programa sem salvar.

4.3.1 Código do “Olá Mundo!”

Como o objetivo é apenas demonstrar o funcionamento, o código neste exemplo é apenas uma linha de código e extremamente simples.

print("Olá Mundo!")

4.3.2 Teste prático do Olá mundo

Quando utiliza o comando “print”, os valores são impressos no canto inferior da tela do Thonny IDE, veja só:

Primeiro teste no RP2040 em micropython

5 Como instalar bibliotecas no Thonny IDE

Quem já usa o Arduino IDE sabe o quão fácil é instalar bibliotecas e no Thonny também é bem fácil, talvez até um pouco mais fácil.

Neste tópico você vai aprender como instalar qualquer biblioteca através de dois métodos. Para exemplificar, vamos instalar a biblioteca do display OLED e também do módulo RFID, ambas as bibliotecas serão usadas respectivamente nos exemplos de comunicação I2C e SPI.

5.1 Primeiro método

No primeiro método, toda instalação é feita pela própria IDE, neste caso vamos instalar a biblioteca SSD1306 para o display OLED 0.92”.

Para começar a instalação, clique no botão “tools” na área de menu no canto superior esquerdo, em seguida clique em “Manager Packages”.

Ah, para fazer isso o pico não pode estar executando nenhum código durante o processo, caso contrário ocorrerá um erro.

Após ter feito a tarefa anterior, será exibido uma janela de instalação de bibliotecas, pesquise por SSD1306 e clique em “Search on PyPI”

A biblioteca em questão é a “Micropython-ssd1306”, clique em instalar, após a instalação a biblioteca vai aparecer na lista da esquerda aonde tem “<INSTALL>”.

Como instalar bibliotecas no Thonny IDE

Quando toda instalação terminar, perceba que será criado uma pasta “lib” no Pico contendo os arquivos em Python que são as bibliotecas. 

Estrutura de arquivos do Thonny IDE

Fiz um pequeno teste com esse “lib” e notei que o código da biblioteca pode ficar fora dessa pasta que vai funcionar, porém dentro fica mais organizado, concorda?

5.2 Segundo método

Ao fazer a instalação da biblioteca pelo método acima, é possível entender como podemos fazer a instalação de forma manual. 

Neste segundo método, vamos instalar a biblioteca do MFRC522 de forma manual. Talvez você possa estar se perguntando sobre a necessidade de aprender esse outro método e a resposta é bem simples, nem tudo está disponível pelo o outro método, esse do MFRC522 não está. 

Para começarmos, vamos primeiramente abrir um novo arquivo, para isso basta clicar no papel do canto superior esquerdo.

Instalando biblioteca de forma manual no Thonny IDE

Pronto, basta você copiar todo o código a seguir e colar dentro desse novo arquivo que acabamos de criar. 

from machine import Pin, SPI
from os import uname
class MFRC522:
    DEBUG = False
    OK = 0
    NOTAGERR = 1
    ERR = 2
    REQIDL = 0x26
    REQALL = 0x52
    AUTHENT1A = 0x60
    AUTHENT1B = 0x61
  
    PICC_ANTICOLL1 = 0x93
    PICC_ANTICOLL2 = 0x95
    PICC_ANTICOLL3 = 0x97
  
    def __init__(self, sck, mosi, miso, rst, cs,baudrate=1000000,spi_id=0):
        self.sck = Pin(sck, Pin.OUT)
        self.mosi = Pin(mosi, Pin.OUT)
        self.miso = Pin(miso)
        self.rst = Pin(rst, Pin.OUT)
        self.cs = Pin(cs, Pin.OUT)
        self.rst.value(0)
        self.cs.value(1)
        
        board = uname()[0]
        if board == 'WiPy' or board == 'LoPy' or board == 'FiPy':
            self.spi = SPI(0)
            self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso))
        elif (board == 'esp8266') or (board == 'esp32'):
            self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso)
            self.spi.init()
        elif board == 'rp2':
            self.spi = SPI(spi_id,baudrate=baudrate,sck=self.sck, mosi= self.mosi, miso= self.miso)
        else:
            raise RuntimeError("Unsupported platform")
        self.rst.value(1)
        self.init()
    def _wreg(self, reg, val):
        self.cs.value(0)
        self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e)))
        self.spi.write(b'%c' % int(0xff & val))
        self.cs.value(1)
    def _rreg(self, reg):
        self.cs.value(0)
        self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80)))
        val = self.spi.read(1)
        self.cs.value(1)
        return val[0]
    def _sflags(self, reg, mask):
        self._wreg(reg, self._rreg(reg) | mask)
    def _cflags(self, reg, mask):
        self._wreg(reg, self._rreg(reg) & (~mask))
    def _tocard(self, cmd, send):
        recv = []
        bits = irq_en = wait_irq = n = 0
        stat = self.ERR
        if cmd == 0x0E:
            irq_en = 0x12
            wait_irq = 0x10
        elif cmd == 0x0C:
            irq_en = 0x77
            wait_irq = 0x30
        self._wreg(0x02, irq_en | 0x80)
        self._cflags(0x04, 0x80)
        self._sflags(0x0A, 0x80)
        self._wreg(0x01, 0x00)
        for c in send:
            self._wreg(0x09, c)
        self._wreg(0x01, cmd)
        if cmd == 0x0C:
            self._sflags(0x0D, 0x80)
        i = 2000
        while True:
            n = self._rreg(0x04)
            i -= 1
            if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
                break
        self._cflags(0x0D, 0x80)
        if i:
            if (self._rreg(0x06) & 0x1B) == 0x00:
                stat = self.OK
                if n & irq_en & 0x01:
                    stat = self.NOTAGERR
                elif cmd == 0x0C:
                    n = self._rreg(0x0A)
                    lbits = self._rreg(0x0C) & 0x07
                    if lbits != 0:
                        bits = (n - 1) * 8 + lbits
                    else:
                        bits = n * 8
                    if n == 0:
                        n = 1
                    elif n > 16:
                        n = 16
                    for _ in range(n):
                        recv.append(self._rreg(0x09))
            else:
                stat = self.ERR
        return stat, recv, bits
    def _crc(self, data):
        self._cflags(0x05, 0x04)
        self._sflags(0x0A, 0x80)
        for c in data:
            self._wreg(0x09, c)
        self._wreg(0x01, 0x03)
        i = 0xFF
        while True:
            n = self._rreg(0x05)
            i -= 1
            if not ((i != 0) and not (n & 0x04)):
                break
        return [self._rreg(0x22), self._rreg(0x21)]
    def init(self):
        self.reset()
        self._wreg(0x2A, 0x8D)
        self._wreg(0x2B, 0x3E)
        self._wreg(0x2D, 30)
        self._wreg(0x2C, 0)
        self._wreg(0x15, 0x40)
        self._wreg(0x11, 0x3D)
        self.antenna_on()
    def reset(self):
        self._wreg(0x01, 0x0F)
    def antenna_on(self, on=True):
        if on and ~(self._rreg(0x14) & 0x03):
            self._sflags(0x14, 0x03)
        else:
            self._cflags(0x14, 0x03)
    def request(self, mode):
        self._wreg(0x0D, 0x07)
        (stat, recv, bits) = self._tocard(0x0C, [mode])
        if (stat != self.OK) | (bits != 0x10):
            stat = self.ERR
        return stat, bits
  
    def anticoll(self,anticolN):
        ser_chk = 0
        ser = [anticolN, 0x20]
        self._wreg(0x0D, 0x00)
        (stat, recv, bits) = self._tocard(0x0C, ser)
        if stat == self.OK:
            if len(recv) == 5:
                for i in range(4):
                    ser_chk = ser_chk ^ recv[i]
                if ser_chk != recv[4]:
                    stat = self.ERR
            else:
                stat = self.ERR
        return stat, recv
    
    def PcdSelect(self, serNum,anticolN):
        backData = []
        buf = []
        buf.append(anticolN)
        buf.append(0x70)
        #i = 0
        ###xorsum=0;
        for i in serNum:
            buf.append(i)
        #while i<5:
        #    buf.append(serNum[i])
        #    i = i + 1
        pOut = self._crc(buf)
        buf.append(pOut[0])
        buf.append(pOut[1])
        (status, backData, backLen) = self._tocard( 0x0C, buf)
        if (status == self.OK) and (backLen == 0x18):
            return  1
        else:
            return 0
    
    
    def SelectTag(self, uid):
        byte5 = 0
        
        #(status,puid)= self.anticoll(self.PICC_ANTICOLL1)
        #print("uid",uid,"puid",puid)
        for i in uid:
            byte5 = byte5 ^ i
        puid = uid + [byte5]
        
        if self.PcdSelect(puid,self.PICC_ANTICOLL1) == 0:
            return (self.ERR,[])
        return (self.OK , uid)
        
    def tohexstring(self,v):
        s="["
        for i in v:
            if i != v[0]:
                s = s+ ", "
            s=s+ "0x{:02X}".format(i)
        s= s+ "]"
        return s
        
  
            
    
    def SelectTagSN(self):
        valid_uid=[]
        (status,uid)= self.anticoll(self.PICC_ANTICOLL1)
        #print("Select Tag 1:",self.tohexstring(uid))
        if status != self.OK:
            return  (self.ERR,[])
        
        if self.DEBUG:   print("anticol(1) {}".format(uid))
        if self.PcdSelect(uid,self.PICC_ANTICOLL1) == 0:
            return (self.ERR,[])
        if self.DEBUG:   print("pcdSelect(1) {}".format(uid))
        
        #check if first byte is 0x88
        if uid[0] == 0x88 :
            #ok we have another type of card
            valid_uid.extend(uid[1:4])
            (status,uid)=self.anticoll(self.PICC_ANTICOLL2)
            #print("Select Tag 2:",self.tohexstring(uid))
            if status != self.OK:
                return (self.ERR,[])
            if self.DEBUG: print("Anticol(2) {}".format(uid))
            rtn =  self.PcdSelect(uid,self.PICC_ANTICOLL2)
            if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn,uid))
            if rtn == 0:
                return (self.ERR,[])
            if self.DEBUG: print("PcdSelect2() {}".format(uid))
            #now check again if uid[0] is 0x88
            if uid[0] == 0x88 :
                valid_uid.extend(uid[1:4])
                (status , uid) = self.anticoll(self.PICC_ANTICOLL3)
                #print("Select Tag 3:",self.tohexstring(uid))
                if status != self.OK:
                    return (self.ERR,[])
                if self.DEBUG: print("Anticol(3) {}".format(uid))
                if self.MFRC522_PcdSelect(uid,self.PICC_ANTICOLL3) == 0:
                    return (self.ERR,[])
                if self.DEBUG: print("PcdSelect(3) {}".format(uid))
        valid_uid.extend(uid[0:5])
        # if we are here than the uid is ok
        # let's remove the last BYTE whic is the XOR sum
        
        return (self.OK , valid_uid[:len(valid_uid)-1])
        #return (self.OK , valid_uid)
    
    
   
       
    
    def auth(self, mode, addr, sect, ser):
        return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]
    
    def authKeys(self,uid,addr,keyA=None, keyB=None):
        status = self.ERR
        if keyA is not None:
            status = self.auth(self.AUTHENT1A, addr, keyA, uid)
        elif keyB is not None:
            status = self.auth(self.AUTHENT1B, addr, keyB, uid)
        return status
       
    def stop_crypto1(self):
        self._cflags(0x08, 0x08)
    def read(self, addr):
        data = [0x30, addr]
        data += self._crc(data)
        (stat, recv, _) = self._tocard(0x0C, data)
        return stat, recv
    def write(self, addr, data):
        buf = [0xA0, addr]
        buf += self._crc(buf)
        (stat, recv, bits) = self._tocard(0x0C, buf)
        if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
            stat = self.ERR
        else:
            buf = []
            for i in range(16):
                buf.append(data[i])
            buf += self._crc(buf)
            (stat, recv, bits) = self._tocard(0x0C, buf)
            if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
                stat = self.ERR
        return stat
    def writeSectorBlock(self,uid, sector, block, data, keyA=None, keyB = None):
        absoluteBlock =  sector * 4 + (block % 4)
        if absoluteBlock > 63 :
            return self.ERR
        if len(data) != 16:
            return self.ERR
        if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR :
            return self.write(absoluteBlock, data)
        return self.ERR
    def readSectorBlock(self,uid ,sector, block, keyA=None, keyB = None):
        absoluteBlock =  sector * 4 + (block % 4)
        if absoluteBlock > 63 :
            return self.ERR, None
        if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR :
            return self.read(absoluteBlock)
        return self.ERR, None
    def MFRC522_DumpClassic1K(self,uid, Start=0, End=64, keyA=None, keyB=None):
        for absoluteBlock in range(Start,End):
            status = self.authKeys(uid,absoluteBlock,keyA,keyB)
            # Check if authenticated
            print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock//4 , absoluteBlock % 4),end="")
            if status == self.OK:                    
                status, block = self.read(absoluteBlock)
                if status == self.ERR:
                    break
                else:
                    for value in block:
                        print("{:02X} ".format(value),end="")
                    print("  ",end="")
                    for value in block:
                        if (value > 0x20) and (value < 0x7f):
                            print(chr(value),end="")
                        else:
                            print('.',end="")
                    print("")
            else:
                break
        if status == self.ERR:
            print("Authentication error")
            return self.ERR
        return self.OK


Para instalar a biblioteca, basta clicar em salvar e escolher a seguinte opção abaixo. Caso você queira criar uma cópia, recomendo salvar no computador também, mas se não tiver no pico, não funciona.

Salvando código no RP2040

Como já foi dito anteriormente, não importa se o arquivo está dentro de “lib” ou largado no diretório principal, mas neste exemplo optei por em “/lib” para organizar melhor o código.

Salvando código no RP2040

Pronto, agora a instalação está concluída. A vantagem da biblioteca está no Pico é que não precisa ficar instalando em toda IDE, basta conectar e usar.

Porém, isso acaba consumindo ainda mais a memória e 2MB talvez fique pouco se você costuma usar muitas bibliotecas, se esse for seu caso, sugiro buscar uma memória de 16MB para evitar sufocos de falta de memória.

6 Raspberry Pi Pico exemplos

O RP2040 tem 30 GPIO’s disponíveis para uso, porém no pino há apenas 27 disponíveis nos pin’s header, sendo que a GPIO25 está dedicada a um LED na placa. 

Neste tópico você vai aprender todo o básico necessário pra você conseguir ter uma base de como usar o Raspberry Pi Pico em cada aplicação.

Em todas as aplicações a seguir, você deve ficar de olho na tensão que entra no microcontrolador, não podendo ultrapassar os 3.3v.

6.1 Leitura e escrita de sinais digital e leitura analógica

Acionamento de uma porta digital geralmente é o “hello word” do hardware, mas como o objetivo desse post é te mostrar se o Pico vale a pena e como usa-lo. 

6.1.1 Raspberry Pi Pico Saída Digital: Ligando LED a cada 1s

Primeiramente, o mais básico de todos é o acionamento de um LED com a porta digital, neste caso, vamos aproveitar o LED on-board que está conectado na GPIO25. 

A alternância de ligar ou desligar será temporizada a cada um segundo, para que não seja necessário o uso de um botão.

6.1.1.1 Código Blink Temporizado

O interessante do Micropython é que já existe uma função que faz a troca do estado lógico de uma GPIO, o comando ‘toggle()’ substitui o comando ‘digitalWrite(LED, !digitalRead(LED))’ comumente usado no Arduino IDE.

Uma coisa que você deve notar neste e nos demais exemplos que envolvem LED, é que há uma redundância, pois todos os códigos terá acionamento também do LED da GPIO25.

from machine import Pin
import utime

led_onboard = Pin(25, Pin.OUT)
led_offboard = Pin(15, Pin.OUT)

led_onboard.low()
led_offboard.low()

while True:
    led_onboard.toggle()
    led_offboard.toggle()
    utime.sleep(1)

6.1.1.2 Circuito exemplo do Acionamento de LED

Assim como os demais diagramas a seguir, os LED não serão necessário no circuito, você adiciona se quiser, pois já há um on-board na GPIO25.

BLink Raspberry Pi Pico

6.1.1.3 Teste prático pisca LED

Por questões de praticidade não usei o segundo LED externo por questão de já ter um LED on-board, mas se você por, vai funcionar.

6.1.2 Raspberry Pi Pico Entrada Digital: Acionando LED ao clicar no botão

Aqui já vamos fazer uma adição de um botão no circuito para que possamos ler o sinal do botão e acionar o mesmo LED do tópico acima. 

6.1.2.1 Código para ler estado do botão e inverter o estado do LED

No circuito não vamos por resistor no botão, pois vamos aproveitar o resistor presente internamente na GPIO. Por isso, vamos definir um resistor de pull down.

from machine import Pin
import utime

led_onboard = Pin(25, Pin.OUT)
led_offboard = Pin(15, Pin.OUT)
button = Pin(5, Pin.IN, Pin.PULL_DOWN)

led_onboard.low()
led_offboard.low()

while True:
    if button.value() == True:
        led_onboard.toggle()
        led_offboard.toggle()
        utime.sleep(1)

6.1.2.2 Circuito exemplo de entrada digital

Note que não houve praticamente alteração nenhuma no circuito, apenas foi adicionado um botão, caso você não tenha, poderá usar fios para simular um botão.

Leitura digital do Raspberry Pi Pico

6.1.2.3 Teste prático de entrada digital

6.1.3 Raspberry Pi Pico ADC: Leitura ADC com conversão para volts

O RP2040 tem apenas três entradas ADC, então ficamos limitados a usar apenas essas 3, claro que há formas de contornar isso, como a multiplexação das portas ADC. 

6.1.3.1 Calculo para converter Raw ADC em tensão

Para esse teste vamos apenas fazer um jumper entre o VCC a GPIO28 para que possamos medir a tensão da alimentação. Como o ADC do pico é de 12 bits, então basta a gente fazer a seguinte equação para achar a tensão. 

Calculo ADC

Neste caso vamos considerar o Vref como 3.3v e o RawADC representa o valor AD lido pelo microcontrolador.

Se considerarmos que Vref vai ser uma constante, podemos simplesmente já calcular antecipadamente essa divisão e deixar apenas o processo de multiplicação do RawADC para obter a tensão.

No código eu já coloquei esse valor calculado, você pode por toda equação porém como são valores constantes, o resultado também será, então realizar essa operação por completa em toda leitura é um desperdício de processamento, por mais que isso seja irrisório.

6.1.3.2 Código de conversão ADC para volts

Como preferi não por a formula acima para cada leitura e já calculei, o resultado deu 5.0354772259098191805905241474022e-5.

Quando a tensão for maior ou igual a 3.3v, os LED’s se acenderão, caso contrário irá se apagar. Como não há filtros de entrada, o ADC tende a oscilar e consequentemente a tensão.

Por isso, quando você for testar, possa ser que em algum momento o LED pique por um curto período de tempo.

from machine import Pin
import utime

led_onboard = Pin(25, Pin.OUT)
led_offboard = Pin(15, Pin.OUT)
adc = machine.ADC(28)

led_onboard.low()
led_offboard.low()

while True:
    v = adc.read_u16() * 5.0354772259098191805905241474022e-5
    print(v)
    
    if v >= 3.3:
        led_onboard.value(1)
        led_offboard.value(1)
    else:
        led_onboard.value(0)
        led_offboard.value(0)
        
    utime.sleep(0.5)

6.1.3.3 Circuito exemplo de entrada ADC

Note que no circuito abaixo optei por por um jumper entre o pino de 3v3 e a GPIO28 para simplificar ao máximo sua montagem, mas você pode por o que quiser.

Leitura ADC no Raspberry Pi Pico

6.1.3.4 Teste prático de leitura ADC

Note que após desconectar o jumper do barramento do VCC o LED se apaga e ao conectar novamente, o LED se acende.

6.1.4 Raspberry Pi Pico PWM: Exemplo com LED

O RP2040 não tem DAC (Digital Analog Converter) porém em compensação, todas as 30 GPIO’s tem suporte a PWM, o que possibilita criar um “falso DAC”. O Pico tem um PWM de 16 bits que dá um range de 0 a 65535.

6.1.4.1 Não caia nesse erro do PWM

No site oficial da Raspberry Pi o exemplo de PWM está com o valor 65025 como se fosse o máximo do PWM, mas isso na verdade é um erro de publicação, o que é completamente compreensível, mas  o correto é 65535

Erro de resolução do Raspberry

Se você é uma pessoa que já tem certo conhecimento, deve ter notado isso, mas caso você seja iniciante basta elevar 2^16 e subtrair por um, esse será o verdadeiro 100% do ADC.

Isso pode ser comprovado com um pequeno teste com o osciloscópio, veja que o valor 65025 representa na verdade, 99,2% do PWM e não 100%. Na imagem abaixo fica bem claro isso.

 Erro PWM do Raspberry Pi Pico

Porém se trocarmos 65025 para 65535 e mantermos a mesma configuração do teste anterior, vamos ver que a onda ficará contínua, isso significa que o duty foi para 100%, mas como o nível não cai o app registra o duty como indefinido. 

Correção da resolução do PWM do Raspberry Pi Pico

6.1.4.2 Exemplo de PWM no Raspberry Pi Pico

Como é possível escolher qualquer pino para o PWM, vamos usar novamente a GPIO25 para mais um exemplo.

Neste caso não vamos usar nada mais além disso, vamos apenas fazer com que a luminosidade aumente e diminua de forma progressiva. 

6.1.4.2.1 Código em Micropython para PWM

Veja como é intuitivo o código em Micropython, só de ler já é possível entender mesmo que não tenha domínio em Python.

from machine import Pin, PWM
from time import sleep

led_onboard = PWM(Pin(25))
led_offboard = PWM(Pin(15))

led_onboard.freq(1000)
led_offboard.freq(1000)

while True:
    for duty in range(65535):
        led_onboard.duty_u16(duty)
        led_offboard.duty_u16(duty)
        sleep(0.0001)

    for duty in range(65535, 0, -1):
        led_onboard.duty_u16(duty)
        led_offboard.duty_u16(duty)
        sleep(0.00001)



6.1.4.2.2 Circuito básico para PWM

O PWM está presente em muitas aplicações em nosso dia dia e o LED é a forma mais básica, mas você pode substituir um LED por um MOSFET e controlar um motor por exemplo.

Exemplo do PWM no Raspberry Pi Pico

6.1.4.2.3 Teste prático do PWM

Se você já usou o PWM no Arduino ou em outros microcontroladores que tenha uma resolução de 8 bits, é possível perceber que no Pico é bem mais suave, isso é por conta da resolução de 16 bits.

6.2 Raspberry Pi Pico: Interfaces de comunicação UART, SPI e I2C

O Raspberry Pico tem outras interfaces de comunicação como a I2S, VGA e DPI, porém como é algo que está pouco presente no datasheet, não terá exemplos neste post.

Apenas o que de fato está bem documentado no datasheet o RP2040, neste caso, as interfaces de comunicação UART, SPI e I2C.

O interessante que no RP2040 nós temos mais de uma opção para escolher pinos para uma mesma interface de comunicação, isso ajuda muito na montagem do circuito, porém para alguns isso pode atrapalhar, mas vou te mostrar que é bem tranquilo.

6.2.1 Raspberry Pi Pico UART: Exemplo com leitura e escrita da UART0 e UART1

O RP2040 tem duas interfaces de comunicação UART, a UART0 tem três pares de opções para escolha de pinos e a UART1 tem apenas dois pares.

6.2.1.1 Código escrevendo com UART0 e lendo com UART1

Anteriormente comentei sobre a possibilidade de escolha dos pinos, mas como faz isso? É bem simples, basta chamar “Pin(<número da GPIO>)”.

No caso do uso de bibliotecas, talvez isso não seja necessário, apenas basta por o número da GPIO sem chamar “Pin()“, no exemplo da SPI vamos ver isso.

from machine import Pin
import utime

uart0 = machine.UART(0, baudrate = 9600, parity = 0, stop = 1, bits = 8, rx = Pin(13), tx = Pin(12))
uart1 = machine.UART(1, baudrate = 9600, parity = 0, stop = 1, bits = 8, rx = Pin(9), tx = Pin(8))

while True:
    uart0.write("elcereza.com")
    print("UART1: ", uart1.readline())   
    utime.sleep(1)


6.2.1.2 Circuito Exemplo da comunicação UART

Perceba que neste circuito não há nada demais, apaenas conectei o Tx0 em Rx1 e Rx0 em Tx1 da mesma forma que foi feito no post da UART do Maix bit.

comunicação UART no Raspberry Pi Pico

6.2.1.3 Teste prático da comunicação UART

6.2.2 Raspberry Pi Pico SPI: Exemplo com módulo RFID MFRC522

Neste exemplo e no exemplo da comunicação I2C é necessário que você tenha instalado as bibliotecas do tópico “Como instalar bibliotecas no Thonny IDE”.

Para este exemplo vamos usar um módulo de RFID, é algo bem simples e interessante que utiliza a interface SPI.

Este tópico foi estruturado com base no post do site microcontrollerslab, porém conta com algumas modificações.

6.2.2.1 Código de leitura de ID do cartão

Como foi dito no tópico da comunicação UART, neste exemplo não é necessário usar o comando “Pin()” para definir o pino, só informar o número, pois a biblioteca já faz essa definição para a gente.

from mfrc522 import MFRC522
import utime

reader = MFRC522(sck = 14, mosi = 15, miso = 12, rst = 9, cs = 13,baudrate=1000000,spi_id=1)
print("Bring TAG closer...")
print("")


while True:
    reader.init()
    (stat, tag_type) = reader.request(reader.REQIDL)
    if stat == reader.OK:
        (stat, uid) = reader.SelectTagSN()
        if stat == reader.OK:
            card = int.from_bytes(bytes(uid),"little",False)
            print("CARD ID: "+str(card))
            utime.sleep_ms(1000)

6.2.2.2 Circuito exemplo Pico e módulo RFID

Se você olhar no pinout do pico, vai perceber que não tem SPI MISO e nem SPI MOSI, mas suas equivalências são respectivamente SPI Rx e SPI Tx.

Comunicação SPI com Raspberry Pi Pico, exemplo com o módulo RFID MFRC522

6.2.2.3 Teste prático da comunicação SPI

6.2.3 Raspberry Pi Pico I2C: Exemplo com display OLED

Por fim, a última interface de comunicação nesta serie de exemplos de comunicações. Neste exemplo vamos usar o display OLED 0,96″.

Esta interface é a mais fácil de encontrar bibliotecas prontas que possível instalar pelo primeiro método de instalação de bibliotecas no Thonny IDE.

Isso ocorre pelo simples motivo de que muitos sensores digitais usam esta interface para enviar dados, pois é uma interface que pode ter vários dispositivos simultâneos mas é mais fraca em termos de clock em relação a SPI.

6.2.3.1 Código para imprimir o link “elcereza.com”

Neste exemplo vamos usar a interface I2C 1 e como esta biblioteca não possui uma definição de pinos de forma automática, temos que declarar manualmente usando a função “Pin()”.

from machine import Pin, I2C
from ssd1306 import SSD1306_I2C

i2c = I2C(1,sda=Pin(14), scl=Pin(15), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
oled.text("elcereza.com", 0, 0)
oled.show()

6.2.3.2 Circuito do display OLED I2C e RP2040

Aqui é o padrão de sempre, quem já nos acompanha, já cansou de ver circuitos com I2C e com OLED também.

Display OLED com Raspberry Pi Pico

6.2.3.3 Teste prático da comunicação I2C

7 Raspberry Pi Pico vai acabar com Arduino ou ESP?

Muitas pessoas ficam comentando que o ESP32 vai acabar com o Arduino e após o lançamento do Pico, a mesma situação se repetiu. É fato que a Rasp acabou de certa forma virando um concorrente.

Porém o Arduino especificamente, é gigantesco e tem inúmeras placas para diferentes tipos de aplicações, mas no caso do ESP32 o novo Pico W se tornará de fato um concorrente direto para aplicações com WiFi.

7.1 Opinião pessoal

E por mais que o ESP32 tenha um “concorrente forte”, a Rasp é nova no mercado de microcontroladores e o ESP já está presente em inúmeros produtos de outras empresas, além disso é bem mais forte em termos técnicos.

O pico é vendido como uma placa, sendo que poderia ser vendido como uma plaquinha com o mínimo de hardware necessário igual o ESP32 Wroom.

ESP32 wroom, exemplo de como o RP2040 deveria vim para os consumidores

Particularmente acredito que isso é uma grande desvantagem do Pico, pois boa parte das aplicações vai exigir mais hardware externo.

Seria necessário comprar o RP2040, cristal, capacitores, resistores e memória flash tudo de forma individual. No caso do ESP32, só é necessário o uso de um regulador de tensão e olhe lá… Só por isso o pico jamais acabaria com o ESP.

Outra coisa, essas empresas se demonstram parceiras uma da outra, o ESP usa a IDE do Arduino e o próprio Arduino tem uma placa que usa o microcontrolador RP2040.

Novo Arduino Nano que usa o microcontrolador RP2040 da RAspberry Pi
Fonte: Embarcados

Então não tem como uma acabar com a outra, é claro que uma placa ou outra pode ser melhor que da outra empresa, como o ESP32 é melhor que o Pico/Pico w, mas aplicação é uma aplicação e tem mercado para todos.

8 Raspberry Pi Pico W vs ESP32

O Pico e o ESP32 têm aplicações completamente diferentes um do outro, mas para que possamos ser no mínimo mais justos com a comparação, temos que levar em consideração o Pico W, e mesmo assim a diferença é absurda.

Raspberry Pi Pico vs ESP32

Esse comparativo mostra que o ESP32 é ainda mais poderoso, mas isso não quer dizer que é melhor ter o ESP32 do que o Pico, pois cada um vai se destacar melhor para determinadas aplicações.

Particularmente prefiro o ESP para aplicações sem fio no geral, porém se fosse um projeto para criar um osciloscópio ou gerador de funções, usaria o Pico, igual o João do Circuitaria que estar montando um.

9 Afinal, vale a pena aprender a usar o Raspberry Pi Pico?

A resposta é sim! O Rap pico é uma ótima porta de entrada para a arquitetura ARM e nada melhor que a estrutura do Raspberry. 

Várias outras empresas ao redor do mundo usam essa tecnologia e por mais que haja variações entre elas a base vai permanecer, então sua adaptação vai ser maior e mais rápida do que aprender tudo do zero.