Compare commits

...
Sign in to create a new pull request.

33 commits

Author SHA1 Message Date
fschumann0
33d120cf80 dose.py aktualisiert 2026-06-28 18:27:04 +02:00
Stephan I. Böttcher
18f6ba9c2f replace BOM with _dose version 2026-06-09 10:59:12 +02:00
Stephan I. Böttcher
cce9ded5a2 bch commits 2026-03-04 15:39:22 +01:00
Stephan I. Böttcher
8a56b61f79 uart: hide _reader() 2026-03-04 15:31:41 +01:00
Stephan I. Böttcher
e5af295232 flash debugging, no known issues left 2026-02-09 09:04:35 +01:00
Stephan I. Böttcher
2aab85975a flash fixes 2026-02-06 11:05:48 +01:00
Stephan I. Böttcher
13407292dd hallo: add flash 2026-02-04 21:41:19 +01:00
Stephan I. Böttcher
873a2ba93a uart, adc, pwm debugged
hallo.c is a temporary slim version of dose.c.
With python support code.
The First nFET characteristic is recorded.
2026-02-04 21:19:21 +01:00
Stephan I. Böttcher
6fcdb271d3 update assy drawing 2026-02-04 21:09:07 +01:00
Stephan I. Böttcher
302b6bb8d7 hallo: early debaug code, about to be removed 2026-02-03 16:00:14 +01:00
Stephan I. Böttcher
d1e6a031ab divmod85 implementation fixed and tested 2026-01-29 11:26:33 +01:00
Stephan I. Böttcher
0fa9cdbc14 temporarily disable most of main() 2026-01-16 19:12:13 +01:00
Stephan I. Böttcher
899798defa flash_find_free() 2026-01-12 20:16:48 +01:00
Stephan I. Böttcher
26e39e219e ADC_RTC flag 2026-01-12 20:14:31 +01:00
Stephan I. Böttcher
0248c1dab2 spi: isr rework 2026-01-12 15:32:21 +01:00
Stephan I. Böttcher
60abd1ab89 Makefile: all: eeprom 2026-01-12 15:31:56 +01:00
Stephan I. Böttcher
0e878a6fa4 fpga_cmd: provide .cmd 2026-01-12 14:19:13 +01:00
Stephan I. Böttcher
977af7648b The machine is complete, no space left for the main program 2026-01-09 21:00:02 +01:00
Stephan I. Böttcher
133f55edf6 section_status, pipe, fpga, HAVE_<OPT> 2026-01-08 00:36:23 +01:00
Stephan I. Böttcher
65d615407c parse_command refactor 2026-01-06 13:58:50 +01:00
Stephan I. Böttcher
bd0238bb2d fixes and cleanups 2026-01-06 00:11:01 +01:00
Stephan I. Böttcher
f0945b9227 _send_sstr26, save flash in cmd 2026-01-05 14:41:39 +01:00
Stephan I. Böttcher
97d00bc908 one more day … 2026-01-04 22:40:35 +01:00
Stephan I. Böttcher
90aaf8d48e flash_stream complete 2026-01-04 12:04:00 +01:00
Stephan I. Böttcher
46809f6fac coding for 36h straight 2026-01-03 00:30:48 +01:00
Stephan I. Böttcher
bf73d2b586 src: dose.c 2026-01-02 07:04:46 +01:00
Stephan I. Böttcher
429a0bf530 gvp: add bottom mask 2025-12-27 20:57:44 +01:00
Stephan I. Böttcher
867b705561 turbo_dose refdes placement 2025-12-21 21:12:06 +01:00
Stephan I. Böttcher
210779c074 turbo_dose checkout 2025-12-21 21:04:45 +01:00
Stephan I. Böttcher
94625ffdc8 layout complete 2025-12-21 20:56:59 +01:00
Stephan I. Böttcher
45a055527e gsch2pcb 2025-12-21 18:10:25 +01:00
Stephan I. Böttcher
7575ad5cda new footprints 2025-12-21 14:47:55 +01:00
Stephan I. Böttcher
f3e6b2ebf7 schematics of a dosimeter with flash storage 2025-12-21 14:30:09 +01:00
62 changed files with 5494 additions and 2575 deletions

6
.gitignore vendored
View file

@ -22,3 +22,9 @@ src/mul
*.eeprom
*.elf
src/data
BCH-Codes/
*.odt
*.userrow
*.m
src/data
galois

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "src/bch4369"]
path = src/bch4369
url = git@codeberg.org:SiB64/bch4369

510
dose.py Normal file
View file

@ -0,0 +1,510 @@
#! /usr/bin/ipython3 --profile=turbo_dose
import sys, time, getopt, fileinput, struct, csv, os, contextlib
import uart
from base85 import base85_encode, base85_decode
from map import memmap
from iotn424 import SFR
import flash as flash_cmd
flash_cmd = flash_cmd.flash_cmd()
sys.path[1:1] = ["./bch4369"]
from bch4369 import bch
options, files = getopt.gnu_getopt(sys.argv[1:], "F:o:M:", ["debug", "tty=", "output=", "map=", "galois"])
tty = None
baud = 115200
out = None
debug = None
map_fn = "hallo.map"
def Debug(e, *a):
if debug:
import traceback
sys.stdout.flush()
print("xdebug", a, repr(e), file=sys.stderr)
traceback.print_exception(e, limit=-2, file=sys.stderr)
for o,v in options:
if o == "--debug":
debug = True
if o in "-F --tty":
tty = v
v = v.split(",", 1)
if v[1:]:
tty = v[0]
baud = int(v[1])
do_clock = True
if o in "--output":
if out:
raise ValueError("cannot have multiple --outputs")
if v=="-":
out = sys.stdout
elif v=="--":
out = sys.stderr
else:
out = open(v, "a")
if o in "-M --map":
map_fn = v
if o == "--galois":
bch.load_galois()
if not out:
out = sys.stdout
if len(files)==1:
if "/dev/tty" in files[0]:
tty = files[0]
files = []
mmap = memmap(map_fn)
class dose_cmd(uart.uart):
def cmd(self, c, d=None, timeout=0.2):
if not isinstance(c, bytes):
c = c.encode()
if d:
c += b" " + base85_encode(d)
self.flush()
self.ucmd(c)
r = self.resp(timeout)
while r and (r[0] != b'#'[0] or r[1] != c[0]):
r = self.resp(timeout)
d = None
if not r:
return r, d
r = r.split()
e = int(r[-1], 16)
if len(r)==3:
d = base85_decode(r[1])
if self._verbose:
print(f"{r[0]}[{r[2]}] {"".join([f"{b:02x}" for b in d])}", file=sys.stderr)
self.last_cmd = c[0:1]
return r[0], e, d
def poke(self, a, d, s=None, ccp=0):
return self.peek(a, s=s, d=d, ccp=ccp)
def peek(self, a=None, s=None, d=None, ccp=0):
name = None
if a in SFR:
name = a
a, ss = SFR[a]
if s is None:
s = ss
if a in mmap:
a, ss = mmap[a]
if ss and s is None:
s = ss
if isinstance(a, str):
print(f"PEEK: unknown addr {a}", file=sys.stderr)
for k in mmap.keys():
if a in k:
print(f"PEEK: in RAM we have {k}", file=sys.stderr)
for k in SFR.keys():
if a in k:
print(f"PEEK: IO SFR we have {k}", file=sys.stderr)
raise ValueError(a)
FF = {1: "B", 2: "H", 4:"I"}
if a is None:
cc, nn, dd = self.cmd(b"M=")
else:
if d is None:
c = b"M"
d = b''
if s is None:
s = 12
else:
c = b'M!'
if isinstance(d, int) and s in FF:
d = struct.pack(f"<{FF[s]}", d)
else:
d = bytes(d)
if s is None:
s = len(d)
d = struct.pack("<HBB", a,s,ccp) + d + b'\0\0\0\0\0\0\0\0\0\0\0\0'
cc, nn, dd = self.cmd(c, d[:16])
if dd and nn:
aa,ss,cc = struct.unpack("<HBB", dd[:4])
print(f"PEEK: {aa-nn:04x}[{nn}+{ss}]: {b2hex(dd[4:4+nn])}", file=sys.stderr)
if name is not None and nn in FF:
d = struct.unpack(f"<{FF[nn]}", dd[4:4+nn])
print(f"{name}: {d[0]:#x}", file=sys.stderr)
return name, d
return cc, nn, dd
def more(self, flags=b''):
return self.cmd(self.last_cmd + b'=' + flags)
def read_mem(self, a, s=None):
cc, nn, dd = self.peek(a=a, s=s)
r = dd[4:4+nn]
while dd[2]:
cc, nn, dd = self.more()
r += dd[4:4+nn]
return r
_adc_conf = None
_sigrow = None
ADC_MUX = {
0: "GND",
6: "Gate",
7: "Drain1",
10: "Drain2",
0x30: "GND",
0x31: ("Vdd", 10, 0, 0),
0x32: "T",
0x33: "DACREF",
}
ADC_REF = {
0: (3.3, "VDD"),
4: (1.024,),
5: (2.048,),
6: (2.5,),
7: (4.096,),
}
def adc(self, start=False, read_conf=False):
if not self._adc_conf or read_conf:
self._adc_conf = self.read_mem("adc_conf")
self._sigrow = self.read_mem(0x1100, 0x24)
g = self._sigrow[0x20]
o = self._sigrow[0x21]
if o & 0x80:
o -= 0x100
o <<= 6
g /= 1 << 14
self.ADC_MUX[0x32] = ("T°C", g, o, 273)
c = "A!<" if start else "A<"
cc, nn, dd = self.cmd(c)
dd = struct.unpack("<8H", dd)
r = []
for i in range(nn):
pos = self._adc_conf[4*i+2] & 0x3f
ref = self._adc_conf[4*i+1] & 0x07
if ref in self.ADC_REF:
ref = self.ADC_REF[ref]
else:
ref = (1,)
a = dd[i]
if pos != 0x32:
a *= ref[0]/0x10000
if pos in self.ADC_MUX:
pos = self.ADC_MUX[pos]
else:
pos = f"Ain{pos}"
if isinstance(pos, tuple):
a = (a - pos[2])*pos[1] - pos[3]
pos = pos[0]
r.append((a, pos, ref))
print(f"{i} {pos:6s} {a:.4f} {ref!r}", file=sys.stderr)
return r
def nfet_scan(self, t=1, dcs=(0x1000,)):
r = []
for dc in range(*dcs):
self.poke("TCA0_SINGLE_CMP0", dc)
time.sleep(t)
self.cmd("A!")
time.sleep(1)
r.append((dc, self.adc()))
return r
def fet_temp_log(self, fn="fet_temp_log.csv",
dc_start=0, dc_stop=0x1000, dc_step=0x40,
settle=0.5, adc_settle=0.5,
duration=None, sweeps=None, interval=0.0,
idle_dc=0, gate="TCA0_SINGLE_CMP0",
quiet=True, append=False):
"""
Records FET characteristic curve with temperature over time to a CSV.
"""
if dc_step == 0:
raise ValueError("dc_step must not be 0")
if (dc_stop - dc_start) * dc_step <= 0:
raise ValueError(
f"dc_step sign does not move {dc_start:#x} -> {dc_stop:#x}")
if sweeps is not None and sweeps <= 0:
raise ValueError("Sweeps must be positive or None")
if sweeps is None and duration is None:
sweeps = 1
def clamp_dc(x):
x = int(round(x))
return 0 if x < 0 else 0xffff if x > 0xffff else x
dcs = []
dc = dc_start
while (dc < dc_stop) if dc_step > 0 else (dc > dc_stop):
dcs.append(clamp_dc(dc))
dc += dc_step
if not dcs:
raise ValueError("Empty sweep. Check dc_start/dc_stop/dc_step")
devnull = open(os.devnull, "w") if quiet else None
def reader():
return contextlib.redirect_stderr(devnull) if quiet \
else contextlib.nullcontext()
def row_channels(adc_rows):
d = {}
for val, pos, _ref in adc_rows:
name = str(pos)
if name in d:
k = 2
while f"{name}#{k}" in d:
k += 1
name = f"{name}#{k}"
d[name] = val
return d
with reader():
first = self.adc(start=True, read_conf=True)
chan_names = list(row_channels(first).keys())
temp_chan = next((c for c in chan_names if c.startswith("T")), None)
header = ["iso_time", "epoch", "t_s", "sweep", "point", "dc"] + chan_names
write_header = True
if append:
try:
write_header = os.path.getsize(fn) == 0
except OSError:
write_header = True
f = open(fn, "a" if append else "w", newline="")
w = csv.writer(f)
if write_header:
w.writerow(header)
f.flush()
print(f"fet_temp_log: {len(dcs)} points/sweep, dc "
f"{dc_start:#x}..{dc_stop:#x} step {dc_step:#x}; channels "
f"{chan_names}; temp={temp_chan or 'NONE'} -> {fn}",
file=sys.stderr)
if temp_chan is None:
print("fet_temp_log: No temperature channel in the ADC "
"Only the I-V curve will be logged.",
file=sys.stderr)
all_sweeps = []
t0 = time.time()
n = 0
try:
while True:
if sweeps is not None and n >= sweeps:
break
if duration is not None and (time.time() - t0) >= duration:
break
sweep_rows = []
for point, dc in enumerate(dcs):
self.poke(gate, dc)
time.sleep(settle)
self.cmd("A!")
time.sleep(adc_settle)
with reader():
adc_rows = self.adc(start=False)
now = time.time()
chans = row_channels(adc_rows)
w.writerow([
time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(now)),
f"{now:.3f}", f"{now - t0:.3f}", n, point, dc,
] + [chans.get(name, "") for name in chan_names])
f.flush()
sweep_rows.append(dict(epoch=now, t_s=now - t0, sweep=n,
point=point, dc=dc, channels=chans))
drain = chans.get("Drain1", chans.get("Drain2", "?"))
temp = chans.get(temp_chan, "?") if temp_chan else "n/a"
print(f" sweep {n} pt {point:3d} dc={dc:#06x} "
f"gate={chans.get('Gate', '?')} drain={drain} "
f"T={temp}", file=sys.stderr)
all_sweeps.append(sweep_rows)
n += 1
self.poke(gate, clamp_dc(idle_dc))
if sweeps is not None and n >= sweeps:
break
if duration is not None and (time.time() - t0) >= duration:
break
if interval > 0:
time.sleep(interval)
except KeyboardInterrupt:
print("\nfet_temp_log: Interrupted. Closing file.", file=sys.stderr)
finally:
self.poke(gate, clamp_dc(idle_dc))
f.flush()
f.close()
if devnull is not None:
devnull.close()
print(f"fet_temp_log: Wrote {sum(len(s) for s in all_sweeps)} rows in "
f"{len(all_sweeps)} sweep(s) to {fn}", file=sys.stderr)
return all_sweeps
"""
Usage example: fet_temp_log("data.csv", dc_step=0x40, duration=3600, interval=60)
"""
def flash(self, op=None, abort=False, buf=False, **aa):
if op is None:
c = "F"
if abort:
c = "F!"
if buf:
c += "<"
return self.cmd(c)
r = self.cmd("F", flash_cmd.cmd_buffer(op, **aa))
if r[1]:
raise flash_cmd.Flash_Error(op)
return r
def write2fbuffer(self, d, op="WriteBuffer2"):
for i in range(0, 512, 16):
b = i>>4
if not b:
c = "B0@%@"
elif not (b&3):
c = "B0@%"
elif b==31:
c = "B3%!"
else:
c = f"B{b&3}%"
self.cmd(c, d=d[i:i+16])
if (b&3) == 3:
if b==31:
s = 80
else:
s = 64
self.flash(op, byte=i & 0x1c0, size=s)[1]
self.wait_for_spi()
def wait_for_spi(self):
while True:
r = self.cmd("F")[1]
if not r:
break
print(f"SPI busy {r:02x}", file=sys.stderr)
def readfbuffer(self, op="ReadBuffer2"):
d = b''
for i in range(0, 512, 16):
b = i>>4
if (b&3) == 0:
if b==28:
s = 80
else:
s = 64
self.flash(op, byte=i, size=s)
self.wait_for_spi()
d += self.cmd(f"B{b&3}<!")[2]
d += self.cmd(f"B4<!")[2]
self._last_page = d
if min(d) == 255:
print(f"FLASH: page is erased")
elif bch.check_page(d):
if not bch.galois:
raise bch.BCHError("galois module not loaded for Parity Error correction")
d = bch.fix_page(d)
return d[:512]
def read_flash(self, page):
print(f"FLASH: reading from {page=}")
self.flash("Transfer2", page=page)
self.flash_status(True)
return self.readfbuffer()
def read_flash2file(self, fn, page, n):
with open(fn, "wb") as f:
while n > 0:
f.write(self.read_flash(page))
page += 1
n -= 1
def flash_status(self, blocking=False):
r = (0,0,bytes(2))
while not r[2][0] & 0x80:
self.flash("Status", what="cmdbuf")
r = self.cmd("F<")
while r[1]:
r = self.cmd("F<")
if not blocking:
break
return tuple(r[2][:2])
def write_flash(self, page, d):
print(f"FLASH: writing to {page=}")
self.write2fbuffer(d)
self.flash("Program2", page=page)
self.flash_status(True)
def write_file2flash(self, page, fn):
n = 0
with open(fn, "rb") as f:
while True:
b =f.read(512)
if not b:
break
if len(b)<512:
b += bytes(512-len(b))
self.write_flash(page+n, b)
n += 1
return n
def erase_flash_sector(self, page, op="EraseSector"):
"""
op="EraseSector" (default)
page=0: pages 0 7
page=8: pages 8 0xff
page=0x100: pages 0x100 0x1ff
op="ErasePage", page=n
op="EraseBlock", page=n [n&~7 n|7]
op="EraseChip"
"""
self.flash(op, page=page)
self.flash_status(True)
def flash_power(self, on=True):
op = "PowerUp" if on else "PowerDown"
self.flash(op)
def flash_Id(self):
self.flash("Id", what="cmdbuf")
while True:
r = self.cmd("F<")
if not r[1]:
break
i = r[2][:5]
print(f"FLASH chip {b2hex(i)}", file=sys.stderr)
return i
def memsetfbuffer(self, byte=0, what=0xff, size=528, op=0x0887):
for i in range(byte, byte+size, 255):
s = byte+size - i
if s > 255:
s = 255
self.flash(op, size=s, what=what, byte=i)
self.wait_for_spi()
if tty:
tty = dose_cmd(tty, baud)
tty._export(globals())
tty._verbose = False
uart.set_prompt("TurboD")
def b2hex(b, sep=" "):
return sep.join([f"{x:02x}" for x in b])

View file

@ -1,7 +1,7 @@
Element["" "SIL_100_3" "U?" "unknown" 0 0 0 0 0 50 ""] (
Pin[0mil -100mil 70mil 20mil 76mil 30mil "" "1" ""]
Pin[0mil 0mil 70mil 20mil 76mil 30mil "" "2" ""]
Pin[0mil 100mil 70mil 20mil 76mil 30mil "" "3" ""]
Pin[0mil -100mil 70mil 20mil 76mil 38mil "" "1" ""]
Pin[0mil 0mil 70mil 20mil 76mil 38mil "" "2" ""]
Pin[0mil 100mil 70mil 20mil 76mil 38mil "" "3" ""]
ElementLine[50mil -150mil 50mil 150mil 5mil]
ElementLine[50mil 150mil -50mil 150mil 5mil]
ElementLine[-50mil 150mil -50mil -150mil 5mil]

View file

@ -1,8 +1,8 @@
Element["" "SIL_100_4" "U?" "unknown" 0 0 0 0 0 50 ""] (
Pin[0mil -150mil 70mil 20mil 76mil 30mil "" "1" ""]
Pin[0mil -50mil 70mil 20mil 76mil 30mil "" "2" ""]
Pin[0mil 50mil 70mil 20mil 76mil 30mil "" "3" ""]
Pin[0mil 150mil 70mil 20mil 76mil 30mil "" "4" ""]
Pin[0mil -150mil 70mil 20mil 76mil 38mil "" "1" ""]
Pin[0mil -50mil 70mil 20mil 76mil 38mil "" "2" ""]
Pin[0mil 50mil 70mil 20mil 76mil 38mil "" "3" ""]
Pin[0mil 150mil 70mil 20mil 76mil 38mil "" "4" ""]
ElementLine[50mil -200mil 50mil 200mil 5mil]
ElementLine[50mil 200mil -50mil 200mil 5mil]
ElementLine[-50mil 200mil -50mil -200mil 5mil]

15
fp/SOIC_150_8.fp Normal file
View file

@ -0,0 +1,15 @@
Element["" "SOIC_150_8" "U?" "unknown" 0 0 0 0 0 50 ""] (
Pad[-87.5mil -75mil -122.5mil -75mil 25mil 20mil 31mil "" "1" "square"]
Pad[-87.5mil -25mil -122.5mil -25mil 25mil 20mil 31mil "" "2" "square"]
Pad[-87.5mil 25mil -122.5mil 25mil 25mil 20mil 31mil "" "3" "square"]
Pad[-87.5mil 75mil -122.5mil 75mil 25mil 20mil 31mil "" "4" "square"]
Pad[87.5mil 75mil 122.5mil 75mil 25mil 20mil 31mil "" "5" "square"]
Pad[87.5mil 25mil 122.5mil 25mil 25mil 20mil 31mil "" "6" "square"]
Pad[87.5mil -25mil 122.5mil -25mil 25mil 20mil 31mil "" "7" "square"]
Pad[87.5mil -75mil 122.5mil -75mil 25mil 20mil 31mil "" "8" "square"]
ElementLine[-87.5mil -100mil -87.5mil 100mil 5mil]
ElementLine[-87.5mil 100mil 87.5mil 100mil 5mil]
ElementLine[87.5mil 100mil 87.5mil -100mil 5mil]
ElementLine[87.5mil -100mil -87.5mil -100mil 5mil]
ElementLine[-70mil -100mil -70mil 100mil 5mil]
)

View file

@ -1,5 +1,8 @@
PROJ = turbo
SUBPROJ = _dose
SPROJ=$(PROJ)$(SUBPROJ)
VERSION = v01
GERBERS = $(PROJ).plated-drill.cnc
@ -13,9 +16,9 @@ png: $(patsubst %.gvp, %.png, $(wildcard $(PROJ)*.gvp))
%.png: %.gvp $(GERBERS)
$(GVP2MAKE) -o $@ $< -w -B0 -w --background=#ffffff -A group=1.0 -X $(GV_OPT)
zip: $(PROJ)_$(VERSION).zip
zip: $(SPROJ)_$(VERSION).zip
%_$(VERSION).zip: %.README %.plated-drill.cnc
%$(SUBPROJ)_$(VERSION).zip: %.README %.plated-drill.cnc
rm -fv $@
awk '/: *$*/{print $$2}' $< | xargs make
zip $@ $< `awk '/: *$*/{print $$2}' $<`
@ -23,9 +26,9 @@ zip: $(PROJ)_$(VERSION).zip
%.plated-drill.cnc: ../%.pcb
pcb -x gerber --gerberfile $* --name-style single $<
bom: $(PROJ)_bom.pdf
bom: $(SPROJ)_bom.pdf
%.bom: ../%.pcb
%$(SUBPROJ).bom: ../%.pcb
pcb -x bom --bomfile $@ $<
%_bom.txt: %.bom

View file

@ -1,36 +1,36 @@
(gerbv-file-version! "2.0A")
(define-layer! 9 (cons 'filename "turbo.bottommask.gbr")
(define-layer! 10 (cons 'filename "turbo.bottompaste.gbr")
(cons 'visible #t)
(cons 'color #(17973 10705 61411))
)
(define-layer! 9 (cons 'filename "turbo.bottom.gbr")
(cons 'visible #t)
(cons 'color #(45177 46748 64893))
)
(define-layer! 8 (cons 'filename "turbo.bottommask.gbr")
(cons 'inverted #t)
(cons 'visible #f)
(cons 'color #(0 57568 6070))
(cons 'alpha #(21588))
)
(define-layer! 8 (cons 'filename "turbo.bottom.gbr")
(cons 'visible #t)
(cons 'color #(45177 46748 64893))
)
(define-layer! 7 (cons 'filename "turbo.bottomsilk.gbr")
(cons 'visible #t)
(cons 'color #(0 0 0))
)
(define-layer! 6 (cons 'filename "turbo.top.gbr")
(define-layer! 7 (cons 'filename "turbo.top.gbr")
(cons 'visible #f)
(cons 'color #(65535 29244 28836))
(cons 'alpha #(42662))
)
(define-layer! 5 (cons 'filename "turbo.toppaste.gbr")
(define-layer! 6 (cons 'filename "turbo.toppaste.gbr")
(cons 'visible #f)
(cons 'color #(65535 0 6760))
)
(define-layer! 4 (cons 'filename "turbo.topmask.gbr")
(define-layer! 5 (cons 'filename "turbo.topmask.gbr")
(cons 'inverted #t)
(cons 'visible #f)
(cons 'color #(0 64984 7760))
(cons 'alpha #(13364))
)
(define-layer! 3 (cons 'filename "turbo.plated-drill.cnc")
(define-layer! 4 (cons 'filename "turbo.plated-drill.cnc")
(cons 'visible #t)
(cons 'color #(61307 61307 61307))
(cons 'color #(65535 65535 65535))
(cons 'alpha #(65535))
(cons 'attribs (list
(list 'autodetect 'Boolean 1)
@ -39,6 +39,10 @@
(list 'digits 'Integer 4)
))
)
(define-layer! 3 (cons 'filename "turbo.bottomsilk.gbr")
(cons 'visible #t)
(cons 'color #(0 0 0))
)
(define-layer! 2 (cons 'filename "turbo.topsilk.gbr")
(cons 'visible #f)
(cons 'color #(0 0 0))
@ -54,7 +58,7 @@
(cons 'color #(0 0 0))
(cons 'alpha #(65535))
)
(define-layer! -1 (cons 'filename "/home/falbala/stephan/eda/turbo_weather/gerber")
(cons 'color #(65535 65535 65535))
(define-layer! -1 (cons 'filename "/home/blaulicht/stephan/eda/turbo_weather/gerber")
(cons 'color #(51265 51265 51265))
)
(set-render-type! 3)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

View file

@ -1,30 +1,26 @@
# PcbBOM Version 1.0
# Date: So 21 Apr 2024 10:57:09 GMT UTC
# Author:
# Date: Di 09 Jun 2026 08:58:28 GMT UTC
# Author: Stephan Boettcher
# Title: TURBO - PCB BOM
# Quantity, Description, Value, RefDes
# --------------------------------------------
C0603 -BYP_100nF 1/1 C4
C0603 100nF 6/6 C1 C10 C11 C12 C2 C3
C0603 100nF 6/6 C11 C12 C30 C32 C33 C34
C0603 SMD-LED 1/1 D1
C0603.fp -SD:_10kΩ 1/1 R13
C0603.fp 10MΩ 1/1 R9
C0603.fp 10kΩ 4/4 R11 R5 R7 R8
C0603.fp 10kΩ_NTC 1/1 R6
C0603.fp 1MΩ 1/1 R10
C0603.fp 100kΩ 7/7 R30 R31 R32 R33 R34 R35 R36
C0603.fp 10kΩ 1/1 R11
C0603.fp 2.2MΩ 1/1 R4
C0603.fp 220kΩ 1/1 R2
C0603.fp 3.3MΩ 1/1 R3
C0603.fp 330kΩ 1/1 R1
C0603.fp ∞Ω 1/1 R12
C0603.fp ∞Ω/0Ω 1/1 R10
KEYSTONE-1025-7 6V 1/1 B1
MS5534C MS5534C 1/1 U2
P1206 10µF 11/11 C20 C21 C22 C23 C24 C25 C26 C27 C28 C29
C30
P1206 10µF 4/4 C20 C26 C29 C31
SIL_100_3 HE_100_1×3 1/1 J1
SIL_100_4 WRL-10534 1/1 U3
SIL_100_4 SIL4 1/1 U3
SOIC_150_14 ATtiny4x4SS 1/1 U1
SOT23_5 LT1761-SD 1/1 U4
SOIC_150_8 AT45DB161E 1/1 U2
SOT23_3 2N7002 2/2 Q1 Q2
SOT23_5 LT1761-SD_/_-BYP 1/1 U5
SUBD9_PINS D9_pigtail 1/1 CONN1
gseboard turbo 1/1 BOARD

View file

@ -3,7 +3,7 @@
from fp import *
make(SIL, (3,4))
make(SOIC, (14,))
make(SOIC, (8, 14))
make(HEADER, (2, 10,))
make(SOT, (3,5))
make(SOD, ("C0603", "P1206"))

View file

@ -1,19 +1,34 @@
PROJ=bate
PROJ=hallo
PATH:=/usr/local/bin:$(PATH)
VPATH=.:bch4369
default: all
all: $(PROJ).hex
all: $(PROJ).hex $(PROJ)_all
SN_bate = 1
MCU_bate = attiny424
C_FILES_bate = uart.c rtc.c spi.c adc.c calib.c mul.c cmd.c
S_FILES_bate = uart_tx.S
hallo_all: hallo.eeprom
CFLAGS_hallo = -Ibch4369 -DHAVE_nFETs -DSEND_HEX -DHALLO
MCU_hallo = attiny424
SN_hallo = 1
C_FILES_hallo = config.c uart.c cmd.c base85.c rtc.c adc.c pwm.c spi.c flash.c bch4369.c
S_FILES_hallo = uart_tx.S base85a.S
dose_all: dose.eeprom dose.userrow
SN_dose = 1
MCU_dose = $(MCU_$(VAR))
MCU_nFETs = attiny424
MCU_FPGA = attiny3224
VAR=nFETs
C_FILES_nFETs =
C_FILES_FPGA = fpga.c
CFLAGS_dose = -DHAVE_$(VAR)
C_FILES_dose = config.c rtc.c adc.c pwm.c $(C_FILES_$(VAR)) uart.c cmd.c pipe.c base85.c bch4369.c spi.c flash.c
S_FILES_dose = uart_tx.S base85a.S
MCU = $(MCU_$(PROJ))
# When flash gets tight, use `OPT=-Os`, or use more assembler :-)
OPT = -O2
OPT = -Os
CC=avr-gcc -Wall -Wno-parentheses -MMD -std=c99 $(OPT) \
-mmcu=$(MCU) \
@ -22,24 +37,26 @@ CC=avr-gcc -Wall -Wno-parentheses -MMD -std=c99 $(OPT) \
-fpack-struct \
-fshort-enums \
-mtiny-stack \
-mint8 \
-fverbose-asm
-mint8
SN = $(SN_$(PROJ))
CFLAGS = $($*_CFLAGS) $(DEBUG) -I. -DSN="$(SN)"
CFLAGS = $(CFLAGS_$(PROJ)) $(DEBUG) -I. -DSN="$(SN)"
C_FILES = $(C_FILES_$(PROJ))
S_FILES = $(S_FILES_$(PROJ))
OBJS = $(patsubst %.c, %.o, $(C_FILES)) $(patsubst %.S, %.o, $(S_FILES))
%.s: %.c %.o
%.s: %.c
$(CC) $(CFLAGS) -S $<
%.o: %.c
$(CC) -g $(CFLAGS) -c $<
$(CC) $(CFLAGS) -c $<
%.o: %.S
$(CC) -g $(CFLAGS) -c $<
$(CC) $(CFLAGS) -c $<
%.m: %.S
$(CC) $(CFLAGS) -E -dM $< > $@
-include *.d
@ -51,41 +68,36 @@ LDFLAGS = -Teeprom.ld
OBJCOPY = avr-objcopy
%.hex: %.elf
$(OBJCOPY) -O ihex -R .eeprom -R .eemap $< $@
$(OBJCOPY) -O ihex -R .eeprom -R .eemap -R .userrow -R .uumap $< $@
%.eeprom: %.elf
$(OBJCOPY) -O ihex -j .eemap --change-section-lma .eemap=0 $< $@
%.userrow: %.elf
$(OBJCOPY) -O ihex -j .uumap --change-section-lma .uumap=0 $< $@
pMCU-attiny424 = t424
#
#avrdude> dump fuses
#>>> dump fuses 0x0 0x9
#
#Reading | ################################################## | 100% 0.13 s
#
#0000 00 00 7e ff ff f6 ff 00 00 |..~...... |
#
#avrdude>
pMCU-attiny824 = t824
pMCU-attiny824 = t3224
# WDT
fuse0_bate= 0x00
fuse0_dose= 0x00
# BOD
fuse1_bate= 0x00
fuse1_dose= 0x00
# OSC, 20 MHz
fuse2_bate= 0x7e
fuse2_dose= 0x7e
# ???
fuse4_bate= 0xff
fuse4_dose= 0xff
# SYS0 (default 0xf6) RESET, EEPROM erase
fuse5_bate= 0xf7
fuse5_dose= 0xf7
# SYS1 startup time (64ms)
fuse6_bate= 0xff
fuse6_dose= 0xff
# APPEND
fuse7_bate= 0x00
fuse7_dose= 0x00
# BOOTEND
fuse8_bate= 0x00
fuses_bate =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
fuse8_dose= 0x00
fuses_dose =$(patsubst %, 0x%, 00 00 7e ff ff f7 ff 00 00)
AVRDUDEPROG = avrdude
AVRDUDE = $(AVRDUDEPROG)
@ -94,7 +106,8 @@ AVRDUDE_PORT = /dev/ttyUSB1
AD = $(AVRDUDE) -p $(pMCU-$(MCU)) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
sig_bate = 0x1e 0x92 0x2c
sig_dose = 0x1e 0x92 0x2c
sig_hallo = 0x1e 0x92 0x2c
id: $(PROJ).id
%.id:
@ -104,12 +117,12 @@ ad: $(PROJ).ad
%.ad:
$(AD) -v -t
%.burn: %.hex
%.burn: %.hex %.id
$(AD) -U flash:v:$< || $(AD) -U flash:w:$<
%.verify: %.hex %.eeprom
%.verify: %.hex %.eeprom %.userrow
-$(AD) -qq -U fuses:v:"$(fuses_$*)":m
-$(AD) -qq -U userrow:v:"$(BATE_CONFIG)":m
-$(AD) -qq -U userrow:v:$(word 3, $^)
-$(AD) -qq -U eeprom:v:$(word 2, $^)
-$(AD) -qq -U flash:v:$<
@ -119,41 +132,10 @@ fuse: $(PROJ).fuse$F
echo "$*: fuse$F = $(fuse$F_$*)"
[ -n "$(fuse$F_$*)" ] && $(AD) -B 5 -U fuse$F:w:$(fuse$F_$*):m
# see bate.c: Configuration in USERROW
BC_MAGIC = 0xba
BC_VERS = 9
BC_TRIG = 0x29
BC_SEND = 0x59
BC_PWR = 0xfc
BC_TEST = 0
BC_SPI = 4
BC_MDEL = 2
BC_PER = 9
BC_CPER = 30
BC_CLK = 1
BC_MCLK = 0
BC_BAUD = 0 0
BC_UART = 0xd0
BC_PIT = 0xff
BC_IMM = 5
BC_PAD = 0 0 0 0 0 0 0
BC_PRE = 0xff 0xff 0xff 0xff 0xff 0x55 0x0a 0
BATE_CONFIG = $(BC_MAGIC) $(BC_VERS) \
$(BC_TRIG) $(BC_SEND) $(BC_PWR) $(BC_TEST) \
$(BC_SPI) $(BC_MDEL) $(BC_PER) $(BC_CPER) \
$(BC_CLK) $(BC_MCLK) $(BC_BAUD) $(BC_UART) \
$(BC_PIT) $(BC_IMM) $(BC_PAD) $(BC_PRE)
bate.config:
$(AD) -U userrow:v:"$(BATE_CONFIG)":m \
|| $(AD) -U userrow:w:"$(BATE_CONFIG)":m
clean:
rm -f *.hex *.o *.s *.map *.elf *.d
bate.hex: bate.eeprom
# $(PROJ).hex: $(PROJ).eeprom $(PROJ).userrow
.PHONY: eeprom.eeprom
eeprom.eeprom:
@ -161,7 +143,5 @@ eeprom.eeprom:
%.eeprom.burn: %.eeprom
$(AD) -U eeprom:v:$< || $(AD) -U eeprom:w:$<
calib: calib.c mul.c
gcc -DCALIB_DEBUG -o $@ $<
mul: mul.c
gcc -DMUL_TEST -o $@ $<
%.userrow.burn: %.userrow
$(AD) -U userrow:v:$< || $(AD) -U userrow:w:$<

270
src/adc.c
View file

@ -2,81 +2,77 @@
// adc.c
//
#include "config.h"
#include "adc.h"
#include "bate.h"
#include "mul.h"
#include "eeprom.h"
#include <avr/interrupt.h>
#include "pwm.h"
#include "rtc.h"
#ifdef ADC_IMMEDIATE
# define ADC_TRIGGER ADC_MODE_BURST_SCALING_gc | ADC_START_IMMEDIATE_gc
#else
# define ADC_TRIGGER ADC_MODE_SERIES_SCALING_gc | ADC_START_EVENT_TRIGGER_gc
#endif
enum adc_conf_parameter {
INP = ADC_VIA_ADC_gc,
INN = ADC_VIA_ADC_gc,
REF = 10 << ADC_TIMEBASE_gp,
MODE = ADC_MODE_BURST_SCALING_gc | ADC_START_IMMEDIATE_gc,
MODE_DIFF = ADC_MODE_BURST_SCALING_gc | ADC_START_IMMEDIATE_gc | ADC_DIFF_bm,
V_RFP = ADC_MUXPOS_AIN4_gc,
V_NTC = ADC_MUXPOS_AIN6_gc,
V_BAT = ADC_MUXPOS_AIN7_gc,
MODE = ADC_TRIGGER,
MODE_DIFF = ADC_TRIGGER | ADC_DIFF_bm,
};
struct adc_conf adc_conf[N_ADC] ADC_CONF_ADDR = {
{ // Internal Temperature
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_MUXPOS_TEMPSENSE_gc,
.calib = 80 * 256,
.shifts = 1,
},
__attribute__((section(".eeprom1")))
struct adc_conf adc_conf[N_ADC] = {
{ // V_CC / 10
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_MUXPOS_VDDDIV10_gc,
.calib = 1024 * 10,
.shifts = 3, // V [×10]
},
{ // Batterie Voltage / 11
{ // Internal Temperature
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_BAT,
.calib = 1024 * 11,
.shifts = 3, // V [×11]
.inp = INP | ADC_MUXPOS_TEMPSENSE_gc,
},
{ // NTC biased by V_RF
.mode = MODE_DIFF,
#ifdef HAVE_nFETs
{ // VD1
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | V_NTC,
.inn = INP | V_RFP,
.calib = 2048 * 10,
.shifts = 1, // mV
.inp = INP | ADC_D1,
},
{ // NTC biased by V_RF
{ // VD2
.mode = MODE,
.ref = REF | ADC_REFSEL_1024MV_gc,
.inp = INP | ADC_D2,
},
{ // VD1
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_NTC,
.calib = 2500 * 10,
.shifts = 1,
.inp = INP | ADC_D1,
},
{ // NTC biased by V_RF
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_NTC,
},
{ // Transmitter power
{ // VD2
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | V_RFP,
.calib = 2500 * 10,
.shifts = 1,
.inp = INP | ADC_D2,
},
{ // Transmitter power
{ // VDG
.mode = MODE,
.ref = REF | ADC_REFSEL_2500MV_gc,
.inp = INP | ADC_G,
},
{ // VDG
.mode = MODE,
.ref = REF | ADC_REFSEL_VDD_gc,
.inp = INP | V_RFP,
.inp = INP | ADC_G,
},
#endif
};
uint16_t adc_readings[N_ADC];
uint8_t adc_current = 0xff;
section_status(adc.n) uint8_t adc_current;
section_status(adc.r) uint16_t adc_readings[N_ADC];
#ifdef HAVE_nFETs
section_status(pwm) uint16_t pwm_dutycycle;
#endif
static inline
void start_conversion(const struct adc_conf *c)
@ -87,47 +83,42 @@ void start_conversion(const struct adc_conf *c)
ADC.COMMAND = c->mode;
}
void init_adc()
{
PORTA.PIN4CTRL |= PORT_ISC_INPUT_DISABLE_gc;
PORTA.PIN6CTRL |= PORT_ISC_INPUT_DISABLE_gc;
PORTA.PIN7CTRL |= PORT_ISC_INPUT_DISABLE_gc;
}
struct_ioconf(adc_config) = {
conf_prefix(ADC),
conf_io(ADC.CTRLA, ADC_ENABLE_bm),
conf_io(ADC.CTRLB, ADC_PRESC_DIV10_gc),
conf_io(ADC.CTRLE, 250),
conf_io(ADC.CTRLF, ADC_SAMPNUM_ACC256_gc | ADC_LEFTADJ_bm),
conf_io(ADC.INTCTRL, ADC_RESRDY_bm),
conf_prefix(EVSYS),
conf_io(EVSYS.CHANNEL0, EVSYS_CHANNEL0_TCA0_CMP1_LCMP1_gc),
conf_io(EVSYS.USERADC0START, 1),
};
void start_adc()
{
if (!adc_conf->mode)
return;
ADC.CTRLA = ADC_ENABLE_bm | ADC_RUNSTDBY_bm;
ADC.COMMAND = 0;
ADC.CTRLB = ADC_PRESC_DIV10_gc;
ADC.CTRLE = 32 << ADC_SAMPDUR_gp;
ADC.CTRLF = ADC_SAMPNUM_ACC64_gc | ADC_LEFTADJ_bm;
adc_current = 0;
ADC.INTFLAGS = 0xff;
ADC.INTCTRL = ADC_RESRDY_bm;
start_conversion(adc_conf);
}
#if 0
ISR(ADC0_RESRDY_vect)
{
DEBUG_COUNTER(adc_irqs);
uint8_t i = adc_current;
if (i >= N_ADC)
goto stop;
adc_readings[i] = *(volatile uint16_t *) &ADC.RESULT;
if (++i < N_ADC) {
while (++i < N_ADC) {
const struct adc_conf *c = adc_conf + i;
if (c->mode || c->mode == 0xff)
goto stop;
adc_current = i;
start_conversion(c);
return;
if (c->mode) {
start_conversion(c);
adc_current = i;
return;
}
}
stop:
ADC.CTRLA = 0;
ADC.COMMAND = 0;
adc_current = N_ADC;
}
@ -149,7 +140,7 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"push r31" "\n\t"
"lds r24, adc_current" "\n\t"
"cpi r24, %[NADC]" "\n\t"
"brsh 3f" "\n\t"
"brcc 2f" "\n\t"
"mov r30, r24" "\n\t"
"ldi r31, 0" "\n\t"
"lsl r30" "\n\t"
@ -158,22 +149,19 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"lds r25, %[RESULT]" "\n\t"
"st Z, r25" "\n\t"
"lds r25, %[RESULT]+1" "\n\t"
"std Z+1, r25" "\n\t"
"cpi r24, %[NADC]-1" "\n\t"
"brsh 3f" "\n\t"
"subi r24, 0xff" "\n\t"
"std Z+1, r25" "\n"
"1:" "\n\t"
"subi r24, -1" "\n\t"
"cpi r24, %[NADC]" "\n"
"2:" "\n\t"
"clr r25" "\n\t"
"brcc 3f" "\n\t"
"mov r30, r24" "\n\t"
"ldi r31, 0" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"lsl r30" "\n\t"
"subi r30, lo8(-(adc_conf))" "\n\t"
"sbci r31, hi8(-(adc_conf))" "\n"
"1:" "\n\t"
"ld r25, Z" "\n\t"
"subi r25, 1" "\n\t"
"cpi r25, 0xfe" "\n\t"
"brsh 3f" "\n\t"
"sbci r31, hi8(-(adc_conf))" "\n\t"
"ldd r25, Z+1" "\n\t"
"sts %[CTRLC], r25" "\n\t"
"ldd r25, Z+3" "\n\t"
@ -181,8 +169,10 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"ldd r25, Z+2" "\n\t"
"sts %[MUXPOS], r25" "\n\t"
"ld r25, Z" "\n\t"
"sts %[COMMAND], r25" "\n"
"2:" "\n\t"
"tst r25" "\n\t"
"breq 1b" "\n"
"3:" "\n\t"
"sts %[COMMAND], r25" "\n\t"
"sts adc_current, r24" "\n\t"
"pop r31" "\n\t"
"pop r30" "\n\t"
@ -191,15 +181,9 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
"3:" "\n\t"
"clr r24" "\n\t"
"sts %[CTRLA], r24" "\n\t"
"ldi r24, %[NADC]" "\n\t"
"rjmp 2b" "\n"
: :
[NADC] "n" (N_ADC),
[RESULT] "n" (&ADC.RESULT),
[CTRLA] "n" (&ADC.CTRLA),
[CTRLC] "n" (&ADC.CTRLC),
[MUXPOS] "n" (&ADC.MUXPOS),
[MUXNEG] "n" (&ADC.MUXNEG),
@ -208,97 +192,31 @@ ISR(ADC0_RESRDY_vect, ISR_NAKED)
}
#endif
static inline
int16_t calib_temp(uint16_t a)
void adc_start_stream(uint8_t flag)
{
uint16_t calib = SIGROW.TEMPSENSE0;
int16_t offset = (int8_t) SIGROW.TEMPSENSE1;
a >>= 1;
a -= offset << 5;
a = mul16sun(calib, a, 8);
a -= 8741;
// [K × 32]
return a;
#ifdef HAVE_nFETs
if (flag & ADC_PWM)
pwm_set(config.pwm_min, 1);
#endif
start_adc();
}
// saturating addition, signed + signed
static inline
int16_t saddss(int16_t a, int16_t o)
uint8_t adc_poll(uint8_t flags)
{
__asm__(
" add %A[a], %A[o] \n"
" adc %B[a], %B[o] \n"
" brvc 1f \n"
" ldi %A[a], 0xff \n"
" ldi %B[a], 0x7f \n"
" brmi 1f \n"
" adiw %[a], 1 \n"
"1: \n"
: [a] "+w" (a)
: [o] "r" (o)
);
return a;
}
// saturating addition, unsigned + signed
static inline
uint16_t saddus(uint16_t a, int16_t o)
{
__asm__(
" add %A[a], %A[o] \n"
" adc %B[a], %B[o] \n"
" tst %B[o] \n"
" brmi 1f \n"
" brcc 3f \n"
" rjmp 2f \n"
"1: \n"
" brcs 3f \n"
"2: \n"
" ldi %A[a], 0xff \n"
" ldi %B[a], 0xff \n"
" brcs 3f \n"
" adiw %[a], 1 \n"
"3: \n"
: [a] "+w" (a)
: [o] "r" (o)
);
return a;
}
void send_calib_adc(uint8_t i)
{
struct adc_conf *conf = adc_conf + i;
uint8_t sgnd = conf->mode & ADC_DIFF_bm;
int16_t offset = (int16_t)conf->offset;
uint16_t calib = conf->calib;
uint16_t a = adc_readings[i];
if (conf->inp == ADC_MUXPOS_TEMPSENSE_gc) {
a = calib_temp(a);
sgnd = 1;
time(); // fill `clock`
uint8_t r = adc_busy();
if (r)
return r;
if (flags) {
if (flags & ADC_RTC && !rtc_cnt_tick())
return 1;
#ifdef HAVE_nFETs
if (flags & ADC_PWM)
pwm_step(config.pwm_max);
pwm_dutycycle = PWM.CMP0;
if (pwm_busy() || flags & ADC_CONV)
#endif
start_adc();
}
if (sgnd)
a = saddss(a, offset);
else
a = saddus(a, offset);
if (!calib) {
send_str(" 0x");
send_hex_word(a);
return;
}
uint16_t c;
if (sgnd)
c = calib;
else {
c = a;
a = calib;
}
a = mul16sun(a, c, conf->shifts >> 4);
send_char(' ');
send_str(decimal_str(a, conf->shifts & 3));
return 0;
}

View file

@ -10,18 +10,36 @@ struct adc_conf {
uint8_t ref;
uint8_t inp;
uint8_t inn;
int8_t offset;
uint8_t shifts;
uint16_t calib;
};
#define N_ADC (64/sizeof(struct adc_conf))
#ifdef HAVE_nFETs
#define N_ADC 8
#define ADC_D1 7
#define ADC_D1_PINCTRL PORTA.PIN7CTRL
#define ADC_D2 10
#define ADC_D2_PINCTRL PORTB.PIN1CTRL
#define ADC_G 6
#define ADC_G_PINCTRL PORTA.PIN6CTRL
#else
#define N_ADC 2
#endif
extern uint16_t adc_readings[N_ADC];
extern uint8_t adc_current;
void start_adc();
void init_adc();
static inline uint8_t adc_busy() { return ADC.STATUS & 1; }
static inline uint8_t adc_busy() { return ADC.COMMAND; }
extern struct adc_conf adc_conf[N_ADC];
void send_calib_adc(uint8_t i);
uint8_t adc_poll(uint8_t flag);
void adc_start_stream(uint8_t flag);
enum adc_flags {
ADC_CONV = 1,
ADC_PWM = 2,
ADC_SIZE = 0x70, // opencoded in pipe.c
ADC_16 = 0x10,
ADC_32 = 0x20,
ADC_64 = 0x40,
ADC_RTC = 0x80,
};

43
src/base85.c Normal file
View file

@ -0,0 +1,43 @@
#include <stdint.h>
#include <base85.h>
uint32_t divmod85(uint32_t u, uint8_t *m);
uint32_t mul85(uint32_t u, uint8_t m);
uint8_t base85_error;
#ifdef BASE85C
int base85_encode(uint32_t u, uint8_t *s)
{
s += 5;
*s = 0;
u = divmod85(u, --s);
u = divmod85(u, --s);
u = divmod85(u, --s);
u = divmod85(u, --s);
*--s = (u>>24) + 33;
return 5;
}
const uint8_t *base85_decode(const uint8_t *s, uint32_t *u)
{
uint32_t uu = 0;
uint8_t c;
for (int i=0; i<5; i++) {
while (1) {
c = *s++ - 33;
if (c >= 85)
goto error;
uu = mul85(uu, c);
break;
}
}
c = 0;
error:
base85_error = c;
*u = uu;
return s;
}
#endif

4
src/base85.h Normal file
View file

@ -0,0 +1,4 @@
extern uint8_t base85_error;
int base85_encode(uint32_t u, uint8_t *s);
const uint8_t *base85_decode(const uint8_t *s, uint32_t *u);

25
src/base85.py Normal file
View file

@ -0,0 +1,25 @@
import struct
def base85_encode(b):
uu = list(struct.unpack(f">{len(b)//4}I", b))
uu.reverse()
r = []
for u in uu:
for i in range(5):
r[0:0] = [u % 85 + 33]
u //= 85
return bytes(r)
def base85_decode(s):
uu = [];
for j in range(0,len(s),5):
u = 0
for i in range(5):
m = ord(s[j+i:j+i+1]) - 33;
if m < 0 or m >= 85:
raise ValueError(f"invalid base85 char {s}[{i}]")
u *= 85
u += m
uu.append(u)
return struct.pack(f">{len(uu)}I", *uu)

132
src/base85_test.c Normal file
View file

@ -0,0 +1,132 @@
#include <stdio.h>
#include <stdint.h>
uint8_t m;
uint64_t f;
static inline
uint32_t divmod85(uint32_t u)
{
uint64_t uu = u;
uu += uu << 1;
uu += 1;
// f = (uu << 16) + (uu << 8) + uu + (uu >> 8) + (uu >> 16) + (uu >> 24);
f = (uu << 16) + (uu << 8) + uu + (uu >> 8) + (uu >> 16) + (uu >> 24) + (uu>>32);
f >>= 16;
m = ((f & 0xff) * 85 + 0x80) >> 8;
return f >> 8;
}
int d = 0;
typedef uint8_t r;
uint32_t divmod85avr(uint32_t u)
{
#define D(_m) if(d) printf("%-8s c=%x r0=%02x r1=%02x r18=%02x r19=%02x r20=%02x r21=%02x " \
"r22=%02x r23=%02x r24=%02x r25=%02x r30=%02x r31=%02x\n", \
_m, c, r0, r1, r18, r19, r20, r21, r23, r23, r24, r25, r30, r31)
#define ADD(_x, _y) { _c = (_x + _y) >>8; _x += _y; c = _c & 1; D("ADD "#_x);}
#define ADC(_x, _y) { _c = (_x + _y + c) >>8; _x += _y + c; c = _c & 1; D("ADC "#_x);}
#define SUB(_x, _y) { _c = (_x - _y) >>8; _x -= _y; c = _c & 1; D("SUB "#_x);}
#define SBC(_x, _y) { _c = (_x - _y - c) >>8; _x -= _y + c; c = _c & 1; D("SBC "#_x);}
#define LSL(_x) { c = _x >> 7; _x <<= 1; D("LSL "#_x);}
#define ROL(_x) { _c = _x >> 7; _x <<= 1; _x |= c; c = _c; D("ROL "#_x);}
#define CLR(_x) { _x = 0; D("CLR "#_x);}
#define MOV(_x, _y) { _x = _y; D("MOV "#_x);}
#define MOVW(_x, _xh, _y, _yh) { _x = _y; _xh = _yh; D("MOVW "#_x);}
#define SEC {c=1; D("SEC");}
#define LDI(_x, _n) { _x = _n; D("LDI "#_x);}
#define MUL(_x, _y) { r0 = _x * _y; r1 = (_x * _y) >> 8; D("MUL "#_x);}
r c, _c, r0, r18, r19, r20, r21, r30, r31;
r r1 = 0;
r r22 = u>>24;
r r23 = u>>16;
r r24 = u>>8;
r r25 = u;
MOVW(r18,r19, r22,r23);
MOVW(r20,r21, r24,r25);
LSL(r21);
ROL(r20);
ROL(r19);
ROL(r18);
CLR(r0);
ROL(r0);
SEC;
ADC(r21, r25);
ADC(r20, r24);
ADC(r19, r23);
ADC(r18, r22);
ADC(r0, r1);
MOV(r30, r21);
ADD(r30, r20);
CLR(r31);
ROL(r31);
ADD(r30, r19);
ADC(r31, r1);
ADD(r30, r18);
ADC(r31, r1);
ADD(r30, r0);
ADC(r31, r1);
MOV(r22, r31);
ADD(r22, r30);
MOV(r18, r31);
ADC(r18, r30);
MOV(r25, r31);
ADC(r25, r1);
SUB(r30, r21);
SBC(r31, r1);
ADD(r25, r30);
MOV(r24, r31);
ADC(r24, r1);
SUB(r30, r20);
SBC(r31, r1);
ADD(r24, r30);
MOV(r23, r31);
ADC(r23, r1);
SUB(r30, r19);
SBC(r31, r1);
ADD(r23, r30);
MOV(r22, r31);
ADC(r22, r0);
LDI(r19, 85);
MUL(r18, r19);
LSL(r0);
LDI(r19, 0)
ADC(r1, r19);
m = r1;
CLR(r1);
return (r22<<24) | (r23<<16) | (r24<<8) | r25;
}
int main()
{
uint32_t u = 0;
int did_d = 0;
do {
if (!(u & 0xffff))
fprintf(stderr, "%08x\r", u);
uint32_t nn = u / 85;
uint8_t mm = u % 85;
uint32_t n = divmod85avr(u);
if (nn != n || mm != m) {
printf("%08x = %08x × 85 + %02x : %08x %02x : %014lx\n",
u, nn, mm, n, m, f);
if (!did_d) {
did_d = d = 1;
divmod85avr(u);
d = 0;
}
}
} while (++u);
}

175
src/base85a.S Normal file
View file

@ -0,0 +1,175 @@
;uint32_t divmod85(uint32_t u)
;{
; uint64_t uu = u;
; f = (uu << 16) + (uu << 8) + uu + (uu >> 8) + (uu >> 16) + (uu >> 24);
; f += 0x0100;
; f &= 0xffffffffffff00LL;
; f += f << 1;
; m = (((f >> 16) & 0xff) * 85 + 0x80) >> 8;
; return f >> 24;
;}
;
; E,A,B,C,D = 3 × (r25,r24,r23,r22 big endian)
; 3*(A+B+C+D+E) = r30, r31
;
; E D C B A
; E D C B A
; E D C B A
; E D C B
; E D C
; E D
; E
; =============
; u u u u r -
#ifdef BASE85C
.global divmod85
divmod85:
movw r26, r20
#endif
;; ! C-ABI: m=X
_divmod85: ; r25,r24,r23,r22 = A,B,C,D
movw r18, r22
movw r20, r24
lsl r21
rol r20
rol r19
rol r18
clr r0
rol r0
sec
adc r21, r25
adc r20, r24
adc r19, r23
adc r18, r22
adc r0, r1 ; r21,r20,r19,r18,r0 = 3u + 1
mov r30, r21
add r30, r20
clr r31
rol r31
add r30, r19
adc r31, r1
add r30, r18
adc r31, r1
add r30, r0
adc r31, r1 ; r30,r31 = A+B+C+D+E
mov r22, r31 ;
add r22, r30 ; just for the carry bit
mov r18, r31
adc r18, r30 ; r18 = r
mov r25, r31
adc r25, r1
sub r30, r21
sbc r31, r1 ; r30,r31 = B+C+D+E
add r25, r30
mov r24, r31
adc r24, r1
sub r30, r20
sbc r31, r1 ; r30,r31 = C+D+E
add r24, r30
mov r23, r31
adc r23, r1
sub r30, r19
sbc r31, r1 ; r30,r31 = D+E
add r23, r30
mov r22, r31
adc r22, r0
ldi r19, 85
mul r18, r19
lsl r0
ldi r19, '!'
adc r1, r19
st -X, r1
clr r1
ret
#ifndef BASE85C
.global base85_encode
base85_encode:
movw r26, r20
adiw r26, 5
st X, r1
rcall _divmod85
rcall _divmod85
rcall _divmod85
rcall _divmod85
subi r25, -'!'
st -X, r25
ret
#endif
; uint32_t mul85(uint32_t u, uint8_t m);
#ifdef BASE85C
.global mul85
mul85:
#endif
;; TODO for SPACE: inline this
;; big endian r22/r23/r24/r25 *= 85; … += r20
_mul85:
ldi r21, 85
mul r25, r21
movw r18, r0
mul r24, r21
movw r24, r0
mul r23, r21
movw r30, r0
mul r22, r21
add r18, r20
adc r24, r19
adc r30, r25
adc r31, r0
mov r25, r18
mov r23, r30
mov r22, r31
#ifdef BASE85C
clr r1
#endif
ret
#ifndef BASE85C
.global base85_decode
base85_decode:
push r17
push r22
push r23
movw r26, r24
clr r25
clr r24
clr r23
clr r22
ldi r17, 5
1:
ld r20, X+
subi r20, '!'
brcs 2f
cpi r20, 85
brcc 2f
rcall _mul85
subi r17, 1
brne 1b
clr r20
2:
sts base85_error, r20
pop r31
pop r30
st Z, r22
std Z+1, r23
std Z+2, r24
std Z+3, r25
movw r24, r26
pop r17
clr r1
ret
#endif

1
src/bch4369 Submodule

@ -0,0 +1 @@
Subproject commit eebd4bb43039a2ecf63aa7c2fdbe1c4edf469393

View file

@ -1,98 +0,0 @@
//
// calib.c
//
// MS5534-CM.pdf
// https://media.digikey.com/pdf/Data%20Sheets/Measurement%20Specialties%20PDFs/MS5534-CM.pdf
#ifdef CALIB_DEBUG
# include <stdio.h>
# define TESTDATA_ADDR
#else
# include "eeprom.h"
#endif
#include "calib.h"
#include "mul.h"
void bate_calib(const union bate *bate, struct pressure *pt)
{
uint16_t C1 = bate->W1 >> 1;
uint16_t C5 = (bate->W1 & 1) << 13 | (bate->W2 & 0xffc0) >> 3;
uint8_t C6 = bate->W2 & 0x3f;
uint16_t C4 = bate->W3 >> 6;
uint16_t C3 = bate->W4 & 0xffc0;
uint16_t C2 = (bate->W3 & 0x3f) << 6 | bate->W4 & 0x3f;
uint16_t D1 = bate->D1;
uint16_t D2 = bate->D2;
uint16_t UT1 = C5 + 20224;
uint16_t dT = D2 - UT1;
uint16_t TEMPSENS = C6 + 50;
int16_t TEMP = mul16sun(dT, TEMPSENS, 6) + 200;
int16_t TCO = C4 - 512;
uint16_t OFFT1 = C2 << 2;
// fails when TCO < 0
uint16_t OFF = OFFT1 + mul16sun(dT, TCO, 4);
uint16_t SENST1 = C1 + 24576;
uint16_t TCS = C3;
uint16_t SENS = SENST1 + mul16sun(dT, TCS, 0);
int16_t X = mul16sun(D1 - 7168, SENS, 2) - OFF;
int16_t P = mul16sun(X, 80, 8) + 2500;
pt->T = TEMP + 2732;
pt->p = P;
#ifdef CALIB_DEBUG
printf("W1 0x%04x\n", bate->W1);
printf("W2 0x%04x\n", bate->W2);
printf("W3 0x%04x\n", bate->W3);
printf("W4 0x%04x\n", bate->W4);
printf("D1 0x%04x %6d\n", D1, D1 - 7168);
printf("D2 0x%04x\n", D2);
printf("C1 %5u\n", C1);
printf("C2 %5u\n", C2);
printf("C3 %5d %5d\n", C3>>6, C3);
printf("C4 %5d\n", C4);
printf("C5 %5u %5u\n", C5>>3, C5);
printf("C6 %5u\n", C6);
printf("UT1 %5u\n", UT1);
printf("dT %5d\n", dT);
printf("TEMPSENS %5u\n", TEMPSENS);
printf("TEMP %5d\n", TEMP);
printf("TCO %5d\n", TCO);
printf("OFFT1 %5u\n", OFFT1);
printf("OFF %5u\n", OFF);
printf("SENST1 %5u\n", SENST1);
printf("TCS %5u %5u\n", TCS>>6, TCS);
printf("SENS %5u\n", SENS);
printf("X %5d\n", X);
printf("P %5d\n", P);
printf(" %.1f °C %.1f mbar\n\n", TEMP/10., P/10.);
}
#define MUL_DEBUG
#include "mul.c"
int main()
{
struct pressure pt;
for (int i=0; i<5; i++)
bate_calib(testdata+i, &pt);
return 0;
#endif
}
const union bate testdata[] TESTDATA_ADDR = {
{ .w = { 0xA691, 0x0A97, 0x989F, 0xAF28, 0x4896, 0x71F4 }, },
{ .W = { 0xabaf, 0x3c99, 0xa31a, 0xb589}, .D = { 0x470c, 0x773f }, }, // 21.2 °C, 1021.7 mbar
{ .W = { 0xabaf, 0x3c99, 0xa31a, 0xb589}, .D = { 0x1a51, 0x6fed }, }, // 7.5 °C, 5.4 mbar
{ .W = { 0xaa3d, 0x35d9, 0xcbe5, 0xb736}, .D = { 0x4bb7, 0x7487 }, }, // 17.7 °C, 1023.0 mbar
{ .W = { 0xaa3d, 0x35d9, 0xcbe5, 0xb736}, .D = { 0x1e25, 0x650a }, }, // -11.3 °C, 2.4 mbar
};

View file

@ -1,29 +0,0 @@
#include <stdint.h>
// !!! int = int8_t
union bate {
uint8_t b[12];
uint16_t w[6];
struct {
uint16_t W[4];
uint16_t D[2];
};
struct {
uint16_t W1;
uint16_t W2;
uint16_t W3;
uint16_t W4;
uint16_t D1;
uint16_t D2;
};
};
struct pressure {
uint16_t T;
uint16_t p;
};
#define N_TESTDATA (64/sizeof(union bate))
void bate_calib(const union bate *bate, struct pressure *pt);
extern const union bate testdata[N_TESTDATA];

448
src/cmd.c
View file

@ -2,215 +2,279 @@
// cmd.c
//
#include "bate.h"
#include "rtc.h"
#include <string.h>
#include "cmd.h"
#include "adc.h"
#include "flash.h"
#include "bch4369.h"
#include "base85.h"
#include "uart.h"
#include "pipe.h"
#ifdef NOCMD
// no space for this
void parse_command(uint8_t *s, uint8_t n) {}
#else
#ifdef HAVE_FPGA
#include "fpga.h"
#endif
uint8_t rx_params[8];
uint8_t cmd_buffer[16];
uint8_t base85_str[6];
#if 0
__attribute__ ((noinline, noclone))
uint8_t parse_hex_nibble(uint8_t c)
void base85_send(const uint32_t *v)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 0x0a;
return 0xf0;
base85_encode(*v, base85_str);
send_str((const char *)base85_str);
}
static inline
uint8_t parse_rx_params(uint8_t *s, uint8_t n)
void base85_send_buffer(const uint8_t *buf)
{
uint8_t p;
p = 0;
while (n) {
while (n) {
if (*s != ' ')
break;
n--;
s++;
}
if (!n || p >= sizeof(rx_params))
break;
uint8_t d = parse_hex_nibble(*s);
if (d==0xf0)
break;
n--;
s++;
if (n) {
uint8_t h = 0;
h = parse_hex_nibble(*s);
if (h != 0xf0) {
n--;
s++;
d <<= 4;
d |= h;
}
}
rx_params[p++] = d;
DEBUG_POKE(rxhex, d);
}
if (!n || *s != '\n')
p = 0;
DEBUG_POKE(rxpar, p);
return p;
base85_send((void*)(buf+0));
base85_send((void*)(buf+4));
base85_send((void*)(buf+8));
base85_send((void*)(buf+12));
}
#else
static inline
uint8_t parse_rx_params(uint8_t *s, uint8_t n)
__attribute__((naked))
void base85_send_buffer(const uint8_t *buf)
{
uint8_t p;
__asm__("\n"
"1: \n"
" clr %[p] \n"
" tst %[n] \n"
" breq 8f \n"
" ldi r26, lo8(rx_params) \n"
" ldi r27, hi8(rx_params) \n"
"2: \n"
" ld r21, Z+ \n"
" cpi r21, ' ' \n"
" breq 6f \n"
" cpi r21, '\\n' \n"
" breq 8f \n"
" rcall nibble \n"
" brcc 7f \n"
" mov r25, r21 \n"
" subi %[n], 1 \n"
" breq 7f \n"
" ld r21, Z \n"
" rcall nibble \n"
" brcc 5f \n"
" subi %[n], 1 \n"
" breq 7f \n"
" adiw r30, 1 \n"
" swap r25 \n"
" or r25, r21 \n"
"5: \n"
" cpi %[p], 8 ; sz rx_params \n"
" brcc 7f \n"
" subi %[p], -1 \n"
" st X+, r25 \n"
#ifdef DEBUG
" sts debug_data + 6, r25 \n"
" sts debug_data + 9, %[p] \n"
#endif
" rjmp 2b \n"
" \n"
"nibble: \n"
" subi r21, '0' \n"
" cpi r21, 10 \n"
" brcs 9f \n"
" subi r21, 'a'-'0' \n"
" cpi r21, 6 \n"
" brcc 9f \n"
" subi r21, -10 \n"
" sec \n"
"9: ret \n"
" \n"
"6: \n"
" subi %[n], 1 \n"
" brne 2b \n"
"7: \n"
" clr %[p] \n"
"8: \n"
: [p] "=a" (p), // r24 (r16…)
[s] "+z" (s), // r30,r31
[n] "+a" (n) // r22 (r16…)
: : "r21", "r25", "r26", "r27"
);
DEBUG_POKE(rxpar, p);
return p;
// send_str26 does not gobble r18, r20, r21, r24.
__asm__(
""
"push r28" "\n\t"
"push r29" "\n\t"
"movw r28, r24" "\n\t"
"rcall _base85_send28" "\n\t"
"rcall _base85_send28" "\n\t"
"rcall _base85_send28" "\n\t"
"rcall _base85_send28" "\n\t"
"pop r29" "\n\t"
"pop r28" "\n\t"
"ret" "\n"
"_base85_send28:" "\n\t"
"ld r22, Y+" "\n\t"
"ld r23, Y+" "\n\t"
"ld r24, Y+" "\n\t"
"ld r25, Y+" "\n\t"
"ldi r20, lo8(base85_str)" "\n\t"
"ldi r21, hi8(base85_str)" "\n\t"
"rcall base85_encode" "\n\t"
"rjmp _send_str26" "\n"
);
}
#endif
void parse_command(uint8_t *s, uint8_t n)
static inline
const uint8_t *base85_fill_buffer(const uint8_t *s) {
base85_error = 0;
for (int i=0; !base85_error && i<16; i+=4)
s = base85_decode(s, (uint32_t *)(cmd_buffer + i));
return s;
}
struct peak_poke {
uint8_t *addr;
uint8_t size;
uint8_t ccp;
uint8_t data[12];
};
static inline
uint8_t poke(struct peak_poke *p, uint8_t poke)
{
if (!n || n >= 15)
goto fail;
uint8_t cmd = *s++;
uint8_t p = parse_rx_params(s, n-1);
uint8_t *pp = rx_params;
uint8_t *a = (uint8_t *)(((uint16_t)pp[0] << 8) | pp[1]);
if (cmd == 'R' && p==1)
__asm__("\n"
" ldi r18, 1 \n"
" out %[ccp], %[p] \n"
" sts %[swrr], r18 \n"
:
: [p] "r" (pp[0]),
[ccp] "n" (_SFR_IO_ADDR(CCP)),
[swrr] "n" (&RSTCTRL.SWRR)
: "r18"
);
if (p >= 3 && p < 8 && (
cmd == 'C' && pp[0]==0xba
|| cmd == 'E'
|| cmd == 'U')) {
uint8_t *e = (uint8_t*) &config;
if (cmd=='E')
e = (uint8_t*) EEPROM_START;
if (cmd=='U')
e = (uint8_t*) &USERROW;
e += pp[1];
pp[p] = *e;
memcpy(e, pp+2, p-2);
if (cmd != 'C')
__asm__("\n"
" ldi r24, %[erwp] \n"
" out %[ccp], %[p] \n"
" sts %[ctrl], r24 \n"
:
: [p] "r" (pp[0]),
[ccp] "n" (_SFR_IO_ADDR(CCP)),
[ctrl] "n" (&NVMCTRL.CTRLA),
[erwp] "n" (NVMCTRL_CMD_PAGEERASEWRITE_gc)
: "r24"
uint8_t s = p->size;
uint8_t *a = p->addr;
uint8_t n = s;
if (n > 12)
n = 12;
p->size = s - n;
p->addr = a + n;
if (!n)
return 0;
if (poke) {
uint8_t ccp = p->ccp;
if (ccp)
__asm__(
"out __CCP__, %[key] \n\t"
"sts %[ctrla], %[cmd] \n"
::
[ctrla] "n" (&NVMCTRL.CTRLA),
[key] "r" (ccp),
[cmd] "r" (NVMCTRL_CMD_PAGEBUFCLR_gc)
: "memory"
);
_memcopy(a, p->data, n);
if (ccp && !(0x8000 & (uint16_t)a))
__asm__(
"out __CCP__, %[key] \n\t"
"sts %[ctrla], %[cmd] \n"
::
[ctrla] "n" (&NVMCTRL.CTRLA),
[key] "r" (ccp),
[cmd] "r" (NVMCTRL_CMD_PAGEERASEWRITE_gc)
: "memory"
);
}
else if (cmd == 'T' && p==1) {
immediate += pp[0];
pp[1] = immediate;
}
else if (cmd == 'M' && p==2) {
pp[2] = *a;
}
else if (cmd == 'W' && p==3) {
pp[3] = *a;
*a = pp[2];
}
else if (cmd == 'K' && p==4) {
cli();
pp[4] = clock_tick;
clock = *(uint32_t*)pp;
sei();
}
else if (cmd == 'D' && p==1) {
pp[1] = test_calib;
test_calib = pp[0];
}
else
goto fail;
send_char('R');
send_char('!');
send_hex(cmd, pp, p+1, 1);
return;
fail:
send_str("R?\n");
_memcopy(p->data, a, n);
return n;
}
const uint8_t *cmd_flags;
#if 0
static __attribute__((noinline))
uint8_t cmd_flag(uint8_t f)
{
if (*cmd_flags == f) {
cmd_flags++;
send_char(f);
return f;
}
return 0;
}
#else
static __attribute__((noinline, naked))
uint8_t cmd_flag(uint8_t f)
{
// _send_char22 does not gobble r24.
__asm__(
""
"lds r30, cmd_flags" "\n\t"
"lds r31, cmd_flags+1" "\n\t"
"ld r22, Z+" "\n\t"
"cp r22, r24" "\n\t"
"mov r24, r1" "\n\t"
"breq 1f" "\n\t"
"ret" "\n"
"1:" "\n\t"
"mov r24, r22" "\n\t"
"sts cmd_flags, r30" "\n\t"
"sts cmd_flags+1, r31" "\n\t"
"rjmp _send_char22" "\n"
);
}
#endif
uint8_t cmd_pending;
void parse_command(const uint8_t *s, uint8_t n)
{
// ^[A-Z][!-@]+( [!-u]{20}])?$
// cmd flags ␣base85
uint8_t r = 0;
uint8_t cmd = *s++;
if (cmd < 'A' || cmd > 'Z')
return;
send_char('#');
send_char(cmd);
uint8_t bflg = *s - '0';
uint8_t *bptr = cmd_buffer;
if (bflg <= 5) {
bptr = flash_buffer + 16*bflg;
bflg = 1<<bflg;
send_char(*s++);
}
else
bflg = 0;
cmd_flags = s;
while (*s > ' ' && *s < 'A')
s++;
uint8_t have_b = 0;
if (*s==' ') {
cmd_pending = 0;
s = base85_fill_buffer(s+1);
r = base85_error;
if (r)
goto error;
have_b = 1;
cmd_pending = cmd;
}
else if (cmd_flag('='))
have_b = cmd_pending == cmd;
if (*s != '\n')
goto error;
switch(cmd) {
case 'A':
r = adc_current;
if (cmd_flag('!'))
start_adc();
if (cmd_flag('<')) {
bptr = (void*)adc_readings;
goto send_buffer;
}
break;
case 'B':
if (cmd_flag('@'))
pipe.valid = 0;
r = pipe.valid;
if (have_b) {
if (cmd_flag('!') || ~r & bflg) {
memcpy(bptr, cmd_buffer, 16);
r = pipe.valid |= bflg;
}
else
goto error;
}
if (cmd_flag('%')) {
if (cmd_flag('@'))
bch4369_init(config.bch_salt);
bch4369_str(bptr, 16);
if (cmd_flag('!')) {
bch4369_fini();
memcpy(flash_buffer+64, bch_parity, 16);
r = pipe.valid |= 0x10;
}
}
if (cmd_flag('<')) {
if (cmd_flag('!')) goto send_buffer;
if (~r & bflg)
goto error;
pipe.valid &=~ bflg;
goto send_buffer;
}
break;
case 'D':
flash_find_free();
bptr = (void*)&fs;
goto send_buffer;
case 'F':
if (have_b)
r = flash_submit_command(cmd_buffer);
else
r = spi_busy_p();
if (cmd_flag('<'))
goto send_buffer;
break;
#ifndef HALLO
case 'P':
if (have_b)
pipe_config((void*)cmd_buffer);
else
r = pipe_poll();
break;
goto send_buffer;
break;
#ifdef HAVE_FPGA
case 'C':
if (cmd_flag('@')) {
fpga_reset();
break;
}
if (!have_b)
goto error;
fpga_cmd((void*)cmd_buffer);
goto send_buffer;
#endif
#endif // HALLO
case 'M':
if (!have_b)
goto error;
r = poke((void*)cmd_buffer, cmd_flag('!'));
send_buffer:
send_char(' ');
base85_send_buffer(bptr);
break;
default:
error:
send_char('?');
}
send_hex_byte_eol(r);
}

6
src/cmd.h Normal file
View file

@ -0,0 +1,6 @@
#include <stdint.h>
extern uint8_t base85_error;
void parse_command(const uint8_t *s, uint8_t n);
extern uint8_t cmd_buffer[16];

67
src/config.c Normal file
View file

@ -0,0 +1,67 @@
#include "config.h"
#include "flash.h"
#include "adc.h"
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in USERROW
__attribute__((section(".userrow")))
const struct config config = {
.magic = MAGIC,
.version = VERSION,
.cpu_clk = CLKCTRL_PDIV_2X_gc | 1, // 10MHz (max @ 3V)
.flash_page_size = FM_528 >> 8,
.bch_salt = 1,
.burn_page = 0x88, // Buffer 1 Page Program w/o Erase
.write_buffer = 0x84 | FM_WRITE, // Buffer 1 Write
.read_array = 0x03 | FM_READ, // Continuous Array Read (Low-Frequency)
.read_buffer = {
[0] = 0xd1 | FM_READ, // Buffer 1 Read (Low-Frequency)
[1] = 0xd3 | FM_READ, // Buffer 2 Read (Low-Frequency)
},
.page_start = 0x0800,
.page_end = 0x1000,
#ifdef HAVE_nFETs
.pwm_min = 0x0000,
.pwm_max = 0xffff,
#endif
};
////////////////////////////////////////////////////////////////////////////////
//
// Configuration in EEPROM
struct_ioconf(port_config) = {
conf_prefix(PORTA),
conf_io(RxD_PINCTRL, PORT_PULLUPEN_bm),
#ifdef HAVE_nFETs
conf_io(ADC_D1_PINCTRL, PORT_ISC_INPUT_DISABLE_gc),
conf_io(ADC_D2_PINCTRL, PORT_ISC_INPUT_DISABLE_gc),
conf_io(ADC_G_PINCTRL, PORT_ISC_INPUT_DISABLE_gc),
#endif
#ifdef HAVE_FPGA
conf_io(nSTATUS_PINCTRL, PORT_PULLUPEN_bm),
conf_io(CRCERR_PINCTRL, PORT_PULLUPEN_bm),
#endif
conf_io(PORTA.OUT, Bit(SSEL_PIN)),
conf_io(PORTA.DIR, Bit(SSEL_PIN) | Bit(MOSI_PIN) | Bit(SCK_PIN)
#ifdef HAVE_nFETs
| Bit(DRAIN_PIN)
#endif
#ifdef HAVE_FPGA
| Bit(nCONFIG_PIN) | Bit(TxE_PIN)
#endif
),
conf_io(PORTB.OUT, Bit(TxD_PIN)),
conf_io(PORTB.DIR, Bit(TxD_PIN)
#ifdef HAVE_nFETs
| Bit(PWM_PIN)
#endif
#ifdef HAVE_FPGA
| Bit(TxE_PIN) | Bit(PEN_PIN)
#endif
),
};

198
src/config.h Normal file
View file

@ -0,0 +1,198 @@
#ifndef _CONFIG_H
#define _CONFIG_H
#include <avr/io.h>
#include <stdint.h>
// USERROW
struct config {
uint8_t magic;
uint8_t version;
uint8_t cpu_clk;
uint8_t cron;
uint8_t flash_page_size;
uint8_t bch_salt;
uint16_t burn_page;
uint16_t write_buffer;
uint16_t read_array;
uint16_t read_buffer[2];
uint16_t page_start;
uint16_t page_end;
#ifdef HAVE_nFETs
uint16_t pwm_min;
uint16_t pwm_max;
#endif
};
enum magic_flags {
#ifdef HAVE_nFETs
MAGIC = 0xD0,
VERSION = 0x00,
#endif
#ifdef HAVE_FPGA
MAGIC = 0xC5,
VERSION = 0x01,
#endif
};
extern const struct config config;
// EEPROM
struct io_config {
uint8_t addr;
uint8_t val;
};
#define struct_ioconf(_n) struct io_config _n[] \
__attribute__((section(".eeprom9"), aligned(2)))
#define conf_prefix(_io) { 0xff, (uint16_t)&(_io) >> 8 }
#define conf_io(_io, _v) { (uint16_t)&(_io) & 0xff, (_v) }
#define conf_ioo(_io, _o, _v) { (uint16_t)&(_io) + _o & 0xff, (_v) }
#define conf_iow(_io, _v) conf_io(_io, (_v) & 0xff), conf_ioo(_io, 1, (uint16_t)(_v) >> 8)
extern struct io_config ee9_start[], ee9_end[];
#define Bit(x) (1<<(x))
#define SPI_VPORT VPORTA
#define SPI_PORT PORTA
#define MOSI_PIN 1
#define MISO_PIN 2
#define SCK_PIN 3
#define UART_VPORT VPORTB
#define UART_PORT PORTB
#define TxE_PIN 1
#define TxD_PIN 2
#define RxD_PIN 3
#define RxD_PINCTRL UART_PORT.PIN3CTRL
#ifdef HAVE_FPGA
#define SSEL_VPORT VPORTA
#define SSEL_PORT PORTA
#define SSEL_PIN 7
#define nCONFIG_VPORT VPORTA
#define nCONFIG_PORT PORTA
#define nCONFIG_PIN 6
#define nSTATUS_VPORT VPORTA
#define nSTATUS_PORT PORTA
#define nSTATUS_PIN 4
#define nSTATUS_PINCTRL nSTATUS_PORT.PIN4CTRL
#define CRCERR_VPORT VPORTA
#define CRCERR_PORT PORTA
#define CRCERR_PIN 5
#define CRCERR_PINCTRL CRCERR_PORT.PIN5CTRL
#define PEN_VPORT VPORTB
#define PEN_PORT PORTB
#define PEN_PIN 1
#else
#define SSEL_VPORT VPORTA
#define SSEL_PORT PORTA
#define SSEL_PIN 4
#endif
#ifdef HAVE_nFETs
#define DRAIN_VPORT VPORTA
#define DRAIN_PORT PORTA
#define DRAIN_PIN 5
#define PWM_VPORT VPORTB
#define PWM_PORT PORTB
#define PWM_PIN 0
#endif
#if 0
static inline
void apply_config()
{
uint8_t n = ee_end - ee_start;
struct io_config = ee_start;
uint8_t prefix = 0;
while (n--) {
const struct io_config *io = c++;
if (io->addr == 0xff)
prefix = io->val;
else {
uint8_t *p = (uint8_t*)((uint16_t)prefix << 8 | io->addr);
*p = io->val;
}
}
}
#else
static inline
void apply_config()
{
__asm__ volatile(
"ldi r30, lo8(ee9_start)" "\n\t"
"ldi r31, hi8(ee9_start)" "\n\t"
"ldi r24, lo8(ee9_size)" "\n\t"
"clr r25" "\n"
"1:" "\n\t"
"mov r27, r25" "\n\t"
"subi r24, 2" "\n\t"
"brcs 3f" "\n"
"2:" "\n\t"
"ld r26, Z+" "\n\t"
"ld r25, Z+" "\n\t"
"cpi r26, 0xff" "\n\t"
"breq 1b" "\n\t"
"st X, r25" "\n\t"
"subi r24, 2" "\n\t"
"brcc 2b" "\n"
"3:" "\n\t"
::: "r24", "r25", "r26", "r27", "r30", "r31");
}
#endif
#define section_status(_n) __attribute__((section(".bss."#_n)))
extern struct magic {
uint8_t magic;
uint8_t reset_source;
} magic;
#if 0
#define _memcopy memcpy
#define _memcopyyz memcpy
#else
// avoid the avr-libc memcpy.
// ¡ n must not be zero !
static inline
void _memcopy(uint8_t *d, uint8_t *s, uint8_t n)
{
__asm__ volatile ("\n"
"1:" "\n\t"
"ld r0, Z+" "\n\t"
"st X+, r0" "\n\t"
"dec %[N]" "\n\t"
"brne 1b" "\n"
: [D] "+x" (d),
[S] "+z" (s),
[N] "+r" (n)
:: "r0", "memory");
}
static inline
void _memcopyyz(uint8_t *d, uint8_t *s, uint8_t n)
{
__asm__ volatile ("\n"
"1:" "\n\t"
"ld r0, Z+" "\n\t"
"st Y+, r0" "\n\t"
"dec %[N]" "\n\t"
"brne 1b" "\n"
: [D] "+y" (d),
[S] "+z" (s),
[N] "+r" (n)
:: "r0", "memory");
}
#endif
#endif // _CONFIG_H

75
src/dose.c Normal file
View file

@ -0,0 +1,75 @@
//
// dose.c
//
// !!! int = int8_t
#include <stdint.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include "config.h"
#include "dose.h"
#include "pipe.h"
#include "uart.h"
#include "rtc.h"
#include "cmd.h"
#include "adc.h"
#include "pwm.h"
////////////////////////////////////////////////////////////////////////////////
//
// main()
section_status(main) struct magic magic;
struct pipe_config cron_job[1] =
{
[0] = {
.pipe = {
.source = pipe_adc,
.dest = pipe_cmd,
.status = PS_OUT | PS_BCH,
.valid = 0,
.adc = ADC_64 | ADC_PWM | ADC_RTC,
},
},
};
int main()
{
while (CLKCTRL.MCLKCTRLB != config.cpu_clk) {
CCP = CCP_IOREG_gc;
CLKCTRL.MCLKCTRLB = config.cpu_clk;
}
sleep_enable();
magic.magic = config.magic;
while (magic.magic != MAGIC)
sleep_cpu();
apply_config();
magic.reset_source = RSTCTRL.RSTFR;
RSTCTRL.RSTFR = magic.reset_source;
send_str("\nV Turbo Dose V0.0");
send_hex_byte_eol(magic.reset_source);
// cron_job[0].page = flash_find_free();
// cron_job[0].npages = config.page_end - cron_job[0].page;
// if (config.cron & 1)
// pipe_config(cron_job);
while (1) {
sei();
sleep_cpu();
command();
// pipe_poll();
// if (config.cron & 2 && rtc_cnt_tick())
// pipe_config(cron_job);
}
}

11
src/dose.h Normal file
View file

@ -0,0 +1,11 @@
//
// dose.h
//
////////////////////////////////////////////////////////////////////////////////
//
// Configuration
#include <stdint.h>
#include <avr/io.h>

370
src/dose.py Executable file
View file

@ -0,0 +1,370 @@
#! /usr/bin/ipython3 --profile=turbo_dose
import sys, time, getopt, fileinput, struct
import uart
from base85 import base85_encode, base85_decode
from map import memmap
from iotn424 import SFR
import flash as flash_cmd
flash_cmd = flash_cmd.flash_cmd()
sys.path[1:1] = ["./bch4369"]
from bch4369 import bch
options, files = getopt.gnu_getopt(sys.argv[1:], "F:o:M:", ["debug", "tty=", "output=", "map=", "galois"])
tty = None
baud = 115200
out = None
debug = None
map_fn = "hallo.map"
def Debug(e, *a):
if debug:
import traceback
sys.stdout.flush()
print("xdebug", a, repr(e), file=sys.stderr)
traceback.print_exception(e, limit=-2, file=sys.stderr)
for o,v in options:
if o == "--debug":
debug = True
if o in "-F --tty":
tty = v
v = v.split(",", 1)
if v[1:]:
tty = v[0]
baud = int(v[1])
do_clock = True
if o in "--output":
if out:
raise ValueError("cannot have multiple --outputs")
if v=="-":
out = sys.stdout
elif v=="--":
out = sys.stderr
else:
out = open(v, "a")
if o in "-M --map":
map_fn = v
if o == "--galois":
bch.load_galois()
if not out:
out = sys.stdout
if len(files)==1:
if "/dev/tty" in files[0]:
tty = files[0]
files = []
mmap = memmap(map_fn)
class dose_cmd(uart.uart):
def cmd(self, c, d=None, timeout=0.2):
if not isinstance(c, bytes):
c = c.encode()
if d:
c += b" " + base85_encode(d)
self.flush()
self.ucmd(c)
r = self.resp(timeout)
while r and (r[0] != b'#'[0] or r[1] != c[0]):
r = self.resp(timeout)
d = None
if not r:
return r, d
r = r.split()
e = int(r[-1], 16)
if len(r)==3:
d = base85_decode(r[1])
if self._verbose:
print(f"{r[0]}[{r[2]}] {"".join([f"{b:02x}" for b in d])}", file=sys.stderr)
self.last_cmd = c[0:1]
return r[0], e, d
def poke(self, a, d, s=None, ccp=0):
return self.peek(a, s=s, d=d, ccp=ccp)
def peek(self, a=None, s=None, d=None, ccp=0):
name = None
if a in SFR:
name = a
a, ss = SFR[a]
if s is None:
s = ss
if a in mmap:
a, ss = mmap[a]
if ss and s is None:
s = ss
if isinstance(a, str):
print(f"PEEK: unknown addr {a}", file=sys.stderr)
for k in mmap.keys():
if a in k:
print(f"PEEK: in RAM we have {k}", file=sys.stderr)
for k in SFR.keys():
if a in k:
print(f"PEEK: IO SFR we have {k}", file=sys.stderr)
raise ValueError(a)
FF = {1: "B", 2: "H", 4:"I"}
if a is None:
cc, nn, dd = self.cmd(b"M=")
else:
if d is None:
c = b"M"
d = b''
if s is None:
s = 12
else:
c = b'M!'
if isinstance(d, int) and s in FF:
d = struct.pack(f"<{FF[s]}", d)
else:
d = bytes(d)
if s is None:
s = len(d)
d = struct.pack("<HBB", a,s,ccp) + d + b'\0\0\0\0\0\0\0\0\0\0\0\0'
cc, nn, dd = self.cmd(c, d[:16])
if dd and nn:
aa,ss,cc = struct.unpack("<HBB", dd[:4])
print(f"PEEK: {aa-nn:04x}[{nn}+{ss}]: {b2hex(dd[4:4+nn])}", file=sys.stderr)
if name is not None and nn in FF:
d = struct.unpack(f"<{FF[nn]}", dd[4:4+nn])
print(f"{name}: {d[0]:#x}", file=sys.stderr)
return name, d
return cc, nn, dd
def more(self, flags=b''):
return self.cmd(self.last_cmd + b'=' + flags)
def read_mem(self, a, s=None):
cc, nn, dd = self.peek(a=a, s=s)
r = dd[4:4+nn]
while dd[2]:
cc, nn, dd = self.more()
r += dd[4:4+nn]
return r
_adc_conf = None
_sigrow = None
ADC_MUX = {
0: "GND",
6: "Gate",
7: "Drain1",
10: "Drain2",
0x30: "GND",
0x31: ("Vdd", 10, 0, 0),
0x32: "T",
0x33: "DACREF",
}
ADC_REF = {
0: (3.3, "VDD"),
4: (1.024,),
5: (2.048,),
6: (2.5,),
7: (4.096,),
}
def adc(self, start=False, read_conf=False):
if not self._adc_conf or read_conf:
self._adc_conf = self.read_mem("adc_conf")
self._sigrow = self.read_mem(0x1100, 0x24)
g = self._sigrow[0x20]
o = self._sigrow[0x21]
if o & 0x80:
o -= 0x100
o <<= 6
g /= 1 << 14
self.ADC_MUX[0x32] = ("T°C", g, o, 273)
c = "A!<" if start else "A<"
cc, nn, dd = self.cmd(c)
dd = struct.unpack("<8H", dd)
r = []
for i in range(nn):
pos = self._adc_conf[4*i+2] & 0x3f
ref = self._adc_conf[4*i+1] & 0x07
if ref in self.ADC_REF:
ref = self.ADC_REF[ref]
else:
ref = (1,)
a = dd[i]
if pos != 0x32:
a *= ref[0]/0x10000
if pos in self.ADC_MUX:
pos = self.ADC_MUX[pos]
else:
pos = f"Ain{pos}"
if isinstance(pos, tuple):
a = (a - pos[2])*pos[1] - pos[3]
pos = pos[0]
r.append((a, pos, ref))
print(f"{i} {pos:6s} {a:.4f} {ref!r}", file=sys.stderr)
return r
def nfet_scan(self, t=1, dcs=(0x1000,)):
r = []
for dc in range(*dcs):
self.poke("TCA0_SINGLE_CMP0", dc)
time.sleep(t)
self.cmd("A!")
time.sleep(1)
r.append((dc, self.adc()))
return r
def flash(self, op=None, abort=False, buf=False, **aa):
if op is None:
c = "F"
if abort:
c = "F!"
if buf:
c += "<"
return self.cmd(c)
r = self.cmd("F", flash_cmd.cmd_buffer(op, **aa))
if r[1]:
raise flash_cmd.Flash_Error(op)
return r
def write2fbuffer(self, d, op="WriteBuffer2"):
for i in range(0, 512, 16):
b = i>>4
if not b:
c = "B0@%@"
elif not (b&3):
c = "B0@%"
elif b==31:
c = "B3%!"
else:
c = f"B{b&3}%"
self.cmd(c, d=d[i:i+16])
if (b&3) == 3:
if b==31:
s = 80
else:
s = 64
self.flash(op, byte=i & 0x1c0, size=s)[1]
self.wait_for_spi()
def wait_for_spi(self):
while True:
r = self.cmd("F")[1]
if not r:
break
print(f"SPI busy {r:02x}", file=sys.stderr)
def readfbuffer(self, op="ReadBuffer2"):
d = b''
for i in range(0, 512, 16):
b = i>>4
if (b&3) == 0:
if b==28:
s = 80
else:
s = 64
self.flash(op, byte=i, size=s)
self.wait_for_spi()
d += self.cmd(f"B{b&3}<!")[2]
d += self.cmd(f"B4<!")[2]
self._last_page = d
if min(d) == 255:
print(f"FLASH: page is erased")
elif bch.check_page(d):
if not bch.galois:
raise bch.BCHError("galois module not loaded for Parity Error correction")
d = bch.fix_page(d)
return d[:512]
def read_flash(self, page):
print(f"FLASH: reading from {page=}")
self.flash("Transfer2", page=page)
self.flash_status(True)
return self.readfbuffer()
def read_flash2file(self, fn, page, n):
with open(fn, "wb") as f:
while n > 0:
f.write(self.read_flash(page))
page += 1
n -= 1
def flash_status(self, blocking=False):
r = (0,0,bytes(2))
while not r[2][0] & 0x80:
self.flash("Status", what="cmdbuf")
r = self.cmd("F<")
while r[1]:
r = self.cmd("F<")
if not blocking:
break
return tuple(r[2][:2])
def write_flash(self, page, d):
print(f"FLASH: writing to {page=}")
self.write2fbuffer(d)
self.flash("Program2", page=page)
self.flash_status(True)
def write_file2flash(self, page, fn):
n = 0
with open(fn, "rb") as f:
while True:
b =f.read(512)
if not b:
break
if len(b)<512:
b += bytes(512-len(b))
self.write_flash(page+n, b)
n += 1
return n
def erase_flash_sector(self, page, op="EraseSector"):
"""
op="EraseSector" (default)
page=0: pages 0 7
page=8: pages 8 0xff
page=0x100: pages 0x100 0x1ff
op="ErasePage", page=n
op="EraseBlock", page=n [n&~7 n|7]
op="EraseChip"
"""
self.flash(op, page=page)
self.flash_status(True)
def flash_power(self, on=True):
op = "PowerUp" if on else "PowerDown"
self.flash(op)
def flash_Id(self):
self.flash("Id", what="cmdbuf")
while True:
r = self.cmd("F<")
if not r[1]:
break
i = r[2][:5]
print(f"FLASH chip {b2hex(i)}", file=sys.stderr)
return i
def memsetfbuffer(self, byte=0, what=0xff, size=528, op=0x0887):
for i in range(byte, byte+size, 255):
s = byte+size - i
if s > 255:
s = 255
self.flash(op, size=s, what=what, byte=i)
self.wait_for_spi()
if tty:
tty = dose_cmd(tty, baud)
tty._export(globals())
tty._verbose = False
uart.set_prompt("TurboD")
def b2hex(b, sep=" "):
return sep.join([f"{x:02x}" for x in b])

View file

@ -1,3 +0,0 @@
#define ADC_CONF_ADDR __attribute__((__section__(".eeprom")))
#define TESTDATA_ADDR __attribute__((__section__(".eeprom")))

View file

@ -6,12 +6,25 @@ MEMORY
{
eemap : ORIGIN = 0x1400, LENGTH = 0x80
eedef : ORIGIN = 0x810000, LENGTH = 0x80
uumap : ORIGIN = 0x1300, LENGTH = 0x20
uudef : ORIGIN = 0x850000, LENGTH = 0x20
}
SECTIONS
{
.eemap 0x1400:
{
ee1_start = .;
*(.eeprom1)
ee1_end = .;
*(.eeprom)
ee9_start = .;
*(.eeprom9)
ee9_end = .;
ee9_size = ee9_end - ee9_start;
} >eemap AT >eedef
.uumap 0x1300:
{
*(.userrow)
} >uumap AT >uudef
}
INSERT BEFORE .eeprom

364
src/flash.c Normal file
View file

@ -0,0 +1,364 @@
/******************************************************
- opcode (hex)
- address
- pb page and byte address
- xb byte address
- px page address
- kx block address
- sx sector address
- xxx don't care
- padding
- 0 1 byte
- 00 2 bytes
- 0000 4 bytes
- data
- data bytes
- | no data bytes
read commands
- Main Memory Page Read D2 pb 0000
- Continuous Array Read (Low-Power Mode) 01 pb
- Continuous Array Read (Low-Frequency) 03 pb
- Continuous Array Read (High-Frequency) 0B pb 0
- Continuous Array Read (High-Frequency) 1B pb 00
- Continuous Array Read (Legacy Command) E8 pb 0000
- Buffer 1 Read (Low-Frequency) D1 xb
- Buffer 2 Read (Low-Frequency) D3 xb
- Buffer 1 Read (High-Frequency) D4 xb 0
- Buffer 2 Read (High-Frequency) D6 xb 0
write commands
- Buffer 1 Write 84 xb
- Buffer 2 Write 87 xb
- Buffer 1 Page Program with Erase 83 px |
- Buffer 2 Page Program with Erase 86 px |
- Buffer 1 Page Program w/o Erase 88 px |
- Buffer 2 Page Program w/o Erase 89 px |
- Page through Buffer 1 with Erase 82 pb
- Page through Buffer 2 with Erase 85 pb
- Byte/Page through Buffer 1 w/o Erase 02 pb
- Page Erase 81 px |
- Block Erase 50 kx |
- Sector Erase 7C sx |
- Chip Erase C7_94_80_9A |
- Program/Erase Suspend B0 |
- Program/Erase Resume D0 |
- Read-Modify-Write through Buffer 1 58 pbx
- Read-Modify-Write through Buffer 2 59 pbx
security commands
- Enable Sector Protection 3D_2A_7F_A9 |
- Disable Sector Protection 3D_2A_7F_9A |
- Erase Sector Protection Register 3D_2A_7F_CF |
- Program Sector Protection Register 3D_2A_7F_FC
- Read Sector Protection Register 32 xxx
- Sector Lockdown 3D_2A_7F_30
- Read Sector Lockdown Register 35 xxx
- Freeze Sector Lockdown 34_55_AA_40 |
- Program Security Register 9B_00_00_00
- Read Security Register 77 xxx
miscellanious
- Main Memory Page to Buffer 1 Transfer 53 px |
- Main Memory Page to Buffer 2 Transfer 55 px |
- Main Memory Page to Buffer 1 Compare 60 px |
- Main Memory Page to Buffer 2 Compare 61 px |
- Auto Page Rewrite through Buffer 1 58 px |
- Auto Page Rewrite through Buffer 2 59 px |
- Deep Power-Down B9 |
- Resume from Deep Power-Down AB |
- Ultra-Deep Power-Down 79 |
- Status Register Read D7
- Manufacturer and Device ID Read 9F
- Power of 2 (Binary) Page Size 3D_2A_80_A6 |
- Standard DataFlash Page Size 3D_2A_80_A7 |
- Software Reset F0_00_00_00 |
*/
#include "config.h"
#include "flash.h"
#include "cmd.h"
section_status(flash) uint8_t flash_cmd_buffer[4];
section_status(flash) uint8_t flash_status_bytes[2];
uint8_t flash_buffer[FB_SIZE];
uint8_t flash_cmd_na(uint16_t mode, uint16_t what)
{
return flash_cmd(mode, what, 0, 0);
}
uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte)
{
uint8_t spi_mode = SPI_FLASH | mode & FM_SPI;
uint8_t op = mode;
uint8_t size = what;
what >>= 8;
uint8_t s = spi_select(spi_mode);
if (s)
return s;
uint8_t *b = flash_cmd_buffer;
*b++ = op ;
switch (mode & FM_ADDR) {
case FM_528:
page <<= 1;
case FM_512:
byte |= page << 9;
page >>= 7;
case FM_SEC:
*b++ = page;
*b++ = byte>>8;
*b++ = byte;
}
uint8_t csize = b - flash_cmd_buffer;
uint8_t pads = 0;
switch (mode & FM_PAD) {
case FM_PAD4: pads = 2;
case FM_PAD2: pads += 1;
case FM_PAD1: pads += 1;
}
if (!(mode & FM_START)) {
spi.zsize = pads + size;
spi.zero = what;
spi_start_cmd(csize, flash_cmd_buffer);
return 0;
}
if (size > 80) {
// for read of the security register
pads += size-64;
size = 64;
}
spi.zsize = pads;
if (what + size <= 80)
b = flash_buffer + what;
else if (what>=96 && what+size <= 112)
b = cmd_buffer + (what-96);
else if (size <= 2)
b = flash_status_bytes;
switch (mode & FM_START) {
case FM_WRITE:
spi_start_write(csize, flash_cmd_buffer, size, b);
break;
case FM_READ:
spi_start_read(csize, flash_cmd_buffer, size, b);
break;
case FM_WAIT:
spi.mask = 0x80;
spi_start_read(csize, flash_cmd_buffer, 2, b);
break;
}
return 0;
}
struct flash_cmd {
uint16_t mode, what, page, byte;
};
uint8_t flash_submit_command(uint8_t *cmd)
{
struct flash_cmd *c = (void*)cmd;
return flash_cmd(c->mode, c->what, c->page, c->byte);
}
struct flash_stream fs;
__attribute__ ((noinline, noclone))
uint8_t flash_stream_submit(uint16_t mode, uint8_t size)
{
uint8_t b = fs.block;
uint16_t p = fs.page;
uint8_t r = fs.status & ~FS_Ready;
mode |= (uint16_t)config.flash_page_size << 8;
if (size) {
if (b & 8) {
b = 0;
fs.page = ++p;
fs.npages--;
}
else if (b==7 && r & FS_528)
size |= 16;
}
else if ((r & FS_Dir) == FS_Erase) {
fs.page += (uint16_t) b + 1;
fs.npages -= (uint16_t) b + 1;
b = 8;
}
fs.block = b+1;
uint8_t e = flash_cmd(mode, size, p, (uint16_t)(b&7) << 6);
if (e)
r |= FS_Ready; // FS_Error
r |= FS_Busy;
fs.status = r;
return e;
}
__attribute__ ((noinline, noclone))
uint8_t flash_stream_done()
{
uint8_t r = fs.status & FS_Error;
if (!r || r == FS_Error)
return 1;
if (fs.npages || !(fs.block & 8))
return 0;
return 1;
}
static inline
uint8_t flash_write_next_block()
{
return flash_stream_submit(config.write_buffer, 64);
}
static inline
uint8_t flash_read_next_block()
{
uint16_t mode;
if (fs.page & 0x1000)
mode = config.read_buffer[fs.page&1];
else
mode = config.read_array;
return flash_stream_submit(mode, 64);
}
static inline
uint8_t flash_erase_next_page()
{
uint16_t mode = 0x81; // Page Erase
uint8_t n = 0;
if (fs.page && !(fs.page & 0xff) && fs.npages & 0xff00) {
mode = 0x7c; // Sector 1…15 Erase
n = 0xff;
}
else if (!(fs.page & 7) && fs.npages >= 8) {
mode = 0x50; // Block Erase
n = 7;
}
fs.block = n;
return flash_stream_submit(mode, 0);
}
static inline
uint8_t flash_burn_page()
{
return flash_stream_submit(config.burn_page, 0);
}
uint8_t flash_poll(uint8_t rr)
{
uint8_t r = fs.status;
if (spi_busy_p())
return r;
if ((r & FS_Error) == FS_Error)
return r;
if (r & FS_StBsy) {
// status bytes arrived
if (~flash_status_bytes[0] & 0x80)
// flash is still busy burning
goto rd_status;
// not busy any more, move Bsy → Rdy
if (r & FS_Busy)
r |= FS_Ready;
goto ready;
}
if (!(r & FS_Busy))
goto ready;
if (rr) {
r |= FS_Error;
fs.status = r;
return r;
}
if (r & FS_Write) {
if (fs.block == 9) {
rd_status:
// request status bytes for pending Write or Error
r |= FS_StBsy;
fs.status = r;
flash_cmd_na(0xd7 | FM_READ, 0xf002);
return r;
}
}
ready:
r &= ~(FS_Busy | FS_StBsy);
if (rr)
r |= FS_Ack;
fs.status = r;
if (r & FS_Dir == FS_Write && fs.block == 8)
flash_burn_page();
else if (!flash_stream_done()) {
if (r & (FS_Dir|FS_Ack) == (FS_Read|FS_Ack))
flash_read_next_block();
else if (r & (FS_Dir|FS_Ack) == (FS_Write|FS_Ack))
flash_write_next_block();
else if (r & FS_Dir == FS_Erase)
flash_erase_next_page();
fs.status &=~ FS_Ack;
}
return fs.status;
}
uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags)
{
uint8_t r = flash_poll(0);
if ((r & FS_Error) == FS_Busy)
return FS_Error;
r = flags | FS_Ready;
if (config.flash_page_size != FM_528)
r &=~ FS_528;
fs.page = page;
fs.block = 0;
fs.npages = npages;
fs.status = r;
flash_status_bytes[0] = 0xff;
return flash_poll(0);
}
static inline
uint8_t flash_memset_buffer2()
{
for (uint16_t i=0; i<528; i += 176) {
uint8_t r = flash_cmd((uint16_t)config.flash_page_size << 8 | 0x87, 176|0xff00, 0, i);
if (r)
return r;
while (spi_busy_p()) ;
}
return 0;
}
static inline
uint8_t flash_compare_buffer2(uint16_t p)
{
uint8_t r;
r = flash_cmd((uint16_t)config.flash_page_size << 8 | 0x61, 0, p, 0);
if (r)
return r;
while (spi_busy_p()) ;
r = flash_cmd_na(0xd7 | FM_WAIT, 0xf002);
if (r)
return r;
while (spi_busy_p()) ;
return (flash_status_bytes[0] | flash_status_bytes[1]) & 0x40;
}
uint16_t flash_find_free()
{
flash_memset_buffer2();
uint16_t a = config.page_start;
uint16_t e = config.page_end;
while (e > a) {
uint16_t p = (a+e)>>1;
if (flash_compare_buffer2(p))
a = p+1;
else
e = p;
}
fs.free = a;
return a;
}

56
src/flash.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef _FLASH_H
#define _FLASH_H
#include "spi.h"
enum flash_mode_bits {
FM_PAD1 = 0x0100,
FM_PAD2 = 0x0200,
FM_PAD4 = 0x0300,
FM_PAD = 0x0300,
FM_512 = 0x0400,
FM_528 = 0x0800,
FM_SEC = 0x0c00,
FM_ADDR = 0x0c00,
FM_WRITE = 0x1000,
FM_READ = 0x2000,
FM_WAIT = 0x3000,
FM_START = 0x3000,
FM_CONT = (uint16_t)SPI_CONT << 8,
FM_SPI = FM_CONT,
FM_NSTR = 0x8000,
};
uint8_t flash_cmd_na(uint16_t mode, uint16_t what);
uint8_t flash_cmd(uint16_t mode, uint16_t what, uint16_t page, uint16_t byte);
#define FB_SIZE 80
extern uint8_t flash_buffer[FB_SIZE];
uint8_t flash_submit_command(uint8_t *cmd);
uint8_t flash_start_stream(uint16_t page, uint16_t npages, uint8_t flags);
uint8_t flash_poll(uint8_t rr);
uint16_t flash_find_free();
extern
struct flash_stream {
uint16_t free; // first free page
uint16_t page; // page address of buffer number
uint16_t npages; // more pages to read
uint8_t block; // next block to read 0…9
uint8_t status; // FS_… flags
} fs;
enum {
FS_Ready = 1, // ready for the next buffer
FS_Busy = 2, // processing …
FS_Error = 3, // Aborted
FS_Read = 4, // array read
FS_Write = 8, // buffer write and burn
FS_Erase = 12, // Erase
FS_Dir = 12, // Mask for the last three
FS_Ack = 32, // Next buffer is provided, continue …
FS_StBsy = 64, // Waiting to Flash status register
FS_528 = 128, // do 528 byte pages.
};
#endif

113
src/flash.py Normal file
View file

@ -0,0 +1,113 @@
import struct
class flash_cmd:
class Flash_Error(IOError):
pass
M = {
0: 0x0000,
1: 0x0100,
2: 0x0200,
4: 0x0300,
512: 0x0400,
528: 0x0800,
"addr": 0x0c00,
"secure": 0x0c00,
"write": 0x1000,
"read": 0x2000,
"wait": 0x3000,
"start": 0x3000,
}
def __init__(self, pagesize=528):
self.set_pagesize(pagesize)
def set_pagesize(self, pagesize):
self.pagesize = pagesize
M = self.M
A = M[pagesize]
self.A = A
self.OP = {
"ReadPage": (0xd2 | A | M["read"] | M[4],),
"ReadLPower": (0x01 | A | M["read"] | M[0],),
"Read": (0x03 | A | M["read"] | M[0],),
"ReadHFreq1": (0x0b | A | M["read"] | M[1],),
"ReadHFreq2": (0x1b | A | M["read"] | M[2],),
"ReadLegacy": (0xe8 | A | M["read"] | M[4],),
"ReadBuffer1": (0xd1 | A | M["read"] | M[0],),
"ReadBuffer2": (0xd3 | A | M["read"] | M[0],),
"ReadBufHF1": (0xd4 | A | M["read"] | M[1],),
"ReadBufHF2": (0xd6 | A | M["read"] | M[1],),
"WriteBuffer1": (0x84 | A | M["write"],),
"WriteBuffer2": (0x87 | A | M["write"],),
"ProgErase1": (0x83 | A,),
"ProgErase2": (0x86 | A,),
"Program1": (0x88 | A,),
"Program2": (0x89 | A,),
"Transfer1": (0x53 | A,),
"Transfer2": (0x55 | A,),
"Compare1": (0x60 | A,),
"Compare2": (0x61 | A,),
"ErasePage": (0x81 | A,),
"EraseBlock": (0x50 | A,),
"EraseSector": (0x7c | A,),
"EraseChip": (0xc7 | M["secure"], 0x9a8094),
"Status": (0xd7 | M["read"], 2),
"PowerDown": (0xb9,),
"PowerUp": (0xab,),
"Id": (0x9f | M["read"], 5),
"PageSize512": (0x3d | M["secure"], 0xa6802a),
"PageSize528": (0x3d | M["secure"], 0xa7802a),
"Reset": (0xf0 | M["secure"], 0x0),
}
WHAT = {
"buffer": 0,
"cmdbuf": 96,
"cmd": 96+8,
"status": 128,
}
def cmd_buffer(self, op, mode=0, size=None, page=0, byte=0, what=0, data=None):
if op in self.OP:
opp = self.OP[op]
op = opp[0]
if len(opp) > 1:
if (op & self.M["addr"]) == self.M["secure"]:
page = opp[1] >> 16
byte = opp[1] & 0xffff;
elif op & self.M["read"] and size is None:
size = opp[1]
if mode:
op = op & 0xff | mode & 0xff00
if what in self.WHAT:
what = self.WHAT[what]
if size is None:
size = 0
if op & self.M["start"]:
if what >=112:
size = 2
elif what >= 96:
size = 112 - what
elif what < 80:
if byte == 448 and self.pagesize == 528:
size = 80
else:
size = 64
if size + byte > self.pagesize:
size = self.pagesize - byte
if what+size > 80:
size = 80 - what
elif data is not None and not what and size <= 8:
what = self.WHAT["cmd"]
if data is None:
data = 0
size |= what << 8;
if self.verbose:
from sys import stderr
print("Flash cmd", *map(hex,(op, size, page, byte, data)), file=stderr)
return struct.pack("<4HQ", op, size, page, byte, data)
verbose = False

68
src/fpga.c Normal file
View file

@ -0,0 +1,68 @@
#include "pipe.h"
#include "fpga.h"
#include "spi.h"
void fpga_cmd(struct fpga_cmd *c)
{
if (fpga_reset_poll()) {
c->n |= 0x20; // busy flag
return;
}
if (c->n & 1 && spi_abort()) {
c->n |= 0x10; // aborted flag
return;
}
else if (spi_busy_p()) {
c->n |= 0x20; // busy flag
return;
}
c->n &=~ 0x10; // not busy
if (c->n & 0x40)
return;
c->n |= 0x40; // submitted flag
uint8_t n = c->n & 0x0e; // send up to seven cmd words
uint8_t z = c->z & 0x7e; // and up to 63 zeros
spi_select(0);
spi.zero = c->n & 0x80; // send nop: 0x8080 (please), or zeros
spi.csize = n;
spi.cmd = c->d;
spi.rdata = c->d;
if (c->z & 0x80) {
spi.isize = n + z>>3 & 0x0e; // ignore cmd ± (z[6:4])
spi.zsize = spi.rsize = z & 0x0e; // read x[3:1] words
spi.mask = 0xff; // start reading at the first nonzero byte after cmd
}
else {
spi.zsize = z; // send z zeros/nop after cmd
spi.rsize = 14; // save the last 7 words returned
if (n + z > 14)
spi.isize = n + z - 14;
else
spi.isize = 0;
}
spi_start();
}
void fpga_start(uint8_t write)
{
uint8_t mode = 0;
if (pipe.fpga.zero == SPI_CONFIG)
mode = SPI_CONFIG;
spi_select(mode);
_memcopy(&spi.csize, &pipe.fpga.csize, 6);
spi.cmd = pipe.fpga.cmd;
spi.wdata = flash_buffer;
spi.rdata = flash_buffer;
uint8_t n = 64;
if (n > pipe.fpga.size)
n = pipe.fpga.size;
pipe.fpga.size -= n;
if (!n)
return;
if (write)
spi.wsize = n;
else
spi.rsize = n;
spi_start();
}

27
src/fpga.h Normal file
View file

@ -0,0 +1,27 @@
#include "config.h"
struct fpga_cmd {
uint8_t n;
uint8_t z;
uint8_t d[14];
};
static inline
void fpga_reset()
{
nCONFIG_VPORT.OUT |= (1<<nCONFIG_PIN);
}
static inline
uint8_t fpga_reset_poll()
{
uint8_t r = !(nSTATUS_VPORT.IN & (1 << nSTATUS_PIN));
if (r)
nCONFIG_VPORT.OUT |= 1 << nCONFIG_PIN;
return r;
}
void fpga_cmd(struct fpga_cmd *c);
void fpga_start(uint8_t write);

48
src/hallo.c Normal file
View file

@ -0,0 +1,48 @@
//
// hallo.c
//
// int = int8_t
#include <stdint.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "config.h"
#include "uart.h"
#include "pipe.h"
////////////////////////////////////////////////////////////////////////////////
//
// main()
section_status(pipe) struct pipe pipe;
section_status(main) struct magic magic;
int main()
{
while (CLKCTRL.MCLKCTRLB != config.cpu_clk) {
CCP = CCP_IOREG_gc;
CLKCTRL.MCLKCTRLB = config.cpu_clk;
}
sleep_enable();
magic.magic = config.magic;
while (magic.magic != MAGIC || config.version != VERSION)
sleep_cpu();
apply_config();
magic.reset_source = RSTCTRL.RSTFR;
RSTCTRL.RSTFR = magic.reset_source;
send_str("\nV Turbo Hallo V0.0");
send_hex_byte_eol(magic.reset_source);
while (1) {
sei();
sleep_cpu();
command();
}
}

1
src/io.S Normal file
View file

@ -0,0 +1 @@
#include <avr/io.h>

443
src/iotn424.py Normal file
View file

@ -0,0 +1,443 @@
SFR = {
"USART0_TXDATAL": (0x0802, 1),
"PORTB_PIN0CTRL": (0x0430, 1),
"ADC0_WINHTH": (0x061F, 1),
"ADC0_WINHTL": (0x061E, 1),
"RTC_PITCTRLA": (0x0150, 1),
"USERROW_USERROW13": (0x130D, 1),
"USART0_RXDATAH": (0x0801, 1),
"USART0_RXDATAL": (0x0800, 1),
"USERROW_USERROW17": (0x1311, 1),
"TCA0_SPLIT_LCMP0": (0x0A28, 1),
"PORTB_DIRCLR": (0x0422, 1),
"TCA0_SINGLE_CMP1H": (0x0A2B, 1),
"USERROW_USERROW19": (0x1313, 1),
"PORTB_OUTSET": (0x0425, 1),
"USART0_BAUDH": (0x0809, 1),
"USART0_BAUDL": (0x0808, 1),
"TCA0_SPLIT_LCNT": (0x0A20, 1),
"TWI0_SCTRLB": (0x08AA, 1),
"PORTC_PORTCTRL": (0x044A, 1),
"PORTB_INTFLAGS": (0x0429, 1),
"CCL_LUT0CTRLA": (0x01C8, 1),
"CCL_LUT0CTRLB": (0x01C9, 1),
"CCL_LUT0CTRLC": (0x01CA, 1),
"TCA0_SPLIT_CTRLECLR": (0x0A04, 1),
"NVMCTRL_INTFLAGS": (0x1004, 1),
"CPUINT_CTRLA": (0x0110, 1),
"EVSYS_USERTCB1COUNT": (0x01B3, 1),
"VPORTA_OUT": (0x0001, 1),
"BOD_INTFLAGS": (0x008A, 1),
"ADC0_PGACTRL": (0x060B, 1),
"CLKCTRL_OSC20MCALIBA": (0x0071, 1),
"BOD_VLMCTRLA": (0x0088, 1),
"TCA0_SPLIT_LPER": (0x0A26, 1),
"USERROW_USERROW21": (0x1315, 1),
"USERROW_USERROW25": (0x1319, 1),
"PORTC_OUTSET": (0x0445, 1),
"USERROW_USERROW29": (0x131D, 1),
"EVSYS_USERTCB1CAPT": (0x01B2, 1),
"SLPCTRL_CTRLA": (0x0050, 1),
"ADC0_WINLTL": (0x061C, 1),
"TCA0_SPLIT_CTRLB": (0x0A01, 1),
"TCA0_SPLIT_CTRLD": (0x0A03, 1),
"TCA0_SINGLE_CMP0H": (0x0A29, 1),
"TCA0_SINGLE_CMP0L": (0x0A28, 1),
"PORTC_DIR": (0x0440, 1),
"TCA0_SINGLE_CMP1L": (0x0A2A, 1),
"ADC0_INTFLAGS": (0x0605, 1),
"USERROW_USERROW2": (0x1302, 1),
"USART1_DBGCTRL": (0x082B, 1),
"TWI0_MDATA": (0x08A8, 1),
"CCL_TRUTH0": (0x01CB, 1),
"PORTB_PIN3CTRL": (0x0433, 1),
"TCA0_SINGLE_CMP2H": (0x0A2D, 1),
"PORTA_PIN0CTRL": (0x0410, 1),
"TCA0_SINGLE_CMP2L": (0x0A2C, 1),
"CCL_LUT1CTRLA": (0x01CC, 1),
"CCL_LUT1CTRLC": (0x01CE, 1),
"SIGROW_SERNUM0": (0x1103, 1),
"SIGROW_SERNUM1": (0x1104, 1),
"SIGROW_SERNUM2": (0x1105, 1),
"SIGROW_SERNUM3": (0x1106, 1),
"SIGROW_SERNUM4": (0x1107, 1),
"SIGROW_SERNUM5": (0x1108, 1),
"SIGROW_SERNUM6": (0x1109, 1),
"SIGROW_SERNUM7": (0x110A, 1),
"SIGROW_SERNUM8": (0x110B, 1),
"SIGROW_SERNUM9": (0x110C, 1),
"TCA0_SINGLE_PERBUFH": (0x0A37, 1),
"TCA0_SINGLE_PERBUFL": (0x0A36, 1),
"USERROW_USERROW31": (0x131F, 1),
"TCB0_CTRLA": (0x0A80, 1),
"TCB0_CTRLB": (0x0A81, 1),
"CPUINT_LVL0PRI": (0x0112, 1),
"VPORTA_IN": (0x0002, 1),
"EVSYS_USERTCB0CAPT": (0x01B0, 1),
"PORTC_OUT": (0x0444, 1),
"RTC_PITINTCTRL": (0x0152, 1),
"PORTB_IN": (0x0428, 1),
"TCA0_SINGLE_CTRLB": (0x0A01, 1),
"ADC0_TEMP0": (0x0618, 1),
"PORTB_DIR": (0x0420, 1),
"ADC0_TEMP2": (0x061A, 1),
"GPIO0": (0x001C, 1),
"GPIO1": (0x001D, 1),
"GPIO3": (0x001F, 1),
"RTC_CNTH": (0x0149, 1),
"EVSYS_CHANNEL0": (0x0190, 1),
"EVSYS_CHANNEL1": (0x0191, 1),
"EVSYS_CHANNEL2": (0x0192, 1),
"EVSYS_CHANNEL3": (0x0193, 1),
"EVSYS_CHANNEL4": (0x0194, 1),
"EVSYS_CHANNEL5": (0x0195, 1),
"PORTC_DIRSET": (0x0441, 1),
"CCL_LUT3CTRLA": (0x01D4, 1),
"TWI0_SADDRMASK": (0x08AE, 1),
"TCA0_SINGLE_CTRLECLR": (0x0A04, 1),
"PORTA_DIRCLR": (0x0402, 1),
"PORTC_PIN2CTRL": (0x0452, 1),
"CCP": (0x0034, 1),
"CLKCTRL_OSC20MCALIBB": (0x0072, 1),
"USART0_CTRLA": (0x0805, 1),
"TWI0_SCTRLA": (0x08A9, 1),
"TCB1_CCMPL": (0x0A9C, 1),
"PORTC_IN": (0x0448, 1),
"SIGROW_DEVICEID2": (0x1102, 1),
"TCB1_INTCTRL": (0x0A95, 1),
"USART0_TXPLCTRL": (0x080D, 1),
"FUSE_WDTCFG": (0x1280, 1),
"CCL_LUT2CTRLB": (0x01D1, 1),
"CCL_LUT2CTRLC": (0x01D2, 1),
"PORTB_OUT": (0x0424, 1),
"PORTMUX_USARTROUTEA": (0x05E2, 1),
"CLKCTRL_OSC32KCTRLA": (0x0078, 1),
"TWI0_SDATA": (0x08AD, 1),
"PORTA_DIR": (0x0400, 1),
"PORTA_IN": (0x0408, 1),
"ADC0_DBGCTRL": (0x0607, 1),
"TCB1_CNTL": (0x0A9A, 1),
"PORTA_PIN1CTRL": (0x0411, 1),
"TCB0_TEMP": (0x0A89, 1),
"TCA0_SINGLE_CTRLA": (0x0A00, 1),
"TCA0_SINGLE_CTRLC": (0x0A02, 1),
"TCA0_SINGLE_CTRLD": (0x0A03, 1),
"ADC0_MUXPOS": (0x060C, 1),
"TCB1_INTFLAGS": (0x0A96, 1),
"EVSYS_USERADC0START": (0x01A8, 1),
"NVMCTRL_ADDRH": (0x1009, 1),
"NVMCTRL_ADDRL": (0x1008, 1),
"TWI0_DBGCTRL": (0x08A2, 1),
"RTC_PITDBGCTRL": (0x0155, 1),
"SIGROW_OSCCAL20M1": (0x111B, 1),
"VREF_CTRLA": (0x00A0, 1),
"TCA0_SPLIT_INTCTRL": (0x0A0A, 1),
"TCA0_SINGLE_CMP0BUFH": (0x0A39, 1),
"AC0_INTCTRL": (0x0686, 1),
"TCA0_SINGLE_CNTL": (0x0A20, 1),
"TCA0_SPLIT_HCMP0": (0x0A29, 1),
"TCA0_SPLIT_HCMP1": (0x0A2B, 1),
"TCA0_SPLIT_HCMP2": (0x0A2D, 1),
"TCA0_SINGLE_CMP2BUFH": (0x0A3D, 1),
"TCA0_SINGLE_CMP2BUFL": (0x0A3C, 1),
"USART1_TXPLCTRL": (0x082D, 1),
"PORTA_OUT": (0x0404, 1),
"CLKCTRL_MCLKSTATUS": (0x0063, 1),
"PORTC_PIN4CTRL": (0x0454, 1),
"PORTC_OUTCLR": (0x0446, 1),
"CCL_LUT3CTRLB": (0x01D5, 1),
"WDT_CTRLA": (0x0100, 1),
"SIGROW_DEVICEID0": (0x1100, 1),
"SIGROW_DEVICEID1": (0x1101, 1),
"TCB0_CNTL": (0x0A8A, 1),
"USERROW_USERROW1": (0x1301, 1),
"PORTMUX_CCLROUTEA": (0x05E1, 1),
"PORTC_PIN7CTRL": (0x0457, 1),
"ADC0_CTRLB": (0x0601, 1),
"ADC0_CTRLC": (0x0602, 1),
"ADC0_CTRLD": (0x0603, 1),
"ADC0_CTRLE": (0x0608, 1),
"ADC0_CTRLF": (0x0609, 1),
"RTC_INTCTRL": (0x0142, 1),
"RTC_PERL": (0x014A, 1),
"GPIO2": (0x001E, 1),
"PORTA_PIN7CTRL": (0x0417, 1),
"LOCKBIT_LOCKBIT": (0x128A, 1),
"SIGROW_OSCCAL20M0": (0x111A, 1),
"TCB1_TEMP": (0x0A99, 1),
"FUSE_SYSCFG1": (0x1286, 1),
"PORTB_DIRSET": (0x0421, 1),
"PORTC_OUTTGL": (0x0447, 1),
"ADC0_CTRLA": (0x0600, 1),
"TCA0_SPLIT_LCMP1": (0x0A2A, 1),
"TCA0_SPLIT_LCMP2": (0x0A2C, 1),
"TCA0_SINGLE_CMP1BUFH": (0x0A3B, 1),
"TCA0_SINGLE_CMP1BUFL": (0x0A3A, 1),
"USART1_TXDATAL": (0x0822, 1),
"ADC0_TEMP1": (0x0619, 1),
"TCA0_SPLIT_INTFLAGS": (0x0A0B, 1),
"PORTMUX_SPIROUTEA": (0x05E3, 1),
"TCA0_SPLIT_CTRLESET": (0x0A05, 1),
"TCA0_SINGLE_CTRLFSET": (0x0A07, 1),
"SPI0_INTCTRL": (0x08C2, 1),
"PORTC_PIN6CTRL": (0x0456, 1),
"NVMCTRL_STATUS": (0x1002, 1),
"CCL_TRUTH1": (0x01CF, 1),
"CCL_TRUTH2": (0x01D3, 1),
"CLKCTRL_MCLKLOCK": (0x0062, 1),
"SYSCFG_REVID": (0x0F01, 1),
"USERROW_USERROW10": (0x130A, 1),
"USERROW_USERROW11": (0x130B, 1),
"USERROW_USERROW12": (0x130C, 1),
"USERROW_USERROW14": (0x130E, 1),
"USERROW_USERROW15": (0x130F, 1),
"USERROW_USERROW16": (0x1310, 1),
"USERROW_USERROW18": (0x1312, 1),
"USART1_CTRLA": (0x0825, 1),
"USART1_CTRLB": (0x0826, 1),
"USART1_CTRLC": (0x0827, 1),
"USART1_CTRLD": (0x082A, 1),
"USERROW_USERROW20": (0x1314, 1),
"USERROW_USERROW22": (0x1316, 1),
"USERROW_USERROW23": (0x1317, 1),
"USERROW_USERROW24": (0x1318, 1),
"USERROW_USERROW26": (0x131A, 1),
"USERROW_USERROW27": (0x131B, 1),
"USERROW_USERROW28": (0x131C, 1),
"VPORTC_OUT": (0x0009, 1),
"CCL_LUT2CTRLA": (0x01D0, 1),
"USERROW_USERROW30": (0x131E, 1),
"TCA0_SINGLE_CMP0BUFL": (0x0A38, 1),
"PORTA_OUTSET": (0x0405, 1),
"FUSE_OSCCFG": (0x1282, 1),
"ADC0_STATUS": (0x0606, 1),
"CPU_SREG": (0x003F, 1),
"AC0_CTRLA": (0x0680, 1),
"TCA0_SINGLE_PERH": (0x0A27, 1),
"EVSYS_USEREVSYSEVOUTA": (0x01A9, 1),
"EVSYS_USEREVSYSEVOUTB": (0x01AA, 1),
"TCA0_SINGLE_PERL": (0x0A26, 1),
"TCA0_SPLIT_DBGCTRL": (0x0A0E, 1),
"PORTB_PIN7CTRL": (0x0437, 1),
"TCB0_INTCTRL": (0x0A85, 1),
"PORTC_PIN5CTRL": (0x0455, 1),
"AC0_STATUS": (0x0687, 1),
"CLKCTRL_OSC20MCTRLA": (0x0070, 1),
"TCB0_INTFLAGS": (0x0A86, 1),
"ADC0_SAMPLEL": (0x0614, 1),
"RTC_DBGCTRL": (0x0145, 1),
"CLKCTRL_MCLKCTRLA": (0x0060, 1),
"CLKCTRL_MCLKCTRLB": (0x0061, 1),
"USART1_BAUDH": (0x0829, 1),
"USART1_BAUDL": (0x0828, 1),
"VPORTB_IN": (0x0006, 1),
"GPIO_GPIOR0": (0x001C, 1),
"GPIO_GPIOR1": (0x001D, 1),
"GPIO_GPIOR2": (0x001E, 1),
"GPIO_GPIOR3": (0x001F, 1),
"BOD_INTCTRL": (0x0089, 1),
"CPU_SPH": (0x003E, 1),
"CPU_SPL": (0x003D, 1),
"SIGROW_TEMPSENSE0": (0x1120, 1),
"SIGROW_TEMPSENSE1": (0x1121, 1),
"CLKCTRL_XOSC32KCTRLA": (0x007C, 1),
"AC0_DACREF": (0x0684, 1),
"ADC0_SAMPLEH": (0x0615, 1),
"CRCSCAN_STATUS": (0x0122, 1),
"RTC_PERH": (0x014B, 1),
"TCA0_SINGLE_CTRLESET": (0x0A05, 1),
"TCA0_SINGLE_CTRLFCLR": (0x0A06, 1),
"PORTC_INTFLAGS": (0x0449, 1),
"PORTA_DIRSET": (0x0401, 1),
"PORTB_OUTTGL": (0x0427, 1),
"TCB0_STATUS": (0x0A87, 1),
"PORTB_PIN6CTRL": (0x0436, 1),
"TCA0_SINGLE_CNTH": (0x0A21, 1),
"USERROW_USERROW0": (0x1300, 1),
"USERROW_USERROW3": (0x1303, 1),
"USERROW_USERROW4": (0x1304, 1),
"USERROW_USERROW5": (0x1305, 1),
"USERROW_USERROW7": (0x1307, 1),
"USERROW_USERROW8": (0x1308, 1),
"USERROW_USERROW9": (0x1309, 1),
"TCB1_EVCTRL": (0x0A94, 1),
"TCB1_DBGCTRL": (0x0A98, 1),
"SIGROW_OSCCAL16M0": (0x1118, 1),
"SIGROW_OSCCAL16M1": (0x1119, 1),
"EVSYS_USERUSART1IRDA": (0x01AD, 1),
"RTC_CLKSEL": (0x0147, 1),
"TCA0_SINGLE_INTCTRL": (0x0A0A, 1),
"USART0_DBGCTRL": (0x080B, 1),
"TCA0_SPLIT_HCNT": (0x0A21, 1),
"WDT_STATUS": (0x0101, 1),
"PORTMUX_TCAROUTEA": (0x05E4, 1),
"USART1_STATUS": (0x0824, 1),
"GPIOR0": (0x001C, 1),
"GPIOR1": (0x001D, 1),
"GPIOR2": (0x001E, 1),
"GPIOR3": (0x001F, 1),
"TCA0_SPLIT_HPER": (0x0A27, 1),
"RSTCTRL_RSTFR": (0x0040, 1),
"CCL_INTFLAGS": (0x01C7, 1),
"PORTB_OUTCLR": (0x0426, 1),
"TWI0_SSTATUS": (0x08AB, 1),
"BOD_STATUS": (0x008B, 1),
"VPORTC_INTFLAGS": (0x000B, 1),
"PORTB_PIN5CTRL": (0x0435, 1),
"SPH": (0x003E, 1),
"SPL": (0x003D, 1),
"PORTC_PIN3CTRL": (0x0453, 1),
"TCB1_CTRLA": (0x0A90, 1),
"EVSYS_USERTCA0CNTA": (0x01AE, 1),
"EVSYS_USERTCA0CNTB": (0x01AF, 1),
"NVMCTRL_INTCTRL": (0x1003, 1),
"CRCSCAN_CTRLA": (0x0120, 1),
"PORTB_DIRTGL": (0x0423, 1),
"ADC0_MUXNEG": (0x060D, 1),
"NVMCTRL_CTRLA": (0x1000, 1),
"NVMCTRL_CTRLB": (0x1001, 1),
"USART1_EVCTRL": (0x082C, 1),
"PORTC_DIRTGL": (0x0443, 1),
"CCL_CTRLA": (0x01C0, 1),
"USART1_RXPLCTRL": (0x082E, 1),
"RTC_INTFLAGS": (0x0143, 1),
"FUSE_SYSCFG0": (0x1285, 1),
"AC0_MUXCTRLA": (0x0682, 1),
"TWI0_MSTATUS": (0x08A5, 1),
"SPI0_CTRLA": (0x08C0, 1),
"SPI0_CTRLB": (0x08C1, 1),
"TCB0_CNTH": (0x0A8B, 1),
"PORTB_PORTCTRL": (0x042A, 1),
"EVSYS_USERCCLLUT0A": (0x01A0, 1),
"EVSYS_SWEVENTA": (0x0180, 1),
"PORTA_PIN6CTRL": (0x0416, 1),
"EVSYS_USERCCLLUT0B": (0x01A1, 1),
"CCL_LUT1CTRLB": (0x01CD, 1),
"EVSYS_USERCCLLUT1A": (0x01A2, 1),
"EVSYS_USERCCLLUT1B": (0x01A3, 1),
"PORTB_PIN4CTRL": (0x0434, 1),
"EVSYS_USERCCLLUT2A": (0x01A4, 1),
"EVSYS_USERCCLLUT2B": (0x01A5, 1),
"EVSYS_USERCCLLUT3A": (0x01A6, 1),
"EVSYS_USERCCLLUT3B": (0x01A7, 1),
"RTC_PITSTATUS": (0x0151, 1),
"TCA0_SINGLE_INTFLAGS": (0x0A0B, 1),
"TWI0_MBAUD": (0x08A6, 1),
"TCB0_EVCTRL": (0x0A84, 1),
"TCB1_CTRLB": (0x0A91, 1),
"TCB0_CCMPH": (0x0A8D, 1),
"TCB0_CCMPL": (0x0A8C, 1),
"PORTA_OUTTGL": (0x0407, 1),
"USART1_TXDATAH": (0x0823, 1),
"TCA0_SINGLE_EVCTRL": (0x0A09, 1),
"TCA0_SINGLE_DBGCTRL": (0x0A0E, 1),
"BOD_CTRLA": (0x0080, 1),
"USART1_RXDATAH": (0x0821, 1),
"USART1_RXDATAL": (0x0820, 1),
"TWI0_CTRLA": (0x08A0, 1),
"VPORTB_OUT": (0x0005, 1),
"RTC_TEMP": (0x0144, 1),
"PORTB_PIN1CTRL": (0x0431, 1),
"VPORTB_INTFLAGS": (0x0007, 1),
"ADC0_RESULT0": (0x0610, 1),
"ADC0_RESULT1": (0x0611, 1),
"ADC0_RESULT2": (0x0612, 1),
"PORTA_PIN5CTRL": (0x0415, 1),
"CCL_TRUTH3": (0x01D7, 1),
"CPUINT_LVL1VEC": (0x0113, 1),
"CCL_INTCTRL0": (0x01C5, 1),
"TCB1_CNTH": (0x0A9B, 1),
"PORTC_PIN1CTRL": (0x0451, 1),
"SPI0_DATA": (0x08C4, 1),
"USART0_STATUS": (0x0804, 1),
"CPU_CCP": (0x0034, 1),
"RTC_CTRLA": (0x0140, 1),
"RTC_STATUS": (0x0141, 1),
"PORTA_INTFLAGS": (0x0409, 1),
"CPUINT_STATUS": (0x0111, 1),
"BOD_CTRLB": (0x0081, 1),
"USART0_RXPLCTRL": (0x080E, 1),
"CRCSCAN_CTRLB": (0x0121, 1),
"PORTA_PORTCTRL": (0x040A, 1),
"TCB0_DBGCTRL": (0x0A88, 1),
"RTC_PITINTFLAGS": (0x0153, 1),
"PORTC_DIRCLR": (0x0442, 1),
"PORTA_DIRTGL": (0x0403, 1),
"CCL_SEQCTRL0": (0x01C1, 1),
"CCL_SEQCTRL1": (0x01C2, 1),
"PORTA_PIN4CTRL": (0x0414, 1),
"VPORTC_DIR": (0x0008, 1),
"TCB1_STATUS": (0x0A97, 1),
"FUSE_BODCFG": (0x1281, 1),
"PORTB_PIN2CTRL": (0x0432, 1),
"TWI0_MADDR": (0x08A7, 1),
"FUSE_BOOTEND": (0x1288, 1),
"EVSYS_USERUSART0IRDA": (0x01AC, 1),
"RTC_CALIB": (0x0146, 1),
"TWI0_MCTRLA": (0x08A3, 1),
"TWI0_MCTRLB": (0x08A4, 1),
"PORTC_PIN0CTRL": (0x0450, 1),
"ADC0_WINLTH": (0x061D, 1),
"RSTCTRL_SWRR": (0x0041, 1),
"PORTMUX_TCBROUTEA": (0x05E5, 1),
"VPORTC_IN": (0x000A, 1),
"TCA0_SINGLE_TEMP": (0x0A0F, 1),
"TCA0_SPLIT_CTRLA": (0x0A00, 1),
"FUSE_APPEND": (0x1287, 1),
"RTC_CMPH": (0x014D, 1),
"RTC_CMPL": (0x014C, 1),
"TCA0_SPLIT_CTRLC": (0x0A02, 1),
"CCL_LUT3CTRLC": (0x01D6, 1),
"RTC_CNTL": (0x0148, 1),
"USERROW_USERROW6": (0x1306, 1),
"VPORTA_INTFLAGS": (0x0003, 1),
"PORTA_OUTCLR": (0x0406, 1),
"PORTMUX_EVSYSROUTEA": (0x05E0, 1),
"PORTA_PIN3CTRL": (0x0413, 1),
"VPORTB_DIR": (0x0004, 1),
"ADC0_INTCTRL": (0x0604, 1),
"NVMCTRL_DATAH": (0x1007, 1),
"VPORTA_DIR": (0x0000, 1),
"VREF_CTRLB": (0x00A1, 1),
"NVMCTRL_DATAL": (0x1006, 1),
"TWI0_SADDR": (0x08AC, 1),
"USART0_CTRLB": (0x0806, 1),
"USART0_CTRLC": (0x0807, 1),
"USART0_CTRLD": (0x080A, 1),
"EVSYS_USERTCB0COUNT": (0x01B1, 1),
"TCB1_CCMPH": (0x0A9D, 1),
"SPI0_INTFLAGS": (0x08C3, 1),
"ADC0_COMMAND": (0x060A, 1),
"ADC0_RESULT3": (0x0613, 1),
"SREG": (0x003F, 1),
"PORTA_PIN2CTRL": (0x0412, 1),
"USART0_EVCTRL": (0x080C, 1),
"USART0_TXDATAH": (0x0803, 1),
"ADC0_SAMPLE": (0x0614, 2),
"TCA0_SINGLE_CMP0": (0x0A28, 2),
"TCA0_SINGLE_CMP1": (0x0A2A, 2),
"TCA0_SINGLE_CNT": (0x0A20, 2),
"RTC_CMP": (0x014C, 2),
"RTC_CNT": (0x0148, 2),
"CPU_SP": (0x003D, 2),
"TCA0_SINGLE_CMP2": (0x0A2C, 2),
"TCA0_SINGLE_CMP2BUF": (0x0A3C, 2),
"NVMCTRL_DATA": (0x1006, 2),
"TCA0_SINGLE_CMP1BUF": (0x0A3A, 2),
"NVMCTRL_ADDR": (0x1008, 2),
"USART0_BAUD": (0x0808, 2),
"TCA0_SINGLE_CMP0BUF": (0x0A38, 2),
"TCB0_CCMP": (0x0A8C, 2),
"RTC_PER": (0x014A, 2),
"TCB1_CNT": (0x0A9A, 2),
"TCB1_CCMP": (0x0A9C, 2),
"USART1_BAUD": (0x0828, 2),
"TCB0_CNT": (0x0A8A, 2),
"ADC0_WINHT": (0x061E, 2),
"ADC0_WINLT": (0x061C, 2),
"SP": (0x003D, 2),
"TCA0_SINGLE_PERBUF": (0x0A36, 2),
"TCA0_SINGLE_PER": (0x0A26, 2),
"ADC0_RESULT": (0x0610, 4),
}

33
src/map.py Normal file
View file

@ -0,0 +1,33 @@
class memmap(dict):
def __init__(self, fn="hallo.map"):
self.parsemap(fn)
def parsemap(self, fn):
with open(fn) as f:
laddr = None
for l in f:
try:
ll = l.split()
if l[:1] == "." and len(ll) >= 3:
section = ll[0]
saddr = int(ll[1], 0) & 0xffff
slen = int(ll[2], 0)
self[section] = (saddr, slen)
laddr = saddr
last = None
continue
if l[:1] != " ":
continue
if len(ll)==2 and ll[0][:2] == "0x" or len(ll)==4 and ll[2]=="=" and ll[3]==".":
cur = ll[1]
addr = int(ll[0],0) & 0xffff
self[cur] = (addr,0)
if last is not None:
self[last] = (self[last][0], addr-laddr)
last = cur
laddr = addr
except Exception as e:
from sys import stderr
print(f"{repr(e)}\n{l}", file=stderr)

284
src/mul.c
View file

@ -1,284 +0,0 @@
//
// mul.c
//
// To save space in ATtinys.
// 16bit × 16bit → 16bit multiplication for AVR w/ `mul`, `muls`, `mulsu`,
// returning the high bits, not the low bits.
//
// Also provides a variant for printing decimal numbers.
#include "mul.h"
// To save space, omit what is not needed. Use -D when needed.
#ifndef MUL_NONE
# define MUL16SUN
# define MUL_DECIMAL_STR
# ifdef MUL_ALL
# define MUL16SU
# define MUL16SS
# define MUL16UU
# endif
#endif
#ifdef MUL_TEST
#define MUL_DEBUG
#include <stdio.h>
int main()
{
int16_t tests[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
89, 99, 100, 101, 102, 0xff, 0x100, 0x101,
999, 1000, 1001, 9999, 10000, 10001,
0x7ffe, 0x7fff
};
for (uint8_t dec = 0; dec < 5; dec++)
for (int i=0; i < sizeof(tests)/sizeof(*tests); i++)
for (int s=0; s<2; s++) {
int16_t n = tests[i];
if (s)
n = -n;
printf("%u 0x%04x %+d %s\n",
dec, n & 0xffff, n,
decimal_str(n, dec));
}
return 0;
}
#endif
#ifndef MUL_DEBUG
#ifdef MUL16SU
int16_t mul16su(int16_t s, uint16_t u)
{
__asm__(
"movw r18, %[s]" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r20, r1" "\n\t"
"mulsu r19, %B[u]" "\n\t"
"movw %[s], r0" "\n\t"
"mul r18, %B[u]" "\n\t"
"clr r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"mulsu r19, %A[u]" "\n\t"
"sbc %B[s], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"clr r1" "\n\t"
: [s] "+r" (s)
: [u] "a" (u)
: "r0", "r1", "r18", "r19", "r20"
);
return s;
}
#endif
#ifdef MUL16SUN
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n)
{
// n = 0 … 8
__asm__(
"movw r18, %[s]" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r21, r1" "\n\t"
"mulsu r19, %B[u]" "\n\t"
"movw %[s], r0" "\n\t"
"mul r18, %B[u]" "\n\t"
"clr r18" "\n\t"
"add r21, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"mulsu r19, %A[u]" "\n\t"
"sbc %B[s], r18" "\n\t"
"add r21, r0" "\n\t"
"adc %A[s], r1" "\n\t"
"adc %B[s], r18" "\n\t"
"cpi %[n], 0" "\n\t"
"breq 3f" "\n\t"
"sbrs %[n], 3" "\n\t"
"rjmp 2f" "\n\t"
"mov %B[s], %A[s]" "\n\t"
"mov %A[s], r21" "\n\t"
"rjmp 3f" "\n"
"1:" "\t"
"lsl r21" "\n\t"
"rol %A[s]" "\n\t"
"rol %B[s]" "\n\t"
"2:" "\t"
"dec %[n]" "\n\t"
"brpl 1b" "\n"
"3:" "\t"
"clr r1" "\n\t"
: [s] "+r" (s)
: [u] "a" (u),
[n] "r" (n)
: "r0", "r1", "r18", "r19", "r21"
);
return s;
}
#endif
#ifdef MUL16SS
int16_t mul16ss(int16_t a, int16_t b)
{
// ((int32_t)a*b) >> 16
__asm__(
"movw r18, %[a]" "\n\t"
"mul r18, %A[b]" "\n\t"
"mov r20, r1" "\n\t"
"muls r19, %B[b]" "\n\t"
"movw %[a], r0" "\n\t"
"mulsu %B[b], r18" "\n\t"
"clr r18" "\n\t"
"sbc %B[a], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"mulsu r19, %A[b]" "\n\t"
"sbc %B[a], r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"clr r1" "\n\t"
: [a] "+r" (a)
: [b] "a" (b)
: "r0", "r1", "r18", "r19", "r20"
);
return a;
}
#endif
#ifdef MUL16UU
int16_t mul16uu(uint16_t a, uint16_t b)
{
// ((uint32_t)a*(uint32_t)b) >> 16
__asm__(
"movw r18, %[a]" "\n\t"
"mul r18, %A[b]" "\n\t"
"mov r20, r1" "\n\t"
"mul r19, %B[b]" "\n\t"
"movw %[a], r0" "\n\t"
"mul %B[b], r18" "\n\t"
"clr r18" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"mul r19, %A[b]" "\n\t"
"add r20, r0" "\n\t"
"adc %A[a], r1" "\n\t"
"adc %B[a], r18" "\n\t"
"clr r1" "\n\t"
: [a] "+r" (a)
: [b] "a" (b)
: "r0", "r1", "r18", "r19", "r20"
);
return a;
}
#endif
#if defined(MUL_DIVMOD10) || defined(MUL_DECIMAL_STR)
#ifndef MUL_DIVMOD10
static inline
#endif
uint16_t divmod10(uint16_t u, uint8_t *mod)
{
uint16_t r;
uint8_t d;
__asm__(
"ldi r18, lo8(6554)" "\n\t"
"ldi r19, hi8(6554)" "\n\t"
"mul r18, %A[u]" "\n\t"
"mov r20, r1" "\n\t"
"mul r19, %B[u]" "\n\t"
"movw %[r], r0" "\n\t"
"mul r19, %A[u]" "\n\t"
"clr r19" "\n\t"
"add r20, r0" "\n\t"
"adc %A[r], r1" "\n\t"
"adc %B[r], r19" "\n\t"
"mul r18, %B[u]" "\n\t"
"add r20, r0" "\n\t"
"adc %A[r], r1" "\n\t"
"adc %B[r], r19" "\n\t"
"ldi r19, 10" "\n\t"
"inc r20" "\n\t"
"mul r19, r20" "\n\t"
"mov %[d], r1" "\n\t"
"clr r1" "\n\t"
: [r] "=&r" (r),
[d] "=r" (d)
: [u] "d" (u)
: "r0", "r1", "r18", "r19", "r20"
);
*mod = d;
return r;
}
#endif
#else // MUL_DEBUG
// Models for what the assembly is supposed to do.
// Also usefull for testing in non-AVR hosts.
int16_t mul16su(int16_t s, uint16_t u)
{
uint32_t r = (int32_t)s * (int32_t)u;
return r >> 16;
}
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n)
{
// n = 0 … 8
uint32_t r = (int32_t)s * (int32_t)u;
return r >> (16-n);
}
int16_t mul16ss(int16_t a, int16_t b)
{
uint32_t r = (int32_t)a * (int32_t)b;
return r >> 16;
}
int16_t mul16uu(uint16_t a, uint16_t b)
{
uint32_t r = (int32_t)a * (int32_t)b;
return r >> 16;
}
uint16_t divmod10(uint16_t u, uint8_t *mod)
{
uint32_t r = (uint32_t)u * (uint32_t)6554;
*mod = (((r & 0xff00)+0x100)*10) >> 16;
return r >> 16;
}
#endif // MUL_DEBUG
#ifdef MUL_DECIMAL_STR
static uint8_t decimals[8];
char *decimal_str(int16_t b, uint8_t dec)
{
uint8_t s = 0;
if (b<0) {
s = 1;
b = -b;
}
uint8_t *c = decimals+6;
uint8_t n = 0;
while ((b || n < dec) && n<7) {
b = divmod10(b, c);
*c-- += '0';
if (++n==dec) {
*c-- = '.';
n++;
}
}
if (!n)
*c = '0';
else if (s)
*c = '-';
else
*c = '+';
return (char *)c;
}
#endif

View file

@ -1,15 +0,0 @@
//
// mul.h
//
#include <stdint.h>
int16_t mul16su(int16_t s, uint16_t u);
int16_t mul16ss(int16_t a, int16_t b);
int16_t mul16uu(uint16_t a, uint16_t b);
int16_t mul16sun(int16_t s, uint16_t u, uint8_t n);
char *decimal_str(int16_t b, uint8_t dec);
#if defined(MUL_DIVMOD10) || defined(MUL_ALL)
uint16_t divmod10(uint16_t u, uint8_t *mod);
#endif

132
src/pipe.c Normal file
View file

@ -0,0 +1,132 @@
#include "pipe.h"
#include "bch4369.h"
#include "adc.h"
#ifdef HAVE_FPGA
#include "fpga.h"
#endif
#include <string.h>
section_status(pipe) struct pipe pipe;
uint8_t pipe_poll()
{
uint8_t r = pipe.status;
uint8_t fl = flash_poll(0);
if (spi_busy_p() || fl & FS_Busy || adc_poll(0))
goto done;
#ifdef HAVE_FPGA
if (fpga_reset_poll())
goto done;
#endif
if (r & PS_OUT) {
if (pipe.dest & pipe_cmd && pipe.valid & 0x1f)
// cmd did not drain the buffer, yet
goto done;
if (pipe.dest & pipe_flash && !(fl & FS_Ready))
// flash did not finish successfully
goto done;
if (pipe.source & pipe_adc && !adc_poll(pipe.adc))
goto done;
// fpga is OUT when the spi is ready.
// We are done with this buffer
pipe.valid = 0;
if (pipe.source & pipe_flash)
flash_poll(1);
#ifdef HAVE_FPGA
else if (pipe.source & pipe_fpga)
fpga_start(0);
#endif
r &=~ (PS_OUT|4);
r++;
goto done;
}
uint8_t bflgs = 0x0f;
if ((r & PS_5) == PS_5)
bflgs = 0x1f;
if (pipe.source & pipe_adc) {
uint8_t n = pipe.adc & 0x70;
if (!n)
goto adc_done;
uint8_t f = n-1;
uint8_t o = -16;
uint8_t v = pipe.valid;
#if 0
uint8_t c;
do {
o += 16;
f <<= 1;
c = v & 1;
v >>= 1;
} while (c); // please use the carry bit!
#else
// saves one instruction
__asm__ volatile
("\n"
"1: \n\t"
"subi %[O], -16 \n\t"
"lsl %[F] \n\t"
"lsr %[V] \n\t"
"brcs 1b \n"
: [O] "+r" (o),
[F] "+r" (f),
[V] "+r" (v)
);
#endif
if (o > 48)
goto adc_done;
if (o + n >= 64) {
f = 0xff;
n = 64-o;
}
pipe.valid |= f>>4;
_memcopy(flash_buffer+o, (void*)&magic, n);
}
adc_done:
if (pipe.source & pipe_flash && fl & FS_Ready)
pipe.valid = bflgs;
#ifdef HAVE_FPGA
else if (pipe.source & pipe_fpga)
pipe.valid = 0x0f;
#endif
if (~pipe.valid & 0x0f)
goto done;
if (r & PS_BCH) {
if (!(r&3))
bch4369_init();
uint8_t *bend = bch4369_stri(flash_buffer, 64);
if (!(~r & 3)) {
// reuse Y=bend
_memcopyyz(bend, bch_parity, 16);
pipe.valid = 0x01f;
}
}
if (~pipe.valid & bflgs)
goto done;
#ifdef HAVE_FPGA
if (pipe.dest & pipe_fpga)
fpga_start(1);
#endif
r |= PS_OUT;
done:
pipe.status = r;
return r;
}
void pipe_config(const struct pipe_config *c)
{
pipe = c->pipe;
if ((pipe.source | pipe.dest) & pipe_flash)
flash_start_stream(c->page, c->npages, c->flash);
if (pipe.source & pipe_adc)
adc_start_stream(pipe.adc);
}

47
src/pipe.h Normal file
View file

@ -0,0 +1,47 @@
#include "flash.h"
enum pipe_ports {
pipe_cmd = 1,
pipe_adc = 2,
pipe_flash = 4,
pipe_fpga = 8,
PS_OUT = 0x80,
PS_BCH = 0x10,
PS_528 = 0x08,
PS_BLK = 0x03,
PS_5 = PS_BLK | PS_528, // need 16 bytes more
};
extern
struct pipe {
uint8_t source;
uint8_t dest;
uint8_t status;
uint8_t valid;
uint8_t adc;
#ifdef HAVE_FPGA
struct {
uint16_t size;
uint8_t cmd[4];
uint8_t csize;
uint8_t zsize;
uint8_t isize;
uint8_t zero;
uint8_t wait;
uint8_t mask;
} fpga;
#endif
} pipe;
struct pipe_config {
struct pipe pipe;
uint16_t page;
uint16_t npages;
uint8_t flash;
};
uint8_t pipe_poll();
void pipe_cron();
void pipe_config(const struct pipe_config *c);

20
src/prompt.py Normal file
View file

@ -0,0 +1,20 @@
import sys
def set_prompt(prompt):
sys.ps1 = prompt + "> "
sys.ps2 = prompt + ". "
try:
ip=get_ipython()
from IPython.terminal.prompts import Prompts, Token
class myprompts(Prompts):
def in_prompt_tokens(self, *wtf):
return [(Token, prompt+'> ')]
def continuation_prompt_tokens(self, *wtf):
return [(Token, prompt+'. ')]
def out_prompt_tokens(self, *wtf):
return [(Token, prompt+'= ')]
def rewrite_prompt_tokens(self, *wtf):
return [(Token, prompt+'- ')]
ip.prompts=myprompts(ip)
except:
pass

17
src/pwm.c Normal file
View file

@ -0,0 +1,17 @@
// pwm.c
#include "pwm.h"
struct_ioconf(pwm_config) = {
conf_prefix(PWM),
#ifdef HAVE_nFETs
conf_io(PWM.CTRLB, TCA_SINGLE_WGMODE_SINGLESLOPE_gc | TCA_SINGLE_CMP0EN_bm),
#else
conf_io(PWM.CTRLB, TCA_SINGLE_WGMODE_SINGLESLOPE_gc),
#endif
conf_iow(PWM.PER, 0x0fff),
conf_iow(PWM.CMP0, 0xffff),
conf_iow(PWM.CMP1, 0x00ff),
conf_io(PWM.CTRLA, TCA_SINGLE_ENABLE_bm),
};

40
src/pwm.h Normal file
View file

@ -0,0 +1,40 @@
#include "config.h"
#define PWM TCA0.SINGLE
#ifdef HAVE_nFETs
static inline
void pwm_set(uint16_t dc, uint8_t d)
{
PWM.CMP0 = dc;
if (d)
DRAIN_VPORT.OUT |= 1 << DRAIN_PIN;
else
DRAIN_VPORT.OUT &=~ (1 << DRAIN_PIN);
}
static inline
void pwm_bias()
{
pwm_set(0xffff, 0);
}
static inline
void pwm_step(uint16_t max)
{
uint16_t c = PWM.CMP0+1;
if (c > max)
pwm_bias();
else
pwm_set(c, 1);
}
static inline
uint8_t pwm_busy()
{
return DRAIN_VPORT.OUT & (1 << DRAIN_PIN);
}
#endif

177
src/rtc.c
View file

@ -4,69 +4,154 @@
// !!! int = int8_t
#include "bate.h"
#include "config.h"
#include "rtc.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define Bit(x) (1<<(x))
volatile uint32_t clock;
volatile uint8_t clock_tick;
section_status(rtc) uint32_t clock;
volatile uint16_t clockh;
volatile uint8_t pit_tick;
volatile uint8_t rtc_tick;
void init_rtc(uint8_t p)
uint8_t rtc_cnt_tick()
{
if (p>=16)
p = RTC_PERIOD_CYC1024_gc;
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
RTC.PITINTCTRL = 1;
RTC.PITCTRLA = p;
while (RTC.PITSTATUS & 1) ;
RTC.PITCTRLA = p | 1;
cli();
uint8_t r = rtc_tick;
rtc_tick = 0;
sei();
return r;
}
struct_ioconf(rtc_config) = {
conf_prefix(RTC),
conf_iow(RTC.CMP, 10), // rtc_cnt_tick() period [s]
conf_io(RTC.CLKSEL, RTC_CLKSEL_INT1K_gc),
conf_io(RTC.PITINTCTRL, 1),
conf_io(RTC.PITCTRLA, RTC_PERIOD_CYC1024_gc | RTC_PITEN_bm),
conf_iow(RTC.PER, 0xffff),
conf_io(RTC.CTRLA, RTC_PRESCALER_DIV1024_gc | RTC_RUNSTDBY_bm | RTC_RTCEN_bm),
conf_io(RTC.INTCTRL, RTC_CMP_bm | RTC_OVF_bm),
};
#if 1
ISR(RTC_PIT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
#ifdef DEBUG
"lds r24, debug_data+9" "\n\t"
"subi r24, -1" "\n\t"
"sts debug_data+9, r24" "\n\t"
#endif
"ldi r24,1" "\n\t"
"sts %[flag], r24" "\n\t"
"sts %[tick], r24" "\n\t"
"lds r24, %[clock]" "\n\t"
"subi r24, -1" "\n\t"
"sts %[clock], r24" "\n\t"
"lds r24, %[clock]+1" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+1, r24" "\n\t"
"lds r24, %[clock]+2" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+2, r24" "\n\t"
"lds r24, %[clock]+3" "\n\t"
"sbci r24, -1" "\n\t"
"sts %[clock]+3, r24" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
:[tick] "+m" (clock_tick),
[clock] "+m" (clock)
:[flag] "n" (&RTC.PITINTFLAGS)
__asm__ (
"push r24" "\n\t"
"ldi r24, 1" "\n\t"
"sts %[flag], r24" "\n\t"
"sts pit_tick, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
::[flag] "n" (&RTC.PITINTFLAGS)
);
}
ISR(RTC_CNT_vect, ISR_NAKED)
{
__asm__ (
"push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
"lds r24, %[flag]" "\n\t"
"sts %[flag], r24" "\n\t"
"sts rtc_tick, r24" "\n\t"
"sbrs r24, 1" "\n\t"
"rjmp 1f" "\n\t"
"push r24" "\n\t"
"push r25" "\n\t"
"push r26" "\n\t"
"lds r24, %[CMPL]" "\n\t"
"lds r25, %[CMPH]" "\n\t"
"lds r26, rtc_config+3" "\n\t"
"add r24, r26" "\n\t"
"sts %[CMPL], r24" "\n\t"
"lds r26, rtc_config+5" "\n\t"
"adc r25, r26" "\n\t"
"sts %[CMPH], r25" "\n\t"
"pop r26" "\n\t"
"pop r25" "\n\t"
"pop r24" "\n"
"1:" "\n\t"
"sbrs r24, 0" "\n\t"
"rjmp 2f" "\n\t"
"lds r24, clockh" "\n\t"
"subi r24, -1" "\n\t"
"sts clockh, r24" "\n\t"
"lds r24, clockh+1" "\n\t"
"sbci r24, -1" "\n\t"
"sts clockh+1, r24" "\n\t"
"2:" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n"
"pop r24" "\n\t"
"reti" "\n"
::
[flag] "n" (&RTC.INTFLAGS),
[CMPL] "n" (&RTC.CMP),
[CMPH] "n" (&RTC.CMP+1)
);
}
#else
ISR(RTC_PIT_vect)
{
clock_tick = 1;
RTC.PITINTFLAGS = 1;
clock++;
}
ISR(RTC_CNT_vect)
{
uint8_t flag = RTC.INTFLAGS;
RTC.INTFLAGS = flag;
rtc_tick = flag;
if (flag & RTC_CNT_bm)
RTC.CMP += rtc_config[1].val | rtc_config[2].val << 8;
if (flag & RTC_OVF_bm)
clock += 1;
return;
}
#endif
#if 0
uint32_t time()
{
uint16_t cl, ch;
do {
cli();
cl = RTC.CNT;
ch = clockh;
sei();
} while (cl != RTC.CMP);
return clock = cl | (uint32_t)ch << 16;
}
#else
__attribute__((naked))
uint32_t time()
{
__asm__ volatile("\n"
"1:" "\n\t"
"cli" "\n\t"
"lds r22, %[CNT]" "\n\t"
"lds r23, %[CNT]+1" "\n\t"
"lds r24, clockh" "\n\t"
"lds r25, clockh+1" "\n\t"
"sei" "\n\t"
"lds r18, %[CNT]" "\n\t"
"lds r19, %[CNT]+1" "\n\t"
"cp r18, r22" "\n\t"
"cpc r19, r23" "\n\t"
"brne 1b" "\n\t"
"ldi r30, lo8(clock)" "\n\t"
"ldi r31, hi8(clock)" "\n\t"
"st Z+, r22" "\n\t"
"st Z+, r23" "\n\t"
"st Z+, r24" "\n\t"
"st Z+, r25" "\n\t"
"ret" "\n"
:: [CNT] "n" (&RTC.CNT)
: "r18", "r19", "r30", "r31", "memory"
);
}
#endif

View file

@ -3,7 +3,12 @@
//
#include <stdint.h>
#include <avr/interrupt.h>
volatile extern uint32_t clock;
volatile extern uint8_t clock_tick;
void init_rtc(uint8_t p);
extern uint32_t clock;
volatile extern uint16_t clockh;
volatile extern uint8_t pit_tick;
volatile extern uint8_t rtc_tick;
uint32_t time();
uint8_t rtc_cnt_tick();

299
src/spi.c
View file

@ -2,67 +2,266 @@
// spi.c
//
// !!! int = int8_t
// ! int = int8_t
#include "spi.h"
#include "bate.h"
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <string.h>
volatile uint8_t spi_tick;
section_status(spi) struct spi_job spi;
// The Pressure sensor samples on the rising edge, MODE = 0
static const uint8_t SPI_Mode_Write = SPI_SSD_bm | SPI_BUFEN_bm;
struct_ioconf(spi_config) = {
conf_prefix(SPI),
conf_io(SPI.CTRLB, SPI_SSD_bm | SPI_BUFEN_bm),
conf_io(SPI.CTRLA, SPI_MASTER_bm | SPI_ENABLE_bm | SPI_SPEED),
};
// The Pressure sensor delivers on the rising edge, MODE = 1
static const uint8_t SPI_Mode_Read = SPI_SSD_bm | SPI_BUFEN_bm | SPI_MODE_0_bm;
void init_spi(uint8_t div)
#if 0
ISR(SPI0_INT_vect)
{
if (div & ~SPI_PRESC_gm)
div = SPI_PRESC_DIV64_gc;
SPI.CTRLB = SPI_Mode_Write;
SPI.CTRLA = SPI_MASTER_bm | SPI_ENABLE_bm | div;
SPI.DATA;
SPI.DATA;
SPI.INTFLAGS = 0xff;
SPI.INTCTRL = SPI_TXCIE_bm;
}
uint16_t spi_frame(uint16_t d)
{
if (d)
SPI.CTRLB = SPI_Mode_Write;
else
SPI.CTRLB = SPI_Mode_Read;
SPI.DATA;
SPI.DATA;
SPI.INTFLAGS = 0xff;
spi_tick = 0;
SPI.DATA = d >> 8;
SPI.DATA = d;
uint8_t timeout = 20; // 20×15µs = 300µs
sei();
while (!spi_tick) {
sleep_cpu();
if (!--timeout) {
DEBUG_COUNTER(spi_timeout);
break;
uint8_t d;
uint8_t ifg = SPI.INTFLAGS;
if (ifg & SPI_DREIF_bm) {
repeat:
if (spi.csize) {
spi.csize--;
d = *spi.cmd++;
}
else if (spi.zsize) {
spi.zsize--;
d = spi.zero;
}
else if (spi.wsize) {
spi.wsize--;
d = *spi.wdata++;
}
else {
SPI.INTCTRL = SPI_TXCIF_bm | SPI_RXCIF_bm;
goto done_write;
}
SPI.DATA = d;
// clear a stray TXCIF, at high SCK rate
SPI.INTFLAGS = SPI_TXCIF_bm;
SPI.INTCTRL = SPI_TXCIF_bm | SPI_DREIF_bm | SPI_RXCIF_bm;
done_write:
ifg = SPI.INTFLAGS;
}
if (ifg & SPI_RXCIF_bm) {
d = SPI.DATA; // clears the IF flag
if (spi.isize)
spi.isize --;
else {
if (spi.mask) {
if ((d & spi.mask) == spi.wait) {
spi.zsize++;
goto cont;
}
spi.mask = 0;
}
if (spi.rsize) {
spi.rsize--;
*spi.rdata++ = d;
}
}
cont:
ifg = SPI.INTFLAGS;
if (ifg & SPI_DREIF_bm)
// This may prevent this ISR to terminate
// before the job is done at high clock rate.
// DRE is kept set when all bytes were sent.
// With just the right clock rate, we may send
// two bytes per interrupt.
goto repeat;
}
if (ifg & SPI_TXCIF_bm) {
if (!(spi.mode & SPI_CONT))
SSEL_VPORT.OUT |= 1 << SSEL_PIN;
SPI.INTCTRL = SPI_RXCIF_bm;
}
uint16_t b = SPI.DATA;
return (b<<8) | SPI.DATA;
}
#else
ISR(SPI0_INT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"lds r24, %[flag]" "\n\t"
"sts %[flag], r24" "\n\t"
"sts spi_tick, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
: : [flag] "n" (&SPI.INTFLAGS)
__asm__ volatile(
"push r24" "\n\t"
"in r24, __SREG__" "\n\t"
"push r24" "\n\t"
"push r25" "\n\t"
"push r26" "\n\t"
"push r30" "\n\t"
"push r31" "\n\t"
"lds r24, %[IFLGS]" "\n\t"
"sbrs r24, %[DRE]" "\n\t"
"rjmp 3f" "\n"
"5:" "\n"
"lds r25, %[CSZ]" "\n\t"
"subi r25, 1" "\n\t"
"brcs 1f" "\n\t"
"sts %[CSZ], r25" "\n\t"
"lds r30, %[CMD]" "\n\t"
"lds r31, %[CMD]+1" "\n\t"
"ld r25, Z+" "\n\t"
"sts %[CMD], r30" "\n\t"
"sts %[CMD]+1, r31" "\n\t"
"rjmp 2f" "\n"
"1:" "\n\t"
"lds r25, %[ZSZ]" "\n\t"
"subi r25, 1" "\n\t"
"brcs 1f" "\n\t"
"sts %[ZSZ], r25" "\n\t"
"lds r25, %[ZERO]" "\n\t"
"rjmp 2f" "\n"
"1:" "\n\t"
"lds r25, %[WSZ]" "\n\t"
"subi r25, 1" "\n\t"
"ldi r26, %[RIFLGS]" "\n\t"
"brcs 1f" "\n\t"
"sts %[WSZ], r25" "\n\t"
"lds r30, %[WD]" "\n\t"
"lds r31, %[WD]+1" "\n\t"
"ld r25, Z+" "\n\t"
"sts %[WD], r30" "\n\t"
"sts %[WD]+1, r31" "\n"
"2:" "\n\t"
"sts %[DATA], r25" "\n\t"
"ldi r25, 1<<%[TXC]" "\n\t"
"sts %[IFLGS], r25" "\n\t"
"ldi r26, %[AIFLGS]" "\n"
"1:" "\n\t"
"sts %[ICTRL], r26" "\n\t"
"lds r24, %[IFLGS]" "\n"
"3:" "\n\t"
"sbrs r24, %[RXC]" "\n\t"
"rjmp 3f" "\n\t"
"lds r25, %[DATA]" "\n\t"
"lds r26, %[ISZ]" "\n\t"
"subi r26, 1" "\n\t"
"brcs 1f " "\n\t"
"sts %[ISZ], r26" "\n\t"
"rjmp 4f" "\n"
"1:" "\n\t"
"lds r26, %[MASK]" "\n\t"
"cpi r26, 0" "\n\t"
"breq 1f" "\n\t"
"and r26, r25" "\n\t"
"lds r24, %[WAIT]" "\n\t"
"cp r24, r26" "\n\t"
"brne 2f" "\n\t"
"lds r25, %[ZSZ]" "\n\t"
"subi r25, -1" "\n\t"
"sts %[ZSZ], r25" "\n\t"
"rjmp 4f" "\n"
"2:" "\n\t"
"clr r26" "\n\t"
"sts %[MASK], r26" "\n"
"1:" "\n\t"
"lds r26, %[RSZ]" "\n\t"
"subi r26, 1" "\n\t"
"brcs 4f" "\n\t"
"sts %[RSZ], r26" "\n\t"
"lds r30, %[RD]" "\n\t"
"lds r31, %[RD]+1" "\n\t"
"st Z+, r25" "\n\t"
"sts %[RD], r30" "\n\t"
"sts %[RD]+1, r31" "\n"
"4:" "\n\t"
"lds r24, %[IFLGS]" "\n\t"
"sbrc r24, %[DRE]" "\n\t"
"rjmp 5b" "\n\t"
"3:" "\n\t"
"sbrs r24, %[TXC]" "\n\t"
"rjmp 3f" "\n\t"
"lds r25, %[MODE]" "\n\t"
"sbrs r25, 7" "\n\t"
"sbi %[SPORT], %[SSEL]" "\n\t"
"ldi r25, 1<<%[RXC]" "\n\t"
"sts %[ICTRL], r25" "\n"
"3:" "\n\t"
"pop r31" "\n\t"
"pop r30" "\n\t"
"pop r26" "\n\t"
"pop r25" "\n\t"
"pop r24" "\n\t"
"out __SREG__, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
::
[CSZ] "m" (spi.csize),
[ZSZ] "m" (spi.zsize),
[WSZ] "m" (spi.wsize),
[ISZ] "m" (spi.isize),
[RSZ] "m" (spi.rsize),
[CMD] "m" (spi.cmd),
[ZERO] "m" (spi.zero),
[WD] "m" (spi.wdata),
[RD] "m" (spi.rdata),
[MODE] "m" (spi.mode),
[MASK] "m" (spi.mask),
[WAIT] "m" (spi.wait),
[IFLGS] "n" (&SPI.INTFLAGS),
[TXC] "n" (SPI_TXCIF_bp),
[DRE] "n" (SPI_DREIF_bp),
[RXC] "n" (SPI_RXCIF_bp),
[RIFLGS] "n" (SPI_TXCIF_bm | SPI_RXCIF_bm),
[AIFLGS] "n" (SPI_TXCIF_bm | SPI_RXCIF_bm | SPI_DREIF_bm),
[ICTRL] "n" (&SPI.INTCTRL),
[DATA] "n" (&SPI.DATA),
[SPORT] "n" (_SFR_IO_ADDR(SSEL_VPORT.OUT)),
[SSEL] "n" (SSEL_PIN)
);
}
#endif
uint8_t spi_select(uint8_t mode)
{
uint8_t s = spi_busy_p();
if (s)
return s;
memset(&spi, 0, sizeof(spi));
spi.mode = mode;
if (mode & SPI_FLASH)
SSEL_VPORT.OUT &=~ (1 << SSEL_PIN);
else
SSEL_VPORT.OUT |= 1 << SSEL_PIN;
if (mode & SPI_CONFIG)
SPI.CTRLA |= SPI_DORD_bm;
else
SPI.CTRLA &=~ SPI_DORD_bm;
return 0;
}
void spi_start_cmd(uint8_t csize, uint8_t *cmd)
{
spi.cmd = cmd;
barrier();
spi.csize = csize;
spi_start();
}
void spi_start_write(uint8_t csize, uint8_t *cmd, uint8_t wsize, uint8_t *wdata)
{
spi.wdata = wdata;
barrier();
spi.wsize = wsize;
spi_start_cmd(csize, cmd);
}
void spi_start_read(uint8_t csize, uint8_t *cmd, uint8_t rsize, uint8_t *rdata)
{
spi.rdata = rdata;
spi.isize = csize + spi.zsize;
barrier();
spi.zsize += rsize;
spi.rsize = rsize;
spi_start_cmd(csize, cmd);
}

View file

@ -2,12 +2,64 @@
// spi.h
//
#include <stdint.h>
#include <avr/io.h>
#ifndef _SPI_H
#define _SPI_H
#include "config.h"
#include <avr/interrupt.h>
#define SPI SPI0
#define SPI_SPEED (SPI_CLK2X_bm | SPI_PRESC_DIV4_gc)
extern volatile uint8_t spi_tick;
uint16_t spi_frame(uint16_t d);
void init_spi(uint8_t div);
static inline void spi_off() { SPI.INTCTRL = SPI.CTRLA = 0; }
extern
struct spi_job {
uint8_t mode;
uint8_t csize; // pipe.fpga.…
uint8_t zsize; //
uint8_t isize; //
uint8_t zero; //
uint8_t wait; //
uint8_t mask; //
uint8_t rsize;
uint8_t wsize;
const uint8_t *cmd;
const uint8_t *wdata;
uint8_t *rdata;
} spi;
enum spi_mode_bits {
SPI_FLASH = 0x01,
SPI_CONFIG = 0x02,
SPI_CONT = 0x80,
};
static inline
uint8_t spi_abort()
{
cli();
uint8_t f = SPI.INTFLAGS;
SPI.INTFLAGS = 0;
sei();
return f;
}
static inline
uint8_t spi_busy_p()
{
return SPI.INTCTRL & SPI_TXCIF_bm;
}
static inline
void spi_start()
{
SPI.INTCTRL = SPI_DREIF_bm | SPI_TXCIF_bm| SPI_RXCIF_bm;
}
static inline void barrier() { __asm__("":::"memory"); }
void init_spi(uint8_t spi_div);
uint8_t spi_select(uint8_t mode);
void spi_start_cmd(uint8_t csize, uint8_t *cmd);
void spi_start_write(uint8_t csize, uint8_t *cmd, uint8_t wsize, uint8_t *wdata);
void spi_start_read(uint8_t csize, uint8_t *cmd, uint8_t rsize, uint8_t *rdata);
#endif

View file

@ -4,99 +4,41 @@
// !!! int = int8_t
#include "bate.h"
#include "mul.h"
#include <avr/io.h>
#include "uart.h"
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define Bit(x) (1<<(x))
// 10 MHz / 2400 / 16 * 64
#define UART_DIV 16667
__attribute__ ((noinline, noclone))
void init_uart(uint16_t div, uint8_t mode)
{
if (mode==0xff)
mode = USART_TXEN_bm;
mode |= USART_TXEN_bm;
if (div<64)
div = UART_DIV;
USART0.BAUD = div;
PORTB.DIRSET = Bit(2);
USART0.CTRLB = mode;
if (mode & USART_RXEN_bm)
USART0.CTRLA = USART_RXCIE_bm;
// `BOTHEDGES` should wake from power down sleep()
PORTB.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
}
volatile uint8_t rx_tick;
ISR(PORTB_PORT_vect, ISR_NAKED)
{
__asm__ ("push r24" "\n\t"
"lds r24, %[stat]" "\n\t"
"sts %[stat], r24" "\n\t"
"sts rx_tick, r24" "\n\t"
"pop r24" "\n\t"
"reti" "\n"
: : [stat] "n" (&VPORTB.INTFLAGS)
);
}
__attribute__ ((noinline, noclone))
uint8_t uart_tick()
{
cli();
uint8_t r = rx_tick;
rx_tick = 0;
sei();
return r;
}
struct_ioconf(uart_config) = {
conf_prefix(USART0),
conf_iow(USART0.BAUD, 40000000/115200), // 115200 baud
conf_io(USART0.CTRLC, USART_CHSIZE_8BIT_gc),
conf_io(USART0.CTRLB, USART_TXEN_bm | USART_RXEN_bm),
conf_io(USART0.CTRLA, USART_RXCIE_bm),
};
// `uart_tx` buffer size must be a power of 2, max 256.
// Fix `tx()` and `put_char()` / `put_char:`
// when the size changes.
// For now, we can afford half of the available RAM,
// and still have 128 bytes for the stack
#ifdef UART_TX_SMALL
uint8_t uart_tx[128];
#else
uint8_t uart_tx[256];
#endif
uint8_t uart_tx[32];
#define uart_tx_m (sizeof(uart_tx) - 1)
volatile uint8_t uart_tx_w;
volatile uint8_t uart_tx_r;
volatile uint8_t uart_tx_busy;
uint8_t uart_cks;
#if 0
__attribute__ ((noinline, noclone))
void tx()
{
// interrupts must be disabled
if (USART0.STATUS & USART_TXCIF_bm) {
USART0.STATUS = USART_TXCIF_bm;
uart_tx_busy = 0;
}
// read volatile memory once, for speed
uint8_t r = uart_tx_r;
uint8_t w = uart_tx_w;
while (w - r) {
uart_tx_busy = w - r;
if (!(USART0.STATUS & USART_DREIF_bm)) {
USART0.CTRLA |= USART_DREIE_bm | USART_TXCIE_bm;
USART0.CTRLA |= USART_DREIE_bm;
uart_tx_r = r;
return;
}
USART0.TXDATAL = uart_tx[r++ & uart_tx_m];
// race? TXC while we were looping? Drop it.
USART0.STATUS = USART_TXCIF_bm;
USART0.STATUS |= USART_TXCIF_bm
}
uart_tx_r = r;
USART0.CTRLA &=~ USART_DREIE_bm;
@ -104,100 +46,71 @@ void tx()
ISR(USART0_DRE_vect)
{
DEBUG_COUNTER(tx_irqs);
tx();
}
#else
__attribute__ ((noinline, noclone))
__attribute__ ((noinline, naked))
void tx()
{
// This uses only six registers, to save stack in the ISR.
// r0 and r1 need not be saved
// Avoids unlikely branches.
// [STATUS] is prepared in advance, to avoid two branches and one memory load,
// at the cost of one register on the ISR stack (r25)
// This uses only three registers, to save stack in the ISR.
__asm__("\n"
" lds r21, uart_tx_r \n"
" lds r19, uart_tx_w \n"
" lds r25, %[CTRLA] \n"
" andi r25, ~0x20 ; clr DREIE \n"
" lds r30, %[STATUS] \n"
" ldi r24, 0x40 \n"
" and r30, r24 ; TXCIF \n"
" breq 2f \n"
" sts %[STATUS], r24 ; clr TXCIF \n"
" ldi r31, 0 \n"
" sts uart_tx_busy, r31 \n"
" andi r25, ~(1<<%[DRE]) \n"
" rjmp 2f \n"
" \n"
"1: \n"
" mov r30, r21 ; \n"
" subi r21, 0xff ; r++ & uart_tx_m \n"
#ifdef UART_TX_SMALL
" andi r30, 0x7f ; \n"
#endif
" ldi r31, 0 \n"
" mov r31, r30 ; \n"
" subi r31, 0xff ; r++ & uart_tx_m \n"
" sts uart_tx_r, r31 \n"
" andi r30, 0x1f ; \n"
" clr r31 \n"
" subi r30, lo8(-(uart_tx)) \n"
" sbci r31, hi8(-(uart_tx)) \n"
" ld r24, Z \n"
" sts %[TXDATA], r24 \n"
" ldi r24, 0x40 \n"
" sts %[STATUS], r24 ; clr TXCIF \n"
" ld r30, Z \n"
" sts %[TXDATA], r30 \n"
" ldi r30, 1<<%[TXC] \n"
" sts %[STATUS], r30 \n"
"2: \n"
" cp r21, r19 \n"
" lds r30, uart_tx_r \n"
" lds r31, uart_tx_w \n"
" cp r30, r31 \n"
" breq 3f \n"
" sts uart_tx_busy, r24 ; =0x40 \n"
" lds r24, %[STATUS] \n"
" sbrc r24, 5 ; DREIF \n"
" lds r31, %[STATUS] \n"
" sbrc r31, %[DRE] \n"
" rjmp 1b \n"
" ori r25, 0x60 ; set DREIE TXCIE \n"
" ori r25, 1<<%[DRE] \n"
"3: \n"
" sts %[CTRLA], r25 \n"
" sts uart_tx_r, r21 \n"
" ret \n"
:
: [STATUS] "n" (&USART0.STATUS),
[CTRLA] "n" (&USART0.CTRLA),
[TXDATA] "n" (&USART0.TXDATAL)
: "r19", "r21",
"r24", "r25",
"r30", "r31",
"memory"
:
[TXC] "n" (USART_TXCIF_bp),
[DRE] "n" (USART_DREIF_bp),
[CTRLA] "n" (&USART0.CTRLA),
[STATUS] "n" (&USART0.STATUS),
[TXDATA] "n" (&USART0.TXDATAL)
: "r25", "r30", "r31", "memory"
);
}
ISR(USART0_DRE_vect, ISR_NAKED)
{
// Doing this naked is a bit dangerous,
// but saves five instructions and two bytes stack (r0, r1).
// This saves five instructions and two bytes stack (r0, r1).
// OTOH, the C implementation of tx() is not bad either,
// if we are doing this asm, then we do it agressively.
__asm__("\n"
" push r24 \n"
" in r24, __SREG__ \n"
" push r24 \n"
" push r25 \n"
" push r19 \n"
" push r21 \n"
" in r25, __SREG__ \n"
" push r25 \n"
" push r30 \n"
" push r31 \n"
" \n"
#ifdef DEBUG
" lds r24, debug_data + 3 \n"
" subi r24, 0xff \n"
" sts debug_data + 3, r24 \n"
#endif
" rcall tx \n"
" \n"
" pop r31 \n"
" pop r30 \n"
" pop r21 \n"
" pop r19 \n"
" pop r25 \n"
" pop r24 \n"
" out __SREG__, r24 \n"
" pop r24 \n"
" out __SREG__, r25 \n"
" pop r25 \n"
" reti \n"
);
}
@ -206,26 +119,28 @@ ISR(USART0_DRE_vect, ISR_NAKED)
ISR(USART0_TXC_vect, ISR_ALIASOF(USART0_DRE_vect));
uint8_t uart_rx[16];
uint8_t uart_rx[32];
#define uart_rx_m (sizeof(uart_rx) - 1)
volatile uint8_t uart_rx_w;
volatile uint8_t uart_rx_mes;
section_status(uart.w) volatile uint8_t uart_rx_w;
section_status(uart.h) volatile uint8_t uart_rx_s;
section_status(uart.m) volatile uint8_t uart_rx_mes;
section_status(uart.e) uint8_t uart_rx_err;
section_status(uart.ee) uint8_t uart_rx_errors;
#if 0
ISR(USART0_RXC_vect)
{
DEBUG_COUNTER(rx_irqs);
// s/while/if/ !
if (USART0.STATUS & USART_RXCIF_bm) {
uint8_t c = USART0.RXDATAL;
DEBUG_POKE(rx_char, c);
uart_rx_s |= USART0.RXDATAH;
uint8_t w = uart_rx_w;
uart_rx[w] = c;
if (w < uart_rx_m)
uart_rx_w = ++w;
if (!uart_rx_mes && c=='\n')
uart_rx_mes = w;
if (w<=uart_rx_m) {
uint8_t c = USART0.RXDATAL;
uart_rx[w++] = c;
if (!uart_rx_mes && c=='\n')
uart_rx_mes = w;
uart_rx_w = w;
}
}
}
#else
@ -246,23 +161,19 @@ ISR(USART0_RXC_vect, ISR_NAKED)
" push r24 \n"
" push r30 \n"
" push r31 \n"
#ifdef DEBUG
" lds r24, debug_data + 4 \n"
" subi r24, -1 \n"
" sts debug_data + 4, r24 \n"
#endif
" lds r24, %[RXDATA] \n"
" lds r24, %[DH] \n"
" lds r30, uart_rx_s \n"
" or r24, r30 \n"
" sts uart_rx_s, r24 \n"
" lds r24, %[DL] \n"
" lds r30, uart_rx_w \n"
" cpi r30, 0x20 \n"
" brcc 1f \n"
" ldi r31, 0 \n"
" subi r30, lo8(-(uart_rx)) \n"
" sbci r31, hi8(-(uart_rx)) \n"
" st Z+, r24 \n"
#ifdef DEBUG
" sts debug_data + 5, r24 \n"
#endif
" subi r30, lo8(uart_rx) \n"
" sbrc r30, 4 ; log2(sizeof(uart_rx)) \n"
" subi r30, 1 \n"
" sts uart_rx_w, r30 \n"
" cpi r24, '\n' \n"
" brne 1f \n"
@ -277,145 +188,7 @@ ISR(USART0_RXC_vect, ISR_NAKED)
" out __SREG__, r24 \n"
" pop r24 \n"
" reti \n"
: : [RXDATA] "n" (&USART0.RXDATAL)
:: [DL] "n" (&USART0.RXDATAL), [DH] "n" (&USART0.RXDATAH)
);
}
#endif
#if 0
// These are implemented in `uart_tx.S`
__attribute__ ((noinline, noclone))
uint8_t uart_put_char(uint8_t c)
{
uint8_t r = uart_tx_r;
uint8_t w = uart_tx_w;
uint8_t ww = w + 1;
if ((ww & uart_tx_m) == (r & uart_tx_m))
return 1;
uart_tx[w & uart_tx_m] = c;
__asm__("" ::: "memory");
uart_tx_w = ww;
__asm__("" ::: "memory");
return 0;
}
__attribute__ ((noinline, noclone))
void send_char(uint8_t c)
{
uint8_t s = 0;
uint8_t b;
while (1) {
b = uart_put_char(c);
uart_busy();
if (!b)
break;
sleep_cpu();
s = 1;
}
if (s)
DEBUG_COUNTER(tx_sleep);
}
__attribute__ ((noinline, noclone))
void send_str(const char *s)
{
while (*s) {
uint8_t c = *s++;
if (!uart_put_char(c))
continue;
send_char(c);
}
uart_busy();
}
static inline
void rx_dismiss(uint8_t n)
{
cli();
uint8_t i = 0;
uint8_t m = 0;
uint8_t w = uart_rx_w;
if (w != uart_rx_m)
while (n < w) {
uint8_t c = uart_rx[n++];
uart_rx[i++] = c;
if (!m && c=='\n')
m = i;
}
uart_rx_mes = m;
uart_rx_w = i;
sei();
}
__attribute__ ((noinline, noclone))
uint8_t uart_busy()
{
cli();
tx();
sei();
return uart_tx_busy;
}
static __attribute__ ((noinline, noclone))
void send_hex_nibble(uint8_t b)
{
b += '0';
if (b>'9')
b += '@' - '9';
send_char(b);
}
__attribute__ ((noinline, noclone))
void send_hex_byte(uint8_t b)
{
send_hex_nibble(b >> 4);
send_hex_nibble(b & 0xf);
}
__attribute__ ((noinline, noclone))
void send_hex_word(uint16_t b)
{
send_hex_byte(b >> 8);
send_hex_byte(b);
}
__attribute__ ((noinline, noclone))
void send_hex(uint8_t header, const uint8_t *s, uint8_t n, uint8_t words)
{
// dump little endian words.
send_char(header);
while (n--) {
if (words & 1)
send_char(' ');
uint8_t l = *(s++);
if (words & 2 && n & 1) {
send_hex_byte(*(s++));
n--;
}
send_hex_byte(l);
}
send_char('\n');
}
void command(void)
{
uint8_t m = uart_rx_mes;
if (!m)
return;
uint8_t *s = uart_rx;
uint8_t i = 0;
uint8_t c;
send_str("R ");
while (i++ < m) {
c = *s++;
send_char(c);
}
if (c != '\n')
send_char('\n');
else
parse_command(uart_rx, m);
rx_dismiss(m);
}
#endif

View file

@ -2,32 +2,20 @@
// uart.h
//
#include <stdint.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include "config.h"
void init_uart(uint16_t div, uint8_t mode);
uint8_t uart_tick();
static inline
uint8_t uart_break_p()
{
return !(VPORTB.IN & 0x08);
}
void init_uart(uint16_t mode, uint16_t div);
void send_char(uint8_t c);
void send_eol();
void send_str(const char *s);
void send_hex_byte(uint8_t b);
void send_hex_byte_eol(uint8_t b);
void send_hex_word(uint16_t b);
void send_hex(uint8_t header, const uint8_t *s, uint8_t n, uint8_t words);
void send_hex(const void *s, uint8_t n);
uint8_t uart_busy();
void command(void);
void send_decimal(uint16_t b, uint8_t dec);
void parse_command(uint8_t *s, uint8_t n);
void parse_command(const uint8_t *s, uint8_t n);
static inline
void send_hex_long(uint32_t b)
@ -36,5 +24,5 @@ void send_hex_long(uint32_t b)
send_hex_word(b);
}
extern uint8_t uart_cks;
void send_cks();
extern uint8_t uart_rx_err;
extern uint8_t uart_rx_errors;

132
src/uart.py Executable file
View file

@ -0,0 +1,132 @@
#! /usr/bin/ipython3 --profile=uart
import sys, serial, threading, struct
from prompt import set_prompt
class uart(threading.Thread):
def __init__(self, port, baudrate=115200, parity='N'):
self.baudrate=baudrate
self.serial = serial.Serial(baudrate=baudrate, parity=parity, timeout=10.0,
xonxoff=False, rtscts=False, dsrdtr=False)
self.serial.port = port
self.portname = port.split('/')[-1]
self.serial.open()
self.write = self.serial.write
self.responses = []
self.readbuffer = b""
self.reader_lock = threading.Lock()
self.resp_ready = threading.Condition()
self.alive = True
threading.Thread.__init__(self, target=self._reader)
self.daemon = True
self.start()
def _reader(self):
data = b""
while self.alive:
data += self.serial.read_until(b'\n')
data += self.serial.read_all()
if data and self.reader_lock.acquire(False):
self.readbuffer += data
data = b""
self.parser()
self.reader_lock.release()
def parse(self):
if self.reader_lock.acquire(False):
self.parser()
self.reader_lock.release()
def kill(self):
self.alive = False
self.join()
def parser(self):
"do something with received chars in .readbuffer"
if not self.resp_ready.acquire(False):
return
lines = self.readbuffer.split(b'\n')
self.readbuffer = lines[-1]
lines[-1:] = []
for ll in lines:
if ll:
self.parse_line(ll)
if self.responses:
self.resp_ready.notify()
self.resp_ready.release()
_verbose = True
def parse_line(self, l):
self.responses.append(l)
if self._verbose:
try:
s = l.decode()
print(f"{self.portname}<- {s}", file=sys.stderr)
except:
print(f"{self.portname}<- {repr(l)}", file=sys.stderr)
def resp(self, timeout=10, blocking=True):
self.parse()
if not self.resp_ready.acquire(blocking):
return b""
if blocking and not self.responses:
self.resp_ready.wait(timeout)
l = b""
if self.responses:
l = self.responses[0]
self.responses[:1] = []
self.resp_ready.release()
return l
def flush(self):
r = []
while self.responses:
r.append(self.resp())
return r
def ucmd(self, c):
if not isinstance(c, bytes):
c = c.encode()
if c[-1:] != b'\n':
c = c + b'\n'
if self._verbose:
print(f"{self.portname}-> {c.decode().rstrip()}", file=sys.stderr)
self.write(c)
def cmd(self, c):
self.ucmd(c)
def _export(self, scope=None, prefix=""):
"""usage: ..._export(globals())
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.
"""
r = {
prefix+k: getattr(self, k)
for k in dir(self)
if k.upper() != k
and k[0] != '_'
}
if scope:
scope.update(r)
return r
if __name__=="__main__":
import getopt
oo,ff = getopt.getopt(sys.argv[1:], "F:", ["tty="])
port = "/dev/ttyUSB1"
baud = 115200
for o,v in oo:
if o=="-F" or v=="--tty":
v = v.split(",")
port = v[0]
if len(v)>1:
baud = int(v[1])
tty = uart(port, baud)
tty._export(globals())
set_prompt(tty.portname)

View file

@ -3,168 +3,146 @@
//
// avoid quite a few push and pops and jumps
#include <avr/io.h>
.global send_hex_word
.global send_hex_byte
.global send_hex_byte_eol
.global send_char
.global _send_char22
.global send_str
.global _send_str26
.global uart_busy
.global send_hex
.global send_cks
.global send_eol
.global command
// `tx()` and `put_char()` do not gobble r18, r20, r22, r23, r26, and r27.
// `tx()` and `put_char()` do not gobble r18, r20, r21, r22, r24, r26, and r27.
put_char:
// non-global, non-C
// arg: char r22
lds r23, uart_tx_r
lds r30, uart_tx_w
ldi r19, 1
add r19, r30
eor r23, r19
#ifdef UART_TX_SMALL
andi r23, 0x7f // uart_tx_m
breq 1f
andi r30, 0x7f // uart_tx_m
#else
breq 1f
#endif
ldi r31, 0
subi r30, lo8(-(uart_tx))
sbci r31, hi8(-(uart_tx))
st Z, r22
sts uart_tx_w, r19
lds r19, uart_cks
add r19, r22
sts uart_cks, r19
1:
// r22 preserved
// r23 full when zero
// r18, r26, and 27 must be preseved in the hex functions
// , r20, r21, r24, must be preserved in `uart_busy()` and `put_char()'
send_hex_byte_eol:
ldi r22, ' '
rcall _send_char22
rcall send_hex_byte
send_eol:
ldi r22, '\n'
rcall _send_char22
uart_busy:
cli
rcall tx ; gobbles r25, r30, and r31
sei
lds r25, USART0_STATUS
andi r25, SPI_TXCIF_bm
9:
ret
// r18, r20, r26, and 27 must be preseved in the hex functions
// r22 and r23 must be preserved in `uart_busy()` and `put_char()'
#ifdef HEX_WORD
send_hex_word:
push r24
mov r20, r24
mov r24, r25
rcall send_hex_byte
pop r24
mov r24, r20
#endif
send_hex_byte:
push r24
mov r21, r24
swap r24
rcall send_hex_nibble
pop r24
mov r24, r21
send_hex_nibble:
andi r24, 0x0f
subi r24, -'0'
cpi r24, '9'+1
brlo send_char
subi r24, '9'+1-'A'
send_char:
mov r22, r24
send_char22:
rcall put_char
rcall uart_busy
tst r23
brne 9f
sleep
rjmp send_char22
2:
rcall put_char
tst r23
brne 1f
rcall send_char22
rjmp _send_char22
1:
movw r24, r26
send_str:
movw r26, r24
ld r22, X+
tst r22
brne 2b
uart_busy:
cli
rcall tx ; gobbles only r19, r21, r24, r25, r30, and r31
sei
lds r24, uart_tx_busy
rcall uart_busy
sleep
_send_char22:
// non-global, non-C
// arg: char r22
lds r23, uart_tx_r
lds r30, uart_tx_w
ldi r19, 1
add r19, r30
sub r23, r19
andi r30, 0x1f // uart_tx_m
ldi r31, 0
subi r30, lo8(-(uart_tx))
sbci r31, hi8(-(uart_tx))
subi r23, 0xe0
brcs 1b
st Z, r22
sts uart_tx_w, r19
9:
ret
; send_hex('Q', &uart_cks, 1, 0);
send_cks:
ldi r24, 'Q'
ldi r18, 0
ldi r20, 1
ldi r22, lo8(uart_cks)
ldi r23, hi8(uart_cks)
send_str:
movw r26, r24
rjmp _send_str26
1:
__send_char22_str26:
rcall _send_char22
_send_str26:
ld r22, X+
tst r22
brne 1b
9:
ret
#ifdef SEND_HEX
send_hex:
movw r26, r22
rcall send_char
rjmp 3f
movw r26, r24
mov r18, r22
1:
ldi r22, ' '
sbrc r18, 0
rcall send_char22
ld r24, X+
sbrs r18, 1
rjmp 2f
sbrs r20, 0
rjmp 2f
push r24
subi r20, 1
ld r24, X+
rcall send_hex_byte
pop r24
2:
rcall send_hex_byte
3:
subi r20, 1
brcc 1b
send_eol:
ldi r22, 10
rjmp send_char22
command:
lds r20, uart_rx_mes
tst r20
breq 9b
ldi r22, 'R'
rcall send_char22
ldi r22, '>'
rcall send_char22
ldi r26, lo8(uart_rx)
ldi r27, hi8(uart_rx)
mov r18, r20
1:
ld r22, X+
rcall send_char22
subi r18, 1
brne 1b
9:
ret
#endif
command:
;; when any frame errors occured, dismiss the buffer
lds r22, uart_rx_s
andi r22, 0x46
brne uart_errs_p
;; when there is no message, check for overflow
lds r22, uart_rx_mes
tst r22
breq uart_full_p
;; when the buffer was dismissed for errors, skip this command
push r22
lds r24, uart_rx_err
sts uart_rx_err, r1
lds r25, uart_rx_errors
or r25, r24
sts uart_rx_errors, r25
tst r24
brne 2f
ldi r24, lo8(uart_rx)
ldi r25, hi8(uart_rx)
mov r22, r20
push r20
rcall parse_command
2:
pop r24
rx_dismiss:
cli
lds r18, uart_rx_w
clr r19
clr r20
cpi r18, 15
brcc 3f
sub r18, r24
breq 3f
brcs 3f // TCNH, n > w
brcs 3f
ldi r26, lo8(uart_rx)
ldi r27, hi8(uart_rx)
movw r30, r26
add r30, r24
adc r31, r1
adc r31, r20
1:
ld r25, Z+
st X+, r25
@ -175,7 +153,7 @@ rx_dismiss:
brne 2f
mov r20, r19
2:
cp r19, r18
subi r18, 1
brne 1b
3:
sts uart_rx_mes, r20
@ -183,3 +161,15 @@ rx_dismiss:
sei
9:
ret
uart_full_p:
lds r22, uart_rx_w
andi r22, 0x20
breq 9b
uart_errs_p:
lds r24, uart_rx_err
or r24, r22
rx_dismiss_buffer:
sts uart_rx_err, r24
lds r24, uart_rx_w
rjmp rx_dismiss

108
sym/AT45DB081D-1.sym Normal file
View file

@ -0,0 +1,108 @@
v 20140308 2
T 1000 1600 8 10 1 1 0 4 1
refdes=U?
T 300 2950 8 10 0 1 0 0 1
device=AT45DB081D
T 1000 1050 8 10 1 1 0 4 1
footprint=SOIC8
T 300 3350 8 10 0 0 0 0 1
author=bpaddock(at)csonline.net
T 300 3550 8 10 0 0 0 0 1
documentation=http://www.atmel.com/
T 300 3750 8 10 0 0 0 0 1
description=2-Megabit DataFlash
T 300 3950 8 10 0 0 0 0 1
numslots=0
P 1100 2600 1100 2300 1 0 0
{
T 1050 2400 5 8 1 1 90 0 1
pinnumber=6
T 1150 2400 5 8 0 1 90 2 1
pinseq=6
T 1100 2250 9 8 1 1 90 6 1
pinlabel=VCC
T 1100 2250 5 8 0 1 90 8 1
pintype=pwr
}
P 2000 1900 1700 1900 1 0 0
{
T 1800 1950 5 8 1 1 0 0 1
pinnumber=4
T 1800 1850 5 8 0 1 0 2 1
pinseq=4
T 1650 1900 9 8 1 1 0 6 1
pinlabel=\_CS\_
T 1650 1900 5 8 0 1 0 8 1
pintype=in
}
P 2000 1500 1700 1500 1 0 0
{
T 1800 1550 5 8 1 1 0 0 1
pinnumber=8
T 1800 1450 5 8 0 1 0 2 1
pinseq=8
T 1650 1500 9 8 1 1 0 6 1
pinlabel=SO
T 1650 1500 5 8 0 1 0 8 1
pintype=tri
}
P 0 700 300 700 1 0 0
{
T 200 750 5 8 1 1 0 6 1
pinnumber=3
T 200 650 5 8 0 1 0 8 1
pinseq=3
T 350 700 9 8 1 1 0 0 1
pinlabel=\_RESET\_
T 350 700 5 8 0 1 0 2 1
pintype=in
}
P 0 1100 300 1100 1 0 0
{
T 200 1150 5 8 1 1 0 6 1
pinnumber=2
T 200 1050 5 8 0 1 0 8 1
pinseq=2
T 350 1100 9 8 1 1 0 0 1
pinlabel=SCK
T 350 1100 5 8 0 1 0 2 1
pintype=in
}
P 0 1500 300 1500 1 0 0
{
T 200 1550 5 8 1 1 0 6 1
pinnumber=1
T 200 1450 5 8 0 1 0 8 1
pinseq=1
T 350 1500 9 8 1 1 0 0 1
pinlabel=SI
T 350 1500 5 8 0 1 0 2 1
pintype=in
}
P 0 1900 300 1900 1 0 0
{
T 200 1950 5 8 1 1 0 6 1
pinnumber=5
T 200 1850 5 8 0 1 0 8 1
pinseq=5
T 350 1900 9 8 1 1 0 0 1
pinlabel=\_WP\_
T 350 1900 5 8 0 1 0 2 1
pintype=in
}
P 1100 0 1100 300 1 0 0
{
T 1050 200 5 8 1 1 90 6 1
pinnumber=7
T 1150 200 5 8 0 1 90 8 1
pinseq=7
T 1100 350 9 8 1 1 90 0 1
pinlabel=GND
T 1100 350 5 8 0 1 90 2 1
pintype=pwr
}
B 300 300 1400 2000 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1
T 300 4200 8 10 0 0 0 0 1
symversion=1.0
T 1000 1350 8 10 1 1 0 4 1
value=AT45DB161E

56
sym/nmos-ub.sym Normal file
View file

@ -0,0 +1,56 @@
v 20111231 2
T 600 900 5 10 0 0 0 0 1
device=NMOS_TRANSISTOR
T 600 1100 5 10 0 0 0 0 1
numslots=0
T 600 1300 5 10 0 0 0 0 1
description=generic N channel MOS transistor (enhancement type)
L 250 600 500 600 3 0 0 0 -1 -1
L 250 200 500 200 3 0 0 0 -1 -1
L 250 400 350 450 3 0 0 0 -1 -1
L 250 400 350 350 3 0 0 0 -1 -1
P 0 200 200 200 1 0 0
{
T 0 300 5 10 0 1 0 0 1
pinnumber=1
T 0 300 9 10 0 1 0 0 1
pinlabel=G
T 0 300 5 10 0 0 0 0 1
pinseq=2
T 0 300 5 10 0 0 0 0 1
pintype=pas
}
P 500 600 500 800 1 0 1
{
T 300 700 5 10 0 1 0 0 1
pinnumber=3
T 300 700 9 10 0 1 0 0 1
pinlabel=D
T 300 700 5 10 0 0 0 0 1
pinseq=1
T 300 700 5 10 0 0 0 0 1
pintype=pas
}
P 500 200 500 0 1 0 1
{
T 300 0 5 10 0 1 0 0 1
pinnumber=2
T 300 0 9 10 0 1 0 0 1
pinlabel=S
T 300 0 5 10 0 0 0 0 1
pinseq=3
T 300 0 5 10 0 0 0 0 1
pintype=pas
}
T 700 600 8 10 1 1 0 0 1
refdes=Q?
L 250 675 250 525 3 0 0 0 -1 -1
L 250 475 250 325 3 0 0 0 -1 -1
L 250 275 250 125 3 0 0 0 -1 -1
L 200 600 200 200 3 0 0 0 -1 -1
L 250 400 400 400 3 0 0 0 -1 -1
L 400 400 400 200 3 0 0 0 -1 -1
T 700 400 8 10 1 1 0 0 1
footprint=UB
T 700 200 8 10 1 1 0 0 1
value=2N7616UB

View file

@ -1,22 +1,22 @@
LEDR R11-1 D1-2
ADJ_RF R2-2 R1-1 U4-4
DRAIN1 Q1-3 R36-1 R34-1
GATE1 Q1-1 C32-2 R32-2
LEDR R11-1 D1-1
BYP C4-2 R13-1 U5-3
ADJ_CC R4-2 R3-1 U5-4
Vbat R9-2 CONN1-5 U4-1 C21-1 C20-1 B1-1 R13-2 U5-1
Vbat CONN1-5 C20-1 B1-1 R10-1 R13-2 U5-1
SCK U2-2 U1-13
MISO U2-8 U1-12
MOSI U2-1 U1-11
Reset J1-1 R12-2 CONN1-2 U1-10
PWM R30-1 U1-9
Vcc U2-5 U2-3 U2-6 J1-3 R12-1 D1-2 C29-1 C26-1 C12-2 C11-2 C4-1 R10-2 R3-2 U5-5 U1-1
TxD CONN1-3 R11-2 U1-7
RxD CONN1-4 U1-6
ADC_B C30-1 R9-1 C3-2 R10-2 U1-5
LED R11-2 U1-3
ADC_V R7-1 C2-2 R8-2 U1-2
RF_EN U4-3 U1-8
ADC_T R5-1 C1-2 R6-2 U1-4
SCLK U1-13 U2-2
MISO U1-12 U2-3
MOSI U1-11 U2-4
MCLK U1-9 U2-5
Vcc J1-3 R12-1 C29-1 C28-1 C27-1 C26-1 C12-2 C11-2 C4-1 R3-2 U5-5 U1-1 U2-6
ANTENNA CONN1-1 U3-4
Vrf R7-2 R5-2 C25-1 C24-1 C23-1 C10-2 C22-1 R1-2 U4-5 U3-3
TxD CONN1-3 U1-7 U3-2
GND J1-2 C30-2 C3-1 R10-1 C2-1 R8-1 C1-1 R6-1 CONN1-6 CONN1-7 CONN1-8 CONN1-9 D1-1 C29-2 C28-2 C27-2 C26-2 C12-1 C11-1 C25-2 C24-2 C23-2 C10-1 C22-2 R2-1 U4-2 R4-1 C21-2 C20-2 B1-2 U5-2 U1-14 U2-1 U3-1 \
BOARD-1
AIN1 C34-2 R36-2 U1-5
SSEL U2-4 U1-2
AIN2 C33-2 R35-2 U1-8
GATE R31-1 R32-1 C31-1 R30-2 U1-4
DRAIN R33-2 R34-2 U1-3 U3-4
DRAIN2 R35-1 R33-1 Q2-3 U3-3
GATE2 R31-2 C30-2 Q2-1 U3-2
GND Q1-2 C34-1 C33-1 U2-7 C30-1 Q2-2 C32-1 C31-2 J1-2 CONN1-6 CONN1-7 CONN1-8 CONN1-9 C29-2 C26-2 C12-1 C11-1 R4-1 C20-2 B1-2 U5-2 U1-14 U3-1 BOARD-1

1154
turbo.pcb

File diff suppressed because it is too large Load diff

1062
turbo.sch

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Before After
Before After