Compare commits

...

4 commits

Author SHA1 Message Date
Stephan I. Böttcher
d5e4d19165 nmahepam: --leia
- add option --leia to nmahepam.py
- to load leia_stepper

leia_stepper fixes and new functions
2025-10-30 10:17:53 +01:00
Stephan I. Böttcher
26d700bbad add submodule avr 2025-10-30 10:17:36 +01:00
Stephan I. Böttcher
4c6bb8cb79 Gd4_2thr config 2025-10-30 10:03:13 +01:00
Stephan I. Böttcher
85b210b9e8 Gd4 (flash_alke) 9ch TWOTHR bitfile 2025-10-30 10:01:25 +01:00
8 changed files with 303 additions and 74 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "avr"]
path = avr
url = ../irena-avr.git

1
avr Submodule

@ -0,0 +1 @@
Subproject commit 50f46b8494bbba65ccbec124f4fd4d9c0c4b72a3

View file

@ -1,2 +1,2 @@
@pressure/inject
@s/if sd_status & 0xc00: sd/init
@clock/short
@s/if !(sec % M): nm/cou/clear/short

View file

@ -21,25 +21,25 @@ irena/nsamples 0 0x3f
irena/adcmask 0x02664
irena/windows 2 5 8
echo Thresholds 100*mV high gain
@var/set V=14000
@irena/l1t 0 1000*V 0x000
@irena/l1t 1 100*V 0x000
@irena/l1t 2 100*V 0x001
@irena/l1t 3 1000*V 0x000
@irena/l1t 4 1000*V 0x000
@irena/l1t 5 100*V 0x001
@irena/l1t 6 100*V 0x001
@irena/l1t 7 1000*V 0x000
@irena/l1t 8 1000*V 0x000
@irena/l1t 9 100*V 0x001
@irena/l1t 10 100*V 0x001
@irena/l1t 11 1000*V 0x000
@irena/l1t 12 1000*V 0x000
@irena/l1t 13 100*V 0x001
@irena/l1t 14 100*V 0x000
@irena/l1t 15 1000*V 0x000
@irena/l1t 16 1000*V 0x000
@irena/l1t 17 100*V 0x000
@var/set mV=14000
@irena/l1t 0 1000*mV 0x000
@irena/l1t 1 100*mV 0x000
@irena/l1t 2 100*mV 0x001
@irena/l1t 3 1000*mV 0x000
@irena/l1t 4 1000*mV 0x000
@irena/l1t 5 100*mV 0x001
@irena/l1t 6 100*mV 0x001
@irena/l1t 7 1000*mV 0x000
@irena/l1t 8 1000*mV 0x000
@irena/l1t 9 100*mV 0x001
@irena/l1t 10 100*mV 0x001
@irena/l1t 11 1000*mV 0x000
@irena/l1t 12 1000*mV 0x000
@irena/l1t 13 100*mV 0x001
@irena/l1t 14 100*mV 0x000
@irena/l1t 15 1000*mV 0x000
@irena/l1t 16 1000*mV 0x000
@irena/l1t 17 100*mV 0x000
@irena/l2t/ch=0/any=0x001/read=0x02664 All
@irena/l2t/ch=1/disable
@irena/l2t/ch=2/disable

BIN
flash_alke/IRENA2TH.RBF Normal file

Binary file not shown.

View file

@ -1,9 +1,9 @@
HW = {
"NCH" : 18,
"NPEEK" : 18,
}
HW.update({
"NCH" : 18,
"NPEEK" : 18,
})
def defaults(nch=18, ntr=6):
adcmask()
@ -58,8 +58,10 @@ pulse_desy = [
pulse_desy.reverse()
def shaper2u2(nch=18, ntr=6, **kk):
HW["NCH"] = nch
global mV
mV=14000
Var("mV", mV)
defaults(nch=nch, ntr=ntr)
pulse(range(nch), pulse2u2, **kk)
readconfig(nch=nch, ntr=ntr)
@ -277,12 +279,11 @@ def TANOS():
readconfig()
def Gd4():
global mV
mV=14000
shaper2u2()
readmask=0x6666
adcmask(readmask)
windows(2,3,4,5)
for c in (0,3,4,7,8,11,12,15,16):
for c in (0,3,4,7,8,11,12,15,16,17):
thres(c, 100*mV, mask=0)
for i in range(4):
thres(4*i+1, 100*mV, mask=(1<<i)|(1<<(2*i+4)))
@ -292,6 +293,27 @@ def Gd4():
l2trig(5, all=2, read=0)
readconfig()
def Gd4_2thr():
cmd("alt/file 'IRENA2TH.RBF'")
Script("IRENA.RC")
shaper2u2(nch=9)
readmask=0x0ff
adcmask(readmask)
windows(2,3,4,5)
for c in (8,17):
thres(c, 1000*mV, mask=0)
for i in range(4):
thres(2*i+0, 100*mV, mask=(1<<i)|(1<<(2*i+4)))
thres(2*i+1, 100*mV, mask=(1<<i)|(1<<(2*i+5)))
thres(2*i+9, 100*mV, mask=0);
thres(2*i+10, 100*mV, mask=0);
l2trig(i, any=1<<i, read=readmask)
l2trig(4, all=1, read=0)
l2trig(5, all=2, read=0)
readconfig()
Var("M", 3)
cmd("s/cron 'CRON.RC'")
def TRES():
shaper2u2()
global mV, AH, BH, CH, AL, BL, CL

View file

@ -13,10 +13,11 @@ class leia_stepper:
def _connect(self, ifc):
self.ifc = ifc
self.ifc.cmd("spi/reset/ssel/rate 30")
print("LEIA stepper: ", self.id(), file=sys.stderr)
def _export(self, scope=None, prefix="stepper_"):
"""usage: ..._export(globals())
return a dict with all mames in self that
return a dict with all names in self that
do not begin with an '_'
and are not all uppercase.
The scope is updated with the dict.
@ -31,14 +32,18 @@ class leia_stepper:
return r
_verbose = 2
cmd_pref = "spi/step"
_cmd_prefix = "spi/step"
def cmd(self, cmd, arg=0, argh=None, Error=False, retry=False, eth_retry=False):
def cmd(self, cmd, arg=0, argh=None,
Error=False, retry=True,
eth_retry=False, sleep=False,
verbose=None):
if verbose is None:
verbose = self._verbose >= 2
c = ord(cmd[0])
arg2 = 0
if len(cmd)>1:
cc = f"{self.cmd_pref} {cmd!r} 0x{arg:x}"
cc = f"{self._cmd_prefix} {cmd!r} 0x{arg:x}"
else:
if isinstance(arg, str):
arg = arg.encode()
@ -49,14 +54,16 @@ class leia_stepper:
if argh is not None:
arg2 = argh
if arg & 0xff00:
cc = f"{self.cmd_pref} {cmd!r} 0x{arg:x}"
cc = f"{self._cmd_prefix} {cmd!r} 0x{arg:x}"
else:
cc = f"{self.cmd_pref} {cmd!r} 0x{arg:x} 0x{arg2:x}"
cc = f"{self._cmd_prefix} {cmd!r} 0x{arg:x} 0x{arg2:x}"
while True:
if sleep:
time.sleep(sleep)
r = self.ifc.cmd(cc, timeout=1000)
if r is None and eth_retry:
continue
if self._verbose >= 2:
if verbose:
print(cc, r, file=sys.stderr)
if self.ifc.cmdn(r) != 280:
if retry and self.ifc.cmdn(r) == 780 and r.split()[-1] == "130":
@ -65,21 +72,29 @@ class leia_stepper:
rr = r.split()
r1 = int(rr[2],0)
r = int(rr[3],0)
if retry and r1==0x45 and (r==0x5945 or r==0x5745):
# "EEY" interruped action by SPI
# "EEW" watchdog int during command Rx
continue
if retry and r1==0x45:
r2 = r & 0xff
if r2==0x53:
# "ES" SPI too slow or early
continue
r3 = r >> 8
if r2==0x45 and r3 != c:
# "EE" unknown command
continue
if r1 != c:
if Error:
r |= r1<<16
else:
raise self.Stepper_Error(f"{chr(r1)} {rr[3]}")
rc = chr(r1)
if rc.isprintable():
raise self.Stepper_Error(f"{rc} {rr[3]}")
raise self.Stepper_Error(f"{rr[2]} {rr[3]}")
break
return r
def id(self):
n = self.hvc('V', 0xff) >> 8
return "".join([chr(self.hvc('V', i) & 0xff) for i in range(n)])
n = self.cmd('v', 0xff, verbose=False) >> 8
return bytes(self.cmd('v', i, verbose=False) & 0xff for i in range(n)).decode()
CONFN = [
"magic",
@ -94,63 +109,130 @@ class leia_stepper:
"reset",
"dir",
"n_steps",
"dac",
"dac_ramp",
"dac_step",
"adc_idx",
"adc_incr",
"adc_period",
"pad1", "pad23", "pad45", "pad67",
"adc00", "adc01", "adc02", "adc03",
"adc04", "adc05", "adc06", "adc07",
"adc08", "adc09", "adc10", "adc11",
"adc12", "adc13", "adc14", "adc15",
"awake",
"ledoff",
"disable",
"dac_ref",
("padc", struct.Struct("<6B")),
("adc_ch", struct.Struct("<16B")),
("adc", struct.Struct("<16H")),
"dac",
"adc_idx",
"eewr_n",
"eewr_a",
("pads", struct.Struct("<42B"))
]
CONFV = (1,)
CONFF = "<HBBHH6B4H4B3H16B"
CONFS = 48
CONFM = b'\x1a\x1e'
CONFV = (1,)
CONFF = struct.Struct("<HBBHH6B3H6B6s16s")
STATF = struct.Struct(CONFF.format + "32sHBBH42s")
def confdict(self, b):
b = struct.unpack(self.CONFF, b)
b = {k:v for k,v in zip(self.CONFN, b)}
return b
if len(b)==self.CONFF.size:
b = self.CONFF.unpack(b)
else:
b = self.STATF.unpack(b)
i = 0
bb = {}
for i, bbb in enumerate(b):
k = self.CONFN[i]
if isinstance(k, tuple):
bbb = list(k[1].unpack(bbb))
k = k[0]
bb[k] = bbb
return bb
def fromdict(self, b):
bb = []
for kk in self.CONFN:
k = kk
if isinstance(kk, tuple):
k = k[0]
if not k in b:
break
bbb = b[k]
if isinstance(kk, tuple):
bbb=kk[1].pack(*bbb)
bb.append(bbb)
try:
return self.CONFF.pack(*bb)
except:
pass
return self.STATF.pack(*bb)
def confbytes(self, b, a=0):
"from str, dict, list, or bytes: make conf bytes"
if isinstance(b, str):
b = self.readhex(b, a, unpack=True)
if isinstance(b, dict):
b = [b[k] for k in self.CONFN]
b = self.fromdict(b)
if isinstance(b, list):
b = struct.pack(self.CONFF, *b)
bb = []
for i, bbb in enumerate(b):
if isinstance(bbb, list):
bbb = self.CONFN[i][1].pack(bbb)
bb.extend(bbb)
else:
bb.append(bbb)
try:
b = struct.pack(self.CONFF, *bb)
except:
b = struct.pack(self.STATF, *bb)
return b
def read_conf(self, unpack=True):
b = []
while True:
r = self.cmd('x', len(b), Error=True)
r = self.cmd('x', len(b), Error=True, sleep=0.001)
if r & 0xff0000:
break
b.append(r & 0xff)
time.sleep(0.005)
b = bytes(b)
if unpack and b[:2]==self.CONFM and b[2] in self.CONFV:
return self.confdict(b)
return b
def read_eeprom(self, a=0, n=CONFF.size):
return bytes([self.cmd('x', aa | 0x800, sleep=0.001) & 0xff
for aa in range(a, a+n)])
def read_ram(self, a=0x100, n=0x800):
return bytes([self.cmd('y', aa, sleep=0.001) & 0xff
for aa in range(a, a+n)])
def read_rom(self, a=0, n=0x8000):
return bytes([self.cmd('y', aa|0x8000, sleep=0.001) & 0xff
for aa in range(a, a+n)])
def readhex(self, fn, a=0, unpack=False):
import intelhex
b = bytes(intelhex.IntelHex(fn).tobinarray())[4*a:]
if not unpack:
return b
return self.confdict(b[:self.CONFS])
return self.confdict(b)
def verify_conf(self, b, a=0):
f = self.confbytes(b, a)
c = self.read_conf(unpack=False)
f = self.confbytes(b, a)[:self.CONFF.size]
c = self.read_conf(unpack=False)[:self.CONFF.size]
return [(4*a+i, b, f[i]) for i, b in enumerate(c) if b != f[i]]
def dictdiff(self, a, b):
return [(k, a[k], b[k]) for k in a if a[k] != b[k]]
def get_confs(self, ihex="avr/leia.eeprom", eaddr=0):
self.conf = self.read_conf(unpack=True)
if ihex:
self.ihex = self.readhex(ihex, unpack=True)
if eaddr is not None:
self.econf = self.confdict(self.read_eeprom(eaddr))
print("eeprom → conf: ", self.dictdiff(self.econf, self.conf), file=sys.stderr)
if ihex:
print("ihex → conf: ", self.dictdiff(self.ihex, self.conf), file=sys.stderr)
def write_conf(self, b, a=0):
b = self.confbytes(b,a)
n = 0
@ -158,18 +240,135 @@ class leia_stepper:
self.cmd('X', i, bb)
n = i+1
return b[:n]
def adc(self, ch=0, what=0, val=0):
return self.cmd('a', val, what<<4 | ch & 0xf);
def dac(self, volt=0.0, dac=None, ramp=True):
ADC_V = {
"data": 0,
"mux": 1,
"idx": 2,
"incr": 3,
"period": 4,
"ADMUX": 5,
"ADCSRA": 6,
"ADCH": 7,
}
def adc(self, ch=0, what=None, val=None, enable=None, **aa):
"send one 'a' or 'A' command"
if what in self.ADC_V:
what = self.ADC_V[what]
for a in aa:
if what is not None or val is not None:
raise ValueError("multiple whatsits not supported")
what = self.ADC_V[a]
val = aa[a]
what = what or 0
if isinstance(val, float):
if what == self.ADC_V["period"]:
val = int(val/self.T0TICK + 0.5) # ms
else:
raise ValueError("fload not supported for this whatsit")
if enable is not None:
if what and enable and val is not None:
raise ValueError("cannot enable and read a whatsit")
what != 0x80
what = (what << 4) | ch;
c = 'A' if val is not None or enable else 'a'
val = val or 0
r = self.cmd(c, val, what);
if not what & 0x70:
return r
if (r>>8) != what:
raise self.Stepper_Error(f"{c}, {val}, {what}{r}")
return r & 0xff
def dac(self, volt=0.0, dac=None, ramp=True, ms=None):
if dac is None:
dac = int(volt/3.3*0x10000)
dac = int(volt/2.5*0x10000)
if dac < 0 or dac >= 0x10000:
raise ValueError(f"DAC voltage out of range {volt}, {dac}")
if not ramp:
self.cmd('P', 0)
if ms is not None:
t = self.adc(what="period")*self.T0TICK
p = int(t * 64/ ms + 0.5)
if p < 1:
p = 1
if p > 1024:
p = 1024
self.cmd('P', p)
if self._verbose >= 1:
print(f"DAC ramp speed {t*64/p:.1f} ms/step", file=sys.stderr)
old = self.cmd('D', dac)
if self._verbose >= 2:
if self._verbose >= 1:
print(f"DAC set to 0x{dac:04x}, old value: 0x{old:04x}",
file=sys.stderr)
self._dacvalue = dac
def Temp(self, a, Vref, **aa):
a *= self.Vcc/Vref
return armlib.NTC(a, β=3940)
def AVR_Temp(self, a, Vref, **aa):
return a/64 * 2.5/Vref - 273
ADC_MUX = {
3: ("Iprim", 1.0, "uncal"),
8: ("NTC1", Temp, "°C"),
9: ("NTC2", Temp, "°C"),
10: ("NTC3", Temp, "°C"),
11: ("Temp", AVR_Temp, "°C"),
12: ("Vcc", 4.0, "V"),
17: ("Bandgap", 1.0, "V"),
18: ("GND", 1.0, "V"),
}
Vcc = 3.3
T0TICK = 0.0925925925925926
def read_adcs(self, chs=tuple(range(16)), update_vcc=True):
r = []
for ch in chs:
mux = self.cmd('a', 0, 0x10+ch)
if not mux & 0x40: # REFS0
r.append((ch, mux))
continue
Vref = 2.5 if mux & 0x80 else self.Vcc; # REFS1
a = self.cmd('a', 0, ch)
if not mux & 0x1f in self.ADC_MUX:
r.append((ch, mux, a))
continue
avg = not mux & 0x20
n = self.ADC_MUX[mux & 0x1f]
v = a
if mux & 0x20:
# ADLAR, single conversion
# !ADLAR: exponential average, τ=64 conversions
v &= 0xffc0
if isinstance(n[1], float):
v *= Vref/0x10000 * n[1]
else:
v = n[1](self, v, mux=mux, Vref=Vref)
r.append((ch, mux, a, n[0], v, n[2], Vref))
if (mux & 0xff) == 0xcc:
self.Vcc = v
for rr in r:
try:
ch, mux, a, n, v, u, Vref = rr
except:
continue
print(f"{ch:2d} 0x{mux:04x} 0x{a:04x} {n:7s} {v:6.3f} {u:5s} {'single' if mux & 0x20 else 'avg '} ({Vref:.2f} V)")
return r
def avrdude(self):
print(self.ifc.cmd("v spi_ssel = 0"), file=sys.stderr)
print(self.ifc.cmd("spi/reset"), file=sys.stderr)
try:
print("SPI reset for programming the AVR", file=sys.stderr)
print("Press ^C when done …", end="", file=sys.stderr)
while True:
sys.stderr.flush()
time.sleep(60)
print("", end="", file=sys.stderr)
except KeyboardInterrupt:
pass
print(" done.\n", self.ifc.cmd("spi/reset/ssel"), file=sys.stderr)
print("commit: ", self.id(), file=sys.stderr)

View file

@ -20,7 +20,7 @@ def HK():
return Vcc, Vss, Icc, Iss, Vprim, Vbias, Ibias, Ibiasp, Tntc7, Tntc8
def findnmahepam():
findxrena(prod=(0xee0c,))
findxrena(prod=(0xee0c,0xee0a))
if connected_p():
set_clock()
messages()
@ -75,8 +75,10 @@ Keep_alive = armlib.Keep_Alive_Schedule(
)
if __name__=="__main__":
ifc,_oo = armlib.init_irena(scope = globals(), name = "AHEPAM", prod = (0xee0c,),
long_options=["seth", "ahbgo"])
ifc,_oo = armlib.init_irena(scope = globals(), name = "AHEPAM", prod = (0xee0c,0xee0a),
long_options=["seth", "ahbgo", "leia"])
if ifc.is_a("USB"):
findnmahepam()
for o,v in _oo[0]:
if o=="--seth":
dorn.CONFIG.seth()
@ -85,9 +87,11 @@ if __name__=="__main__":
if o=="--ahbgo":
dorn.CONFIG.ahbgo()
armlib.set_prompt("AHBGO")
if o=="--leia":
armlib.set_prompt("LEIA")
import leia_stepper
st = leia_stepper.leia_stepper(ifc)
ifc._stream_fifos = 'hk1/f1/f2/f3'
if ifc.is_a("USB"):
findnmahepam()
from dorn import *
dorn._connect(ifc)
ifc._stream_reset |= 0x000c