Još jedna godina je prošla od poslednjeg našeg prikaza i na tržište izlaze novi i uzbudljivi proizvodi kompanije Raspberry Pi. Većina njih se odnosi na ono što biste i očekivali – poboljšanja i nove funkcionalnosti već uspostavljene serije SBC-a Raspberry Pi. Već smo prikazali jedan od njihovih novijih modela – računar Raspberry Pi 4B u jednom od naših prethodnih izdanja. Ali se sada čini da kompanija ulazi u novi segment proizvoda kojim su odavno dominirali Arduino i TI. Razvojni sistemi MCU-a tradicionalno postoje uz single-board računare i prisutna je određena konkurencija između ova dva polja. Sa Raspberri Pi Pico, mikrokontrolerskim razvojnim sistemom kompanije Raspberry Pi, možda ćemo videti početak novog poglavlja na ovom tržištu.
Sa time u mislima, pređimo na prikaz. Raspberry Pi nam je još jednom ljubazno dostavila primerke za prikaz ovog proizvoda. Paket koji smo dobili bila je koverta sa nekoliko malih ploča upakovanih u traku za prenos. Ovo je bilo iznenađujuće, ali i izuzetno pametno rešenje. Troškovi pakovanja su ovim smanjeni na apsolutni minimum (što od srca pozdravljamo) i omogućavaju minimalnu cenu od 4 USD. Ova cena ga čini jednim od najjeftinijih razvojnih sistema koji je danas dostupan, a koji je ipak uspeo da se probije daleko iznad svoje cenovne klase – ovde smo videli karakteristike koje nedostaju na sistemima koji su višestruko skuplji. (Ipak, nije li to baš ono što je Raspberry Pi uradio sa svojim originalnim SBC modelima?)
Nakon raspakivanja (hm … raspakivanja?), dočekao nas je, nedostatak hedera. To je, međutim, brzo prevaziđeno lemilicom uz malo strpljenja, pa smo ni punih deset minuta kasnije imali postavljen Pico spreman za upotrebu na ploči! Kao napomenu ovde treba istaći da je proces lemljenja bio zaista lagan, i da su bakarni jastučići na PCB-u vrlo kvalitetni, pa smatramo da će čak i početnici lako dodati pin-heder na svoju ploču.
Ostatak procesa podešavanja je bio lagan – Thonni IDE je za nas vodio sve konfiguracije. Bilo je to vrlo jednostavno plug-and-play iskustvo. Programi su napisani u MicroPithon-u, pojednostavljenom derivatu programskog jezika Pithon, sa opcionim API-jima dostupnim za C i C++. Ovo omogućava svestranost u toku rada sa pločom, više nego što omogućava recimo Arduino UNO.
Pokretač ove male ploče prvi je Raspberry Pi – in SoC, RP2040. Sastoji se od dva ARM Cortex M0+ jezgra, koja rade na maksimalnih 133 MHz, zajedno sa 264 kB SRAM-a i 2 MB fleš memorije za vaš kompletan kod. SoC takođe uključuje osam stejt mašina povezanih u klaster nazvan PIO – skraćeno od programabilni I/O (više o tome kasnije). Na ploči se nalazi i četvorokanalni 12-bitni ADC (iako izgleda da su samo tri kanala dostupna). Čini se da ove čipove proizvodi TSMC svojim 40nm procesom.
Sve ovo sada čini prilično impresivan paket, posebno sa obzirom na cenu ploče. Razmišljajući o ploči napisali smo par programa, testirali funkcionalnost u MicroPithon-u, i svaki put smo bili iznenađeni koliko smo u mogućnosti da uradimo.
Prvo, API koji ploča koristi je veoma elegantan. Usuđujemo se reći, da je još uvek nešto nižeg nivo od onog koji koristi Arduino, uz neke nedostajuće biblioteke, ali za razliku od C-a prilagođenog Arduinu, Pi MicroPithon omogućava mnogo finiju kontrolu realne ploče i to bez udaljavanja od slatke sintakse koju pruža Pithon. Tajminzi izgledaju vrlo precizno, čak i pri “bit-banging” komunikaciji (o tome takođe kasnije). Raspberrz Pi je uspeo da složenije aspekte MCU programiranja spusti na nivo koji je lako shvatljiv, ali nije preglomazan, sa istim funkcijama koje pružaju jednostavnu i lako razumljivu funkcionalnost, kao i mogućnost finog podešavanja performansi, onim korisnicima kojima je to potrebno.
Drugo, ploča je zastrašujuće brza. Trenutno brza. Čini se da se pokretački lejer MicroPithon-a odmah pokreće, čim ploča primi svojih 5 V napajanja preko USB konektora. Kad već pominjemo napone, ploča koristi IO od 3,3 V, slično većini dostupnih MCU-ova zasnovanih na ARM-u, što ne predstavlja nikakvu veliku smetnju za većinu projekata.
U poređenju sa Raspberri Pi Zero sa cenom od 5 USD po komadu, moglo bi se reći da Pico jednostavno ne može da vredi toliko, ali to je jednostavno potpuno drugi proizvod. Nano je napredni linux računar, sa svim stvarima koje su povezane sa tim OS-om, kao što su propisani načini isključivanja i uočljivo vreme pokretanja sistema (kao i nedostatak ADC-a). Pico je mikrokontrolerska ploča, što znači da čip na njoj radi mnogo bliže ostatku hardvera i ne pokreće ništa osim vašeg koda (i možda bootloader-a). To takođe znači da nema potrebe za pravilnim gašenjem ili postojanjem dugog vremena potrebnog za pokretanje sistema (a tu je i ADC, ura!!!).
Takođe je energetski efikasan. Veoma efikasan. Čak i sa LCD matričnim ekranom priključenim na njega, ploča je vukla nešto više od 120 mA, što je potrošnja od oko 600 mW. Uporedite to sa 5W koje Raspberri Pi 4 troši dok radi u praznom hodu (treba se osvrnuti na priču o termičkom ograničenju čipa, ali je to nešto sa čime se ovde sigurno nećemo baviti) i možete videti zašto je Pico superiorna ploča za embedovane projekte.
Sada prelazimo na deo koji smo obećali više (dva puta). PIO je još jedan važan modul u jedinici. To je skup od osam stejt mašina – sićušnih malih jezgara koje su sposobne za samo 9 osnovnih komandi, među kojima su: SET, JMP, PULL i PUSH. Ove male jedinice možda ne deluju moćno, ali su sposobne da preuzmu pomalo “bit-banging”-a od glavnih jezgara Cortek-a. Objasnimo ovo još malo. Neki uređaji komuniciraju pomoću standardnih protokola, poput I2C ili SPI. RP2040 nudi različite interfejse za prenos podataka, ali mnogo puta je potrebno koristiti prilagođeni protokol koji koristi samo mali broj uređaja. Iako su „velika“ M0+ jezgra izuzetno sposobna i brza, i dalje su jednojezgarna, pa je upravljanje GPIO-om i samo radi komunikacije po nekom prilagođenom standardu ili čak samo treptanje LED dioda, nešto što ne ostavlja prostora procesoru za druge zadatke. Naravno, dvojezgreni SoC donekle ublažava ovaj problem pružajući drugi tred (ovde nema RTOS-a, dakle dva treda, jedan po jezgru), ali to i su još samo dve odvojene komunikacione linije. S druge strane, korišćenje interapta za zadatke koji su vremenski kritični može dovesti do gubitka tačnosti, jer interapt se može pokrenuti u sred ciklusa instrukcija, uzrokujući da se kod u petlji izvrši sa zakašnjenjem.
Rešenje koje Pico nudi ovde je genijalno i jedinstvena je osobina ove ploče. Gore pomenuti PIO sposoban je da posveti svu svoju računarsku snagu samo za prebacivanje GPIO pinova u konfiguraciju obezbeđenu putem prvog pajplajna iz aplikacionog procesora. Svaka stejt mašina može kontrolisati više od jednog pina (mada oni moraju biti uzastopni). Stejt mašine su takođe vrlo precizne, njihova frekvencija je postavljena u kodu na tačnu vrednost, a svaki ciklus izvodi samo jednu instrukciju. Njihovo programiranje u Pithonu se vrši pomoću vrepera za osnovni PIO Assembly jezik. Ovo efektivno čini PIO osmojezgrenom grupom majušnih, moćnih i preciznih čipova sposobnih za menjanje stanja na GPIO pinovima čime preuzimaju opterećenje u radu glavnog procesora. Neki od korisnika su već koristili PIO za dodavanje čak i veoma složenih portova, kao što je digitalnih video izlaz, na svoje Raspberry Pi Pico ploče.
Kao demo napisali smo malu biblioteku upravljačkih procedura za LCD, a takođe smo koristili i PIO jezgro za nezavisnu kontrolu LED statusa. Krivulja učenja nije bila previše strma, a sam API nudi mnogo pogodnosti. Zaista smo uživali u vremenu provedenom na pravljenju ove male demonstracije koja nam nudi uvid u to šta ova ploča sve može da uradi. Detaljnija demonstracija koda i elektronike biće objavljena u posebnom članku na našem sajtu.
Jedini nedostatak koji smo do sada otkrili je to što je online zajednica koja ga podržava još uvek mala. Trenutno jedsnostavno nema puno dostupnog softvera i mnoge stvari zahtijevaju određenu količinu istraživanja da bi se pokrenule. Većina senzora jednostavno nema upravljačke programe na ovoj platformi. To i nije nešto za šta je kompanija Raspberry Pi direktno odgovorna, jer je uobičajeno da svaka nova arhitektura imati svoje rane faze, ali ipak to treba imati na umu.
Sve u svemu, stavili smo Raspberry Pi Pico na sveobuhvatan test. To je jedan od najupečatljivijih novih razvojnih sistema koje smo koristili. Njegov skup funkcija je izuzetno bogat, pa čak i pored njegove male veličine, ova kombinacija dvojezgarnog procesora sa jedinstvenim PIO sistemom daje neverovatnu funkcionalnost. Ploča je sićušna i kompaktna, sa niskom potrošnjom energije, a povrh toga ima i neverovatno nisku cenu. Jedini pravi nedostatak je gore pomenuti nedostatak softverske podrške u ovom trenutku, koji je relativno jednostavno prevazići. Povrh toga, zajednica raste svakim danom, pa nemamo sumnje da će za nekoliko godina Raspberri Pi MCU sistemi imati jednaku podršku kao i trenutni vodeći brendovi. Ova ploča ima neverovatan potencijal i pogodna je za mnoge različite aplikacije. Svakako se nadamo da će biti još modela u ovoj seriji i da će sistemi zasnovani na Raspberry Pi-silicijumu ući u mainstream.
import machine
import utime
import rp2
from machine import Pin
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink():
set(pins, 1) [19]
nop() [19]
nop() [19]
nop() [19]
nop() [19]
set(pins, 0) [19]
nop() [19]
nop() [19]
nop() [19]
nop() [19]
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def stat_off():
set(pins, 1) [1]
wrap_target()
nop()
wrap()
# Init state machine with "blink" program
# (state machine 0, running at 2kHz, base pin is GP25 (LED))
sm = rp2.StateMachine(0, blink, freq=2000, set_base=machine.Pin(25))
bm = rp2.StateMachine(1, stat_off, freq=2000, set_base=machine.Pin(25))
rs = machine.Pin(16,machine.Pin.OUT)
e = machine.Pin(17,machine.Pin.OUT)
d4 = machine.Pin(18,machine.Pin.OUT)
d5 = machine.Pin(19,machine.Pin.OUT)
d6 = machine.Pin(20,machine.Pin.OUT)
d7 = machine.Pin(21,machine.Pin.OUT)
def pulseE():
e.value(1)
delayShort()
e.value(0)
delayShort()
def delayShort():
utime.sleep_us(40)
def delay():
utime.sleep_ms(2)
def delayBig():
utime.sleep(0.3)
def send2LCD4(BinNum):
d4.value((BinNum & 0b00000001) >>0)
d5.value((BinNum & 0b00000010) >>1)
d6.value((BinNum & 0b00000100) >>2)
d7.value((BinNum & 0b00001000) >>3)
pulseE()
def send2LCD8(BinNum):
d4.value((BinNum & 0b00010000) >>4)
d5.value((BinNum & 0b00100000) >>5)
d6.value((BinNum & 0b01000000) >>6)
d7.value((BinNum & 0b10000000) >>7)
pulseE()
d4.value((BinNum & 0b00000001) >>0)
d5.value((BinNum & 0b00000010) >>1)
d6.value((BinNum & 0b00000100) >>2)
d7.value((BinNum & 0b00001000) >>3)
pulseE()
def whichLinePos(line, pos):
b = 0
if (line == 1):
b = 0
if (line == 2):
b = 40
cursorHome()
for x in range(0,b+pos):
moveCursorR()
def clearDisplay():
#blanks the LCD, needs a long delay.
rs.value(0)
send2LCD8(0b00000001)
rs.value(1)
delay()
def cursorHome():
#returns the cursor to home, needs a long delay.
rs.value(0)
send2LCD8(0b00000010)
rs.value(1)
delay()
def cursorMoveForward():
rs.value(0)
send2LCD8(0b00000110)
rs.value(1)
def cursorMoveBack():
rs.value(0)
send2LCD8(0b00000100)
rs.value(1)
def moveCursorR():#write text from left to right
rs.value(0)
send2LCD8(0b00010100)
rs.value(1)
def moveCursorL():#write text from right to left (backwards)
rs.value(0)
send2LCD8(0b00010000)
rs.value(1)
def cursorOff():
rs.value(0)
send2LCD8(0b00001100)
rs.value(1)
def cursorOn():
rs.value(0)
send2LCD8(0b00001110)
rs.value(1)
def blinkOn():
rs.value(0)
send2LCD8(0b00001111)
rs.value(1)
def blinkOff():
rs.value(0)
send2LCD8(0b00001100)
rs.value(1)
def displayShiftR():#move all caractors one space right
rs.value(0)
send2LCD8(0b00011100)
rs.value(1)
def displayShiftL():#move all caractors one space left
rs.value(0)
send2LCD8(0b00011000)
rs.value(1)
def displayOff():
rs.value(0)
send2LCD8(0b00001000)
rs.value(1)
def displayOn():
rs.value(0)
send2LCD8(0b00001100)
rs.value(1)
def setUpLCD():
rs.value(0)
send2LCD4(0b0011)
send2LCD4(0b0011)
send2LCD4(0b0011)
send2LCD4(0b0010)
send2LCD8(0b00101000)
send2LCD8(0b00001100)
send2LCD8(0b00000110)
send2LCD8(0b00000001)
rs.value(1)
setUpLCD()
while True:
sm.active(1)
whichLinePos(1,0)
for x in 'magazin':
send2LCD8(ord(x))
delayBig()
moveCursorL()
cursorMoveBack()
whichLinePos(2,15)
for x in 'akinortahem':
send2LCD8(ord(x))
delayBig()
sm.active(0)
bm.active(1)
utime.sleep(0.1)
bm.active(0)
utime.sleep(2)
clearDisplay()