Guias · 11 min de leitura

Raspberry Pi Pico: PIO e dual-core com _thread, setup1 e WS2812B

Guia de PIO no Raspberry Pi Pico: máquinas de estados, onda quadrada e exemplo com WS2812B, dois núcleos com _thread no MicroPython e setup1/loop1 no Arduino, e mudanças no Pico 2.

O bloco PIO do Raspberry Pi Pico e seus dois núcleos são os dois recursos que diferenciam essa pequena placa da maioria dos microcontroladores na mesma faixa de preço. O chip RP2040, no coração do Pico, tem dois núcleos de processamento separados e 8 máquinas de estados PIO. Graças aos núcleos, você pode ler sensores de um lado enquanto um display ou uma animação de LEDs continua funcionando sem travar do outro. Já o PIO é um conjunto de pequenos mecanismos de temporização que funcionam independentemente do processador: eles produzem o sinal com precisão de nanossegundos de que os LEDs WS2812B precisam, uma UART extra ou uma onda quadrada sem sobrecarregar a CPU.

Neste guia, você primeiro vai executar os dois núcleos com o módulo _thread no MicroPython e depois com setup1() e loop1() na Arduino IDE. Em seguida, vai aprender como o PIO funciona, quantas máquinas de estados existem e quais instruções elas entendem, e escrever dois programas reais: um gerador de onda quadrada que toca notas em um buzzer e um arco-íris com WS2812B. As últimas seções mostram o que muda no Pico 2 (RP2350) e quando você realmente precisa desses recursos. Se você está começando com o Pico, recomendo ler primeiro O que é Raspberry Pi Pico? e O que é MicroPython?.

Placa de desenvolvimento Raspberry Pi Pico
Raspberry Pi Pico: o chip RP2040 no centro contém dois núcleos Cortex-M0+ e dois blocos PIO. Todos os 26 pinos GPIO podem ser conectados ao PIO.

O que são os dois núcleos e o PIO do Raspberry Pi Pico?

O RP2040 tem dois processadores Cortex-M0+ idênticos, chamados núcleo 0 e núcleo 1. Quando seu programa inicia, apenas o núcleo 0 funciona; o núcleo 1 fica em repouso até você iniciá-lo. Ambos compartilham os mesmos 264 KB de RAM e os mesmos pinos, então transferir dados entre eles é fácil, mas eles não devem escrever na mesma variável ao mesmo tempo. Para isso, o chip tem uma pequena fila bidirecional em hardware (FIFO, 8 palavras em cada direção) e 32 travas de hardware (spinlocks). O PIO é um mundo completamente separado: cada um dos dois blocos PIO tem 4 máquinas de estados, uma memória de programa compartilhada de 32 instruções e acesso direto aos pinos. Depois de iniciada, uma máquina de estados executa seu próprio programa ciclo a ciclo, mesmo enquanto o processador está ocupado com outra tarefa.

RecursoRP2040 (Pico, Pico W)Para que serve?
Núcleo de processamento2 x Cortex-M0+, 133 MHz (até 200 MHz nos documentos atuais)Executar dois programas separados realmente ao mesmo tempo
FIFO entre núcleos8 x 32 bits em cada direçãoEnviar números e comandos entre os núcleos
Trava de hardware (spinlock)32Acessar dados compartilhados alternadamente
Bloco PIO2 (PIO0, PIO1)Tirar do processador as tarefas dos pinos que exigem temporização precisa
Máquina de estados4 por bloco, 8 no totalCada uma executa seu próprio programa PIO
Memória de programa PIO32 instruções por blocoCompartilhada pelas 4 máquinas de estados desse bloco
FIFO do PIOTX 4 + RX 4 palavras por máquina (8 quando unidas)Dados entre o processador e uma máquina de estados

O segundo núcleo no MicroPython: o módulo _thread

No MicroPython, a forma de usar o núcleo 1 é a chamada _thread.start_new_thread(function, args). No Pico, ela inicia a função no segundo núcleo, enquanto seu programa principal continua no núcleo 0. Apenas uma thread extra pode funcionar por vez, porque não existe um terceiro núcleo. No exemplo abaixo, o núcleo 0 lê um potenciômetro no GP26 e imprime o resultado, enquanto o núcleo 1 pisca o LED integrado na velocidade definida pelo potenciômetro. A espera de sleep_ms() no núcleo 1 não afeta em nada o núcleo 0; em um único núcleo, você precisaria de um temporizador ou de asyncio para isso.

MicroPython
1# Pico - iki çekirdek (MicroPython _thread) / two cores (MicroPython _thread)
2import _thread
3import time
4from machine import Pin, ADC
5
6led = Pin("LED", Pin.OUT)          # Pico ve Pico W dahili LED / onboard LED on Pico and Pico W
7pot = ADC(26)                      # GP26 = ADC0, potansiyometre / potentiometer
8
9lock = _thread.allocate_lock()     # ortak veri kilidi / shared data lock
10blink_ms = 500                     # çekirdek 0 yazar, çekirdek 1 okur / core 0 writes, core 1 reads
11running = True                     # durdurma bayrağı / stop flag
12
13
14def core1_task():
15    # Çekirdek 1: LED'i kendi hızında yakıp söndürür / Core 1: blinks the LED at its own pace
16    while running:
17        with lock:
18            period = blink_ms
19        led.toggle()
20        time.sleep_ms(period)
21    led.off()
22
23
24_thread.start_new_thread(core1_task, ())   # ikinci çekirdekte başlat / start on the second core
25
26try:
27    while True:
28        # Çekirdek 0: sensörü okur ve hızı ayarlar / Core 0: reads the sensor and sets the speed
29        raw = pot.read_u16()                     # 0-65535
30        with lock:
31            blink_ms = 50 + raw * 950 // 65535   # 50-1000 ms
32        print("ham / raw:", raw, "ms:", blink_ms)
33        time.sleep_ms(200)
34except KeyboardInterrupt:
35    running = False                              # çekirdek 1'e dur de / tell core 1 to stop
36    time.sleep_ms(1100)
  • A trava criada com allocate_lock() garante que, dentro de um bloco with lock:, apenas um núcleo acesse a variável compartilhada.
  • A flag running avisa ao núcleo 1 para sair do laço quando você interrompe o programa com Ctrl+C; caso contrário, o LED continua piscando.
  • Escrever Pin("LED") localiza o LED integrado tanto no Pico quanto no Pico W; no Pico W, o LED está ligado ao chip sem fio, então Pin(25) não funciona.
  • Uma boa divisão é deixar cálculos demorados, desenho no display ou temporização precisa no núcleo 1, e Wi-Fi, menus e interação com o usuário no núcleo 0.
O botão Parar do Thonny interrompe apenas o programa no núcleo 0. Se um laço infinito continuar no núcleo 1, a placa parece ocupada e não é possível carregar código novo. Nesse caso, desconecte e reconecte o cabo USB ou reinicie a placa encostando o pino RUN no GND. Use sempre uma flag de parada como a do exemplo e evite alocações pesadas de memória (listas grandes, concatenações longas de strings) na thread extra; o suporte a múltiplos núcleos no MicroPython ainda é considerado experimental.

Dois núcleos na Arduino IDE: setup1() e loop1()

O pacote do Pico mais usado na Arduino IDE é o core Raspberry Pi Pico/RP2040/RP2350, de Earle Philhower (arduino-pico). Usar os dois núcleos com ele é surpreendentemente fácil: as conhecidas funções setup() e loop() executam no núcleo 0; se você adicionar setup1() e loop1() ao sketch, o núcleo 1 inicia automaticamente e executa essas duas funções. Os dados são enviados entre os núcleos com o objeto rp2040.fifo. O pacote oficial “Arduino Mbed OS RP2040” do Arduino não oferece suporte a setup1(), então escolha o pacote correto no Gerenciador de Placas.

C++ (Arduino IDE)
1// Raspberry Pi Pico - iki çekirdek (Arduino-Pico çekirdeği) / two cores (Arduino-Pico core)
2const int POT_PIN = 26;        // GP26 = ADC0, potansiyometre / potentiometer
3
4// ---- Çekirdek 0 / Core 0 ----
5void setup() {
6  Serial.begin(115200);
7}
8
9void loop() {
10  int raw = analogRead(POT_PIN);   // 0-1023 (varsayılan 10 bit / default 10-bit)
11  rp2040.fifo.push_nb(raw);        // FIFO doluysa bekleme, değeri atla / skip the value if the FIFO is full
12  Serial.print("pot: ");
13  Serial.println(raw);
14  delay(100);
15}
16
17// ---- Çekirdek 1 / Core 1 ----
18void setup1() {
19  pinMode(LED_BUILTIN, OUTPUT);
20}
21
22void loop1() {
23  static uint32_t periodMs = 500;
24  uint32_t value;
25  while (rp2040.fifo.pop_nb(&value)) {           // FIFO'yu boşalt, sonuncuyu kullan / drain the FIFO, keep the last one
26    periodMs = map(value, 0, 1023, 50, 1000);
27  }
28  digitalWrite(LED_BUILTIN, HIGH);
29  delay(periodMs);                               // yalnız çekirdek 1'i bekletir / only blocks core 1
30  digitalWrite(LED_BUILTIN, LOW);
31  delay(periodMs);
32}
FunçãoO que faz
rp2040.fifo.push(x)Envia um valor de 32 bits ao outro núcleo; espera por espaço se a fila estiver cheia
rp2040.fifo.push_nb(x)Tenta enviar sem esperar; retorna false se a fila estiver cheia
rp2040.fifo.pop()Lê um valor do outro núcleo; espera se a fila estiver vazia
rp2040.fifo.pop_nb(&x)Escreve um valor em x e retorna true se houver um disponível; caso contrário, retorna false imediatamente
rp2040.fifo.available()Retorna quantos valores estão esperando para serem lidos
rp2040.idleOtherCore() / resumeOtherCore()Pausa brevemente e retoma o outro núcleo (por exemplo, ao escrever na memória flash)
Não use o mesmo recurso de hardware, como a porta serial, um display ou um barramento SPI, a partir dos dois núcleos ao mesmo tempo. Como no exemplo, atribua cada periférico a um núcleo e deixe o outro enviar números a ele pela FIFO. Assim, você evita textos embaralhados e telas corrompidas sem precisar de travas.

O que é PIO? Como funciona a entrada/saída programável

Na maioria dos microcontroladores, os blocos de comunicação e sinal, como UART, SPI, I2C e PWM, são fixos no silício; se você precisa de um protocolo que não está na lista, o processador cuida dele ligando e desligando os pinos um a um (bit-banging). Isso mantém o processador ocupado, e a temporização falha sempre que uma interrupção interfere. O PIO (entrada/saída programável) resolve isso na raiz: cada máquina de estados é um pequeno processador com nove instruções projetadas apenas para trabalhar com pinos. Cada instrução executa em exatamente um ciclo de clock, e é possível adicionar um atraso de 0 a 31 ciclos ao final dela. Assim, basta contar os ciclos para saber exatamente quantos nanossegundos cada borda de um sinal leva. A velocidade da máquina de estados é definida dividindo o clock do sistema por um divisor entre 1 e 65.536.

InstruçãoFinalidade
set(pins, v)Escreve uma constante de 0–31 nos pinos ou nos registradores X/Y
out(dest, n)Move n bits do registrador de deslocamento de saída (OSR) para os pinos ou X/Y
in_(src, n)Desloca n bits dos pinos ou registradores para o registrador de deslocamento de entrada (ISR)
pull() / push()Retira uma palavra da FIFO TX para o OSR e envia a palavra do ISR para a FIFO RX
jmp(cond, label) (instrução de salto condicional)Salta conforme uma condição (X igual a zero, pino em nível alto, X-- e assim por diante)
wait(polarity, src, idx) (instrução de espera)Espera até que um pino ou uma flag IRQ atinja o nível desejado
mov(dest, src) (instrução de cópia entre registradores)Copia entre registradores, com a opção de inverter os bits
irq(idx)Envia uma flag de interrupção à CPU ou a outra máquina de estados
nop() e .side(v)Instrução vazia; side-set altera um pino no mesmo ciclo de qualquer instrução

No MicroPython, programas PIO são funções Python escritas com o decorador @rp2.asm_pio; a função não é chamada, o MicroPython a monta em código de máquina. Depois, você a carrega em uma máquina de estados com rp2.StateMachine(id, program, freq=..., set_base=...). No Pico, as máquinas de número 0–3 pertencem ao PIO0, e as de 4–7 ao PIO1. O valor de freq é o número de ciclos de instrução por segundo e não pode ser menor que 1/65.536 do clock do sistema; a 125 MHz, isso equivale a cerca de 1,9 kHz, e a 150 MHz, cerca de 2,3 kHz. Se você precisa de um sinal mais lento, use atrasos ou laços com os contadores X/Y.

Onda quadrada com PIO: seu primeiro programa de máquina de estados

No primeiro exemplo, vamos tocar uma escala em um buzzer passivo conectado ao GP15 e depois deixar uma onda quadrada contínua de 1 kHz funcionando. O programa tem apenas duas instruções: colocar o pino em HIGH e esperar 9 ciclos, colocar o pino em LOW e esperar 9 ciclos. Como cada instrução leva 1 ciclo, um período tem 20 ciclos, então basta fornecer à máquina de estados 20 vezes a frequência do tom desejado. Conecte o terminal positivo do buzzer ao GP15 e o negativo ao GND. No lugar do buzzer, você também pode usar um osciloscópio ou um LED com um resistor de 220 ohms; escolha o resistor com a calculadora de resistor para LED.

MicroPython
1# PIO ile kare dalga ve buzzer notaları / square wave and buzzer notes with PIO
2import time
3import rp2
4from machine import Pin
5
6
7@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
8def square():
9    wrap_target()
10    set(pins, 1)   [9]     # 1 + 9 = 10 döngü HIGH / 10 cycles HIGH
11    set(pins, 0)   [9]     # 1 + 9 = 10 döngü LOW / 10 cycles LOW
12    wrap()                 # başa dön, işlemci karışmaz / loop back, no CPU involved
13
14
15def tone(freq_hz):
16    # Bir periyot 20 döngü: durum makinesi frekansı = 20 x ton
17    # One period is 20 cycles: state machine frequency = 20 x tone
18    sm = rp2.StateMachine(0, square, freq=freq_hz * 20, set_base=Pin(15))
19    sm.active(1)
20    return sm
21
22
23notes = [262, 294, 330, 349, 392, 440, 494, 523]   # Do-Re-Mi ... / C4 ... C5
24for n in notes:
25    sm = tone(n)
26    time.sleep_ms(300)        # işlemci boşta, ses PIO'dan / CPU is idle, PIO makes the sound
27    sm.active(0)
28
29sm = tone(1000)               # 1 kHz sürekli kare dalga / continuous 1 kHz square wave
30print("GP15: 1 kHz kare dalga / square wave")

A fórmula aqui é simples: frequência do tom = frequência da máquina de estados / número de ciclos. Para 1.000 Hz, a máquina funciona a 20.000 Hz, bem acima do limite mínimo. Se você remover os atrasos e mantiver apenas as duas instruções set(), um período cai para 2 ciclos; executar a máquina a 125 MHz gera então uma onda quadrada de 62,5 MHz no pino. Lembre que o PWM de hardware pode fazer o mesmo trabalho: para um tom simples ou o brilho de um LED, PWM é suficiente. O diferencial do PIO é que você decide a forma exata do sinal; com poucas linhas, pode gerar pulsos de largura variável, uma quantidade precisa de pulsos ou saídas que reagem a uma entrada.

Controlando LEDs WS2812B (NeoPixel) com PIO

Anel RGB WS2812B de 24 LEDs
Um anel WS2812B de 24 LEDs: cada LED tem seu próprio controlador interno e recebe sua cor em 24 bits por uma única linha de dados.

Os LEDs WS2812B esperam um pulso de cerca de 1,25 microssegundos por bit; o tempo que o pulso permanece em HIGH determina se o bit é 1 ou 0. É um caso clássico para o PIO. O programa abaixo se baseia no exemplo oficial de MicroPython da Raspberry Pi: a máquina de estados funciona a 8 MHz e cada bit leva exatamente 10 ciclos, então são enviados 800.000 bits por segundo. Graças ao autopull, a máquina retira uma nova cor da FIFO a cada 24 bits; o processador apenas coloca o array na FIFO com sm.put(). Ajuste a quantidade de LEDs à sua fita; para entender o funcionamento do WS2812B e sua ordem de cores, veja o guia de WS2812B NeoPixel.

Pino do WS2812BPino do PicoObservação
DIN (entrada de dados)GP16 (pino físico 21)Um resistor de 330 ohms em série protege o sinal
5V / VCCVBUS (pino 40)5 V do USB; use uma fonte externa de 5 V para muitos LEDs
GNDGND (por exemplo, pino 38)O GND da fonte externa também deve ser conectado aqui
MicroPython
1# PIO ile WS2812B (NeoPixel) gökkuşağı / WS2812B (NeoPixel) rainbow with PIO
2import array
3import time
4import rp2
5from machine import Pin
6
7NUM_LEDS = 24          # halka ya da şeritteki LED sayısı / LEDs in the ring or strip
8LED_PIN = 16           # DIN bağlantısı / DIN connection
9BRIGHTNESS = 0.2       # 0.0-1.0, akımı düşük tutar / keeps current low
10
11
12@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT,
13             autopull=True, pull_thresh=24)
14def ws2812():
15    T1 = 2
16    T2 = 5
17    T3 = 3
18    wrap_target()
19    label("bitloop")
20    out(x, 1)               .side(0)    [T3 - 1]   # 1 biti al, hat LOW / take 1 bit, line LOW
21    jmp(not_x, "do_zero")   .side(1)    [T1 - 1]   # hat HIGH / line HIGH
22    jmp("bitloop")          .side(1)    [T2 - 1]   # bit 1: uzun HIGH / long HIGH
23    label("do_zero")
24    nop()                   .side(0)    [T2 - 1]   # bit 0: kısa HIGH / short HIGH
25    wrap()
26
27
28# Bit başına 10 döngü x 8 MHz = 800 kHz / 10 cycles per bit x 8 MHz = 800 kHz
29sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(LED_PIN))
30sm.active(1)
31
32pixels = array.array("I", [0] * NUM_LEDS)
33
34
35def set_pixel(i, r, g, b):
36    r = int(r * BRIGHTNESS)
37    g = int(g * BRIGHTNESS)
38    b = int(b * BRIGHTNESS)
39    pixels[i] = (g << 16) | (r << 8) | b    # WS2812B sırası GRB / byte order is GRB
40
41
42def show():
43    sm.put(pixels, 8)      # 24 biti sola hizala / left-align the 24 bits
44
45
46def wheel(pos):
47    # 0-255 arası değeri renge çevir / turn 0-255 into a colour
48    pos %= 255
49    if pos < 85:
50        return 255 - pos * 3, pos * 3, 0
51    if pos < 170:
52        pos -= 85
53        return 0, 255 - pos * 3, pos * 3
54    pos -= 170
55    return pos * 3, 0, 255 - pos * 3
56
57
58step = 0
59while True:
60    for i in range(NUM_LEDS):
61        set_pixel(i, *wheel(i * 255 // NUM_LEDS + step))
62    show()
63    step = (step + 3) % 255
64    time.sleep_ms(30)      # kare arası bekleme, aynı zamanda reset / frame gap, also the reset time

Vamos acompanhar o programa linha por linha. out(x, 1) retira um bit do OSR e o coloca no registrador X enquanto side-set mantém a linha em LOW (3 ciclos). Depois, a linha vai para HIGH e X é verificado (2 ciclos). Se o bit for 1, a linha permanece em HIGH por mais 5 ciclos; se for 0, a linha é levada a LOW e a máquina espera 5 ciclos. Ambos os caminhos somam 10 ciclos, ou seja, 1,25 µs. pull_thresh=24 informa à máquina que deve usar apenas 24 bits de cada palavra, e por isso a cor é deslocada para a esquerda no lado do Python (sm.put(pixels, 8)). A ordem das cores é GRB, não RGB; se vermelho e verde estiverem trocados, esse é o motivo. Como 24 LEDs em branco com brilho máximo podem consumir até 1,4 A, o brilho é limitado a 20% no exemplo.

Se você só quer acender LEDs, o módulo neopixel integrado ao MicroPython também resolve: np = neopixel.NeoPixel(Pin(16), 24), np[0] = (255, 0, 0), np.write(). A vantagem de escrever a versão PIO é que o processador fica livre durante a transmissão e você pode adaptar a mesma técnica a outros protocolos.

O que mudou com o Pico 2 (RP2350)?

O Raspberry Pi Pico 2 é a nova geração, com o mesmo tamanho e a mesma pinagem, mas com um chip RP2350 dentro. Ele continua com dois núcleos, mas desta vez você pode escolher na inicialização entre dois núcleos Arm Cortex-M33 ou dois núcleos Hazard3 RISC-V. Todo o código deste artigo funciona sem alterações no Pico 2: _thread é igual no MicroPython e, no core Arduino-Pico, setup1()/loop1() e rp2040.fifo mantêm os mesmos nomes. No PIO, foi adicionado um terceiro bloco, totalizando 12 máquinas de estados (0–11 no MicroPython), além de algumas novas opções de instruções.

CaracterísticaPico (RP2040)Pico 2 (RP2350)
Núcleos2 x Cortex-M0+2 x Cortex-M33 ou 2 x Hazard3 RISC-V
Frequência de clock133 MHz150 MHz
RAM264 KB520 KB
Memória flash integrada2 MB4 MB
PIO2 blocos, 8 máquinas de estados3 blocos, 12 máquinas de estados
Ponto flutuanteEm software (rotinas rápidas na ROM)FPU de hardware (precisão simples) + coprocessador de precisão dupla
SegurançaNenhumaTrustZone, inicialização segura, 8 KB de OTP
Arquivo do MicroPythonRPI_PICO (.uf2)RPI_PICO2 (.uf2)

Na prática, a diferença mais perceptível do Pico 2 é a velocidade: graças à FPU de hardware, tarefas com muitos cálculos em ponto flutuante (filtros, fusão de sensores, processamento de áudio) executam várias vezes mais rápido, e o dobro de RAM deixa espaço para grandes buffers de display. O cuidado principal é gravar o arquivo UF2 correto para a placa; o arquivo do Pico não funciona no Pico 2. As primeiras versões do RP2350 tinham um problema conhecido de hardware (E9) nas entradas com pull-down interno, corrigido na revisão A4; usar um resistor externo ao conectar botões é a opção segura em qualquer caso.

Quando você precisa de dois núcleos e quando precisa de PIO?

O que você precisaMelhor opçãoPor quê?
Brilho de LED, servo, tom simplesPWM de hardwareBloco pronto, uma linha de código
Alguns sensores + display + botãoUm núcleo, millis() ou asyncioSem necessidade de complexidade extra
Animação fluida durante cálculos demoradosDois núcleosO cálculo no núcleo 1 não trava o display
Medição contínua junto com Wi-FiDois núcleosOs atrasos da rede não interferem no intervalo de amostragem
WS2812B, UART extra, transmissor IR, encoderPIOPrecisão de nanossegundos, CPU livre
Vídeo VGA/DVI, áudio I2S, sinais muito rápidosPIO + DMATaxas de dados que a CPU não conseguiria acompanhar

Resumindo: pense no segundo núcleo quando as tarefas entram em conflito no software, e no PIO quando os pinos precisam de precisão inferior a um microssegundo. A maioria dos projetos de hobby não precisa de nenhum dos dois; mas, se um dia sua animação WS2812B começar a engasgar durante uma requisição Wi-Fi, ou faltarem UARTs na placa, a solução já está dentro do Pico.

Ideias de projetos com PIO e dois núcleos no Pico

  • Anel de LEDs que reage à música: o núcleo 1 processa amostras do microfone, o PIO controla o anel WS2812B e o núcleo 0 monitora os botões.
  • Gerador de pulsos de oito canais: cada máquina de estados gera uma onda quadrada em uma frequência diferente para alimentar drivers de motores de passo ou bancadas de teste.
  • Datalogger sem lacunas: o núcleo 1 lê o ADC a cada 10 ms enquanto o núcleo 0 envia os dados por Wi-Fi; uma rede lenta nunca faz você perder uma amostra.
  • Portas seriais extras: abra uma terceira e uma quarta UART com PIO e conecte um GPS e um módulo Bluetooth ao mesmo tempo.
  • Contador de encoder rotativo: o PIO conta os pulsos do encoder sem perder nenhum; o processador apenas lê o resultado.
  • Luz noturna com efeito de respiração: deixe as transições suaves de PWM com o núcleo 1 e o sensor de luz com o núcleo 0.

Erros comuns e como corrigi-los

SintomaCausaSolução
A placa não responde após interromper o programaO laço no núcleo 1 continua executandoUse uma flag de parada; desconecte e reconecte o USB se necessário
start_new_thread falha na segunda vezO Pico permite apenas uma thread extraNão inicie uma nova antes de a anterior terminar
As variáveis às vezes recebem valores sem sentidoOs dois núcleos escrevem nos dados compartilhados sem uma travaUse allocate_lock() ou a FIFO
StateMachine gera ValueErrorfreq está abaixo de clock do sistema / 65536Aumente a frequência e reduza a velocidade com atrasos
PIOASMError: delay too large (mensagem de erro: atraso grande demais)Os bits de side-set reduziram o campo de atrasoDivida o atraso ou adicione um nop()
Vermelho e verde trocados no WS2812BA ordem das cores é GRBUse o formato (g << 16) | (r << 8) | b
setup1() nunca executaO pacote Arduino Mbed RP2040 está selecionadoInstale o pacote Raspberry Pi Pico/RP2040 de Earle Philhower
Ao escrever um programa PIO, anote a quantidade de ciclos de cada instrução e confira se o total corresponde ao tempo desejado. Se você tem um osciloscópio ou um analisador lógico barato, ótimo; caso contrário, reduza temporariamente a frequência da máquina de estados para 2–3 kHz e observe o pino com um LED para enxergar os erros com seus próprios olhos.

Dois núcleos e PIO transformam o Pico de um simples pisca-pisca de LED em um sistema embarcado robusto, mas tudo parte da mesma base: conhecer bem os pinos, a temporização e os LEDs. No app BlueGrays, você encontra projetos com Raspberry Pi Pico, como LED piscante, sequencial de LEDs, efeito Super Máquina (Knight Rider), máquina de estados controlada por botão e efeito de respiração com PWM, cada um com esquema do circuito e código MicroPython, passo a passo, prontos para você adicionar as técnicas deste artigo.

Perguntas frequentes

O que é PIO no Raspberry Pi Pico?

PIO (entrada/saída programável) é um conjunto de pequenas máquinas de estados nos chips RP2040 e RP2350 que funcionam independentemente do processador. Cada uma executa um programa escrito em uma linguagem simples de nove instruções, com temporização precisa a cada ciclo. Ele cuida de tarefas que exigem temporização precisa, como LEDs WS2812B, UARTs extras, transmissão IR ou leitura de encoder, sem manter a CPU ocupada.

Quantos núcleos o Raspberry Pi Pico tem?

O chip RP2040 do Pico e do Pico W tem dois núcleos Cortex-M0+. O RP2350 do Pico 2 também tem dois núcleos, e você pode escolher na inicialização entre núcleos Cortex-M33 e Hazard3 RISC-V. Quando seu programa inicia, apenas o núcleo 0 funciona; você inicia o núcleo 1 por conta própria.

Quantas máquinas de estados PIO o Pico tem?

O RP2040 tem dois blocos PIO com 4 máquinas de estados cada, totalizando 8 máquinas de estados. A memória de programa de 32 instruções de cada bloco é compartilhada pelas máquinas desse bloco. O RP2350 do Pico 2 tem três blocos PIO, ou seja, 12 máquinas de estados.

O MicroPython pode usar os dois núcleos do Pico?

Sim. No Pico, uma função iniciada com _thread.start_new_thread() executa no segundo núcleo. Apenas uma thread extra pode ficar aberta por vez, e os dados compartilhados devem ser protegidos por uma trava criada com _thread.allocate_lock(). O suporte a múltiplos núcleos ainda é experimental no MicroPython, então é melhor evitar tarefas pesadas de memória na thread extra.

Como usar o segundo núcleo do Pico na Arduino IDE?

Depois de instalar o pacote Raspberry Pi Pico/RP2040/RP2350 de Earle Philhower, basta adicionar as funções setup1() e loop1() ao sketch; o núcleo 1 as executa automaticamente. Valores de 32 bits são transferidos entre os núcleos com rp2040.fifo.push() e rp2040.fifo.pop(). O pacote RP2040 do Arduino baseado em Mbed não tem esse recurso.

Preciso usar PIO para LEDs WS2812B?

Não. Tanto o módulo neopixel integrado ao MicroPython quanto a biblioteca Adafruit NeoPixel no Arduino funcionam bem no Pico. A vantagem de escrever sua própria versão com PIO é que o processador fica livre durante a transmissão e você pode aplicar a mesma técnica a outros protocolos que exigem temporização precisa.

Ver todos os projetos
Piscar o LED integrado do Raspberry Pi Pico (Pico e Pico W)
#147Iniciante

Piscar o LED integrado do Raspberry Pi Pico (Pico e Pico W)

O primeiro projeto da série Raspberry Pi Pico! Fazemos o LED verde integrado piscar para verificar se a placa funciona — um primeiro experimento no estilo clássico "Hello World". SEM componentes externos, apenas o Pico e um cabo USB. O código foi escrito em MicroPython (a linguagem com suporte oficial da Raspberry Pi Foundation). Aprenda a usar a classe machine.Pin, o modo Pin.OUT, os métodos led.on()/off() e time.sleep() no Thonny IDE.

5.0(1)
10
Raspberry Pi Pico: controle de LED externo (protoboard)
#148Iniciante

Raspberry Pi Pico: controle de LED externo (protoboard)

O próximo passo após o LED integrado do Pico: conectar um LED externo à protoboard e controlá-lo pelo pino GP14. Neste projeto, você aprende a usar qualquer pino GPIO de uso geral do Pico como saída, a polaridade do LED (terminal longo = ânodo/+, terminal curto = cátodo/–) e por que um resistor limitador de corrente (220Ω) é necessário. Sem o resistor, uma corrente excessiva passa pelo LED e pode queimá-lo. O circuito é muito simples: Pico GP14 → resistor de 220Ω → LE

4.5(2)
7
Raspberry Pi Pico: semáforo (3 LEDs em sequência)
#150Iniciante

Raspberry Pi Pico: semáforo (3 LEDs em sequência)

Simulamos um semáforo real com LEDs vermelho, amarelo e verde — o primeiro projeto com 3 LEDs da série Pico! Verde por 4 segundos: "siga"; amarelo por 1,5 segundo: "prepare-se"; vermelho por 3 segundos: "pare"; e vermelho+amarelo por 1 segundo: "saída" (padrão de semáforo europeu). Aprenda definição de funções (def all_off()), lógica de máquina de estados (sequência) e sincronização de vários GPIOs.

4.0(1)
12
Raspberry Pi Pico: LEDs sequenciais (6 LEDs)
#153Iniciante

Raspberry Pi Pico: LEDs sequenciais (6 LEDs)

A clássica animação de LEDs sequenciais com o Pico — versão com 6 LEDs! Os 6 LEDs acendem em sequência da esquerda para a direita, como se a luz percorresse a fileira. Aprenda listas em Python, compreensão de listas ([Pin(p, Pin.OUT) for p in LED_PINS]), controle de pinos com laço for e enumerate(). São 6 LEDs nos pinos GP9-GP14, cada um com um resistor limitador de corrente de 220Ω.

0.0(0)
8
Raspberry Pi Pico: efeito Knight Rider com LEDs (6 LEDs em vaivém)
#154Iniciante

Raspberry Pi Pico: efeito Knight Rider com LEDs (6 LEDs em vaivém)

Efeito clássico de LEDs do "Knight Rider" com o Pico! 6 LEDs acendem da esquerda para a direita e depois no sentido contrário, sem parar. Como a grade dianteira do carro KITT da famosa série dos anos 80. Este projeto é uma evolução do projeto LEDs em sequência: o mesmo circuito com 6 LEDs (GP9-GP14), com outro padrão de código. Aprenda a inverter listas com reversed(), usar dois laços for separados para o vaivém e criar várias animações no mesmo circuito.

0.0(0)
13
Raspberry Pi Pico: semáforo com 4 modos selecionados por botão (máquina de estados)
#158Intermediário

Raspberry Pi Pico: semáforo com 4 modos selecionados por botão (máquina de estados)

4 modos de semáforo com apenas 1 botão! O modo muda a cada acionamento: Ciclo normal (vermelho → amarelo → verde em sequência), Só vermelho (sinal de parada), Amarelo piscante (atenção/modo noturno), Desligado (manutenção). Conceitos: máquina de estados (a variável mode determina qual comportamento executar), alternância cíclica de modos com módulo ((mode + 1) % 4 → ciclo infinito 0→1→2→3→0), temporização sem bloqueio (as fases dos LEDs são temporizadas…

5.0(1)
15
Raspberry Pi Pico: debounce de botão com IRQ ou consulta por software
#159Intermediário
Premium

Raspberry Pi Pico: debounce de botão com IRQ ou consulta por software

Você pressiona o botão uma vez e o código conta 3-4? Isso é oscilação dos contatos — os contatos metálicos oscilam por microssegundos! Compare 2 métodos de contagem lado a lado no Monitor Serial: consulta sem filtro (detecta as oscilações e conta a mais) e IRQ + debounce por software (contagem correta). Aprenda a física da oscilação dos contatos, debounce por software (ticks_diff() > 50ms), registro de interrupção com Pin.irq(), disparo IRQ_FALLING, rotinas ISR, global e…

0.0(0)
12
Raspberry Pi Pico: variação de brilho do LED com PWM (efeito de respiração)
#160Iniciante

Raspberry Pi Pico: variação de brilho do LED com PWM (efeito de respiração)

O LED não só pisca — ele respira! Com o PWM (modulação por largura de pulso) do Raspberry Pi Pico, o brilho do LED aumenta e diminui lentamente, como a luz de um MacBook em repouso. Aprenda a usar a classe machine.PWM, duty_u16() (brilho de 16 bits, de 0–65535), frequência PWM (freq(1000) — 1.000 ciclos de ligar/desligar por segundo, sem cintilação visível), laços com passos usando range() (crescentes e decrescentes) e saída semelhante à analógica em um pino digital.

0.0(0)
4