Programovatelné IO na mikrořadičích RP2040: komunikace s PIO a ovládání PIO

27. 2. 2025
Doba čtení: 30 minut

Sdílet

Raspberry Pi Pico s řadičem RP2040
Autor: Raspberry Pi
Ukážeme si základní způsoby komunikace hlavního programu běžícího na mikrořadiči s PIO programy. Pro tyto účely se používají fronty (FIFO) a vstupní i výstupní posuvné registry.

Obsah

1. Programovatelné IO na mikrořadičích RP2040 – komunikace s PIO a ovládání PIO

2. Krátké zopakování z minula: ovládání LED připojené na GPIO přes PIO

3. Využití druhého registru Y pro řízení střídy

4. Demonstrační příklad: využití registru Y pro řízení střídy při blikání LED

5. Explicitní zápis nekonečné programové smyčky s řízením GPIO

6. Demonstrační příklad: explicitní zápis nekonečné programové smyčky

7. Využití direktiv wrap a wrap_target

8. Demonstrační příklad: direktivy wrap a wrap_target namísto explicitně zapsané smyčky

9. Nastavení střídy z hlavního programu přes FIFO

10. Konfigurace střídy ještě před spuštěním blikání LED

11. Demonstrační příklad: konfigurace střídy před spuštěním hlavní smyčky

12. Realizace neblokujícího čtení z FIFO

13. Neblokující čtení z fronty s využitím výchozí hodnoty

14. Demonstrační příklad: prohození významu registrů X a Y

15. Průběžná změna střídy kdykoli za běhu PIO programu

16. Demonstrační příklad: průběžná změna střídy za běhu PIO programu

17. Realizace PWM řízeného přes PIO

18. Příloha: varianty všech dnešních příkladů pro LED připojenou na GPIO 25

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Programovatelné IO na mikrořadičích RP2040 – komunikace s PIO a ovládání PIO

V úvodních dvou článcích o programovatelném IO (PIO) implementovaném na mikrořadičích RP2040 [1] a [2] jsme si popsali jak celou architekturu PIO, tak i způsob přímého ovládání vstupně-výstupních pinů (GPIO) s využitím programů, které mohou být vykonávány jednotlivými stavovými stroji, které PIO tvoří. V těchto programech jsme využili některé instrukce z instrukčního souboru PIO. Konkrétně se jednalo o instrukce SET (nastavení pinů či nastavení vybraného interního registru) a JMP (nepodmíněné i podmíněné skoky). Známe i pseudoinstrukci NOP a navíc i způsob definice počtu cyklů, které mají být vykonány před dokončením instrukce. Naše PIO programy prozatím pracovaly velmi jednoduše: stavový stroj neustále opakoval tu samou sekvenci instrukcí s předem nastavenou frekvencí.

Ovšem v praxi je nutné s PIO nějakým způsobem komunikovat a ovládat ho z hlavního programu, který běží přímo na procesorových jádrech čipu RP2040 (ARM nebo RISC-V). Například budeme potřebovat realizovat softwarovou verzi sběrnice I2C, asynchronní sériový port UART nebo pulsně-šířkovou modulaci (PWM). V takových případech je nutné do PIO (resp. přesněji řečeno do jednotlivých stavových strojů) předávat data posílaná na sběrnici/port, číst přijatá data nebo měnit bitový vzorek v případě PWM. Teoreticky již víme, jak PIO ovládat – využít lze FIFO (fronty) a k nim připojené posuvné registry ISR (input shift register) a OSR (output shift register). Existuje však ještě jeden způsob ovládání – „vnucení“ nové instrukce stavovému stroji. V dnešním článku si ukážeme použití FIFO a registrů ISR a OSR. Může se zdát, že se jedná o složitou technologii, jak však uvidíme v praxi, je využití FIFO poměrně elegantní a jednoduché.

2. Krátké zopakování z minula: ovládání LED připojené na GPIO přes PIO

V této kapitole si připomeneme, jakým způsobem jsme vlastně přes PIO (resp. přes stavový stroj) ovládali LED připojenou na zvolený port. Realizovali jsme blikání této LED. Ve skutečnosti je to poměrně snadné – nastavíme příslušný GPIO na jedničku a po uplynutí předem známého počtu cyklů tento GPIO vynulujeme. Opět počkáme nějaký počet cyklů a můžeme celý program zopakovat. Nejjednodušší způsob (i když zdaleka nikoli nejelegantnější) je založen na instrukcích SET a na pseudoinstrukci NOP (což je vhodně zakódované „čekání“):

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_10hz():
    set(pins, 1) [19]
    nop()        [19]
    nop()        [19]
    nop()        [19]
    nop()        [19]
    set(pins, 0) [19]
    nop()        [19]
    nop()        [19]
    nop()        [19]
    nop()        [19]
 
 
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(8))
sm.active(1)
Poznámka: v hranatých závorkách jsou zapsány přidané čekací cykly. To znamená, že každá instrukce trvá dvacet cyklů a jedno opakování programu tedy bude trvat 200 cyklů. Frekvence blikání bude rovna 2000/200=10Hz.

Sofistikovanější řešení spočívá ve využití programové smyčky realizované instrukcí JMP. Tato instrukce totiž (kromě dalších variant) dokáže snížit hodnotu interního registru X o jedničku a provést skok v případě, že se ještě nedosáhlo nuly. V programu je realizována dvojice smyček, které obsahují pouze „zpožděnou“ operaci NOP

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
    set(pins, 1)
    set(x, 31)                  [6]
    label("delay_high")
    nop()                       [29]
    jmp(x_dec, "delay_high")
    set(pins, 0)
    set(x, 31)                  [6]
    label("delay_low")
    nop()                       [29]
    jmp(x_dec, "delay_low")
 
 
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(8))
sm.active(1)

V tomto případě může být mnohem čitelnější si uvést program tak, jak by se napsal přímo v assembleru a nikoli nepřímo v MicroPythonu:

             set pins, 1
             set X, 31 [6]
delay_high:  nop       [29]
             jmp X--, delay_high
             set pins, 0
             set X 31 [6]
delay_low:   nop      [29]
             jmp X--, delay_low

Resp. pro úplnost musíme dodat i direktivy .wrap a .wrap_target, které nám umožní realizovat nekonečnu smyčku (MicroPython tyto direktivy vkládá automaticky):

.wrap_target
             set pins, 1
             set X, 31 [6]
delay_high:  nop       [29]
             jmp X--, delay_high
             set pins, 0
             set X 31 [6]
delay_low:   nop      [29]
             jmp X--, delay_low
.wrap

3. Využití druhého registru Y pro řízení střídy

Výše uvedená dvojice programů sice skutečně dokázala blikat LED s využitím PIO, ovšem použité řešení je založeno na konstantách, které jsou do zdrojového kódu „zadrátovány“. To například znamená, že není možné (jednoduše) měnit střídu, tedy poměr časů, v nichž je LED rozsvícena či naopak zhasnuta. Jedno z možných řešení spočívá v náhradě dvou zpožďovacích smyček za smyčku jedinou. Uvnitř této smyčky budeme kontrolovat hodnotu počitadla (tedy registru X) a pokud překročí nastavenou mez, provede se zhasnutí LED. A změnou této meze, která bude uložena ve druhém pomocném registru Y, bude možné ovlivňovat střídu v poměrech od 0:32 až po 32:32:

.wrap_target
             set X, 31         # počitadlo smyčky
             set Y, 16         # hraniční hodnota pro přepnutí pinu s připojenou LED
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap
Poznámka: povšimněte si, jak relativně sofistikované algoritmy můžeme tvořit pouze s využitím instrukcí SET a zejména pak instrukce JMP.

4. Demonstrační příklad: využití registru Y pro řízení střídy při blikání LED

Program pro PIO (stavový stroj), který jsme viděli v předchozí kapitole, je možné snadno přepsat o MicroPythonu, i když je nutné poznamenat, že výsledný kód již není tak čitelný, jako originál (a to zejména kvůli nutnosti definovat návěští formou volání funkce):

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink1():
    set(x, 31)                 # počitadlo smyčky
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
 
 
sm = rp2.StateMachine(0, blink1, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj

Tento program do RPi Pico můžeme přenést přes Minicom, s využitím IDE Thonny atd. Následuje ukázka jeho přenosu a spuštění přes Minicom:

5. Explicitní zápis nekonečné programové smyčky s řízením GPIO

Prozatím jsme celý program pro PIO měli strukturovaný takovým způsobem, že se díky použití direktiv .wrap a .wrap_target celá série instrukcí neustále opakovala. Ovšem ve chvíli, kdy budeme potřebovat měnit střídu resp. přesněji řečeno ji konfigurovat z hlavního programu, je situace nepatrně složitější. Budeme totiž chtít, aby se instrukce set Y, střída neopakovala, ale zavolal se jen jednou. Tato instrukce tedy musí být uvedena mimo opakující se smyčku. Jedno z možných řešení spočívá v tom, že direktivy .wrap a .wrap_target vůbec nepoužijeme a namísto nich realizujeme vlastní nekonečnou programovou smyčku s využitím instrukce JMP bez podmínky. Celý program pro PIO se tak stane nepatrně složitějším, ale stále se jedná o poměrně dobře čitelný a pochopitelný kód:

             set Y, 16         # hraniční hodnota pro přepnutí pinu s připojenou LED
endless_loop:                  # začátek nekonečné smyčky
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
             jmp endless_loop  # opakovat vnější nekonečnou smyčku

6. Demonstrační příklad: explicitní zápis nekonečné programové smyčky

Opět si můžeme program napsaný přímo v assembleru PIO transformovat do MicroPythonu. Prozatím budeme registr Y nastavovat funkcí set(y, 16), která se přeloží na instrukci SET Y, 16, ovšem později provedeme takové úpravy, které zajistí nastavení registru Y přes FIFO a registr OSR (Output Shift Register):

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink2():
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    label("endless_loop")      # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
    jmp("endless_loop")
 
 
sm = rp2.StateMachine(0, blink2, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj
Poznámka: interně se sice stále budou do kódu vkládat direktivy .wrap a .wrap_target, ovšem vzhledem k tomu, že máme explicitně zapsánu nekonečnou smyčku, se tyto direktivy vlastně nikdy nepoužijí.

7. Využití direktiv wrap a wrap_target

Ve skutečnosti je ve světě RP2040 idiomatičtější namísto explicitně zapsané programové smyčky realizované nepodmíněným skokem JMP stále používat direktivy .wrap a .wrap_target, ovšem s tím, že .wrap_target nebude umístěna na začátku programu, ale na to místo, od něhož se má celá sekvence instrukcí PIO znovu opakovat. Jedná se tak vlastně o poněkud odlišně deklarovanou nekonečnou programovou smyčku (lišit se bude i její časování, ale jen nepatrně, což nám prozatím nebude vadit). Nová struktura programu pro PIO tedy může vypadat následovně:

             set Y, 16         # hraniční hodnota pro přepnutí pinu s připojenou LED
.wrap_target
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap

8. Demonstrační příklad: direktivy wrap a wrap_target namísto explicitně zapsané smyčky

Víme již, že MicroPython automaticky vkládá direktivu .wrap_target na začátek celého PIO programu a naopak direktivu .wrap na jeho konec. Toto chování je většinou žádoucí, ovšem v případě potřeby můžeme direktivy specifikovat ručně, a to přesně na těch místech v programu, do nichž mají být umístěny. Pro tyto účely se používají funkce wrap_target() a wrap() zapisované do Pythonovské funkce s dekorátorem rp2.asm_pio:

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink3():
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    wrap_target()              # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink3, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj

9. Nastavení střídy z hlavního programu přes FIFO

Nyní se konečně dostáváme k ústřednímu tématu dnešního článku – jakým způsobem je možné změnit konfiguraci (resp. přesněji řečeno změnit chování) PIO programu, a to před jeho spuštěním nebo i při jeho běhu. Jedná se o velmi často řešenou problematiku, protože vlastně jakýkoli netriviální PIO program buď vyžaduje nějaká data pro zpracování (viz již zmíněnou I2C sběrnici nebo UART) nebo naopak nějaká data produkuje (sběrnice či port v režimu čtení). Pro tyto účely se používají FIFO, tj. jednosměrné nebo obousměrné fronty, které propojují jednotlivé stavové stroje s vlastním mikrořadičem. Připomeňme si, jak vlastně vypadá způsob propojení PIO s mikrořadičem:

Obrázek 1: Jeden z bloků PIO (druhý má totožnou architekturu)
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

Jedna z FIFO je připojena k posuvnému registru OSR (Output Shift Register) a druhá fronta k posuvnému registru ISR (Input Shift Register). Fronty i posuvné registry jsou pojmenovány tak, jak jsou používány z pohledu mikrořadiče, tj. data jsou posílána z mikrořadiče do TX FIFO (TX=transfer) a ukládána do OSR (výstupního posuvného registru). A naopak mikrořadič čte data z RX FIFO (RX=receive), do které se data nasouvají přes ISR (vstupní posuvný registr). To nám bude při programování PIO působit „logické kolize“, protože vstupní data budeme číst z OSR a naopak výstupní data posílat do registru ISR:

Obrázek 2: Výstupní FIFO a registr OSR
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

Obrázek 3: Vstupní FIFO a registr ISR
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

10. Konfigurace střídy ještě před spuštěním blikání LED

Podívejme se nyní na způsob konfigurace střídy, kterou provedeme ještě před spuštěním blikání LED. Co to znamená? Ještě před vstupem do hlavní (nekonečné) programové smyčky přečteme hodnotu z výstupní FIFO (výstupní je ovšem z pohledu mikrořadiče, zatímco z pohledu PIO je tato fronta vstupní) a necháme si tuto hodnotu přenést do registru OSR (Output Shift Register). V případě, že je FIFO prázdná, budeme čekat na zápis hodnoty; bude se tedy jednat o blokující operaci čtení:

pull block

Ve druhém kroku přeneseme celý obsah OSR (tedy všech 32 bitů) do interního registru Y:

mov Y, OSR

Zbytek programu již zůstane stejný:

.wrap_target
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap

Celý program tedy bude vypadat takto:

             pull block        # blokující přečtení 32 bitů z FIFO s jejich zápisem do OSR
             mov Y, OSR        # přenos všech 32 bitů z OSR do interního registru Y
.wrap_target
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap

Pokud nyní příslušný PIO program spustíme, bude čekat na zápis 32bitové hodnoty do výstupní FIFO. Jak je to zařízeno v MicroPythonu si ukážeme v navazující kapitole.

11. Demonstrační příklad: konfigurace střídy před spuštěním hlavní smyčky

Program napsaný v assembleru PIO si nyní přepíšeme do MicroPythonu:

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink4():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(y, osr)                # přesun hodnoty z OSR do registru Y
    wrap_target()              # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme X-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink4, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj

Po spuštění – inicializaci – stavového stroje se zdánlivě nebude nic dít (LED nebude blikat). PIO program totiž čeká na hodnotu zapsanou do výstupní FIFO (původně je totiž FIFO prázdná). Tento zápis se provede velmi jednoduše:

sm.put(10)                     # prahová hodnota, která se pošle do FIFO
Poznámka: smysl má zapisovat hodnoty 0 až 31. Vyšší hodnoty nemají význam, protože je porovnáváme s hodnotou uloženou do čítače X, jenž bude mít rozsah 0..31 (resp. přesněji čítá dolů z 31 do nuly).

12. Realizace neblokujícího čtení z FIFO

Víme již, jak nastavit parametr či parametry před spuštěním hlavní smyčky, která bude ovládat GPIO (vstupně-výstupní piny). Ovšem v praxi je taktéž nutné měnit parametry za běhu této smyčky. V našem konkrétním případě to znamená požadavek na to, aby se mohla měnit střída blikání LED v průběhu tohoto blikání. Teoreticky tedy potřebujeme z FIFO vyčíst novou hodnotu, ovšem bez čekání na tuto hodnotu v případě, že žádná nová hodnota nebyla poslána. Jinými slovy – pokud hlavní procesor (jádro) nepošle nový parametr, budeme chtít, aby LED stále blikala na základě dříve natavené střídy. Samotné čtení z FIFO bez čekání je teoreticky dobře realizovatelné, protože můžeme použít modifikátor noblock:

             pull block        # blokující přečtení 32 bitů z FIFO s jejich zápisem do OSR
             mov Y, OSR        # přenos všech 32 bitů z OSR do interního registru Y
.wrap_target
             pull noblock      # neblokující pokus o přečtení 32 bitů z FIFO, ovšem bez čekání na hodnotu
                               POZOR: nefunkční řešení!!!
             mov Y, OSR        # přenos všech 32 bitů z OSR do interního registru Y
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap

Ovšem takto naznačené řešení ve skutečnosti nebude plně funkční, protože i když je pull noblock neblokující operace (tedy nečeká na data poslaná do FIFO), stále tato operace musí „něco“ zapsat do registru OSR, z něhož následně tuto hodnotu vyčteme. Je tomu tak z toho důvodu, že čtení z OSR je destruktivní operací – ve skutečnosti se totiž nejedná o pouhé čtení, ale o vysunutí bitů z registru a doplnění nulami (zleva či zprava, podle nastavení). Toto řešení tedy budeme muset nepatrně opravit.

Poznámka: povšimněte si, jak moc se vlastně liší chování PIO od běžného CPU.

13. Neblokující čtení z fronty s využitím výchozí hodnoty

Ve skutečnosti provádí instrukce pull noblock následující operaci:

  • V případě, že se ve frontě (FIFO) nachází alespoň jedna hodnota, je tato hodnota z fronty přečtena a předána do posuvného registruOSR.
  • V případě, že je fronta prázdná, je do posuvného registruOSR uložena hodnota z interního registru X. To znamená, že se vlastně jedná o stejnou operaci, jakou provede mov OSR, X.

Pro nás je užitečný především druhý bod, protože ten nám zaručí, že pokud bude v registru X uložena střída (tedy prakticky hodnota 0 až 31), budeme moci provést instrukci pull noblock, která automaticky naplní OSR buď novou hodnotou zapsanou „zvenku“, nebo původní hodnotou, která se již používá. A díky tomu, že OSR bude vždy naplněn správnou hodnotou, nebude nám vadit, že jeho čtení je vlastně destruktivní operací.

14. Demonstrační příklad: prohození významu registrů X a Y

Aby bylo možné v plné míře využít neblokující operaci pull noblock, musíme v našem PIO programu prohodit význam interních registrů X a Y. Je tomu tak z toho důvodu, že vlastnosti těchto registrů nejsou plně symetrické a jejich rozdíly se projeví právě u operace pull v případě, že ve FIFO nejsou uložena žádná data.

V původní verzi PIO programu sloužil registr Y pro uložení střídy a registr X byl použit jako počitadlo programové smyčky:

             pull block        # blokující přečtení 32 bitů z FIFO s jejich zápisem do OSR
             mov Y, OSR        # přenos všech 32 bitů z OSR do interního registru Y
.wrap_target
             set X, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp X--, blink_loop
.wrap

Nová verze PIO programu použití těchto registrů prohodí. Všechny modifikované instrukce (resp. přesněji řečeno jejich operandy) jsou podtrženy:

             pull block        # blokující přečtení 32 bitů z FIFO s jejich zápisem do OSR
             mov X, OSR        # přenos všech 32 bitů z OSR do interního registru X
.wrap_target
             set Y, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp Y--, blink_loop
.wrap
Poznámka: povšimněte si, že nebylo nutné měnit podmíněný skok, protože v něm oba registry porovnáváme na relaci „nerovno“ a nikoli „menší než“ (to ani není podporováno).

Zdrojový kód upraveného demonstračního příkladu vypadá následovně:

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink5():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink5, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj
sm.put(10)                     # prahová hodnota, která se pošle do FIFO

15. Průběžná změna střídy kdykoli za běhu PIO programu

Nyní nám zbývá poslední krok – pokus o průběžné čtení z fronty (FIFO). V případě, že fronta obsahuje nové hodnoty, je první z těchto hodnot přesunuta do registru OSR a následně do X, kde bude použita jako nová hodnota střídy. Pokud bude fronta prázdná, přesune se do registru OSR aktuální obsah registru X, což povede k tomu, že bude použita nová hodnota střídy:

             pull block        # blokující přečtení 32 bitů z FIFO s jejich zápisem do OSR
             mov X, OSR        # přenos všech 32 bitů z OSR do interního registru X
.wrap_target
             pull noblock      # zkusíme přečíst novou poslanou hodnotu, ale nečekáme na ni
                               # pokud hodnota nebyla poslána, doplní se hodnota registru X
             mov X, OSR        # přenos všech 32 bitů z OSR do interního registru X
             set Y, 31         # počitadlo smyčky
             set pins, 1       # nastavit pin (rozsvítit LED)
blink_loop:  jmp X!=Y, skip    # když X!=Y přeskočit další instrukci
             set pins, 0       # vynulovat pin (zhasnout LED)
skip:        nop    [29]       # zpožďovací mechanismus
             jmp Y--, blink_loop
.wrap
Poznámka: mohlo by se zdát, že se jedná pouze o trik, ovšem mnoho jiných možností při komunikaci s PIO nemáme. Existuje ještě možnost vnucení nové instrukce do PIO, což si ukážeme příště.

16. Demonstrační příklad: průběžná změna střídy za běhu PIO programu

Podívejme se nyní na variantu demonstračního příkladu, která nám umožňuje měnit střídu blikání LED kdykoli za běhu PIO programu, tedy i po spuštění příslušné programové smyčky:

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink6():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    pull(noblock)              # zkusíme přečíst novou poslanou hodnotu, ale nečekáme na ni
                               # pokud hodnota nebyla poslána, doplní se hodnota registru X
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink6, freq=2000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj
sm.put(10)                     # prahová hodnota, která se pošle do FIFO

Nyní je možné kdykoli spustit příkaz, který do FIFO pošle novou hodnotu v rozsahu 0 až 31:

sm.put(1)
time.sleep(1)
sm.put(10)
time.sleep(1)
sm.put(20)
...
...
...

17. Realizace PWM řízeného přes PIO

Zvýšením frekvence hodinového signálu, jímž bude řízen stavový stroj, můžeme realizovat PWM neboli pulzně šířkovou modulaci. Frekvence musí být tak vysoká, aby ji nebylo možné postřehnout okem. Samotný PIO program se nebude nijak měnit, protože v jeho zdrojovém kódu se frekvence nenastavuje:

import rp2
from machine import Pin
import time
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink7():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    pull(noblock)              # zkusíme přečíst novou poslanou hodnotu, ale nečekáme na ni
                               # pokud hodnota nebyla poslána, doplní se hodnota registru X
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()

Frekvenci nastavíme při konstrukci objektu, který představuje (z pohledu MicroPythonu) stavový stroj. Pokusme se například nastavit 200 kHz:

sm = rp2.StateMachine(0, blink6, freq=200000, set_base=Pin(8))
sm.active(1)                   # spustit stavový stroj

Nyní je již možné postupně měnit intenzitu svitu LED, například následujícím způsobem:

for duty_cycle in range(0, 32):
    time.sleep(0.3)
    sm.put(duty_cycle)         # prahová hodnota, která se pošle do FIFO
 
for duty_cycle in range(31, 0, -1):
    time.sleep(0.3)
    sm.put(duty_cycle)         # prahová hodnota, která se pošle do FIFO
Poznámka: nastavená frekvence představuje hodinový signál, kterým jsou synchronizovány všechny instrukce PIO (interně se frekvence mikrořadiče dělí nastavenou konstantou). Ovšem blikání LED bude mít mnohem nižší frekvenci, protože nejvíce času se stráví instrukcí nop [29] a celá perioda bliknutí LED je rovna přibližně 2000 cyklům (touto konstantou je nutné vydělit frekvenci hodinového signálu). Samozřejmě můžeme frekvenci hodinového signálu ještě zvýšit popř. zkrátit dobu trvání jedné iterace programové smyčky, ovšem pro blikání LED to není nutné.

18. Příloha: varianty všech dnešních příkladů pro LED připojenou na GPIO 25

Všechny demonstrační příklady popsané v předchozích kapitolách byly navrženy takovým způsobem, aby ovládaly LED připojenou na vstupně-výstupní pin číslo 8 (což není standardní způsob připojení). V této příloze jsou uvedeny všechny příklady v nepatrně odlišné konfiguraci, v níž je LED připojena ke GPIO číslo 25, což je standardní pin používaný v RPi Pico bez Wifi modulu:

import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink1():
    set(x, 31)                 # počitadlo smyčky
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
 
 
sm = rp2.StateMachine(0, blink1, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink2():
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    label("endless_loop")      # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
    jmp("endless_loop")
 
 
sm = rp2.StateMachine(0, blink2, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink3():
    set(y, 16)                 # hraniční hodnota pro přepnutí pinu
    wrap_target()              # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme x-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink3, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink4():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(y, osr)                # přesun hodnoty z OSR do registru Y
    wrap_target()              # cíl skoku
    set(x, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když x!=y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(x_dec, "blink_loop")   # a opakujeme X-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink4, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
sm.put(10)                     # prahová hodnota, která se pošle do FIFO
import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink5():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink5, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
sm.put(10)                     # prahová hodnota, která se pošle do FIFO
import rp2
from machine import Pin
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink6():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    pull(noblock)              # zkusíme přečíst novou poslanou hodnotu, ale nečekáme na ni
                               # pokud hodnota nebyla poslána, doplní se hodnota registru X
    mov(x, osr)                #  přesun hodnoty z OSR do registru X
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink6, freq=2000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
sm.put(10)                     # prahová hodnota, která se pošle do FIFO
import rp2
from machine import Pin
import time
 
 
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink6():
    pull(block)                # přečtení hodnoty z FIFO s čekáním, uložení do OSR
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    wrap_target()              # cíl skoku
    pull(noblock)              # zkusíme přečíst novou poslanou hodnotu, ale nečekáme na ni
                               # pokud hodnota nebyla poslána, doplní se hodnota registru X
    mov(x, osr)                # přesun hodnoty z OSR do registru X
    set(y, 31)                 # počitadlo smyčky
    set(pins, 1)               # nastavit pin
    label("blink_loop")
    jmp(x_not_y, "skip")       # když X!=Y přeskočit další instrukci
    set(pins, 0)               # vynulovat pin
    label("skip")
    nop() [29]                 # zpožďovací mechanismus
    jmp(y_dec, "blink_loop")   # a opakujeme Y-krát
    wrap()
 
 
sm = rp2.StateMachine(0, blink6, freq=200000, set_base=Pin(25))
sm.active(1)                   # spustit stavový stroj
 
for duty_cycle in range(0, 32):
    time.sleep(0.3)
    sm.put(duty_cycle)         # prahová hodnota, která se pošle do FIFO
 
for duty_cycle in range(31, 0, -1):
    time.sleep(0.3)
    sm.put(duty_cycle)         # prahová hodnota, která se pošle do FIFO

19. Repositář s demonstračními příklady

Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro MicroPython běžící na čipech RP2040 s architekturou Cortex-M0 byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V tabulce zobrazené níže jsou odkazy na jednotlivé příklady:

# Demonstrační příklad Stručný popis příkladu Cesta
1 display_version.py tisk verze MicroPythonu https://github.com/tisnik/most-popular-python-libs/blob/master/pio/displa­y_version.py
2 display_frequency.py zjištění hodinové frekvence MCU https://github.com/tisnik/most-popular-python-libs/blob/master/pio/displa­y_frequency.py
3 display_mem_info1.py zobrazení základních informací o paměti MicroPythonu https://github.com/tisnik/most-popular-python-libs/blob/master/pio/displa­y_mem_info1.py
4 display_mem_info2.py zobrazení podrobnějších informací o paměti MicroPythonu https://github.com/tisnik/most-popular-python-libs/blob/master/pio/displa­y_mem_info2.py
5 display_pins.py zobrazení konfigurace GPIO pinů https://github.com/tisnik/most-popular-python-libs/blob/master/pio/display_pins.py
6 blink_gpio25.py blikání LED na GPIO #25 bez použití PIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/blink_gpio25.py
7 blink_gpio8.py blikání LED na GPIO #8 bez použití PIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/blink_gpio8.py
8 pio_blink_pin25.py blikání LED na GPIO #25 s použitím PIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25.py
9 pio_blink_pin8.py blikání LED na GPIO #8 s použitím PIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8.py
       
10 pio_blink_pin8_loop1.py blikání LED na GPIO #8, registr Y je použit pro specifikaci střídy https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_loop1.py
11 pio_blink_pin8_loop2.py blikání LED na GPIO #8, explicitní zápis nekonečné programové smyčky s řízením GPIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_loop2.py
12 pio_blink_pin8_loop3.py blikání LED na GPIO #8, použití direktiv .wrap a .wrap_target https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_loop3.py
13 pio_blink_pin8_control1.py nastavení střídy přes FIFO před začátkem blikání (varianta pro GPIO #8) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_control1.py
14 pio_blink_pin8_control2.py prohození interních registrů X a Y (varianta pro GPIO #8) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_control2.py
15 pio_blink_pin8_control3.py průběžná změna střídy v průběhu blikání (varianta pro GPIO #8) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin8_control3.py
16 pio_pwm_pin8.py PWM řízená přes PIO (varianta pro GPIO #8) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_pwm_pin8.py
       
17 pio_blink_pin25_loop1.py blikání LED na GPIO #25, registr Y je použit pro specifikaci střídy https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_loop1.py
18 pio_blink_pin25_loop2.py blikání LED na GPIO #25, explicitní zápis nekonečné programové smyčky s řízením GPIO https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_loop2.py
19 pio_blink_pin25_loop3.py blikání LED na GPIO #25, použití direktiv .wrap a .wrap_target https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_loop3.py
20 pio_blink_pin25_control1.py nastavení střídy přes FIFO před začátkem blikání (varianta pro GPIO #25) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_control1.py
21 pio_blink_pin25_control2.py prohození interních registrů X a Y (varianta pro GPIO #25) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_control2.py
22 pio_blink_pin25_control3.py průběžná změna střídy v průběhu blikání (varianta pro GPIO #25) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_blin­k_pin25_control3.py
23 pio_pwm_pin25.py PWM řízená přes PIO (varianta pro GPIO #25) https://github.com/tisnik/most-popular-python-libs/blob/master/pio/pio_pwm_pin25­.py

20. Odkazy na Internetu

  1. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu/
  2. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu (2)
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu-2/
  3. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu (dokončení)
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu-dokonceni/
  4. Překlad funkcí přímo do nativního kódu MicroPythonem
    https://www.root.cz/clanky/preklad-funkci-primo-do-nativniho-kodu-micropythonem/
  5. MicroPython ve webovém prohlížeči: lehkotonážní varianta k Pyodide
    https://www.root.cz/clanky/micropython-ve-webovem-prohlizeci-lehkotonazni-varianta-k-pyodide/
  6. Programmable IO
    https://docs.micropython.or­g/en/latest/rp2/tutorial/pi­o.html
  7. Introduction to the PIO (Programmable Input Output) of the RP2040
    https://tutoduino.fr/en/pio-rp2040-en/
  8. MicroPython examples: PIO
    https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio
  9. PIO: Wikipedia CZ (pozor: jedná se o něco jiného!)
    https://cs.wikipedia.org/wiki/PIO
  10. RP2040 (Wikipedia)
    https://en.wikipedia.org/wiki/RP2040
  11. Maximising MicroPython speed
    https://docs.micropython.or­g/en/latest/reference/spe­ed_python.html
  12. Online ARM converter
    https://armconverter.com/?disasm
  13. The 3 different code emitters
    https://www.kickstarter.com/pro­jects/214379695/micro-python-python-for-microcontrollers/posts/664832
  14. The 3 different code emitters, part 2
    https://www.kickstarter.com/pro­jects/214379695/micro-python-python-for-microcontrollers/posts/665145
  15. Fast Filters for the Pyboard
    https://github.com/peterhin­ch/micropython-filters
  16. How to load 32 bit constant from assembler with @micropython.asm_thumb
    https://forum.micropython­.org/viewtopic.php?f=21&t=12931&sid=25­de8871fa9cfcf8cafb6318f9d8ba3a
  17. Pi pico, micropython.asm_thumb: ADR Rd, <label> and LDR Rd, <label> not implemented?
    https://github.com/orgs/mi­cropython/discussions/12257
  18. MicroPython documentation
    https://docs.micropython.or­g/en/latest/index.html
  19. Inline assembler for Thumb2 architectures
    https://docs.micropython.or­g/en/latest/reference/asm_thum­b2_index.html
  20. Inline assembler in MicroPython
    https://docs.micropython.or­g/en/latest/pyboard/tutori­al/assembler.html#pyboard-tutorial-assembler
  21. MCU market turns to 32-bits and ARM
    http://www.eetimes.com/do­cument.asp?doc_id=1280803
  22. Cortex-M0 Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0.php
  23. Cortex-M0+ Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0plus.php
  24. ARM Processors in a Mixed Signal World
    http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world
  25. RISCové mikroprocesory s komprimovanými instrukčními sadami
    https://www.root.cz/clanky/riscove-mikroprocesory-s-komprimovanymi-instrukcnimi-sadami/
  26. RISCové mikroprocesory s komprimovanými instrukčními sadami (2)
    https://www.root.cz/clanky/riscove-mikroprocesory-s-komprimovanymi-instrukcnimi-sadami-2/
  27. ARM Architecture (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_architecture
  28. Cortex-M0 (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_Cortex-M0
  29. Cortex-M0+ (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_Cortex-M#Cortex-M0.2B
  30. Improving ARM Code Density and Performance
    New Thumb Extensions to the ARM Architecture Richard Phelan
  31. The ARM Processor Architecture
    http://www.arm.com/produc­ts/processors/technologies/in­struction-set-architectures.php
  32. Thumb-2 instruction set
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.ddi0344c/Beiiegaf.html
  33. Introduction to ARM thumb
    http://www.eetimes.com/dis­cussion/other/4024632/Intro­duction-to-ARM-thumb
  34. ARM, Thumb, and ThumbEE instruction sets
    http://www.keil.com/suppor­t/man/docs/armasm/armasm_CEG­BEIJB.htm
  35. An Introduction to ARM Assembly Language
    http://dev.emcelettronica­.com/introduction-to-arm-assembly-language
  36. Processors – ARM
    http://www.arm.com/produc­ts/processors/index.php
  37. The ARM Instruction Set
    http://simplemachines.it/doc/ar­m_inst.pdf
  38. The Thumb instruction set
    http://apt.cs.manchester.ac­.uk/ftp/pub/apt/peve/PEVE05/Sli­des/05_Thumb.pdf
  39. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  40. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  41. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  42. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  43. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  44. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/li­nux/prog/asm.html
  45. AT&T Syntax versus Intel Syntax
    https://www.sourceware.or­g/binutils/docs-2.12/as.info/i386-Syntax.html
  46. Linux Assembly website
    http://asm.sourceforge.net/
  47. Raspberry Pi Pico Variants – A Detailed Comparison
    https://circuitdigest.com/ar­ticle/raspberry-pi-pico-variants-comparison
  48. Raspberry Pi Pico 2 vs Original Pico: What’s New?
    https://www.digikey.cz/en/ma­ker/blogs/2024/raspberry-pi-pico-2-vs-original-pico-whats-new
  49. pio-uart
    https://github.com/Sympatron/pio-uart
  50. Datasheet čipu RP2040
    https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.