Berry I2C
Jak ve firmware 0.12.11 používat obecné I2C API v Berry Scriptu pro čtení a zápis vlastních zařízení.
Co Berry I2C řeší
Section titled “Co Berry I2C řeší”Berry I2C zpřístupňuje v kontroleru obecný I2C bus jako objekt io["I2C"].
To znamená, že pro mnoho I2C zařízení už není potřeba přidávat nový hardcoded driver do firmware. Místo toho lze:
- nadefinovat I2C v Controller Configu
- v Berry Scriptu zařízení najít, číst a zapisovat jeho registry
- postavit si vlastní jednoduchý driver přímo v projektu
Typický scénář je například:
- senzor okolního světla
- akcelerometr nebo IMU
- kapacitní touch kontroler
- jiný I2C periferní čip se známým datasheetem
Co je potřeba v Controller Configu
Section titled “Co je potřeba v Controller Configu”Berry I2C funguje nad jedním IO typu I2C, které je definované v Controller Configu.
Příklad:
{ "I2C": { "type": "I2C", "frequency": 400000, "sda": 27, "scl": 14 }}Důležité:
- v aktuální verzi firmware je podporovaný jeden Berry I2C bus na kontroler
- pokud I2C v configu není,
io["I2C"]nebude pro váš script použitelný
Jak k I2C přistoupit z Berry Scriptu
Section titled “Jak k I2C přistoupit z Berry Scriptu”Berry Script získá I2C bus takto:
var bus = io["I2C"]Jakmile máte bus, můžete nad ním volat obecné I2C metody.
Dostupné metody
Section titled “Dostupné metody”scan() -> list<int>
Section titled “scan() -> list<int>”Vrátí seznam nalezených 7bit I2C adres.
var bus = io["I2C"]print("scan:", bus.scan())probe(addr) -> bool
Section titled “probe(addr) -> bool”Ověří, jestli zařízení na dané adrese odpovídá.
if bus.probe(0x10) print("device found")endread(addr, len) -> bytes
Section titled “read(addr, len) -> bytes”Provede čisté I2C čtení o délce len.
Použijte ho ve chvíli, kdy zařízení očekává samotné čtení bez register prefixu.
read(addr, len)samo o sobě nenastavuje adresu registru. Pokud potřebujete nejprve zapsat registr a potom číst, použijtewriteRead(...)neboreadReg(...).
write(addr, data) -> int
Section titled “write(addr, data) -> int”Zapíše na danou adresu byte payload a vrátí počet zapsaných bytů.
var tx = bytes().append(0x03).append(0x00).append(0x00)bus.write(0x10, tx)writeRead(addr, tx, rx_len) -> bytes
Section titled “writeRead(addr, tx, rx_len) -> bytes”Provede transakci typu write-then-read. To je běžný způsob práce s registry u mnoha I2C zařízení.
var data = bus.writeRead(0x10, bytes().append(0x04), 2)readReg(addr, reg, len) -> bytes
Section titled “readReg(addr, reg, len) -> bytes”Zjednodušený helper pro čtení registru.
var data = bus.readReg(0x10, 0x04, 2)writeReg(addr, reg, data) -> int
Section titled “writeReg(addr, reg, data) -> int”Zjednodušený helper pro zápis registru.
data může být:
- jedno byte číslo
- objekt
bytes
bus.writeReg(0x10, 0x03, 0x00)bus.writeReg(0x10, 0x00, bytes().append(0x00).append(0x00))Jak přemýšlet o datech
Section titled “Jak přemýšlet o datech”Berry I2C vrstva pracuje schválně jen s bytovým payloadem.
Interpretaci hodnot si řešíte v Berry Scriptu podle datasheetu zařízení:
- little-endian vs. big-endian
- signed vs. unsigned
- převod registrů na fyzikální jednotky
- vlastní inicializační sekvence
To je záměr. Firmware dává obecný transport a projektový driver si skládáte v Berry.
Minimální příklad: čtení registru
Section titled “Minimální příklad: čtení registru”Tento příklad:
- zkontroluje, že na adrese
0x10něco odpovídá - přečte 16bit little-endian hodnotu z registru
0x04 - vypíše ji do logu
var bus = io["I2C"]
if !bus.probe(0x10) print("I2C device 0x10 not found") returnend
var raw = bus.readReg(0x10, 0x04, 2)var value = raw[0] | (raw[1] << 8)
print("raw:", value)Příklad: jednoduchý VEML7700 reader
Section titled “Příklad: jednoduchý VEML7700 reader”Pro VEML7700 lze v Berry Scriptu udělat i velmi krátký projektový driver.
Následující příklad:
- ověří přítomnost zařízení
- nastaví základní konfiguraci
- jednou za sekundu vypisuje ALS lux hodnotu
var bus = io["I2C"]var ok = falsevar last = -1000
try if bus != nil && bus.probe(0x10) # ALS_CONF = 0x0000 => gain 1x, integration 100 ms bus.writeReg(0x10, 0x00, bytes().append(0x00).append(0x00)) # Power saving off bus.writeReg(0x10, 0x03, 0x00) ok = true endexcept .. ok = falseend
Plugin(def() if !ok || controller.millis() - last < 1000 return end
last = controller.millis()
try var raw = bus.readReg(0x10, 0x04, 2) var als = raw[0] | (raw[1] << 8) var lux = als * 0.0576 print("VEML7700 lux:", lux) except .. print("VEML7700 read failed") endend)Hotové příklady pluginů
Section titled “Hotové příklady pluginů”Níže jsou příklady ve stylu, který se v projektech Spectoda osvědčuje:
- komentáře vysvětlují, co se děje
- samotný kód zůstává krátký a úsporný
- názvy proměnných jsou záměrně stručné, aby script zbytečně nezabíral RAM
VEML7700: print lux
Section titled “VEML7700: print lux”Tento plugin:
- ověří VEML7700 na adrese
0x10 - nastaví základní konfiguraci
- jednou za sekundu vypisuje lux hodnotu
var b = io["I2C"]var ok = falsevar last = -1000
# ALS_CONF = 0x0000 => gain 1x, integration 100 ms.# PSM = 0x00 => normal mode.try if b != nil && b.probe(0x10) b.writeReg(0x10, 0x00, bytes().append(0x00).append(0x00)) b.writeReg(0x10, 0x03, 0x00) ok = true endexcept .. ok = falseend
Plugin(def() if !ok || controller.millis() - last < 1000 return end
last = controller.millis()
try var d = b.readReg(0x10, 0x04, 2) var raw = d[0] | (d[1] << 8) var lux = raw * 0.0576 print("VEML7700 lux:", lux) except .. print("VEML7700 read failed") endend)VEML7700: Daylight plugin jako drop-in replacement
Section titled “VEML7700: Daylight plugin jako drop-in replacement”Tento příklad je kompaktní náhrada za starší plugin, který používal firmware-specific helpery.
Chování zůstává stejné:
- čte stejné
NUMBERhodnoty v milli-lux jako původní helper - publikuje
DAYLI - při chybě emituje
E_INIneboE_REA - upravuje
brighpodle cílové lux hodnoty
def Daylight(S) import controller, math
var id = S["id"] var target = S["target"] var toggl = EVS("toggl", id) var brigh = EVS("brigh", id) var L_ena = EVS("L_ena", id)
# Původní helper používal pevně gain 1x a integration 100 ms. # Pokud to změníte, musí zůstat odpovídající i přepočet raw -> milli-lux. var als_conf = 0x0000 var mlux_per_count_x10 = 576
var bus = nil var ok = false var err_init = EVT("DAYLI", "E_INI", id, LABEL) var err_read = EVT("DAYLI", "E_REA", id, LABEL)
# Přímá náhrada za staré __vmin("I2C", 1.0, 100): # jeden fixní init senzoru bez auto-range. try bus = io["I2C"]
if bus != nil var cfg = bytes() cfg.append(als_conf & 0xff) cfg.append((als_conf >> 8) & 0xff)
bus.writeReg(0x10, 0x00, cfg) bus.writeReg(0x10, 0x03, 0x00) ok = true end except .. ok = false end
if !ok err_init() end
# Přímá náhrada za staré __vmre(): # čte ALS register a vrací NUMBER v milli-lux. var rd = def() if !ok return nil end
try var d = bus.readReg(0x10, 0x04, 2) var raw = d[0] | (d[1] << 8) return int((raw * mlux_per_count_x10) / 10) except .. return nil end end
EVS("DAYLI", id).cb = def(v) if v.is(NULL) var lux = rd() if lux == nil err_read() else EVS.emit("DAYLI", lux, id, NUMBER) end return end end
var last = controller.millis()
return Plugin(def() var now = controller.millis()
if now - last < 1000 || !L_ena || !toggl return end
last = now
var lux = rd()
if lux == nil err_read() return end
var dis = math.abs(lux - target) if dis == 0 || lux == 0 return end
var dif = real(dis) / real(lux)
if dif < 0.025 || dis < 10 return end
var b0 = brigh.get(PERCENTAGE)
if lux < target if dif > 0.1 b0 += 5 else b0 += 1 end else if dif > 0.1 b0 -= 5 else b0 -= 1 end end
if b0 < 0 b0 = 0 elif b0 > 100 b0 = 100 end
brigh.set(b0, PERCENTAGE) end)endLSM6DS3TR: IMU logger
Section titled “LSM6DS3TR: IMU logger”Tento plugin ukazuje, jak přes Berry I2C připojit LSM6DS3TR.
Příklad:
- používá výchozí I2C adresu
0x6A - ověří
WHO_AM_I - zapne akcelerometr a gyroskop na
104 Hz - průběžně vypisuje akceleraci v
ga gyroskop vdps
Pokud máte pin SA0 zapojený jinak, může být potřeba změnit adresu na 0x6B.
var b = io["I2C"]var ok = falsevar last = -1000
# Convert two bytes into signed int16.var s16 = def(lo, hi) var v = lo | (hi << 8) if v >= 0x8000 v -= 0x10000 end return vend
# Default address is 0x6A when SA0 is low.# WHO_AM_I should return 0x6A.# CTRL3_C = 0x44 enables BDU and register auto-increment.# CTRL1_XL = 0x40 enables accelerometer at 104 Hz, ±2 g.# CTRL2_G = 0x40 enables gyroscope at 104 Hz, ±245 dps.try if b != nil && b.probe(0x6A) if b.readReg(0x6A, 0x0F, 1)[0] == 0x6A b.writeReg(0x6A, 0x12, 0x44) b.writeReg(0x6A, 0x10, 0x40) b.writeReg(0x6A, 0x11, 0x40) ok = true end endexcept .. ok = falseend
Plugin(def() if !ok || controller.millis() - last < 500 return end
last = controller.millis()
try # Read gyro XYZ and accel XYZ in one burst. var d = b.readReg(0x6A, 0x22, 12)
var gx = s16(d[0], d[1]) * 0.00875 var gy = s16(d[2], d[3]) * 0.00875 var gz = s16(d[4], d[5]) * 0.00875
var ax = s16(d[6], d[7]) * 0.000061 var ay = s16(d[8], d[9]) * 0.000061 var az = s16(d[10], d[11]) * 0.000061
print("LSM6DS3TR acc[g]:", ax, ay, az, "gyro[dps]:", gx, gy, gz) except .. print("LSM6DS3TR read failed") endend)Doporučený způsob použití
Section titled “Doporučený způsob použití”Když připojujete nové I2C zařízení, osvědčený postup je:
- Nejprve ověřit
scan()aprobe(addr). - Potom ručně přečíst nebo zapsat první registry přes
readReg(...)awriteReg(...). - Až potom si kolem toho obalit malý Berry driver pro konkrétní zařízení.
Prakticky to znamená:
- obecný I2C transport řeší firmware
- device-specific protokol řeší váš Berry Script
- změna zařízení často nevyžaduje změnu C++ firmware
To je vhodné hlavně pro projekty, kde potřebujete rychle doplnit podporu konkrétního senzoru nebo periferního čipu.
Kdy použít readReg(...) a kdy read(...)
Section titled “Kdy použít readReg(...) a kdy read(...)”Ve většině případů pracujte s registry, takže dává smysl:
readReg(...)writeReg(...)- případně
writeRead(...)
Samotné read(...) použijte tehdy, když zařízení opravdu očekává čisté čtení bez register adresy nebo když přesně víte, jak se daný čip v této situaci chová.
Související témata
Section titled “Související témata”- Berry Script pro FW 0.12.11
- Přehled Berry Scriptu
- Controller Config
- Konektivita ve Spectoda ekosystému