Compare commits

...

14 commits

Author SHA1 Message Date
Stephan I. Böttcher
70cefb78d3 avr commits 2025-11-19 19:13:49 +01:00
Stephan I. Böttcher
8539542690 leia_stepper: .enable() rework 2025-11-19 19:13:17 +01:00
Stephan I. Böttcher
bb8884d2ca leia_stepper: class bitnames 2025-11-19 16:54:47 +01:00
Stephan I. Böttcher
57e99bf199 leia_stepper: stepping methods 2025-11-19 16:20:58 +01:00
Stephan I. Böttcher
8ed1b837f0 dorn leia hk: VbiasD is negative 2025-11-19 16:19:45 +01:00
Stephan I. Böttcher
4269bd8293 dorn: LEIA hk() 2025-11-17 23:57:40 +01:00
Stephan I. Böttcher
c5a8f7fef2 leia_stepper: .enable(), .step() 2025-11-17 23:56:52 +01:00
Stephan I. Böttcher
10f2815e5d ahepam doEPOCH() 2025-11-17 23:51:58 +01:00
Stephan I. Böttcher
dd879c5433 update avr 2025-11-17 23:51:04 +01:00
Stephan I. Böttcher
9aee6531e0 flash_LEIA: new fat 2025-11-17 23:50:26 +01:00
Stephan I. Böttcher
1310ad3e53 leia_stepper: dac, adc fixes 2025-11-12 20:21:36 +01:00
Stephan I. Böttcher
0c8591a6ef flash_tarena: 1MByte 2025-11-12 20:20:49 +01:00
Stephan I. Böttcher
8220f10bcb tarena: Bitfile from 2025 2025-11-10 11:39:00 +01:00
Stephan I. Böttcher
122a9fb802 tarena: Bitfile from 2014 2025-11-10 11:38:27 +01:00
15 changed files with 401 additions and 25 deletions

View file

@ -228,8 +228,10 @@ FATNAME_flash_TANOS=TANOS
FATNAME_flash_CHAOS=CHAOS
FATNAME_flash_µM=IRENAuM
FATNAME_flash_NMAHEPAM=NMAHEPAM
FATNAME_flash_LEIA=LEIA
FATNAME=$(FATNAME_$(FLASH))
FATSIZE_flash_tarena=1M
FATSIZE_flash_pirena=1M
FATSIZE_flash_darena=1M
FATSIZE_flash_erena=1M

View file

@ -35,10 +35,20 @@ function isEE() {
return 1
}
function doEPOCH(t) {
if (t < 0x00ffffff && TLAST > 0xff000000) {
EPOCH += 0x100000000
TLAST = t
} else if (t > TLAST) {
TLAST = t
}
return t+EPOCH
}
function isCC() {
N = 0
if (!/^EDB? /) return 0
if (NE && ($2 > T0+20 || $2+20 < T0)) {
if (NE && ($2 > T0+7 || $2+7 < T0)) {
N = NE
TE0 = T0
save = $0
@ -65,14 +75,7 @@ function isCC() {
i = NDCh * $3 + $4
ELINES[i] = $0
NE++
if (N) {
if (TE0 < 0x00ffffff && TLAST > 0xff000000) {
EPOCH += 0x100000000
TLAST = TE0
} else if (TE0 > TLAST) {
TLAST = TE0
}
}
if (N) doEPOCH(TE0)
return N
}
@ -126,6 +129,8 @@ function isSETH() {
}
ETRIG0 = 0
ETRIG1 = 0
iTRIG0 = -1
iTRIG1 = 48
for (i in cTRIG) {
if (EE[i] > ETRIG0) {
ETRIG0 = EE[i]

2
avr

@ -1 +1 @@
Subproject commit 50f46b8494bbba65ccbec124f4fd4d9c0c4b72a3
Subproject commit 7ebd848bd2e58dc30c966ebf915f7ce6ff8e77df

53
dorn.py
View file

@ -47,9 +47,24 @@ def HK7_SETH_fmt(s, i, n, d):
return f"""{i}. {n}
na = {repr(d[:4])}
Text = {d[6]:.1f} °C,
Ibias = {d[4]:.1f} nA.
Ibias = {d[4]:.1f} nA,
Ibias = {d[7]:.1f} nA, Vbias = {d[5]:.1f} V.
"""
def HK3_LEIA_fmt(s, i, n, d):
return f"""{i}. {n}
Tadc = {d[0]:6.2f} °C,
Vadc = {d[1]:6.3f} V,
VbiasD = {d[7]:6.2f} V,
Vbias2 = {d[6]:6.1f} V,
Vpp = {d[3]:6.3f} V, Vnn = {d[2]:6.3f} V,
Vcc = {d[5]:6.3f} V, Vss = {d[4]:6.3f} V.
"""
def HK4_LEIA_fmt(s, i, n, d):
return f"""{i}. {n}
Ibias1 = {d[3]:.1f} nA, Ibias2 = {d[2]:.1f} nA, IbiasD = {d[0]:.1f} nA,
VbiasG = {d[1]:.1f} V, Vbias1 = {d[4]:.1f} V,
Tpa0 = {d[5]:6.2f} °C, Tpa1= {d[6]:6.2f} °C, Tpa2 = {d[7]:6.2f} °C.
"""
class DORN_CONFIG:
# stis_ana_core 2×24 ch
@ -159,7 +174,7 @@ class DORN_CONFIG:
HK4_AHBGO = [[
("Tbgo₁", (degC, {})),
("Tbgo₂", (degC, {})),
("Vbias", -1/0.047),
("Vbias1", -1/0.047),
("Tbgo₃", (degC, {})),
("Ibias₁", 10470/470 * 51/1051 * 100),
("Ibias₂", 10470/470 * 51/1051 * 100),
@ -171,9 +186,37 @@ class DORN_CONFIG:
# ! change the class attribute .HK
self.HK[4] = ("HK PA", self.HK4_AHBGO, HK4_AHBGO_fmt)
HK3_LEIA = [[
("Tadc", (degC, {})),
("Vadc", 2.0),
("Vnn", 2.5, ("Vpp", -1.5)),
("Vpp", 2.0),
("Vss", 2.5, ("Vcc", -1.5)),
("Vcc", 2.0),
("Vbias2", -1/0.022 * 66.6/75.2),
("VbiasD", -14.),
]]
HK4_LEIA = [[
("IbiasD", 100., -1.4),
("VbiasG", 46.3, ("Vref", -45.3)),
("Ibias2", 10470/470 * 51/1051 * 100, -65.0),
("Ibias1", 10470/470 * 51/1051 * 100, -52.0),
("Vbias1", -1/0.047 * 44.7/48.5),
("Tpa0", (degC, {})),
("Tpa1", (degC, {})),
("Tpa2", (degC, {})),
]]
def leia(self):
self.VREF = (3.342,)
self.slices=(0,)
self.HK[3] = ("HK_AD", self.HK3_LEIA, HK3_LEIA_fmt)
self.HK[4] = ("HK_PA", self.HK4_LEIA, HK4_LEIA_fmt)
CONFIG = DORN_CONFIG()
def hk(sl, what="print", data=None):
def hk(sl=0, what="print", data=None):
if not data:
ecmd("alt/stream/off")
_ifc.menable()
@ -207,6 +250,7 @@ def hk(sl, what="print", data=None):
return sl, data
Vref = CONFIG.Vref(sl)/4096
ddata = []
VV = {"Vref": CONFIG.Vref(sl)}
for a in range(8):
V = {}
ddata.append(V)
@ -228,11 +272,12 @@ def hk(sl, what="print", data=None):
else:
data[a][c] *= Vref*H[1]
V[H[0]] = data[a][c]
VV.update(V)
for c in range(8):
H = HK[c]
if len(H) > 2:
if isinstance(H[2], tuple):
data[a][c] += V[H[2][0]] * H[2][1]
data[a][c] += VV[H[2][0]] * H[2][1]
else:
data[a][c] += H[2]
V[H[0]] = data[a][c]

8
flash_LEIA/CRON.RC Normal file
View file

@ -0,0 +1,8 @@
@clock/short
@v S=sec%12
@s/if hk_mes>1: v hk_count=1
@s/if !S: nm/cou/re/cl/fl; s/exit
@s/if S&1: s/exit
@s/if S&2: pres/inj; s/exit
@s/if S&4: v hk_count=1; s/exit
@s/if S&8: dorn/fifo/strobe/inj 0x10; s/exit

12
flash_LEIA/DORN.RC Normal file
View file

@ -0,0 +1,12 @@
@v/cache iter
@s/for 24: s/exe 'DORNCC.RC' 0, i, 0x10000
@dorn/l3 [0,4,0] -4404
@dorn/l3 [0,4,1] 10830
@dorn/l3 [0,4,2] -23912
@dorn/l3 [0,4,3] 0x7fff (overflow: 45271)
@dorn/fifo/enable/inj 0xdb1
@dorn/enable/samples/inj 0
@v $T = 0
@dorn/enable/t1/inj $T
@dorn/enable/t2/inj $T / 0x10000 + $T * 0x100
@dorn/enable/t3/inj $T / 0x100

30
flash_LEIA/DORNCC.RC Normal file
View file

@ -0,0 +1,30 @@
@v $C = 24*$1 + $2
@s/if !Z[$C]: v Z[$C]=8
@dorn/thr [$1,$2] Z[$C]
@s/if !$3: v $3 = 0x10000
@v $A[0] = -1203
@v $A[1] = -1203
@v $A[2] = -1203
@v $A[3] = -1186
@v $A[4] = -39
@v $A[5] = 1709
@v $A[6] = 2000
@v $A[7] = 1125
@v $X = 0
@s/for 7: v $Y=$A[7-i]*$3/0x10000; v $X=$X-$Y; dorn/a [$1,$2,7-i] $Y
@dorn/a [$1,$2,0] $X
@v $A[0] = 0
@v $A[1] = 0
@v $A[2] = 0
@v $A[3] = 0
@v $A[4] = 2000
@v $A[5] = 970
@v $A[6] = -1082
@v $A[7] = -1888
@v $X = 0
@s/for 7: v $Y=$A[i]*$3/0x10000; v $X=$X-$Y; dorn/b [$1,$2,i] $Y
@dorn/b [$1,$2,7] $X
@dorn/l3 [$1,$2,0] -3686
@dorn/l3 [$1,$2,1] 16027
@dorn/l3 [$1,$2,2] 12898
@dorn/l3 [$1,$2,3] 24618

10
flash_LEIA/INIT.RC Normal file
View file

@ -0,0 +1,10 @@
@sleep 1
@var/set verb=3
@var/set script_cron_prio = 3
@var/set dac=0
@s/if product==0xee0a: altera/file "NMLEIAV1.RBF"
@s/if errno>=500: s/exit
@s/if product==0xee0c: altera/file "NMLEIAV2.RBF"
@s/if errno>=500: s/exit
@e/eval "s/exe 'SN%d.RC'", serial
@s/exe "DORN.RC"

BIN
flash_LEIA/NMLEIAV1.RBF Normal file

Binary file not shown.

BIN
flash_LEIA/NMLEIAV2.RBF Normal file

Binary file not shown.

7
flash_LEIA/README.TXT Normal file
View file

@ -0,0 +1,7 @@
FAT12 2MByte Filesystem for NMAHEPAM
INIT.RC script loaded at startup
NMAHEPAM.RBF Altera FPGA bitfile, Cyclone 10CL025
CRON.RC cron script, to to HK every Minute
SN14.RC unit config
I2C.RC SETH magnetometer and accelerometer config

2
flash_LEIA/SN2.RC Normal file
View file

@ -0,0 +1,2 @@
@v bate_hash = 0x3921
pres/read/verify

BIN
flash_tarena/TARENA.RBF Normal file

Binary file not shown.

View file

@ -1,6 +1,33 @@
import sys, armlib, struct, math, time
class bitnames(dict):
def str(self, b):
r = []
for k, m in self.items():
m = self.bits(m)
if (b & m) == m:
r.append(k)
b &= ~m
if not b:
break
if len(r)==1:
r = r[0]
return r
def bits(self, r, mask=False):
if isinstance(r, int):
return r
if isinstance(r, str):
return self.bits(self[r])
if isinstance(r, dict):
return sum([self.bits(rr) for rr in r if mask or r[rr]])
return sum(map(self.bits, r))
def mask_val(self, **aa):
return self.bits(aa, mask=True), self.bits(aa)
class leia_stepper:
class Stepper_Error(armlib.irena_ifc.IrenaError):
@ -184,13 +211,15 @@ class leia_stepper:
b = struct.pack(self.STATF, *bb)
return b
def read_conf(self, unpack=True):
def read_conf(self, stat=False, unpack=True):
b = []
while True:
r = self.cmd('x', len(b), Error=True, sleep=0.001)
if r & 0xff0000:
break
b.append(r & 0xff)
if not stat and len(b) >= self.CONFF.size:
break
b = bytes(b)
if unpack and b[:2]==self.CONFM and b[2] in self.CONFV:
return self.confdict(b)
@ -241,6 +270,14 @@ class leia_stepper:
n = i+1
return b[:n]
def write_eeprom(self, a=0, b=None):
if b is not None:
self.write_conf(b)
self.cmd('zW', 4*a, verbose=True)
while self.cmd('x', 83, verbose=True, Error=True) & 0xff00ff:
time.sleep(0.1)
return self.cmd('?', verbose=True)
ADC_V = {
"data": 0,
"mux": 1,
@ -266,7 +303,7 @@ class leia_stepper:
if what == self.ADC_V["period"]:
val = int(val/self.T0TICK + 0.5) # ms
else:
raise ValueError("fload not supported for this whatsit")
raise ValueError("float 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")
@ -281,13 +318,7 @@ class leia_stepper:
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/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)
def dac(self, volt=None, dac=None, ramp=True, ms=None):
if ms is not None:
t = self.adc(what="period")*self.T0TICK
p = int(t * 64/ ms + 0.5)
@ -298,20 +329,28 @@ class leia_stepper:
self.cmd('P', p)
if self._verbose >= 1:
print(f"DAC ramp speed {t*64/p:.1f} ms/step", file=sys.stderr)
if dac is None:
if volt is None:
return self.cmd('d')
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)
old = self.cmd('D', dac)
if self._verbose >= 1:
print(f"DAC set to 0x{dac:04x}, old value: 0x{old:04x}",
file=sys.stderr)
def Temp(self, a, Vref, **aa):
a *= self.Vcc/Vref
a *= Vref/self.Vcc
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"),
3: ("Iprim", 1.0, "A"),
8: ("NTC1", Temp, "°C"),
9: ("NTC2", Temp, "°C"),
10: ("NTC3", Temp, "°C"),
@ -323,6 +362,7 @@ class leia_stepper:
Vcc = 3.3
T0TICK = 0.0925925925925926
T1TICK = T0TICK/4
def read_adcs(self, chs=tuple(range(16)), update_vcc=True):
r = []
@ -372,3 +412,217 @@ class leia_stepper:
pass
print(" done.\n", self.ifc.cmd("spi/reset/ssel"), file=sys.stderr)
print("commit: ", self.id(), file=sys.stderr)
E_reset = {
None: 0,
"assert": 0x2000,
"home": 0x8000,
}
E_what = {
None: 0,
"reset": 0x10000,
"enable": 0x10100,
"led": 0x10200,
"sleep": 0x10300,
}
E_port = bitnames({
"ALL": 0x78,
"RESET": 0x08,
"ENABLE": 0x10,
"LEDON": 0x20,
"SLEEP": 0x40,
"NONE": 0x00,
})
E_mode = {
None: 0,
True: 0x51c00,
False: 0x51d00,
"emit": 0x01000,
"reset": 0x03000,
"init": 0x04000,
"home": 0x08000,
"assert": 0x21800,
"deassert":0x21400,
"enable": 0x51c00,
"disable": 0x51d00,
"ledoff": 0x51e00,
"sleep": 0x51f00,
"!enable": "disable",
"!disable": "enable",
"!ledoff": "disable",
"!sleep": "disable",
}
def enable(self, mode=None, what=None, bits=None, **aa):
"""spi/stepper 'r'"""
a = 0
# eqiv:
# what="reset", bits=("RESET")
# what="reset", RESET=1
# reset=("RESET")
# or any other whatsit
# (In case the port config jumpers were changed)
#
# sleep=True: assert SLEEP (clear bit)
# led=False: deassert LEDON (set bit)
# emit=False: do not change the port bits
# init=True: initialize the stepper
# reset = "assert": assert RESET (clear port bit)
# reset = "home": start stepper with a reset pulse
if len(aa)==1:
k = list(aa.keys())[0]
if mode is None and aa[k] is False:
mode = self.E_mode["!"+k]
aa = None
elif mode is None and aa[k] is True:
mode = k
aa = None
elif what is None:
what = k
aa = aa[k]
if aa is True or aa is False:
aa = None
if aa:
if bits is None:
bits = aa
else:
raise ValueError("too many bits")
if what is None and mode is None:
mode = True
a |= self.E_what[what]
c = 'r'
if bits is not None:
bits = self.E_port.bits(bits)
if a & 0x10000:
c = 'R'
a |= bits & self.E_port.bits("ALL")
else:
raise ValueError("no what")
bits = self.E_port.str(a & 0xff)
if self.E_mode[mode] & a & 0x50f00:
raise ValueError("too many hows")
a |= self.E_mode[mode]
r = self.cmd(c, a & 0xffff)
old = self.E_port.str(r & 0xff)
port = self.E_port.str(r>>8 & self.E_port.bits("ALL"))
return mode, what, bits, old, port
MS = {
1: 0x02,
2: 0x0a,
4: 0x06,
8: 0x0e,
16: 0x04,
32: 0x0c,
-1: 0x00,
-2: 0x08,
}
def ms(self, n=1, ms=None, torque=False):
"""calculate microstepping bits
n: microsteps per step (1|2|4|8|16|32|-1|-2)
n: -1|-2: full torque
torque=True: n=1|2: full torque
"""
if ms is not None:
ms = (ms&1)<<3 | (ms&2)<<1 | (ms&4)>>1
else:
ms = self.MS[-n if torque else n]
return ms
def microstep(self, *a, emit=True, **aa):
"""set microstepping bits
*a, **aa: see: .ms()
"""
c = 'i'
if emit:
c = 'I'
ms = self.ms(*a, **aa)
ms |= (~ms & 0x0e)<<8
return self.cmd(c, ms)
def dir(self, d=None):
"""set/read direction
d = 0|1 (False|True)
higher bits are ignored
"""
if d is None:
return self.cmd('i')
return self.cmd('i', d&1 | (~d&1)<<8)
MIN_SPEED = 21
def speed(self, period=None):
"""set/read stepper cadence
period (int) in T1TICS = 23.148 µs
period (float) in ms
"""
if period is None:
return self.cmd('q')
if isinstance(period, float):
period = int(period/self.T1TICK) - 1
if period < self.MIN_SPEED:
period = self.MIN_SPEED
return self.cmd('Q', period)
LIMITS = bitnames(
ALL = ("LEDS", "FAULTS"),
FAULTS = ("FAULT1", "FAULT2"),
FAULT1 = 0x80,
FAULT2 = 0x20,
LEDS = ("LED1", "LED2"),
LED1 = 0x01,
LED2 = 0x02,
NONE = 0x00,
LOCK = (0x100, "ALL")
)
def limit(self, mask=None, val="ALL", **aa):
if isinstance(mask, dict):
aa = mask
if aa:
mask, val = self.LIMITS.mask_val(**aa)
else:
if mask is None:
b = self.cmd('m')
return self.LIMITS.str(b&0xff), self.LIMITS.str(b>>8)
mask = self.LIMITS.bits(mask)
val = self.LIMITS.bits(val)
if mask >= 0x100:
mask = 0
val &= 0xff
else:
val &= mask
return self.cmd('M', mask & 0xff, val)
def step(self, stepper=None, n=0, dir=None,
reset=False, ms=None, dac=None, limit=None, speed=None):
if limit is not None:
self.limit(limit)
if dac is not None:
self.dac(dac)
if speed is not None:
self.speed(speed)
if ms:
self.microstep(n=ms)
if reset:
self.cmd('r', 0x8000)
if stepper is None:
if dir is not None:
self.dir(dir)
return self.cmd('S', n)
if not n:
return self.cmd('0')
nn = n & ~(n-1)
if n//nn < 0x100:
while not n & 1 and dir < 0xf0:
n >>= 1
dir += 16
return self.cmd("012"[stepper], dir, n)
self.cmd("012"[stepper], dir)
return self.cmd('S', n)

View file

@ -88,6 +88,7 @@ if __name__=="__main__":
dorn.CONFIG.ahbgo()
armlib.set_prompt("AHBGO")
if o=="--leia":
dorn.CONFIG.leia()
armlib.set_prompt("LEIA")
import leia_stepper
st = leia_stepper.leia_stepper(ifc)