Compare commits
491 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc0e016a6d | ||
|
|
f5c0148665 | ||
|
|
3d78a99758 | ||
|
|
03e97c2d95 | ||
|
|
eaacb928fc | ||
|
|
75ba375b8a | ||
|
|
cbd06f1433 | ||
|
|
1c59a0d30a | ||
|
|
d222ec1024 | ||
|
|
674f584190 | ||
|
|
77fea562c4 | ||
|
|
e0f275cddf | ||
|
|
07a69df62f | ||
|
|
f8750b142f | ||
|
|
3033b03789 | ||
|
|
3506d1e994 | ||
|
|
3c4d14bfa9 | ||
|
|
db97f36631 | ||
|
|
d03cf2b83f | ||
|
|
744c6d114e | ||
|
|
3b9b4e4d6f | ||
|
|
78ba7064a7 | ||
|
|
cc7c99a4a4 | ||
|
|
d3eb148cfa | ||
|
|
00d541b767 | ||
|
|
ecf2596469 | ||
|
|
6a2eaeae40 | ||
|
|
ce7cc798dc | ||
|
|
0ec7eda11b | ||
|
|
fb0f344346 | ||
|
|
afac81c161 | ||
|
|
5c982c90f3 | ||
|
|
381304aa6f | ||
|
|
ce9523fb90 | ||
|
|
06420b0ddf | ||
|
|
c2e1c53356 | ||
|
|
eaeb831107 | ||
|
|
459695e9d7 | ||
|
|
ece1f71c64 | ||
|
|
776d8f9f79 | ||
|
|
61ee63f358 | ||
|
|
6f65ba9214 | ||
|
|
ba837c2641 | ||
|
|
1518032606 | ||
|
|
6469cce2bc | ||
|
|
4c40b50fb5 | ||
|
|
a0f0911a95 | ||
|
|
f658819862 | ||
|
|
8d5a9143bb | ||
|
|
8f8951b4c1 | ||
|
|
6e5bcc69bc | ||
|
|
8d04d3d8fd | ||
|
|
f37fc775e5 | ||
|
|
ead99cf647 | ||
|
|
3982b5030e | ||
|
|
d9fe4b6944 | ||
|
|
387cab27bd | ||
|
|
6dcc44ce2d | ||
|
|
3b4088c23f | ||
|
|
5a85c1667a | ||
|
|
16d2ec3a90 | ||
|
|
4d60567bc6 | ||
|
|
73a1c9d249 | ||
|
|
d851882278 | ||
|
|
3ccecc568d | ||
|
|
cee200e509 | ||
|
|
f1b315e04f | ||
|
|
94bcd9735a | ||
|
|
f662445766 | ||
|
|
008be18f41 | ||
|
|
13812aa1c9 | ||
|
|
2089f19670 | ||
|
|
593741919f | ||
|
|
9d0fcca8a9 | ||
|
|
857eb01bfa | ||
|
|
a100f174f9 | ||
|
|
5dfe4e1eb9 | ||
|
|
b9586bad18 | ||
|
|
f1c2f789b6 | ||
|
|
123719bf92 | ||
|
|
14810d7e80 | ||
|
|
52156d2c41 | ||
|
|
2a6a9eb52f | ||
|
|
bf85c61b48 | ||
|
|
dba488f30a | ||
|
|
d618affd63 | ||
|
|
121c747cc8 | ||
|
|
91b9634198 | ||
|
|
7d17002b33 | ||
|
|
477b3941a6 | ||
|
|
36dd7e7609 | ||
|
|
167a65d826 | ||
|
|
c9cd8cea66 | ||
|
|
dfdcbece53 | ||
|
|
5b62f15c6d | ||
|
|
09e32d1b84 | ||
|
|
68fc6abf74 | ||
|
|
ff56c0fb74 | ||
|
|
36de6118c1 | ||
|
|
7083a33ecd | ||
|
|
7b7f57e01c | ||
|
|
8d0ef49e8f | ||
|
|
7a81bfc4a4 | ||
|
|
0d13834293 | ||
|
|
ac53806e04 | ||
|
|
026b9c336c | ||
|
|
60c77fff06 | ||
|
|
588ecbe868 | ||
|
|
e05c635489 | ||
|
|
f5cc355044 | ||
|
|
78f4c25a14 | ||
|
|
002dc0dfaf | ||
|
|
68d6788413 | ||
|
|
c7c551369f | ||
|
|
0c2919b534 | ||
|
|
d8c75fc608 | ||
|
|
a42cb4fecf | ||
|
|
68ba3d5106 | ||
|
|
80dc1dfcc1 | ||
|
|
931811ab59 | ||
|
|
9d75c3b0ca | ||
|
|
6ab8567d51 | ||
|
|
c727ed3592 | ||
|
|
c72ca983ba | ||
|
|
ec7990796a | ||
|
|
268834e4ae | ||
|
|
b80c488d36 | ||
|
|
f25cb33367 | ||
|
|
4b5f3bec4b | ||
|
|
1f4c6443ef | ||
|
|
0adea120cd | ||
|
|
71bb7acb8e | ||
|
|
4b5109c1b9 | ||
|
|
085817d332 | ||
|
|
f8bd8b97be | ||
|
|
f3da473285 | ||
|
|
f0f4ab7abe | ||
|
|
d8225642fa | ||
|
|
8ebba6d27a | ||
|
|
1051a52755 | ||
|
|
5583b050a0 | ||
|
|
a38082016d | ||
|
|
156e9b7556 | ||
|
|
5e6acf7dbc | ||
|
|
58811b5c44 | ||
|
|
86a762b2b7 | ||
|
|
f886212b44 | ||
|
|
2c272f99a3 | ||
|
|
a9982beacf | ||
|
|
e9d2ec7c41 | ||
|
|
78982ebb51 | ||
|
|
6a63c27542 | ||
|
|
62f77f6bc5 | ||
|
|
88a10fb31c | ||
|
|
e12527b895 | ||
|
|
114c8c5b6d | ||
|
|
d50f4e23e0 | ||
|
|
dca0fc91ea | ||
|
|
981c53682f | ||
|
|
143b7cccf4 | ||
|
|
8ce042bf04 | ||
|
|
85a1e1118f | ||
|
|
51baeb3c2c | ||
|
|
959a20888c | ||
|
|
7bf0ec2fe7 | ||
|
|
c6231a16e3 | ||
|
|
a619aacfc6 | ||
|
|
d469db84ee | ||
|
|
4655a6bfef | ||
|
|
61325c0a14 | ||
|
|
7faa5fe233 | ||
|
|
19090bdd5b | ||
|
|
4a6254fac3 | ||
|
|
649d26e093 | ||
|
|
2ee42997e4 | ||
|
|
17a3e25036 | ||
|
|
c4b8d3ea8b | ||
|
|
118fd21cb8 | ||
|
|
969485c754 | ||
|
|
519e81d0fa | ||
|
|
12ca45a264 | ||
|
|
3ac60b31a2 | ||
|
|
c105ff1c51 | ||
|
|
ae9bc93ccc | ||
|
|
afc665d7c8 | ||
|
|
2d173f51b1 | ||
|
|
b32ba3727b | ||
|
|
5793271308 | ||
|
|
09140a51d5 | ||
|
|
cf662b842b | ||
|
|
0ac518040b | ||
|
|
067fac05a8 | ||
|
|
c7d0358c41 | ||
|
|
c957e4ba86 | ||
|
|
c8dca0a56c | ||
|
|
da3569c490 | ||
|
|
e8356afa26 | ||
|
|
c1bd628ce5 | ||
|
|
849096d5f3 | ||
|
|
e681369a1a | ||
|
|
faa29c8062 | ||
|
|
44f2a2a952 | ||
|
|
292453d306 | ||
|
|
1ae78d08e9 | ||
|
|
1c3cbe9456 | ||
|
|
88f4c38dca | ||
|
|
b7b368a1ed | ||
|
|
d2fafbdd9e | ||
|
|
d981973096 | ||
|
|
b310501970 | ||
|
|
e92ce565dd | ||
|
|
da4f2c5ea0 | ||
|
|
bd48c3a083 | ||
|
|
ba58e0446d | ||
|
|
c03b4921c3 | ||
|
|
c60e4aceed | ||
|
|
156de2e4c2 | ||
|
|
959509496a | ||
|
|
8419e152bb | ||
|
|
1bc3e0a678 | ||
|
|
d093200966 | ||
|
|
72dc21fb1a | ||
|
|
24b8f4ebc1 | ||
|
|
136dccbcdf | ||
|
|
d0e6a0928b | ||
|
|
46846d4297 | ||
|
|
01ee9e16c5 | ||
|
|
38411fd2e7 | ||
|
|
b8094de129 | ||
|
|
73207a12ba | ||
|
|
3af87e1c42 | ||
|
|
d2547ce6b0 | ||
|
|
7e3d7e071f | ||
|
|
d4bed025ed | ||
|
|
a82e949c00 | ||
|
|
ca9756413f | ||
|
|
b9940f0e0d | ||
|
|
f91a49c65d | ||
|
|
4b8ad3fc03 | ||
|
|
e8aabbb40b | ||
|
|
02a5fa2c79 | ||
|
|
944c121a00 | ||
|
|
c9b6662138 | ||
|
|
b85755c0ff | ||
|
|
b6f24e78ac | ||
|
|
fc24aa041b | ||
|
|
c9b44b5bb6 | ||
|
|
2255176228 | ||
|
|
ccaa25eaa5 | ||
|
|
969ee4c8f9 | ||
|
|
c35278e217 | ||
|
|
039d3f0523 | ||
|
|
d56f8407a5 | ||
|
|
142b92b883 | ||
|
|
c292006421 | ||
|
|
5c4cc0d646 | ||
|
|
a361b92184 | ||
|
|
37572bc217 | ||
|
|
233adfe660 | ||
|
|
450c14b286 | ||
|
|
33dfc386c9 | ||
|
|
f331936969 | ||
|
|
8f1d0c2a7c | ||
|
|
02549c9299 | ||
|
|
98f2adbcb5 | ||
|
|
c9d21574d8 | ||
|
|
253517096e | ||
|
|
0fa35254c6 | ||
|
|
fc9fb7473c | ||
|
|
31ca2331d2 | ||
|
|
b5062a07d1 | ||
|
|
4112007314 | ||
|
|
a3162b17d9 | ||
|
|
e177d4f70d | ||
|
|
631b0e6c37 | ||
|
|
a7f339ad1c | ||
|
|
c1c0b2dd38 | ||
|
|
d7a1111955 | ||
|
|
d73340474b | ||
|
|
4f7237de44 | ||
|
|
4096745a58 | ||
|
|
a97e074022 | ||
|
|
917c6aa94a | ||
|
|
05bd6fda7e | ||
|
|
7c78de989d | ||
|
|
35a6d9ba87 | ||
|
|
c63754fc32 | ||
|
|
ccb93068fe | ||
|
|
a6fe355801 | ||
|
|
fe11c3e348 | ||
|
|
56d4422d31 | ||
|
|
e507848a8f | ||
|
|
70599667cb | ||
|
|
1878da228d | ||
|
|
37865d69a2 | ||
|
|
83eba902a3 | ||
|
|
ec805aee2e | ||
|
|
167b18b58f | ||
|
|
4b1a530330 | ||
|
|
19ffaa9ff0 | ||
|
|
0f5167a407 | ||
|
|
563ab5caa5 | ||
|
|
db5b5f121c | ||
|
|
1f417a8441 | ||
|
|
b74b09ea7a | ||
|
|
2cce67ad84 | ||
|
|
2cb935c300 | ||
|
|
b9623c1128 | ||
|
|
7a386cff7d | ||
|
|
839725e3c5 | ||
|
|
8920479f85 | ||
|
|
4c25eae9b4 | ||
|
|
a3a45b5037 | ||
|
|
dc645d76b4 | ||
|
|
daff83ee9a | ||
|
|
9f9e3e61d6 | ||
|
|
1592395036 | ||
|
|
8491b1f86a | ||
|
|
a7b4d70cc6 | ||
|
|
fa193e9618 | ||
|
|
050008f3c8 | ||
|
|
70e53cb080 | ||
|
|
7b03b04c78 | ||
|
|
7a7b98cc31 | ||
|
|
15d5837322 | ||
|
|
f9ebb8b23e | ||
|
|
77b94451de | ||
|
|
ca6245e974 | ||
|
|
1cdddeec30 | ||
|
|
657c908f88 | ||
|
|
d7a0e22d59 | ||
|
|
33b809714f | ||
|
|
b915a2ad7d | ||
|
|
85ed5cef7f | ||
|
|
df42b0d1ac | ||
|
|
98add22891 | ||
|
|
1d81bf5596 | ||
|
|
e4153a536f | ||
|
|
79f31238b0 | ||
|
|
8c712d959d | ||
|
|
c4b1a79db2 | ||
|
|
47f12f107d | ||
|
|
bfad970e4d | ||
|
|
49bdc6fbd1 | ||
|
|
57f279677f | ||
|
|
fff73c7735 | ||
|
|
e44678ceba | ||
|
|
85c0e9c574 | ||
|
|
0c3ec7f9a5 | ||
|
|
74e15b2eb5 | ||
|
|
565861f680 | ||
|
|
7c991399ac | ||
|
|
384c853a39 | ||
|
|
c0380d0280 | ||
|
|
a1c61563a0 | ||
|
|
aa0f1aaeb2 | ||
|
|
03ddd64b93 | ||
|
|
7fc9ba7d3a | ||
|
|
63b6bab5c3 | ||
|
|
eb4eeb6f73 | ||
|
|
6988507998 | ||
|
|
65be6d5146 | ||
|
|
6d05dd07f5 | ||
|
|
7436ec093a | ||
|
|
2b735daae5 | ||
|
|
f8b0c884b0 | ||
|
|
18f4d343f5 | ||
|
|
31e78c90e2 | ||
|
|
3238256b79 | ||
|
|
59b71d5d05 | ||
|
|
4dfa6c6ee4 | ||
|
|
60e488eb17 | ||
|
|
14340ac4df | ||
|
|
efbfc2b1ab | ||
|
|
d4f09bc20d | ||
|
|
d67f962a38 | ||
|
|
6de85d02ae | ||
|
|
f28eb902df | ||
|
|
9702d522a4 | ||
|
|
4f710b0470 | ||
|
|
5f29787dc7 | ||
|
|
1fbb36fa87 | ||
|
|
3cafcc2bc7 | ||
|
|
8d92c898ee | ||
|
|
9bf73cd72d | ||
|
|
f97cf5c3b6 | ||
|
|
5ff2d5aee6 | ||
|
|
1f474742eb | ||
|
|
0041a0079d | ||
|
|
9bb8b0c622 | ||
|
|
df6d3107f2 | ||
|
|
cbdc54843d | ||
|
|
d2027cb4a9 | ||
|
|
e60779bfe1 | ||
|
|
f66b1ac450 | ||
|
|
ff6a96665a | ||
|
|
4388a294a4 | ||
|
|
d21b9280f0 | ||
|
|
92649332ce | ||
|
|
be91c1229f | ||
|
|
168cb95bd5 | ||
|
|
1d796a4e24 | ||
|
|
8e6d5efdac | ||
|
|
0f2478b62f | ||
|
|
e5d7e593ec | ||
|
|
69b927bfe9 | ||
|
|
944d176856 | ||
|
|
cdd5a772e8 | ||
|
|
0a3c23bcf6 | ||
|
|
cb286ede9d | ||
|
|
16e3dbb18c | ||
|
|
a38437f378 | ||
|
|
8d6ecd9af8 | ||
|
|
342a7096ea | ||
|
|
60a4bda9d4 | ||
|
|
d5fc594317 | ||
|
|
64407dc5d2 | ||
|
|
b0329465ec | ||
|
|
0f70b420f2 | ||
|
|
21c4dea0e6 | ||
|
|
bcaf818c0e | ||
|
|
37bac916e7 | ||
|
|
affdbbf9ca | ||
|
|
4fcf0ff2ac | ||
|
|
c19af4fb2b | ||
|
|
f53897758d | ||
|
|
54002c4391 | ||
|
|
6a53eaefc0 | ||
|
|
4bc114336c | ||
|
|
47f1d377f5 | ||
|
|
566699f68a | ||
|
|
29ba92a551 | ||
|
|
38e9484f9f | ||
|
|
fec12030a9 | ||
|
|
bdfdf7ef55 | ||
|
|
9f65ae72c3 | ||
|
|
3434ea540c | ||
|
|
29131c873a | ||
|
|
ab1eb70d1c | ||
|
|
71256f9456 | ||
|
|
6179839215 | ||
|
|
0ca96e543c | ||
|
|
acb0b8f599 | ||
|
|
20d0936fa2 | ||
|
|
c24b7a7ef9 | ||
|
|
074495a13a | ||
|
|
e14d86d8b8 | ||
|
|
528c29c01c | ||
|
|
1bb7a22115 | ||
|
|
19ed67331d | ||
|
|
667b72870f | ||
|
|
4194ebf9df | ||
|
|
5beceaae5c | ||
|
|
9c1bf1387c | ||
|
|
fc6a31eac8 | ||
|
|
064e8bdd84 | ||
|
|
262ccbcf30 | ||
|
|
7567885115 | ||
|
|
ed715ec437 | ||
|
|
9a44a20a9d | ||
|
|
f335045273 | ||
|
|
4ea091339e | ||
|
|
8378b7345b | ||
|
|
4a71c7a2bd | ||
|
|
b2885a53cb | ||
|
|
46b6b4037d | ||
|
|
93d3a6e1d1 | ||
|
|
eebaeeff96 | ||
|
|
3a7a77d49e | ||
|
|
c87c090264 | ||
|
|
b26922978a | ||
|
|
5a5bd2596a | ||
|
|
38ca051381 | ||
|
|
91056809dd | ||
|
|
f75430e95f | ||
|
|
8e797e6830 | ||
|
|
73c4be3fd3 | ||
|
|
c552ba06b4 | ||
|
|
6bd5f4e44e | ||
|
|
6138d18f4b | ||
|
|
d028f42e99 | ||
|
|
860fc3e91d | ||
|
|
2e03d84755 | ||
|
|
f2b406fc5e | ||
|
|
f46bc0ef04 | ||
|
|
800d53db6a | ||
|
|
a9444d3399 | ||
|
|
4a16053c00 | ||
|
|
d0c61f0f76 | ||
|
|
451ffd567d | ||
|
|
f3a49604f1 |
32
Makefile
@@ -1,6 +1,6 @@
|
||||
# Klipper build system
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
@@ -22,7 +22,7 @@ OBJCOPY=$(CROSS_PREFIX)objcopy
|
||||
OBJDUMP=$(CROSS_PREFIX)objdump
|
||||
STRIP=$(CROSS_PREFIX)strip
|
||||
CPP=cpp
|
||||
PYTHON=python
|
||||
PYTHON=python2
|
||||
|
||||
# Source files
|
||||
src-y =
|
||||
@@ -32,18 +32,15 @@ dirs-y = src
|
||||
cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \
|
||||
; then echo "$(2)"; else echo "$(3)"; fi ;)
|
||||
|
||||
CFLAGS-y := -I$(OUT) -Isrc -I$(OUT)board-generic/ -O2 -MD -g \
|
||||
CFLAGS := -I$(OUT) -Isrc -I$(OUT)board-generic/ -O2 -MD -g \
|
||||
-Wall -Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
|
||||
-ffunction-sections -fdata-sections
|
||||
CFLAGS-y += -flto -fwhole-program -fno-use-linker-plugin
|
||||
CFLAGS += -flto -fwhole-program -fno-use-linker-plugin
|
||||
|
||||
LDFLAGS-y := -Wl,--gc-sections -fno-whole-program
|
||||
CFLAGS_klipper.elf = $(CFLAGS) -Wl,--gc-sections
|
||||
|
||||
CPPFLAGS = -I$(OUT) -P -MD -MT $@
|
||||
|
||||
CFLAGS = $(CFLAGS-y)
|
||||
LDFLAGS = $(LDFLAGS-y)
|
||||
|
||||
# Default targets
|
||||
target-y := $(OUT)klipper.elf
|
||||
|
||||
@@ -77,23 +74,18 @@ $(OUT)board-link: $(KCONFIG_CONFIG)
|
||||
$(Q)mkdir -p $(OUT)board-generic
|
||||
$(Q)ln -Tsf $(PWD)/src/generic $(OUT)board-generic/board
|
||||
|
||||
$(OUT)declfunc.lds: src/declfunc.lds.S
|
||||
@echo " Precompiling $@"
|
||||
$(Q)$(CPP) $(CPPFLAGS) -D__ASSEMBLY__ $< -o $@
|
||||
$(OUT)%.o.ctr: $(OUT)%.o
|
||||
$(Q)$(OBJCOPY) -j '.compile_time_request' -O binary $^ $@
|
||||
|
||||
$(OUT)klipper.o: $(patsubst %.c, $(OUT)src/%.o,$(src-y)) $(OUT)declfunc.lds
|
||||
@echo " Linking $@"
|
||||
$(Q)$(CC) $(CFLAGS) -Wl,-r -Wl,-T,$(OUT)declfunc.lds -nostdlib $(patsubst %.c, $(OUT)src/%.o,$(src-y)) -o $@
|
||||
|
||||
$(OUT)compile_time_request.o: $(OUT)klipper.o ./scripts/buildcommands.py
|
||||
$(OUT)compile_time_request.o: $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) ./scripts/buildcommands.py
|
||||
@echo " Building $@"
|
||||
$(Q)$(OBJCOPY) -j '.compile_time_request' -O binary $< $(OUT)klipper.o.compile_time_request
|
||||
$(Q)$(PYTHON) ./scripts/buildcommands.py -d $(OUT)klipper.dict $(OUT)klipper.o.compile_time_request $(OUT)compile_time_request.c
|
||||
$(Q)cat $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) > $(OUT)klipper.compile_time_request
|
||||
$(Q)$(PYTHON) ./scripts/buildcommands.py -d $(OUT)klipper.dict $(OUT)klipper.compile_time_request $(OUT)compile_time_request.c
|
||||
$(Q)$(CC) $(CFLAGS) -c $(OUT)compile_time_request.c -o $@
|
||||
|
||||
$(OUT)klipper.elf: $(OUT)klipper.o $(OUT)compile_time_request.o
|
||||
$(OUT)klipper.elf: $(patsubst %.c, $(OUT)src/%.o,$(src-y)) $(OUT)compile_time_request.o
|
||||
@echo " Linking $@"
|
||||
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
|
||||
$(Q)$(CC) $(CFLAGS_klipper.elf) $^ -o $@
|
||||
|
||||
################ Kconfig rules
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Support for internal testing with the "simulavr" program. To use
|
||||
# this config, compile the firmware for an AVR atmega644p, disable the
|
||||
# AVR watchdog timer, set the MCU frequency to 20000000, and set the
|
||||
# serial baud rate to 115200.
|
||||
# serial baud rate to 250000.
|
||||
|
||||
[stepper_x]
|
||||
# Pins: PA5, PA4, PA1
|
||||
@@ -42,11 +42,11 @@ step_pin: ar19
|
||||
dir_pin: ar18
|
||||
enable_pin: ar25
|
||||
step_distance: .004242
|
||||
max_velocity: 200000
|
||||
max_accel: 3000
|
||||
nozzle_diameter: 0.500
|
||||
filament_diameter: 3.500
|
||||
heater_pin: ar4
|
||||
thermistor_pin: analog1
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog1
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
@@ -57,19 +57,17 @@ max_temp: 210
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: ar3
|
||||
thermistor_pin: analog0
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog0
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 110
|
||||
|
||||
[fan]
|
||||
pin: ar14
|
||||
hard_pwm: 1
|
||||
|
||||
[mcu]
|
||||
serial: /tmp/pseudoserial
|
||||
baud: 250000
|
||||
pin_map: arduino
|
||||
|
||||
[printer]
|
||||
|
||||
81
config/example-corexy.cfg
Normal file
@@ -0,0 +1,81 @@
|
||||
# This file serves as documentation for config parameters of corexy
|
||||
# style printers. One may copy and edit this file to configure a new
|
||||
# corexy printer. Only parameters unique to corexy printers are
|
||||
# described here - see the "example.cfg" file for description of
|
||||
# common config parameters.
|
||||
|
||||
# DO NOT COPY THIS FILE WITHOUT CAREFULLY READING AND UPDATING IT
|
||||
# FIRST. Incorrectly configured parameters may cause damage.
|
||||
|
||||
# The stepper_x section is used to describe the X axis as well as the
|
||||
# stepper controlling the X+Y movement.
|
||||
[stepper_x]
|
||||
step_pin: ar54
|
||||
dir_pin: ar55
|
||||
enable_pin: !ar38
|
||||
step_distance: .01
|
||||
endstop_pin: ^ar3
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
# The stepper_y section is used to describe the Y axis as well as the
|
||||
# stepper controlling the X-Y movement.
|
||||
[stepper_y]
|
||||
step_pin: ar60
|
||||
dir_pin: ar61
|
||||
enable_pin: !ar56
|
||||
step_distance: .01
|
||||
endstop_pin: ^ar14
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: ar46
|
||||
dir_pin: ar48
|
||||
enable_pin: !ar62
|
||||
step_distance: .01
|
||||
endstop_pin: ^ar18
|
||||
position_endstop: 0.5
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: ar26
|
||||
dir_pin: ar28
|
||||
enable_pin: !ar24
|
||||
step_distance: .0022
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: ar10
|
||||
sensor_type: ATC Semitec 104GT-2
|
||||
sensor_pin: analog13
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: ar8
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog14
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: ar9
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
pin_map: arduino
|
||||
|
||||
[printer]
|
||||
kinematics: corexy
|
||||
# This option must be "corexy" for corexy printers.
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 25
|
||||
max_z_accel: 30
|
||||
@@ -9,20 +9,22 @@
|
||||
|
||||
# The stepper_a section describes the stepper controlling the front
|
||||
# left tower (at 210 degrees). This section also controls the homing
|
||||
# parameters (homing_speed, homing_retract_dist) and maximum tower
|
||||
# length (position_max) for all towers.
|
||||
# parameters (homing_speed, homing_retract_dist) for all towers.
|
||||
[stepper_a]
|
||||
step_pin: ar54
|
||||
dir_pin: ar55
|
||||
enable_pin: !ar38
|
||||
step_distance: .01
|
||||
endstop_pin: ^ar2
|
||||
homing_speed: 50.0
|
||||
position_endstop: 297.05
|
||||
position_max: 297.55
|
||||
#angle:
|
||||
# This option specifies the angle (in degrees) that the tower is
|
||||
# at. The default is 210 for stepper_a, 330 for stepper_b, and 90
|
||||
# for stepper_c.
|
||||
homing_speed: 50
|
||||
|
||||
# The stepper_b section describes the stepper controlling the front
|
||||
# right tower (at 330 degrees)
|
||||
# right tower (at 330 degrees).
|
||||
[stepper_b]
|
||||
step_pin: ar60
|
||||
dir_pin: ar61
|
||||
@@ -32,7 +34,7 @@ endstop_pin: ^ar15
|
||||
position_endstop: 297.05
|
||||
|
||||
# The stepper_c section describes the stepper controlling the rear
|
||||
# tower (at 90 degrees)
|
||||
# tower (at 90 degrees).
|
||||
[stepper_c]
|
||||
step_pin: ar46
|
||||
dir_pin: ar48
|
||||
@@ -46,11 +48,11 @@ step_pin: ar26
|
||||
dir_pin: ar28
|
||||
enable_pin: !ar24
|
||||
step_distance: .0022
|
||||
max_velocity: 200
|
||||
max_accel: 3000
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: ar10
|
||||
thermistor_pin: analog13
|
||||
thermistor_type: ATC Semitec 104GT-2
|
||||
sensor_type: ATC Semitec 104GT-2
|
||||
sensor_pin: analog13
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
@@ -60,36 +62,40 @@ max_temp: 250
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: ar8
|
||||
thermistor_pin: analog14
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog14
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
# Extruder print fan (omit section if fan not present)
|
||||
# Print cooling fan (omit section if fan not present).
|
||||
#[fan]
|
||||
#pin: ar9
|
||||
#hard_pwm: 1
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
baud: 250000
|
||||
pin_map: arduino
|
||||
|
||||
[printer]
|
||||
kinematics: delta
|
||||
# This option must be "delta" for linear delta printers
|
||||
# This option must be "delta" for linear delta printers.
|
||||
max_velocity: 300
|
||||
# Maximum velocity (in mm/s) of the toolhead relative to the
|
||||
# print. This parameter must be specified.
|
||||
max_accel: 3000
|
||||
max_z_velocity: 200
|
||||
# Maximum acceleration (in mm/s^2) of the toolhead relative to the
|
||||
# print. This parameter must be specified.
|
||||
max_z_velocity: 150
|
||||
# For delta printers this limits the maximum velocity (in mm/s) of
|
||||
# moves with z axis movement. This setting can be used to reduce the
|
||||
# maximum speed of up/down moves (which require a higher step rate
|
||||
# than other moves on a delta printer).
|
||||
# than other moves on a delta printer). The default is to use
|
||||
# max_velocity for max_z_velocity.
|
||||
delta_arm_length: 333.0
|
||||
# Length (in mm) of the diagonal rods that connect the linear axes
|
||||
# to the print head
|
||||
# to the print head. This parameter must be provided.
|
||||
delta_radius: 174.75
|
||||
# Radius (in mm) of the horizontal circle formed by the three linear
|
||||
# axis towers. This parameter may also be calculated as:
|
||||
# delta_radius = smooth_rod_offset - effector_offset - carriage_offset
|
||||
# This parameter must be provided.
|
||||
|
||||
153
config/example-extras.cfg
Normal file
@@ -0,0 +1,153 @@
|
||||
# This file serves as documentation for config parameters of
|
||||
# additional devices that may be configured on a printer. The snippets
|
||||
# in this file may be copied into the main printer.cfg file. See the
|
||||
# "example.cfg" file for description of common config parameters.
|
||||
|
||||
|
||||
# In a multi-extruder printer add an additional extruder section for
|
||||
# each additional extruder. The additional extruder sections should be
|
||||
# named "extruder1", "extruder2", "extruder3", and so on. See the
|
||||
# "extruder" section in example.cfg for a description of available
|
||||
# parameters.
|
||||
#[extruder1]
|
||||
#step_pin: ar36
|
||||
#dir_pin: ar34
|
||||
#...
|
||||
#deactivate_gcode:
|
||||
# A list of G-Code commands (one per line) to execute on a G-Code
|
||||
# tool change command (eg, "T1") that deactivates this extruder and
|
||||
# activates some other extruder. It only makes sense to define this
|
||||
# section on multi-extruder printers. The default is to not run any
|
||||
# special G-Code commands on deactivation.
|
||||
#activate_gcode:
|
||||
# A list of G-Code commands (one per line) to execute on a G-Code
|
||||
# tool change command (eg, "T0") that activates this extruder. It
|
||||
# only makes sense to define this section on multi-extruder
|
||||
# printers. The default is to not run any special G-Code commands on
|
||||
# activation.
|
||||
|
||||
|
||||
# Heater cooling fans (one may define any number of sections with a
|
||||
# "heater_fan" prefix). A "heater fan" is a fan that will be enabled
|
||||
# whenever its associated heater is active. In the event of an MCU
|
||||
# software error the heater_fan will be set to its max_power.
|
||||
#[heater_fan my_nozzle_fan]
|
||||
# See the "fan" section for fan configuration parameters.
|
||||
#pin: ar4
|
||||
# The remaining variables are specific to heater_fan.
|
||||
#heater: extruder
|
||||
# Name of the config section defining the heater that this fan is
|
||||
# associated with. The default is "extruder".
|
||||
#heater_temp: 50.0
|
||||
# A temperature (in Celsius) that the heater must drop below before
|
||||
# the fan is disabled. The default is 50 Celsius.
|
||||
#fan_speed:
|
||||
# The fan speed (expressed as a value from 0.0 to 1.0) that the fan
|
||||
# will be set to when its associated heater is enabled. The default
|
||||
# is max_power.
|
||||
|
||||
|
||||
# Additional micro-controllers (one may define any number of sections
|
||||
# with an "mcu" prefix). Additional micro-controllers introduce
|
||||
# additional pins that may be configured as heaters, steppers, fans,
|
||||
# etc.. For example, if an "[mcu extra_mcu]" section is introduced,
|
||||
# then pins such as "extra_mcu:ar9" may then be used elsewhere in the
|
||||
# config (where "ar9" is a hardware pin name or alias name on the
|
||||
# given mcu).
|
||||
#[mcu my_extra_mcu]
|
||||
# See the "mcu" section in example.cfg for configuration parameters.
|
||||
|
||||
|
||||
# Servos (one may define any number of sections with a "servo"
|
||||
# prefix). The servos may be controlled using the SET_SERVO g-code
|
||||
# command. For example: SET_SERVO SERVO=my_servo ANGLE=180
|
||||
#[servo my_servo]
|
||||
#pin: ar7
|
||||
# PWM output pin controlling the servo. This parameter must be
|
||||
# provided.
|
||||
#maximum_servo_angle: 180
|
||||
# The maximum angle (in degrees) that this servo can be set to. The
|
||||
# default is 180 degrees.
|
||||
#minimum_pulse_width: 0.001
|
||||
# The minimum pulse width time (in seconds). This should correspond
|
||||
# with an angle of 0 degrees. The default is 0.001 seconds.
|
||||
#maximum_pulse_width: 0.002
|
||||
# The maximum pulse width time (in seconds). This should correspond
|
||||
# with an angle of maximum_servo_angle. The default is 0.002
|
||||
# seconds.
|
||||
|
||||
|
||||
# Statically configured digital output pins (one may define any number
|
||||
# of sections with a "static_digital_output" prefix). Pins configured
|
||||
# here will be setup as a GPIO output during MCU configuration.
|
||||
#[static_digital_output my_output_pins]
|
||||
#pins:
|
||||
# A comma separated list of pins to be set as GPIO output pins. The
|
||||
# pin will be set to a high level unless the pin name is prefaced
|
||||
# with "!". This parameter must be provided.
|
||||
|
||||
|
||||
# Statically configured PWM output pins (one may define any number of
|
||||
# sections with a "static_pwm_output" prefix). Pins configured here
|
||||
# will be setup as PWM outputs during MCU configuration.
|
||||
#[static_pwm_output my_output_pwm]
|
||||
#pin:
|
||||
# The pin to configure as PWM output. This parameter must be
|
||||
# provided.
|
||||
#value:
|
||||
# The value to statically set the PWM output to. This is typically
|
||||
# set to a number between 0.0 and 1.0 with 1.0 being full on and 0.0
|
||||
# being full off. However, the range may be changed with the 'scale'
|
||||
# parameter (see below). This parameter must be provided.
|
||||
#hard_pwm:
|
||||
# Set this value to force hardware PWM instead of software PWM. Set
|
||||
# to 1 to force a hardware PWM at the fastest rate; set to a higher
|
||||
# number to force hardware PWM with the given cycle time in clock
|
||||
# ticks. The default is to use software PWM.
|
||||
#cycle_time: 0.100
|
||||
# The amount of time (in seconds) per PWM cycle when using software
|
||||
# based PWM. The default is 0.100 seconds.
|
||||
#scale:
|
||||
# This parameter can be used to alter how the 'value' parameter is
|
||||
# interpreted. If provided, then the 'value' parameter should be
|
||||
# between 0.0 and 'scale'. This may be useful when configuring a PWM
|
||||
# pin that controls a stepper voltage reference. The 'scale' can be
|
||||
# set to the equivalent stepper amperage if the PWM were fully
|
||||
# enabled, and then the 'value' parameter can be specified using the
|
||||
# desired amperage for the stepper. The default is to not scale the
|
||||
# 'value' parameter.
|
||||
|
||||
|
||||
# Statically configured AD5206 digipots connected via SPI bus (one may
|
||||
# define any number of sections with an "ad5206" prefix).
|
||||
#[ad5206 my_digipot]
|
||||
#enable_pin:
|
||||
# The pin corresponding to the AD5206 chip select line. This pin
|
||||
# will be set to low at the start of SPI messages and raised to high
|
||||
# after the message completes. This parameter must be provided.
|
||||
#channel_1:
|
||||
#channel_2:
|
||||
#channel_3:
|
||||
#channel_4:
|
||||
#channel_5:
|
||||
#channel_6:
|
||||
# The value to statically set the given AD5206 channel to. This is
|
||||
# typically set to a number between 0.0 and 1.0 with 1.0 being the
|
||||
# highest resistance and 0.0 being the lowest resistance. However,
|
||||
# the range may be changed with the 'scale' parameter (see
|
||||
# below). If a channel is not specified then it is left
|
||||
# unconfigured.
|
||||
#scale:
|
||||
# This parameter can be used to alter how the 'channel_x' parameters
|
||||
# are interpreted. If provided, then the 'channel_x' parameters
|
||||
# should be between 0.0 and 'scale'. This may be useful when the
|
||||
# AD5206 is used to set stepper voltage references. The 'scale' can
|
||||
# be set to the equivalent stepper amperage if the AD5206 were at
|
||||
# its highest resistance, and then the 'channel_x' parameters can be
|
||||
# specified using the desired amperage value for the stepper. The
|
||||
# default is to not scale the 'channel_x' parameters.
|
||||
|
||||
|
||||
# Replicape support - see the generic-replicape.cfg file for further
|
||||
# details.
|
||||
#[replicape]
|
||||
@@ -1,6 +1,9 @@
|
||||
# This file serves as documentation for config parameters. One may
|
||||
# copy and edit this file to configure a new cartesian style
|
||||
# printer. For delta style printers, see the "example-delta.cfg" file.
|
||||
# printer. For delta style printers, see the "example-delta.cfg"
|
||||
# file. For corexy/h-bot style printers, see the "example-corexy.cfg"
|
||||
# file. Only common config sections are described here - see the
|
||||
# "example-extras.cfg" file for configuring less common devices.
|
||||
|
||||
# DO NOT COPY THIS FILE WITHOUT CAREFULLY READING AND UPDATING IT
|
||||
# FIRST. Incorrectly configured parameters may cause damage.
|
||||
@@ -16,30 +19,50 @@
|
||||
|
||||
|
||||
# The stepper_x section is used to describe the stepper controlling
|
||||
# the X axis in a cartesian robot
|
||||
# the X axis in a cartesian robot.
|
||||
[stepper_x]
|
||||
step_pin: ar29
|
||||
# Step GPIO pin (triggered high)
|
||||
dir_pin: !ar28
|
||||
# Direction GPIO pin (high indicates positive direction)
|
||||
enable_pin: !ar25
|
||||
# Enable pin (default is enable high; use ! to indicate enable low)
|
||||
step_pin: ar54
|
||||
# Step GPIO pin (triggered high). This parameter must be provided.
|
||||
dir_pin: ar55
|
||||
# Direction GPIO pin (high indicates positive direction). This
|
||||
# parameter must be provided.
|
||||
enable_pin: !ar38
|
||||
# Enable pin (default is enable high; use ! to indicate enable
|
||||
# low). If this parameter is not provided then the stepper motor
|
||||
# driver must always be enabled.
|
||||
step_distance: .0225
|
||||
# Distance in mm that each step causes the axis to travel
|
||||
endstop_pin: ^ar0
|
||||
# Endstop switch detection pin
|
||||
homing_speed: 50.0
|
||||
# Maximum velocity (in mm/s) of the stepper when homing
|
||||
homing_retract_dist: 5.0
|
||||
# Distance to backoff (in mm) before homing a second time during homing
|
||||
homing_positive_dir: False
|
||||
# If true, homes in a positive direction (away from zero)
|
||||
homing_stepper_phases: 0
|
||||
# Distance in mm that each step causes the axis to travel. This
|
||||
# parameter must be provided.
|
||||
endstop_pin: ^ar3
|
||||
# Endstop switch detection pin. This parameter must be provided for
|
||||
# the X, Y, and Z steppers on cartesian style printers.
|
||||
#position_min: 0
|
||||
# Minimum valid distance (in mm) the user may command the stepper to
|
||||
# move to. The default is 0mm.
|
||||
position_endstop: 0
|
||||
# Location of the endstop (in mm). This parameter must be provided
|
||||
# for the X, Y, and Z steppers on cartesian style printers.
|
||||
position_max: 200
|
||||
# Maximum valid distance (in mm) the user may command the stepper to
|
||||
# move to. This parameter must be provided for the X, Y, and Z
|
||||
# steppers on cartesian style printers.
|
||||
#homing_speed: 5.0
|
||||
# Maximum velocity (in mm/s) of the stepper when homing. The default
|
||||
# is 5mm/s.
|
||||
#homing_retract_dist: 5.0
|
||||
# Distance to backoff (in mm) before homing a second time during
|
||||
# homing. The default is 5mm.
|
||||
#homing_positive_dir:
|
||||
# If true, homing will cause the stepper to move in a positive
|
||||
# direction (away from zero); if false, home towards zero. The
|
||||
# default is true if position_endstop is near position_max and false
|
||||
# if near position_min.
|
||||
#homing_stepper_phases: 0
|
||||
# One may optionally set this to the number of phases of the stepper
|
||||
# motor driver (which is the number of micro-steps multiplied by
|
||||
# four). When set, the phase of the stepper driver will be used
|
||||
# during homing to improve the accuracy of the endstop switch.
|
||||
homing_endstop_accuracy: 0.200
|
||||
#homing_endstop_accuracy: 0.200
|
||||
# Sets the expected accuracy (in mm) of the endstop. This represents
|
||||
# the maximum error distance the endstop may trigger (eg, if an
|
||||
# endstop may occasionally trigger 100um early or up to 100um late
|
||||
@@ -54,38 +77,28 @@ homing_endstop_accuracy: 0.200
|
||||
# reset. If this is not set, but homing_stepper_phases is set, then
|
||||
# the stepper phase will be detected on the first home and that
|
||||
# phase will be used on all subsequent homes.
|
||||
position_min: -0.25
|
||||
# Minimum valid distance (in mm) the user may command the stepper to
|
||||
# move to
|
||||
position_endstop: 0
|
||||
# Location of the endstop (in mm)
|
||||
position_max: 200
|
||||
# Maximum valid distance (in mm) the user may command the stepper to
|
||||
# move to
|
||||
|
||||
# The stepper_y section is used to describe the stepper controlling
|
||||
# the Y axis in a cartesian robot. It has the same settings as the
|
||||
# stepper_x section
|
||||
# stepper_x section.
|
||||
[stepper_y]
|
||||
step_pin: ar27
|
||||
dir_pin: ar26
|
||||
enable_pin: !ar25
|
||||
step_pin: ar60
|
||||
dir_pin: !ar61
|
||||
enable_pin: !ar56
|
||||
step_distance: .0225
|
||||
endstop_pin: ^ar1
|
||||
position_min: -0.25
|
||||
endstop_pin: ^ar14
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
|
||||
# The stepper_z section is used to describe the stepper controlling
|
||||
# the Z axis in a cartesian robot. It has the same settings as the
|
||||
# stepper_x section
|
||||
# stepper_x section.
|
||||
[stepper_z]
|
||||
step_pin: ar23
|
||||
dir_pin: !ar22
|
||||
enable_pin: !ar25
|
||||
step_pin: ar46
|
||||
dir_pin: ar48
|
||||
enable_pin: !ar62
|
||||
step_distance: .005
|
||||
endstop_pin: ^ar2
|
||||
position_min: 0.1
|
||||
endstop_pin: ^ar18
|
||||
position_endstop: 0.5
|
||||
position_max: 200
|
||||
|
||||
@@ -93,119 +106,195 @@ position_max: 200
|
||||
# controlling the printer extruder and the heater parameters for the
|
||||
# nozzle. The stepper configuration has the same settings as the
|
||||
# stepper_x section and the heater configuration has the same settings
|
||||
# as the heater_bed section
|
||||
# as the heater_bed section (described below).
|
||||
[extruder]
|
||||
step_pin: ar19
|
||||
dir_pin: ar18
|
||||
enable_pin: !ar25
|
||||
step_pin: ar26
|
||||
dir_pin: ar28
|
||||
enable_pin: !ar24
|
||||
step_distance: .004242
|
||||
max_velocity: 200000
|
||||
nozzle_diameter: 0.500
|
||||
# Diameter of the nozzle orifice (in mm). This parameter must be
|
||||
# provided.
|
||||
filament_diameter: 3.500
|
||||
# Diameter of the raw filament (in mm) as it enters the
|
||||
# extruder. This parameter must be provided.
|
||||
#max_extrude_cross_section:
|
||||
# Maximum area of the cross section of an extrusion line (in
|
||||
# mm^2). This setting prevents excessive amounts of extrusion during
|
||||
# relatively small XY moves. If a move requests an extrusion rate
|
||||
# that would exceed this value it will cause an error to be
|
||||
# returned. The default is: 4.0 * nozzle_diameter^2
|
||||
#max_extrude_only_distance: 50.0
|
||||
# Maximum length (in mm of raw filament) that an extrude only move
|
||||
# may be. If an extrude only move requests a distance greater than
|
||||
# this value it will cause an error to be returned. The default is
|
||||
# 50mm.
|
||||
#max_extrude_only_velocity:
|
||||
# Maximum velocity (in mm/s) of the extruder motor for extrude only
|
||||
# moves.
|
||||
max_accel: 3000
|
||||
# moves. If this is not specified then it is calculated to match the
|
||||
# limit an XY printing move with a max_extrude_cross_section
|
||||
# extrusion would have.
|
||||
#max_extrude_only_accel:
|
||||
# Maximum acceleration (in mm/s^2) of the extruder motor for extrude
|
||||
# only moves.
|
||||
#
|
||||
# The remaining variables describe the extruder heater
|
||||
pressure_advance: 0.0
|
||||
# only moves. If this is not specified then it is calculated to
|
||||
# match the limit an XY printing move with a
|
||||
# max_extrude_cross_section extrusion would have.
|
||||
#pressure_advance: 0.0
|
||||
# The amount of raw filament to push into the extruder during
|
||||
# extruder acceleration. An equal amount of filament is retracted
|
||||
# during deceleration. It is measured in millimeters per
|
||||
# millimeter/second
|
||||
heater_pin: ar4
|
||||
# PWM output pin controlling the heater
|
||||
thermistor_pin: analog1
|
||||
# Analog input pin connected to thermistor
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
# Type of thermistor (see klippy/heater.py for available types)
|
||||
pullup_resistor: 4700
|
||||
# The resistance (in ohms) of the pullup attached to the thermistor
|
||||
# millimeter/second. The default is 0, which disables pressure
|
||||
# advance.
|
||||
#pressure_advance_lookahead_time: 0.010
|
||||
# A time (in seconds) to "look ahead" at future extrusion moves when
|
||||
# calculating pressure advance. This is used to reduce the
|
||||
# application of pressure advance during cornering moves that would
|
||||
# otherwise cause retraction followed immediately by pressure
|
||||
# buildup. This setting only applies if pressure_advance is
|
||||
# non-zero. The default is 0.010 (10 milliseconds).
|
||||
#
|
||||
# The remaining variables describe the extruder heater.
|
||||
heater_pin: ar10
|
||||
# PWM output pin controlling the heater. This parameter must be
|
||||
# provided.
|
||||
#max_power: 1.0
|
||||
# The maximum power (expressed as a value from 0.0 to 1.0) that the
|
||||
# heater_pin may be set to. The value 1.0 allows the pin to be set
|
||||
# fully enabled for extended periods, while a value of 0.5 would
|
||||
# allow the pin to be enabled for no more than half the time. This
|
||||
# setting may be used to limit the total power output (over extended
|
||||
# periods) to the heater. The default is 1.0.
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
# Type of sensor - this may be "EPCOS 100K B57560G104F", "ATC
|
||||
# Semitec 104GT-2", "NTC 100K beta 3950", or "AD595". This parameter
|
||||
# must be provided.
|
||||
sensor_pin: analog13
|
||||
# Analog input pin connected to the sensor. This parameter must be
|
||||
# provided.
|
||||
#pullup_resistor: 4700
|
||||
# The resistance (in ohms) of the pullup attached to the
|
||||
# thermistor. This parameter is only valid when the sensor is a
|
||||
# thermistor. The default is 4700 ohms.
|
||||
#adc_voltage: 5.0
|
||||
# The ADC comparison voltage. This parameter is only valid when the
|
||||
# sensor is an AD595. The default is 5 volts.
|
||||
control: pid
|
||||
# Control algorithm (either pid or watermark)
|
||||
# Control algorithm (either pid or watermark). This parameter must
|
||||
# be provided.
|
||||
pid_Kp: 22.2
|
||||
# Kp is the "proportional" constant for the pid
|
||||
# Kp is the "proportional" constant for the pid. This parameter must
|
||||
# be provided for PID heaters.
|
||||
pid_Ki: 1.08
|
||||
# Ki is the "integral" constant for the pid
|
||||
# Ki is the "integral" constant for the pid. This parameter must be
|
||||
# provided for PID heaters.
|
||||
pid_Kd: 114
|
||||
# Kd is the "derivative" constant for the pid
|
||||
pid_deriv_time: 2.0
|
||||
# Kd is the "derivative" constant for the pid. This parameter must
|
||||
# be provided for PID heaters.
|
||||
#pid_deriv_time: 2.0
|
||||
# A time value (in seconds) over which the derivative in the pid
|
||||
# will be smoothed to reduce the impact of measurement noise
|
||||
pid_integral_max: 255
|
||||
# The maximum "windup" the integral term may accumulate
|
||||
min_extrude_temp: 170
|
||||
# will be smoothed to reduce the impact of measurement noise. The
|
||||
# default is 2 seconds.
|
||||
#pid_integral_max:
|
||||
# The maximum "windup" the integral term may accumulate. The default
|
||||
# is to use the same value as max_power.
|
||||
#min_extrude_temp: 170
|
||||
# The minimum temperature (in Celsius) at which extruder move
|
||||
# commands may be issued
|
||||
# commands may be issued. The default is 170 Celsius.
|
||||
min_temp: 0
|
||||
# Minimum temperature in Celsius (mcu will shutdown if not met)
|
||||
# Minimum temperature in Celsius (mcu will shutdown if not
|
||||
# met). This parameter must be provided.
|
||||
max_temp: 210
|
||||
# Maximum temperature (mcu will shutdown if temperature is above
|
||||
# this value)
|
||||
# this value). This parameter must be provided.
|
||||
|
||||
# The heater_bed section describes a heated bed (if present - omit
|
||||
# section if not present).
|
||||
[heater_bed]
|
||||
heater_pin: ar3
|
||||
thermistor_pin: analog0
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
heater_pin: ar8
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog14
|
||||
control: watermark
|
||||
max_delta: 2.0
|
||||
# The number of degrees in Celsius above the target temperature
|
||||
# before disabling the heater as well as the number of degrees below
|
||||
# the target before re-enabling the heater.
|
||||
#max_delta: 2.0
|
||||
# On 'watermark' controlled heaters this is the number of degrees in
|
||||
# Celsius above the target temperature before disabling the heater
|
||||
# as well as the number of degrees below the target before
|
||||
# re-enabling the heater. The default is 2 degrees Celsius.
|
||||
min_temp: 0
|
||||
max_temp: 110
|
||||
|
||||
# Extruder print fan (omit section if fan not present)
|
||||
# Print cooling fan (omit section if fan not present).
|
||||
[fan]
|
||||
pin: ar14
|
||||
# PWM output pin controlling the heater
|
||||
hard_pwm: 1
|
||||
pin: ar9
|
||||
# PWM output pin controlling the fan. This parameter must be
|
||||
# provided.
|
||||
#max_power: 1.0
|
||||
# The maximum power (expressed as a value from 0.0 to 1.0) that the
|
||||
# pin may be set to. The value 1.0 allows the pin to be set fully
|
||||
# enabled for extended periods, while a value of 0.5 would allow the
|
||||
# pin to be enabled for no more than half the time. This setting may
|
||||
# be used to limit the total power output (over extended periods) to
|
||||
# the fan. The default is 1.0.
|
||||
#hard_pwm: 0
|
||||
# Set this value to force hardware PWM instead of software PWM. Set
|
||||
# to 1 to force a hardware PWM at the fastest rate; set to a higher
|
||||
# number (eg, 1024) to force hardware PWM with the given cycle time
|
||||
# in clock ticks.
|
||||
kick_start_time: 0.100
|
||||
# number to force hardware PWM with the given cycle time in clock
|
||||
# ticks. The default is 0 which enables software PWM with a cycle
|
||||
# time of 10ms.
|
||||
#kick_start_time: 0.100
|
||||
# Time (in seconds) to run the fan at full speed when first enabling
|
||||
# it (helps get the fan spinning)
|
||||
# it (helps get the fan spinning). The default is 0.100 seconds.
|
||||
|
||||
# Micro-controller information
|
||||
# Micro-controller information.
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
# The serial port to connect to the MCU
|
||||
baud: 250000
|
||||
# The baud rate to use
|
||||
# The serial port to connect to the MCU. The default is /dev/ttyS0
|
||||
#baud: 250000
|
||||
# The baud rate to use. The default is 250000.
|
||||
pin_map: arduino
|
||||
# This option may be used to enable Arduino pin name aliases
|
||||
custom:
|
||||
# This option may be used to specify a set of custom
|
||||
# micro-controller commands to be sent at the start of the
|
||||
# connection. It may be used to configure the initial settings of
|
||||
# LEDs, to configure micro-stepping pins, to configure a digipot,
|
||||
# etc.
|
||||
# This option may be used to enable Arduino pin name aliases. The
|
||||
# default is to not enable the aliases.
|
||||
#restart_method: arduino
|
||||
# This controls the mechanism the host will use to reset the
|
||||
# micro-controller. The choices are 'arduino', 'rpi_usb', and
|
||||
# 'command'. The 'arduino' method (toggle DTR) is common on Arduino
|
||||
# boards and clones. The 'rpi_usb' method is useful on Raspberry Pi
|
||||
# boards with micro-controllers powered over USB - it briefly
|
||||
# disables power to all USB ports to accomplish a micro-controller
|
||||
# reset. The 'command' method involves sending a Klipper command to
|
||||
# the micro-controller so that it can reset itself. The default is
|
||||
# 'arduino'.
|
||||
|
||||
# The printer section controls high level printer settings
|
||||
# The printer section controls high level printer settings.
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
# This option must be "cartesian" for cartesian printers
|
||||
# This option must be "cartesian" for cartesian printers.
|
||||
max_velocity: 500
|
||||
# Maximum velocity (in mm/s) of the toolhead (relative to the
|
||||
# print)
|
||||
# print). This parameter must be specified.
|
||||
max_accel: 3000
|
||||
# Maximum acceleration (in mm/s^2) of the toolhead (relative to the
|
||||
# print)
|
||||
max_z_velocity: 250
|
||||
# print). This parameter must be specified.
|
||||
#max_accel_to_decel:
|
||||
# A pseudo acceleration (in mm/s^2) controlling how fast the
|
||||
# toolhead may go from acceleration to deceleration. It is used to
|
||||
# reduce the top speed of short zig-zag moves (and thus reduce
|
||||
# printer vibration from these moves). The default is half of
|
||||
# max_accel.
|
||||
max_z_velocity: 25
|
||||
# For cartesian printers this sets the maximum velocity (in mm/s) of
|
||||
# movement along the z axis. This setting can be used to restrict
|
||||
# the maximum speed of the z stepper motor on cartesian printers.
|
||||
# the maximum speed of the z stepper motor on cartesian
|
||||
# printers. The default is to use max_velocity for max_z_velocity.
|
||||
max_z_accel: 30
|
||||
# For cartesian printers this sets the maximum acceleration (in
|
||||
# mm/s^2) of movement along the z axis. It limits the acceleration
|
||||
# of the z stepper motor on cartesian printers.
|
||||
motor_off_time: 60
|
||||
# of the z stepper motor on cartesian printers. The default is to
|
||||
# use max_accel for max_z_accel.
|
||||
#motor_off_time: 600
|
||||
# Time (in seconds) of idle time before the printer will try to
|
||||
# disable active motors.
|
||||
junction_deviation: 0.02
|
||||
# disable active motors. The default is 600 seconds.
|
||||
#junction_deviation: 0.02
|
||||
# Distance (in mm) used to control the internal approximated
|
||||
# centripetal velocity cornering algorithm. A larger number will
|
||||
# permit higher "cornering speeds" at the junction of two moves.
|
||||
# permit higher "cornering speeds" at the junction of two moves. The
|
||||
# default is 0.02mm.
|
||||
|
||||
79
config/generic-cramps.cfg
Normal file
@@ -0,0 +1,79 @@
|
||||
# This file contains an example configuration for a Beaglebone PRU
|
||||
# micro-controller attached to a CRAMPS board.
|
||||
|
||||
# THIS FILE HAS NOT BEEN TESTED - PROCEED WITH CAUTION!
|
||||
|
||||
# NOTE: Klipper does not alter the input/output state of the
|
||||
# Beaglebone pins and it does not control their pull-up resistors. In
|
||||
# order to set the pin state one must use a "device tree overlay" or
|
||||
# use the config-pin program.
|
||||
|
||||
# See the example.cfg file for a description of available parameters.
|
||||
|
||||
[stepper_x]
|
||||
step_pin: P8_13
|
||||
dir_pin: P8_12
|
||||
enable_pin: !P9_14
|
||||
step_distance: .0125
|
||||
endstop_pin: ^P8_8
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_y]
|
||||
step_pin: P8_15
|
||||
dir_pin: P8_14
|
||||
enable_pin: !P9_14
|
||||
step_distance: .0125
|
||||
endstop_pin: ^P8_10
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: P8_19
|
||||
dir_pin: P8_18
|
||||
enable_pin: !P9_14
|
||||
step_distance: 0.00025
|
||||
endstop_pin: ^P9_13
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: P9_16
|
||||
dir_pin: P9_12
|
||||
enable_pin: !P9_14
|
||||
step_distance: .002
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: P9_15
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: P9_36
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: P8_11
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: P9_33
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: P9_41
|
||||
|
||||
[mcu]
|
||||
serial: /dev/rpmsg_pru30
|
||||
pin_map: beaglebone
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 5
|
||||
max_z_accel: 100
|
||||
76
config/generic-melzi.cfg
Normal file
@@ -0,0 +1,76 @@
|
||||
# This file contains common pin mappings for Melzi v2.0 boards. To use
|
||||
# this config, the firmware should be compiled for the AVR
|
||||
# atmega1284p.
|
||||
|
||||
# Note that the "make flash" command does not work with Melzi
|
||||
# boards. The boards are typically flashed with this command:
|
||||
# avrdude -p atmega1284p -c avrisp -P /dev/ttyUSB0 -U flash:w:out/klipper.elf.hex
|
||||
|
||||
# See the example.cfg file for a description of available parameters.
|
||||
|
||||
[stepper_x]
|
||||
step_pin: PD7
|
||||
dir_pin: PC5
|
||||
enable_pin: !PD6
|
||||
step_distance: .0125
|
||||
endstop_pin: ^!PC2
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_y]
|
||||
step_pin: PC6
|
||||
dir_pin: PC7
|
||||
enable_pin: !PD6
|
||||
step_distance: .0125
|
||||
endstop_pin: ^!PC3
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: PB3
|
||||
dir_pin: !PB2
|
||||
enable_pin: !PA5
|
||||
step_distance: 0.00025
|
||||
endstop_pin: ^!PC4
|
||||
position_endstop: 0.5
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: PB1
|
||||
dir_pin: PB0
|
||||
enable_pin: !PD6
|
||||
step_distance: .002
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: PD5
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PA7
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: PD2
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PA6
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: PB4
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyUSB0
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 5
|
||||
max_z_accel: 100
|
||||
109
config/generic-rambo.cfg
Normal file
@@ -0,0 +1,109 @@
|
||||
# This file contains common pin mappings for RAMBo boards. To use this
|
||||
# config, the firmware should be compiled for the AVR atmega2560.
|
||||
|
||||
# See the example.cfg file for a description of available parameters.
|
||||
|
||||
[stepper_x]
|
||||
step_pin: PC0
|
||||
dir_pin: PL1
|
||||
enable_pin: !PA7
|
||||
step_distance: .0125
|
||||
endstop_pin: ^PB6
|
||||
#endstop_pin: ^PA2
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_y]
|
||||
step_pin: PC1
|
||||
dir_pin: !PL0
|
||||
enable_pin: !PA6
|
||||
step_distance: .0125
|
||||
endstop_pin: ^PB5
|
||||
#endstop_pin: ^PA1
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: PC2
|
||||
dir_pin: PL2
|
||||
enable_pin: !PA5
|
||||
step_distance: 0.00025
|
||||
endstop_pin: ^PB4
|
||||
#endstop_pin: ^PC7
|
||||
position_endstop: 0.5
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: PC3
|
||||
dir_pin: PL6
|
||||
enable_pin: !PA4
|
||||
step_distance: .002
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: PH6
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PF0
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
#[extruder1]
|
||||
#step_pin: PC4
|
||||
#dir_pin: PL7
|
||||
#enable_pin: !PA3
|
||||
#heater_pin: PH4
|
||||
#sensor_pin: PF1
|
||||
#...
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: PE5
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PF2
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: PH5
|
||||
|
||||
#[heater_fan nozzle_cooling_fan]
|
||||
#pin: PH3
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 5
|
||||
max_z_accel: 100
|
||||
|
||||
[ad5206 stepper_digipot]
|
||||
enable_pin: PD7
|
||||
# Scale the config so that the channel value can be specified in amps.
|
||||
# (For Rambo v1.0d boards, use 1.56 instead.)
|
||||
scale: 2.08
|
||||
# Channel 1 is E0, 2 is E1, 3 is unused, 4 is Z, 5 is X, 6 is Y
|
||||
channel_1: 1.34
|
||||
channel_2: 1.0
|
||||
channel_4: 1.1
|
||||
channel_5: 1.1
|
||||
channel_6: 1.1
|
||||
|
||||
# Enable 16 micro-steps on steppers X, Y, Z, E0, E1
|
||||
[static_digital_output stepper_config]
|
||||
pins:
|
||||
PG1, PG0,
|
||||
PK7, PG2,
|
||||
PK6, PK5,
|
||||
PK3, PK4,
|
||||
PK2, PK1
|
||||
|
||||
[static_digital_output yellow_led]
|
||||
pins: !PB7
|
||||
84
config/generic-ramps.cfg
Normal file
@@ -0,0 +1,84 @@
|
||||
# This file contains common pin mappings for RAMPS (v1.3 and later)
|
||||
# boards. RAMPS boards typically use a firmware compiled for the AVR
|
||||
# atmega2560 (though other AVR chips are also possible).
|
||||
|
||||
# See the example.cfg file for a description of available parameters.
|
||||
|
||||
[stepper_x]
|
||||
step_pin: ar54
|
||||
dir_pin: ar55
|
||||
enable_pin: !ar38
|
||||
step_distance: .0125
|
||||
endstop_pin: ^ar3
|
||||
#endstop_pin: ^ar2
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_y]
|
||||
step_pin: ar60
|
||||
dir_pin: !ar61
|
||||
enable_pin: !ar56
|
||||
step_distance: .0125
|
||||
endstop_pin: ^ar14
|
||||
#endstop_pin: ^ar15
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: ar46
|
||||
dir_pin: ar48
|
||||
enable_pin: !ar62
|
||||
step_distance: 0.00025
|
||||
endstop_pin: ^ar18
|
||||
#endstop_pin: ^ar19
|
||||
position_endstop: 0.5
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: ar26
|
||||
dir_pin: ar28
|
||||
enable_pin: !ar24
|
||||
step_distance: .002
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: ar10
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog13
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
#[extruder1]
|
||||
#step_pin: ar36
|
||||
#dir_pin: ar34
|
||||
#enable_pin: !ar30
|
||||
#heater_pin: ar9
|
||||
#sensor_pin: analog15
|
||||
#...
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: ar8
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: analog14
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: ar9
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
pin_map: arduino
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 5
|
||||
max_z_accel: 100
|
||||
124
config/generic-replicape.cfg
Normal file
@@ -0,0 +1,124 @@
|
||||
# This file contains an example configuration for the Replicape rev B3
|
||||
# board. To use this config, one must compile and install the
|
||||
# micro-controller code for the "Beaglebone PRU", and then compile and
|
||||
# install the micro-controller code a second time for a "Linux
|
||||
# process".
|
||||
|
||||
# NOTE: Klipper does not alter the input/output state of the
|
||||
# Beaglebone pins and it does not control their pull-up resistors.
|
||||
# Typically the correct settings are automatically applied when the
|
||||
# Beaglebone detects the Replicape board, but if changes are needed
|
||||
# they must be specified in a "device tree overlay" or via the
|
||||
# config-pin program.
|
||||
|
||||
# See the example.cfg file for a description of available parameters.
|
||||
|
||||
[stepper_x]
|
||||
step_pin: P8_17
|
||||
dir_pin: P8_26
|
||||
enable_pin: replicape:stepper_x_enable
|
||||
step_distance: .0125
|
||||
endstop_pin: ^P9_25
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_y]
|
||||
step_pin: P8_12
|
||||
dir_pin: P8_19
|
||||
enable_pin: replicape:stepper_y_enable
|
||||
step_distance: .0125
|
||||
endstop_pin: ^P9_23
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
|
||||
[stepper_z]
|
||||
step_pin: P8_13
|
||||
dir_pin: P8_14
|
||||
enable_pin: replicape:stepper_z_enable
|
||||
step_distance: 0.00025
|
||||
endstop_pin: ^P9_13
|
||||
position_endstop: 0
|
||||
position_max: 200
|
||||
|
||||
[extruder]
|
||||
step_pin: P9_12
|
||||
dir_pin: P8_15
|
||||
enable_pin: replicape:stepper_e_enable
|
||||
step_distance: .002
|
||||
nozzle_diameter: 0.400
|
||||
filament_diameter: 1.750
|
||||
heater_pin: replicape:power_e
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: host:analog4
|
||||
control: pid
|
||||
pid_Kp: 22.2
|
||||
pid_Ki: 1.08
|
||||
pid_Kd: 114
|
||||
min_temp: 0
|
||||
max_temp: 250
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: replicape:power_hotbed
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: host:analog6
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 130
|
||||
|
||||
[fan]
|
||||
pin: replicape:power_fan0
|
||||
|
||||
[mcu]
|
||||
serial: /dev/rpmsg_pru30
|
||||
pin_map: beaglebone
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 25
|
||||
max_z_accel: 30
|
||||
|
||||
[mcu host]
|
||||
serial: /tmp/klipper_host_mcu
|
||||
|
||||
# The "replicape" config section adds "replicape:stepper_x_enable"
|
||||
# virtual stepper enable pins (for steppers x, y, z, e, and h) and
|
||||
# "replicape:power_x" PWM output pins (for hotbed, e, h, fan0, fan1,
|
||||
# fan2, and fan3) that may then be used elsewhere in the config file.
|
||||
[replicape]
|
||||
revision: B3
|
||||
# The replicape hardware revision. Currently only revision "B3" is
|
||||
# supported. This parameter must be provided.
|
||||
#enable_pin: !P9_41
|
||||
# The replicape global enable pin. The default is !P9_41.
|
||||
host_mcu: host
|
||||
# The name of the mcu config section that communicates with the
|
||||
# Klipper "linux process" mcu instance. This parameter must be
|
||||
# provided.
|
||||
stepper_x_microstep_mode: spread16
|
||||
# This parameter controls the CFG1 and CFG2 pins of the given
|
||||
# stepper motor driver. Available options are: disable, 1, 2,
|
||||
# spread2, 4, 16, spread4, spread16, stealth4, and stealth16. The
|
||||
# default is disable.
|
||||
stepper_x_current: 0.5
|
||||
# The configured maximum current (in Amps) of the stepper motor
|
||||
# driver. This parameter must be provided if the stepper is not in a
|
||||
# disable mode.
|
||||
#stepper_x_chopper_off_time_high: False
|
||||
# This parameter controls the CFG0 pin of the stepper motor driver
|
||||
# (True sets CFG0 high, False sets it low). The default is False.
|
||||
#stepper_x_chopper_hysteresis_high: False
|
||||
# This parameter controls the CFG4 pin of the stepper motor driver
|
||||
# (True sets CFG4 high, False sets it low). The default is False.
|
||||
#stepper_x_chopper_blank_time_high: True
|
||||
# This parameter controls the CFG5 pin of the stepper motor driver
|
||||
# (True sets CFG5 high, False sets it low). The default is True.
|
||||
stepper_y_microstep_mode: spread16
|
||||
stepper_y_current: 0.5
|
||||
stepper_z_microstep_mode: spread16
|
||||
stepper_z_current: 0.5
|
||||
stepper_e_microstep_mode: 16
|
||||
stepper_e_current: 0.5
|
||||
@@ -9,12 +9,11 @@ dir_pin: !PL1
|
||||
enable_pin: !PA7
|
||||
step_distance: .0225
|
||||
endstop_pin: ^!PB6
|
||||
homing_speed: 50.0
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .200
|
||||
position_min: -0.25
|
||||
position_endstop: 0.0
|
||||
position_max: 200
|
||||
homing_speed: 50
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .200
|
||||
|
||||
[stepper_y]
|
||||
step_pin: PC1
|
||||
@@ -22,12 +21,11 @@ dir_pin: PL0
|
||||
enable_pin: !PA6
|
||||
step_distance: .0225
|
||||
endstop_pin: ^!PB5
|
||||
homing_speed: 50.0
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .200
|
||||
position_min: -0.25
|
||||
position_endstop: 0.0
|
||||
position_max: 250
|
||||
homing_speed: 50
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .200
|
||||
|
||||
[stepper_z]
|
||||
step_pin: PC2
|
||||
@@ -35,25 +33,24 @@ dir_pin: !PL2
|
||||
enable_pin: !PA5
|
||||
step_distance: .005
|
||||
endstop_pin: ^!PB4
|
||||
homing_speed: 4.0
|
||||
homing_retract_dist: 2.0
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .050
|
||||
position_min: 0.1
|
||||
position_endstop: 0.7
|
||||
position_max: 200
|
||||
homing_retract_dist: 2.0
|
||||
homing_stepper_phases: 32
|
||||
homing_endstop_accuracy: .070
|
||||
|
||||
[extruder]
|
||||
step_pin: PC3
|
||||
dir_pin: PL6
|
||||
enable_pin: !PA4
|
||||
step_distance: .004242
|
||||
max_velocity: 200000
|
||||
max_accel: 3000
|
||||
nozzle_diameter: 0.350
|
||||
filament_diameter: 1.750
|
||||
pressure_advance: 0.07
|
||||
heater_pin: PH6
|
||||
thermistor_pin: PF0
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PF0
|
||||
control: pid
|
||||
pid_Kp: 7.0
|
||||
pid_Ki: 0.1
|
||||
@@ -63,44 +60,48 @@ max_temp: 210
|
||||
|
||||
[heater_bed]
|
||||
heater_pin: PE5
|
||||
thermistor_pin: PF2
|
||||
thermistor_type: EPCOS 100K B57560G104F
|
||||
sensor_type: EPCOS 100K B57560G104F
|
||||
sensor_pin: PF2
|
||||
control: watermark
|
||||
min_temp: 0
|
||||
max_temp: 100
|
||||
|
||||
[fan]
|
||||
pin: PH5
|
||||
|
||||
[heater_fan nozzle_fan]
|
||||
pin: PH3
|
||||
max_power: 0.61
|
||||
hard_pwm: 1
|
||||
|
||||
[mcu]
|
||||
serial: /dev/ttyACM0
|
||||
baud: 250000
|
||||
custom:
|
||||
# Nozzle fan
|
||||
set_pwm_out pin=PH3 cycle_ticks=1 value=155
|
||||
# Turn off yellow led
|
||||
set_digital_out pin=PB7 value=0
|
||||
# Stepper micro-step pins
|
||||
set_digital_out pin=PG1 value=1
|
||||
set_digital_out pin=PG0 value=1
|
||||
set_digital_out pin=PK7 value=1
|
||||
set_digital_out pin=PG2 value=1
|
||||
set_digital_out pin=PK6 value=1
|
||||
set_digital_out pin=PK5 value=1
|
||||
set_digital_out pin=PK3 value=1
|
||||
set_digital_out pin=PK4 value=1
|
||||
# Initialize digipot
|
||||
send_spi_message pin=PD7 msg=0487 # X = ~0.75A
|
||||
send_spi_message pin=PD7 msg=0587 # Y = ~0.75A
|
||||
send_spi_message pin=PD7 msg=0387 # Z = ~0.75A
|
||||
send_spi_message pin=PD7 msg=00A5 # E0
|
||||
send_spi_message pin=PD7 msg=017D # E1
|
||||
|
||||
[printer]
|
||||
kinematics: cartesian
|
||||
max_velocity: 500
|
||||
max_accel: 3000
|
||||
max_z_velocity: 250
|
||||
max_z_velocity: 25
|
||||
max_z_accel: 30
|
||||
motor_off_time: 600
|
||||
|
||||
[ad5206 stepper_digipot]
|
||||
enable_pin: PD7
|
||||
# Scale the config so that the channel value can be specified in amps
|
||||
scale: 1.56
|
||||
# Channel 1 is E0, 2 is E1, 3 is unused, 4 is Z, 5 is X, 6 is Y
|
||||
channel_1: 1.0
|
||||
channel_2: 0.75
|
||||
channel_4: 0.82
|
||||
channel_5: 0.82
|
||||
channel_6: 0.82
|
||||
|
||||
# Enable 8 micro-steps on steppers X, Y, Z, E0
|
||||
[static_digital_output stepper_config]
|
||||
pins:
|
||||
PG1, PG0,
|
||||
PK7, PG2,
|
||||
PK6, PK5,
|
||||
PK3, PK4
|
||||
|
||||
[static_digital_output yellow_led]
|
||||
pins: !PB7
|
||||
|
||||
@@ -8,16 +8,17 @@ The **src/** directory contains the C source for the micro-controller
|
||||
code. The **src/avr/** directory contains specific code for Atmel
|
||||
ATmega micro-controllers. The **src/sam3x8e/** directory contains code
|
||||
specific to the Arduino Due style ARM micro-controllers. The
|
||||
**src/simulator/** contains code stubs that allow the micro-controller
|
||||
to be test compiled on other architectures. The **src/generic/**
|
||||
directory contains helper code that may be useful across different
|
||||
host architectures. The build arranges for includes of
|
||||
"board/somefile.h" to first look in the current architecture directory
|
||||
(eg, src/avr/somefile.h) and then in the generic directory (eg,
|
||||
src/generic/somefile.h).
|
||||
**src/pru/** directory contains code specific to the Beaglebone's
|
||||
on-board PRU micro-controller. The **src/simulator/** contains code
|
||||
stubs that allow the micro-controller to be test compiled on other
|
||||
architectures. The **src/generic/** directory contains helper code
|
||||
that may be useful across different host architectures. The build
|
||||
arranges for includes of "board/somefile.h" to first look in the
|
||||
current architecture directory (eg, src/avr/somefile.h) and then in
|
||||
the generic directory (eg, src/generic/somefile.h).
|
||||
|
||||
The **klippy/** directory contains the C and Python source for the
|
||||
host part of the firmware.
|
||||
host part of the software.
|
||||
|
||||
The **lib/** directory contains external 3rd-party library code that
|
||||
is necessary to build some targets.
|
||||
@@ -43,10 +44,12 @@ all functions that have been tagged with the DECL_INIT() macro. It
|
||||
then goes on to repeatedly run all functions tagged with the
|
||||
DECL_TASK() macro.
|
||||
|
||||
One of the main task functions is command_task() located in
|
||||
**src/command.c**. This function processes incoming serial commands
|
||||
and runs the associated command function for them. Command functions
|
||||
are declared using the DECL_COMMAND() macro.
|
||||
One of the main task functions is command_dispatch() located in
|
||||
**src/command.c**. This function is called from the board specific
|
||||
input/output code (eg, **src/avr/serial.c**) and it runs the command
|
||||
functions associated with the commands found in the input
|
||||
stream. Command functions are declared using the DECL_COMMAND() macro
|
||||
(see the [protocol](Protocol.md) document for more information).
|
||||
|
||||
Task, init, and command functions always run with interrupts enabled
|
||||
(however, they can temporarily disable interrupts if needed). These
|
||||
@@ -54,13 +57,13 @@ functions should never pause, delay, or do any work that lasts more
|
||||
than a few micro-seconds. These functions schedule work at specific
|
||||
times by scheduling timers.
|
||||
|
||||
Timer functions are scheduled by calling sched_timer() (located in
|
||||
Timer functions are scheduled by calling sched_add_timer() (located in
|
||||
**src/sched.c**). The scheduler code will arrange for the given
|
||||
function to be called at the requested clock time. Timer interrupts
|
||||
are initially handled in an architecture specific interrupt handler
|
||||
(eg, **src/avr/timer.c**), but this just calls sched_timer_kick()
|
||||
located in **src/sched.c**. The timer interrupt leads to execution of
|
||||
schedule timer functions. Timer functions always run with interrupts
|
||||
(eg, **src/avr/timer.c**) which calls sched_timer_dispatch() located
|
||||
in **src/sched.c**. The timer interrupt leads to execution of schedule
|
||||
timer functions. Timer functions always run with interrupts
|
||||
disabled. The timer functions should always complete within a few
|
||||
micro-seconds. At completion of the timer event, the function may
|
||||
choose to reschedule itself.
|
||||
@@ -92,8 +95,8 @@ some functionality in C code.
|
||||
Initial execution starts in **klippy/klippy.py**. This reads the
|
||||
command-line arguments, opens the printer config file, instantiates
|
||||
the main printer objects, and starts the serial connection. The main
|
||||
execution of gcode commands is in the process_commands() method in
|
||||
**klippy/gcode.py**. This code translates the gcode commands into
|
||||
execution of G-code commands is in the process_commands() method in
|
||||
**klippy/gcode.py**. This code translates the G-code commands into
|
||||
printer object calls, which frequently translate the actions to
|
||||
commands to be executed on the micro-controller (as declared via the
|
||||
DECL_COMMAND macro in the micro-controller code).
|
||||
@@ -106,3 +109,169 @@ messages from the micro-controller in the Python code (see
|
||||
**klippy/serialhdl.py**). The fourth thread writes debug messages to
|
||||
the log (see **klippy/queuelogger.py**) so that the other threads
|
||||
never block on log writes.
|
||||
|
||||
Code flow of a move command
|
||||
===========================
|
||||
|
||||
A typical printer movement starts when a "G1" command is sent to the
|
||||
Klippy host and it completes when the corresponding step pulses are
|
||||
produced on the micro-controller. This section outlines the code flow
|
||||
of a typical move command. The [kinematics](Kinematics.md) document
|
||||
provides further information on the mechanics of moves.
|
||||
|
||||
* Processing for a move command starts in gcode.py. The goal of
|
||||
gcode.py is to translate G-code into internal calls. Changes in
|
||||
origin (eg, G92), changes in relative vs absolute positions (eg,
|
||||
G90), and unit changes (eg, F6000=100mm/s) are handled here. The
|
||||
code path for a move is: `process_data() -> process_commands() ->
|
||||
cmd_G1()`. Ultimately the ToolHead class is invoked to execute the
|
||||
actual request: `cmd_G1() -> ToolHead.move()`
|
||||
|
||||
* The ToolHead class (in toolhead.py) handles "look-ahead" and tracks
|
||||
the timing of printing actions. The codepath for a move is:
|
||||
`ToolHead.move() -> MoveQueue.add_move() -> MoveQueue.flush() ->
|
||||
Move.set_junction() -> Move.move()`.
|
||||
* ToolHead.move() creates a Move() object with the parameters of the
|
||||
move (in cartesian space and in units of seconds and millimeters).
|
||||
* MoveQueue.add_move() places the move object on the "look-ahead"
|
||||
queue.
|
||||
* MoveQueue.flush() determines the start and end velocities of each
|
||||
move.
|
||||
* Move.set_junction() implements the "trapezoid generator" on a
|
||||
move. The "trapezoid generator" breaks every move into three parts:
|
||||
a constant acceleration phase, followed by a constant velocity
|
||||
phase, followed by a constant deceleration phase. Every move
|
||||
contains these three phases in this order, but some phases may be of
|
||||
zero duration.
|
||||
* When Move.move() is called, everything about the move is known -
|
||||
its start location, its end location, its acceleration, its
|
||||
start/crusing/end velocity, and distance traveled during
|
||||
acceleration/cruising/deceleration. All the information is stored in
|
||||
the Move() class and is in cartesian space in units of millimeters
|
||||
and seconds.
|
||||
|
||||
The move is then handed off to the kinematics classes: `Move.move()
|
||||
-> kin.move()`
|
||||
|
||||
* The goal of the kinematics classes is to translate the movement in
|
||||
cartesian space to movement on each stepper. The kinematics classes
|
||||
are in cartesian.py, corexy.py, delta.py, and extruder.py. The
|
||||
kinematic class is given a chance to audit the move
|
||||
(`ToolHead.move() -> kin.check_move()`) before it goes on the
|
||||
look-ahead queue, but once the move arrives in *kin*.move() the
|
||||
kinematic class is required to handle the move as specified. The
|
||||
kinematic classes translate the three parts of each move
|
||||
(acceleration, constant "cruising" velocity, and deceleration) to
|
||||
the associated movement on each stepper. Note that the extruder is
|
||||
handled in its own kinematic class. Since the Move() class specifies
|
||||
the exact movement time and since step pulses are sent to the
|
||||
micro-controller with specific timing, stepper movements produced by
|
||||
the extruder class will be in sync with head movement even though
|
||||
the code is kept separate.
|
||||
|
||||
* For efficiency reasons, the stepper pulse times are generated in C
|
||||
code. The code flow is: `kin.move() -> MCU_Stepper.step_const() ->
|
||||
stepcompress_push_const()`, or for delta kinematics:
|
||||
`DeltaKinematics.move() -> MCU_Stepper.step_delta() ->
|
||||
stepcompress_push_delta()`. The MCU_Stepper code just performs unit
|
||||
and axis transformation (millimeters to step distances), and calls
|
||||
the C code. The C code calculates the stepper step times for each
|
||||
movement and fills an array (struct stepcompress.queue) with the
|
||||
corresponding micro-controller clock counter times for every
|
||||
step. Here the "micro-controller clock counter" value directly
|
||||
corresponds to the micro-controller's hardware counter - it is
|
||||
relative to when the micro-controller was last powered up.
|
||||
|
||||
* The next major step is to compress the steps: `stepcompress_flush()
|
||||
-> compress_bisect_add()` (in stepcompress.c). This code generates
|
||||
and encodes a series of micro-controller "queue_step" commands that
|
||||
correspond to the list of stepper step times built in the previous
|
||||
stage. These "queue_step" commands are then queued, prioritized, and
|
||||
sent to the micro-controller (via stepcompress.c:steppersync and
|
||||
serialqueue.c:serialqueue).
|
||||
|
||||
* Processing of the queue_step commands on the micro-controller starts
|
||||
in command.c which parses the command and calls
|
||||
`command_queue_step()`. The command_queue_step() code (in stepper.c)
|
||||
just appends the parameters of each queue_step command to a per
|
||||
stepper queue. Under normal operation the queue_step command is
|
||||
parsed and queued at least 100ms before the time of its first
|
||||
step. Finally, the generation of stepper events is done in
|
||||
`stepper_event()`. It's called from the hardware timer interrupt at
|
||||
the scheduled time of the first step. The stepper_event() code
|
||||
generates a step pulse and then reschedules itself to run at the
|
||||
time of the next step pulse for the given queue_step parameters. The
|
||||
parameters for each queue_step command are "interval", "count", and
|
||||
"add". At a high-level, stepper_event() runs the following, 'count'
|
||||
times: `do_step(); next_wake_time = last_wake_time + interval;
|
||||
interval += add;`
|
||||
|
||||
The above may seem like a lot of complexity to execute a
|
||||
movement. However, the only really interesting parts are in the
|
||||
ToolHead and kinematic classes. It's this part of the code which
|
||||
specifies the movements and their timings. The remaining parts of the
|
||||
processing is mostly just communication and plumbing.
|
||||
|
||||
Time
|
||||
====
|
||||
|
||||
Fundamental to the operation of Klipper is the handling of clocks,
|
||||
times, and timestamps. Klipper executes actions on the printer by
|
||||
scheduling events to occur in the near future. For example, to turn on
|
||||
a fan, the code might schedule a change to a GPIO pin in a 100ms. It
|
||||
is rare for the code to attempt to take an instantaneous action. Thus,
|
||||
the handling of time within Klipper is critical to correct operation.
|
||||
|
||||
There are three types of times tracked internally in the Klipper host
|
||||
software:
|
||||
* System time. The system time uses the system's monotonic clock - it
|
||||
is a floating point number stored as seconds and it is (generally)
|
||||
relative to when the host computer was last started. System times
|
||||
have limited use in the software - they are primarily used when
|
||||
interacting with the operating system. Within the host code, system
|
||||
times are frequently stored in variables named *eventtime* or
|
||||
*curtime*.
|
||||
* Print time. The print time is synchronized to the main
|
||||
micro-controller clock (the micro-controller defined in the "[mcu]"
|
||||
config section). It is a floating point number stored as seconds and
|
||||
is relative to when the main mcu was last restarted. It is possible
|
||||
to convert from a "print time" to the main micro-controller's
|
||||
hardware clock by multiplying the print time by the mcu's statically
|
||||
configured frequency rate. The high-level host code uses print times
|
||||
to calculates almost all physical actions (eg, head movement, heater
|
||||
changes, etc.). Within the host code, print times are generally
|
||||
stored in variables named *print_time* or *move_time*.
|
||||
* MCU clock. This is the hardware clock counter on each
|
||||
micro-controller. It is stored as an integer and its update rate is
|
||||
relative to the frequency of the given micro-controller. The host
|
||||
software translates its internal times to clocks before transmission
|
||||
to the mcu. The mcu code only ever tracks time in clock
|
||||
ticks. Within the host code, clock values are tracked as 64bit
|
||||
integers, while the mcu code uses 32bit integers. Within the host
|
||||
code, clocks are generally stored in variables with names containing
|
||||
*clock* or *ticks*.
|
||||
|
||||
Conversion between the different time formats is primarily implemented
|
||||
in the **klippy/clocksync.py** code.
|
||||
|
||||
Some things to be aware of when reviewing the code:
|
||||
* 32bit and 64bit clocks: To reduce bandwidth and to improve
|
||||
micro-controller efficiency, clocks on the micro-controller are
|
||||
tracked as 32bit integers. When comparing two clocks in the mcu
|
||||
code, the `timer_is_before()` function must always be used to ensure
|
||||
integer rollovers are handled properly. The host software converts
|
||||
32bit clocks to 64bit clocks by appending the high-order bits from
|
||||
the last mcu timestamp it has received - no message from the mcu is
|
||||
ever more than 2^31 clock ticks in the future or past so this
|
||||
conversion is never ambiguous. The host converts from 64bit clocks
|
||||
to 32bit clocks by simply truncating the high-order bits. To ensure
|
||||
there is no ambiguity in this conversion, the
|
||||
**klippy/serialqueue.c** code will buffer messages until they are
|
||||
within 2^31 clock ticks of their target time.
|
||||
* Multiple micro-controllers: The host software supports using
|
||||
multiple micro-controllers on a single printer. In this case, the
|
||||
"MCU clock" of each micro-controller is tracked separately. The
|
||||
clocksync.py code handles clock drift between micro-controllers by
|
||||
modifying the way it converts from "print time" to "MCU clock". On
|
||||
secondary mcus, the mcu frequency that is used in this conversion is
|
||||
regularly updated to account for measured drift.
|
||||
|
||||
47
docs/Contact.md
Normal file
@@ -0,0 +1,47 @@
|
||||
This page provides information on how to contact the Klipper
|
||||
developers.
|
||||
|
||||
Bug reporting
|
||||
=============
|
||||
|
||||
Bug reports are submitted through github issues. All bug reports must
|
||||
include the full /tmp/klippy.log log file from the session that
|
||||
produced the error. To acquire this log file, ssh into the computer
|
||||
running the klipper host software, and run:
|
||||
|
||||
```
|
||||
gzip -k /tmp/klippy.log
|
||||
```
|
||||
|
||||
Then scp the resulting `/tmp/klippy.log.gz` file from the host machine
|
||||
to your desktop. (If your desktop does not have scp installed, there
|
||||
are a number of free scp programs available - just do a web search for
|
||||
`windows scp` to find one.) Open a new issue at
|
||||
https://github.com/KevinOConnor/klipper/issues , attach the
|
||||
`klippy.log.gz` file to that issue, and provide a description of the
|
||||
problem.
|
||||
|
||||
Mailing list
|
||||
============
|
||||
|
||||
There is a mailing list for general discussions on Klipper. In order
|
||||
to send am email to the list, one must first subscribe:
|
||||
https://www.freelists.org/list/klipper . Once subscribed, emails may
|
||||
be sent to `klipper@freelists.org`.
|
||||
|
||||
Archives of the mailing list are available at:
|
||||
https://www.freelists.org/archive/klipper/
|
||||
|
||||
IRC
|
||||
===
|
||||
|
||||
One may join the #klipper channel on freenode.net (
|
||||
irc://chat.freenode.net:6667 ).
|
||||
|
||||
To communicate in this IRC channel one will need an IRC
|
||||
client. Configure it to connect to chat.freenode.net on port 6667 and
|
||||
join the #klipper channel (`/join #klipper`).
|
||||
|
||||
If asking a question on IRC, be sure to ask the question and then stay
|
||||
connected to the channel to receive responses. Due to timezone
|
||||
differences, it may take several hours before receiving a response.
|
||||
@@ -1,17 +1,18 @@
|
||||
The Klippy host code has some tools to help in debugging the firmware.
|
||||
The Klippy host code has some tools to help in debugging.
|
||||
|
||||
Translating gcode files to firmware commands
|
||||
============================================
|
||||
Translating gcode files to micro-controller commands
|
||||
====================================================
|
||||
|
||||
The Klippy host code can run in a batch mode to produce the low-level
|
||||
firmware commands associated with a gcode file. Inspecting these
|
||||
low-level firmware commands is useful when trying to understand the
|
||||
micro-controller commands associated with a gcode file. Inspecting
|
||||
these low-level commands is useful when trying to understand the
|
||||
actions of the low-level hardware. It can also be useful to compare
|
||||
the difference in firmware commands after a code change.
|
||||
the difference in micro-controller commands after a code change.
|
||||
|
||||
To run Klippy in this batch mode, there is a one time step necessary
|
||||
to generate the firmware "data dictionary". This is done by compiling
|
||||
the firmware code to obtain the **out/klipper.dict** file:
|
||||
to generate the micro-controller "data dictionary". This is done by
|
||||
compiling the micro-controller code to obtain the **out/klipper.dict**
|
||||
file:
|
||||
|
||||
```
|
||||
make menuconfig
|
||||
@@ -34,13 +35,13 @@ output. This output can be translated to readable text with:
|
||||
```
|
||||
|
||||
The resulting file **test.txt** contains a human readable list of
|
||||
firmware commands.
|
||||
micro-controller commands.
|
||||
|
||||
The batch mode disables certain response / request commands in order
|
||||
to function. As a result, there will be some differences between
|
||||
actual firmware commands and the above output. The generated data is
|
||||
useful for testing and inspection; it is not useful for sending to a
|
||||
real micro-controller.
|
||||
actual commands and the above output. The generated data is useful for
|
||||
testing and inspection; it is not useful for sending to a real
|
||||
micro-controller.
|
||||
|
||||
Testing with simulavr
|
||||
=====================
|
||||
@@ -74,25 +75,20 @@ cd /patch/to/klipper
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
and compile the firmware for an AVR atmega644p, disable the AVR
|
||||
watchdog timer, and set the MCU frequency to 20000000. Then one can
|
||||
compile Klipper (run `make`) and then start the simulation with:
|
||||
and compile the micro-controller software for an AVR atmega644p, set
|
||||
the MCU frequency to 20Mhz, and select SIMULAVR software emulation
|
||||
support. Then one can compile Klipper (run `make`) and then start the
|
||||
simulation with:
|
||||
|
||||
```
|
||||
PYTHONPATH=/path/to/simulavr/src/python/ ./scripts/avrsim.py -m atmega644 -s 20000000 -b 250000 out/klipper.elf
|
||||
```
|
||||
|
||||
It may be necessary to create a python virtual environment to run
|
||||
Klippy on the target machine. To do so, run:
|
||||
|
||||
```
|
||||
virtualenv ~/klippy-env
|
||||
~/klippy-env/bin/pip install cffi==1.6.0 pyserial==2.7
|
||||
```
|
||||
|
||||
Then, with simulavr running in another window, one can run the
|
||||
following to read gcode from a file (eg, "test.gcode"), process it
|
||||
with Klippy, and send it to Klipper running in simulavr:
|
||||
with Klippy, and send it to Klipper running in simulavr (see
|
||||
[installation](Installation.md) for the steps necessary to build the
|
||||
python virtual environment):
|
||||
|
||||
```
|
||||
~/klippy-env/bin/python ./klippy/klippy.py config/avrsim.cfg -i test.gcode -v
|
||||
@@ -129,3 +125,26 @@ Klipper source code). To do so, run:
|
||||
```
|
||||
~/klippy-env/bin/python ./klippy/console.py /tmp/pseudoserial 250000
|
||||
```
|
||||
|
||||
Generating load graphs
|
||||
======================
|
||||
|
||||
The Klippy log file (/tmp/klippy.log) stores statistics on bandwidth,
|
||||
micro-controller load, and host buffer load. It can be useful to graph
|
||||
these statistics after a print.
|
||||
|
||||
To generate a graph, a one time step is necessary to install the
|
||||
"matplotlib" package:
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install python-matplotlib
|
||||
```
|
||||
|
||||
Then graphs can be produced with:
|
||||
|
||||
```
|
||||
~/klipper/scripts/graphstats.py /tmp/klippy.log loadgraph.png
|
||||
```
|
||||
|
||||
One can then view the resulting **loadgraph.png** file.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
Klipper is an experimental 3d printer firmware. It has several
|
||||
compelling features:
|
||||
Klipper has several compelling features:
|
||||
|
||||
* High precision stepper movement. Klipper utilizes an application
|
||||
processor (such as a low-cost Raspberry Pi) when calculating printer
|
||||
@@ -7,20 +6,19 @@ compelling features:
|
||||
stepper motor, it compresses those events, transmits them to the
|
||||
micro-controller, and then the micro-controller executes each event
|
||||
at the requested time. Each stepper event is scheduled with a
|
||||
precision of no less than 50 micro-seconds. The software does not
|
||||
use kinematic estimations (such as the Bresenham algorithm) -
|
||||
instead it calculates precise step times based on the physics of
|
||||
acceleration and the physics of the machine kinematics. More precise
|
||||
stepper movement translates to quieter and more stable printer
|
||||
operation.
|
||||
precision of 25 micro-seconds or better. The software does not use
|
||||
kinematic estimations (such as the Bresenham algorithm) - instead it
|
||||
calculates precise step times based on the physics of acceleration
|
||||
and the physics of the machine kinematics. More precise stepper
|
||||
movement translates to quieter and more stable printer operation.
|
||||
|
||||
* Best in class performance. Klipper is able to achieve high stepping
|
||||
rates on both new and old micro-controllers. Even an old 8bit AVR
|
||||
micro-controller can obtain rates up to 150K steps per second. On
|
||||
more recent ARM micro-controllers, rates over 350K steps per second
|
||||
are possible. Higher stepper rates enable higher print
|
||||
velocities. The stepper event timing remains precise even at high
|
||||
speeds which improves overall stability.
|
||||
micro-controller can obtain rates over 175K steps per second. On
|
||||
more recent micro-controllers, rates over 500K steps per second are
|
||||
possible. Higher stepper rates enable higher print velocities. The
|
||||
stepper event timing remains precise even at high speeds which
|
||||
improves overall stability.
|
||||
|
||||
* Configuration via simple config file. There's no need to reflash the
|
||||
micro-controller to change a setting. All of Klipper's configuration
|
||||
@@ -34,16 +32,29 @@ compelling features:
|
||||
micro-controller architectures as well.
|
||||
|
||||
* Simpler code. Klipper uses a very high level language (Python) for
|
||||
most code. The kinematics algorithms, the gcode parsing, the heating
|
||||
and thermistor algorithms, etc. are all written in Python. This
|
||||
makes it easier to develop new functionality.
|
||||
most code. The kinematics algorithms, the G-code parsing, the
|
||||
heating and thermistor algorithms, etc. are all written in
|
||||
Python. This makes it easier to develop new functionality.
|
||||
|
||||
* Advanced features. Klipper implements the "pressure advance"
|
||||
algorithm for extruders. When properly tuned, pressure advance
|
||||
reduces extruder ooze. Klipper also implements a novel "stepper
|
||||
phase endstop" algorithm that can dramatically improve the accuracy
|
||||
of typical endstop switches. When properly tuned it can improve a
|
||||
print's first layer bed adhesion.
|
||||
* Advanced features:
|
||||
* Klipper implements the "pressure advance" algorithm for
|
||||
extruders. When properly tuned, pressure advance reduces extruder
|
||||
ooze.
|
||||
* Klipper supports printers with multiple micro-controllers. For
|
||||
example, one micro-controller could be used to control an
|
||||
extruder, while another could control the printer's heaters, while
|
||||
a third controls the rest of the printer. The Klipper host
|
||||
software implements clock synchronization to account for clock
|
||||
drift between micro-controllers. No special code is needed to
|
||||
enable multiple micro-controllers - it just requires a few extra
|
||||
lines in the config file.
|
||||
* Klipper also implements a novel "stepper phase endstop" algorithm
|
||||
that can dramatically improve the accuracy of typical endstop
|
||||
switches. When properly tuned it can improve a print's first layer
|
||||
bed adhesion.
|
||||
* Support for limiting the top speed of short "zigzag" moves to
|
||||
reduce printer vibration and noise. See the
|
||||
[kinematics](Kinematics.md) document for more information.
|
||||
|
||||
To get started with Klipper, read the [installation](Installation.md)
|
||||
guide.
|
||||
@@ -65,12 +76,12 @@ Klipper supports many standard 3d printer features:
|
||||
gradually accelerate from standstill to cruising speed and then
|
||||
decelerate back to a standstill.
|
||||
|
||||
* "Lookahead" support. The incoming stream of G-Code movement commands
|
||||
are queued and analyzed - the acceleration between movements in a
|
||||
similar direction will be optimized to reduce print stalls and
|
||||
improve overall print time.
|
||||
* "Look-ahead" support. The incoming stream of G-Code movement
|
||||
commands are queued and analyzed - the acceleration between
|
||||
movements in a similar direction will be optimized to reduce print
|
||||
stalls and improve overall print time.
|
||||
|
||||
* Support for both delta printers and cartesian style printers.
|
||||
* Support for cartesian, delta, and corexy style printers.
|
||||
|
||||
Step Benchmarks
|
||||
===============
|
||||
@@ -78,8 +89,14 @@ Step Benchmarks
|
||||
Below are the results of stepper performance tests. The numbers shown
|
||||
represent total number of steps per second on the micro-controller.
|
||||
|
||||
| Micro-controller | 1 stepper active | 3 steppers active |
|
||||
| ----------------- | ---------------- | ----------------- |
|
||||
| 20Mhz AVR | 158.7K | 103K |
|
||||
| 16Mhz AVR | 126.9K | 82K |
|
||||
| Arduino Due (ARM) | 352.9K | 288K |
|
||||
| Micro-controller | Fastest step rate | 3 steppers active |
|
||||
| ----------------- | ----------------- | ----------------- |
|
||||
| 20Mhz AVR | 189K | 125K |
|
||||
| 16Mhz AVR | 151K | 100K |
|
||||
| Arduino Due (ARM) | 382K | 337K |
|
||||
| Beaglebone PRU | 689K | 689K |
|
||||
|
||||
On AVR platforms, the highest achievable step rate is with just one
|
||||
stepper stepping. On the Due, the highest step rate is with two
|
||||
simultaneous steppers stepping. On the PRU, the highest step rate is
|
||||
with three simultaneous steppers.
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
This document provides high-level information on common firmware
|
||||
commands. It is not an authoritative reference for these commands, nor
|
||||
is it an exclusive list of all available firmware commands.
|
||||
|
||||
This document may be useful for users needing to configure a set of
|
||||
hardware actions that their printer may require at startup (via the
|
||||
"custom" field in the printer config file), and it may be useful for
|
||||
developers wishing to obtain a high-level feel for available firmware
|
||||
commands.
|
||||
|
||||
See the [protocol](Protocol.md) document for more information on the
|
||||
format of commands and their low-level transmission. The commands here
|
||||
are described using their "printf" style syntax - for those unfamiliar
|
||||
with that format, just note that where a '%...' sequence is seen it
|
||||
should be replaced with an actual integer.
|
||||
|
||||
Startup Commands
|
||||
================
|
||||
|
||||
It may be necessary to take certain one-time actions to configure the
|
||||
micro-controller and its peripherals. This section lists common
|
||||
commands available for that purpose. Unlike other firmware commands,
|
||||
these commands run as soon as they are received by the firmware and
|
||||
they do not require any particular setup.
|
||||
|
||||
These commands are most useful in the "custom" block of the "mcu"
|
||||
section of the printer configuration file. This feature is typically
|
||||
used to configure the initial settings of LEDs, to configure
|
||||
micro-stepping pins, to configure a digipot, etc.
|
||||
|
||||
Several of these commands will take a "pin=%u" parameter. The
|
||||
low-level firmware uses integer encodings of the hardware pin numbers,
|
||||
but to make things more readable the host will translate human
|
||||
readable pin names (eg, "PA3") to their equivalent integer
|
||||
encodings. By convention, any parameter named "pin" or that has a
|
||||
"_pin" suffix will use pin name translation by the host.
|
||||
|
||||
Common startup commands:
|
||||
|
||||
* set_digital_out pin=%u value=%c : This command immediately
|
||||
configures the given pin as a digital out GPIO and it sets it to
|
||||
either a low level (value=0) or a high level (value=1). This command
|
||||
may be useful for configuring the initial value of LEDs and for
|
||||
configuring the initial value of stepper driver micro-stepping pins.
|
||||
|
||||
* set_pwm_out pin=%u cycle_ticks=%u value=%c : This command will
|
||||
immediately configure the given pin to use hardware based
|
||||
pulse-width-modulation (PWM) with the given number of
|
||||
cycle_ticks. The "cycle_ticks" is the number of MCU clock ticks each
|
||||
power on and power off cycle should last. A cycle_ticks value of 1
|
||||
can be used to request the fastest possible cycle time. The "value"
|
||||
parameter is between 0 and 255 with 0 indicating a full off state
|
||||
and 255 indicating a full on state. This command may be useful for
|
||||
enabling CPU and nozzle cooling fans.
|
||||
|
||||
* send_spi_message pin=%u msg=%*s : This command can be used to
|
||||
transmit messages to a serial-peripheral-interface (SPI) component
|
||||
connected to the micro-controller. It has been used to configure the
|
||||
startup settings of AD5206 digipots. The 'pin' parameter specifies
|
||||
the chip select line to use during the transmission. The 'msg'
|
||||
indicates the binary message to transmit to the given chip.
|
||||
|
||||
Firmware configuration
|
||||
======================
|
||||
|
||||
Most commands in the firmware require an initial setup before they can
|
||||
be successfully invoked. This section provides a high-level overview
|
||||
of the micro-controller configuration process. This section and the
|
||||
following sections are likely only of interest to developers
|
||||
interested in the internal details of Klipper.
|
||||
|
||||
When the host first connects to the firmware it always starts by
|
||||
obtaining the firmware's data dictionary (see [protocol](Protocol.md)
|
||||
for more information). After the data dictionary is obtained the host
|
||||
will check if the firmware is in a "configured" state and configure it
|
||||
if not. Configuration involves the following phases:
|
||||
|
||||
* get_config : The host starts by checking if the firmware is already
|
||||
configured. The firmware responds to this command with a "config"
|
||||
response message. At micro-controller power-on the firmware always
|
||||
starts in an unconfigured state. It remains in this state until the
|
||||
host completes the configuration processes (by issuing a
|
||||
finalize_config command). If the firmware is already configured (and
|
||||
is configured with the desired settings) from a previous
|
||||
host/firmware session then no further action is needed by the host
|
||||
and the configuration process ends successfully.
|
||||
|
||||
* allocate_oids count=%c : This command is issued to inform the
|
||||
firmware the maximum number of object-ids (oid) that the host
|
||||
requires. It is only valid to issue this command once. An oid is an
|
||||
integer identifier allocated to each stepper, each endstop, and each
|
||||
schedulable gpio pin. The host determines in advance the number of
|
||||
oids it will require to operate the hardware and passes this to the
|
||||
firmware so that the firmware may allocate sufficient memory to
|
||||
store a mapping from oid to internal firmware object.
|
||||
|
||||
* config_XXX oid=%c ... : By convention any command starting with the
|
||||
"config_" prefix creates a new firmware object and assigns the given
|
||||
oid to it. For example, the config_digital_out command will
|
||||
configure the specified pin as a digital output GPIO and create an
|
||||
internal object that the host can use to schedule changes to the
|
||||
given GPIO. The oid parameter passed into the config command is
|
||||
selected by the host and must be between zero and the maximum count
|
||||
supplied in the allocate_oids command. The config commands may only
|
||||
be run when the firmware is not in a configured state (ie, prior to
|
||||
the host sending finalize_config) and after the allocate_oids
|
||||
command has been sent.
|
||||
|
||||
* finalize_config crc=%u : The finalize_config command transitions the
|
||||
firmware from an unconfigured state to a configured state. The crc
|
||||
parameter passed to the firmware is stored in the firmware and
|
||||
provided back to the host in "config" response messages. By
|
||||
convention, the host takes a 32bit CRC of the firmware configuration
|
||||
it will request and at the start of subsequent host/firmware
|
||||
communication sessions it checks that the CRC stored in the firmware
|
||||
exactly matches its desired CRC. If the CRC does not match then the
|
||||
host knows the firmware has not been configured in the state desired
|
||||
by the host.
|
||||
|
||||
Common firmware objects
|
||||
-----------------------
|
||||
|
||||
This section lists some commonly used config commands.
|
||||
|
||||
* config_digital_out oid=%c pin=%u default_value=%c max_duration=%u :
|
||||
This command creates an internal firmware object for the given GPIO
|
||||
'pin'. The pin will be configured in digital output mode and set to
|
||||
an initial value as specified by 'default_value' (0 for low, 1 for
|
||||
high). Creating a digital_out object allows the host to schedule
|
||||
GPIO updates for the given pin at specified times (see the
|
||||
schedule_digital_out command described below). Should the firmware
|
||||
go into shutdown mode then all configured digital_out objects will
|
||||
be set back to their default values. The 'max_duration' parameter is
|
||||
used to implement a safety check - if it is non-zero then it is the
|
||||
maximum number of clock ticks that the host may set the given GPIO
|
||||
to a non-default value without further updates. For example, if the
|
||||
default_value is zero and the max_duration is 16000 then if the host
|
||||
sets the gpio to a value of one then it must schedule another update
|
||||
to the gpio pin (to either zero or one) within 16000 clock
|
||||
ticks. This safety feature can be used with heater pins to ensure
|
||||
the host does not set the heater to a value of one and then go
|
||||
off-line.
|
||||
|
||||
* config_pwm_out oid=%c pin=%u cycle_ticks=%u default_value=%c
|
||||
max_duration=%u : This command creates an internal object for
|
||||
hardware based PWM pins that the host may schedule updates for. Its
|
||||
usage is analogous to config_digital_out - see the description of
|
||||
the 'set_pwm_out' and 'config_digital_out' commands for parameter
|
||||
description.
|
||||
|
||||
* config_soft_pwm_out oid=%c pin=%u cycle_ticks=%u default_value=%c
|
||||
max_duration=%u : This command creates an internal firmware object
|
||||
for software implemented PWM. Unlike hardware pwm pins, a software
|
||||
pwm object does not require any special hardware support (other than
|
||||
the ability to configure the pin as a digital output GPIO). Because
|
||||
the output switching is implemented in the software of the firmware,
|
||||
it is recommended that the cycle_ticks parameter correspond to a
|
||||
time of 10ms or greater. See the description of the 'set_pwm_out'
|
||||
and 'config_digital_out' commands for parameter description.
|
||||
|
||||
* config_analog_in oid=%c pin=%u : This command is used to configure a
|
||||
pin in analog input sampling mode. Once configured, the pin can be
|
||||
sampled at regular interval using the query_analog_in command (see
|
||||
below).
|
||||
|
||||
* config_stepper oid=%c step_pin=%c dir_pin=%c min_stop_interval=%u
|
||||
invert_step=%c : This command creates an internal stepper
|
||||
object. The 'step_pin' and 'dir_pin' parameters specify the step and
|
||||
direction pins respectively; this command will configure them in
|
||||
digital output mode. The 'invert_step' parameter specifies whether a
|
||||
step occurs on a rising edge (invert_step=0) or falling edge
|
||||
(invert_step=1). The 'min_stop_interval' implements a safety
|
||||
feature - it is checked when the firmware finishes all moves for a
|
||||
stepper - if it is non-zero it specifies the minimum number of clock
|
||||
ticks since the last step. It is used as a check on the maximum
|
||||
stepper velocity that a stepper may have before stopping.
|
||||
|
||||
* config_end_stop oid=%c pin=%c pull_up=%c stepper_oid=%c : This
|
||||
command creates an internal "endstop" object. It is used to specify
|
||||
the endstop pins and to enable "homing" operations (see the
|
||||
end_stop_home command below). The command will configure the
|
||||
specified pin in digital input mode. The 'pull_up' parameter
|
||||
determines whether hardware provided pullup resistors for the pin
|
||||
(if available) will be enabled. The 'stepper_oid' parameter
|
||||
specifies the oid of an associated stepper for the given endstop -
|
||||
it is used during homing operations.
|
||||
|
||||
Common commands
|
||||
===============
|
||||
|
||||
This section lists some commonly used run-time commands. It is likely
|
||||
only of interest to developers looking to gain insight into Klippy.
|
||||
|
||||
* schedule_digital_out oid=%c clock=%u value=%c : This command will
|
||||
schedule a change to a digital output GPIO pin at the given clock
|
||||
time. To use this command a 'config_digital_out' command with the
|
||||
same 'oid' parameter must have been issued during firmware
|
||||
configuration.
|
||||
|
||||
* schedule_pwm_out oid=%c clock=%u value=%c : Schedules a change to a
|
||||
hardware PWM output pin. See the 'schedule_digital_out' and
|
||||
'config_pwm_out' commands for more info.
|
||||
|
||||
* schedule_soft_pwm_out oid=%c clock=%u value=%c : Schedules a change
|
||||
to a software PWM output pin. See the 'schedule_digital_out' and
|
||||
'config_soft_pwm_out' commands for more info.
|
||||
|
||||
* query_analog_in oid=%c clock=%u sample_ticks=%u sample_count=%c
|
||||
rest_ticks=%u min_value=%hu max_value=%hu : This command sets up a
|
||||
recurring schedule of analog input samples. To use this command a
|
||||
'config_analog_in' command with the same 'oid' parameter must have
|
||||
been issued during firmware configuration. The samples will start as
|
||||
of 'clock' time, it will report on the obtained value every
|
||||
'rest_ticks' clock ticks, it will over-sample 'sample_count' number
|
||||
of times, and it will pause 'sample_ticks' number of clock ticks
|
||||
between over-sample samples. The 'min_value' and 'max_value'
|
||||
parameters implement a safety feature - the firmware will verify the
|
||||
sampled value (after any oversampling) is always between the
|
||||
supplied range. This is intended for use with pins attached to
|
||||
thermistors controlling heaters - it can be used to check that a
|
||||
heater is within a temperature range.
|
||||
|
||||
* get_status : This command causes the firmware to generate a "status"
|
||||
response message. The host sends this command once a second to
|
||||
obtain the value of the micro-controller clock and to estimate the
|
||||
drift between host and micro-controller clocks. It enables the host
|
||||
to accurately estimate the micro-controller clock.
|
||||
|
||||
Stepper commands
|
||||
----------------
|
||||
|
||||
* queue_step oid=%c interval=%u count=%hu add=%hi : This command
|
||||
schedules 'count' number of steps for the given stepper, with
|
||||
'interval' number of clock ticks between each step. The first step
|
||||
will be 'interval' number of clock ticks since the last scheduled
|
||||
step for the given stepper. If 'add' is non-zero then the interval
|
||||
will be adjusted by 'add' amount after each step. This command
|
||||
appends the given interval/count/add sequence to a per-stepper
|
||||
queue. There may be hundreds of these sequences queued during normal
|
||||
operation. New sequence are appended to the end of the queue and as
|
||||
each sequence completes its 'count' number of steps it is popped
|
||||
from the front of the queue. This system allows the firmware to
|
||||
queue potentially hundreds of thousands of steps - all with reliable
|
||||
and predictable schedule times.
|
||||
|
||||
* set_next_step_dir oid=%c dir=%c : This command specifies the value
|
||||
of the dir_pin that the next queue_step command will use.
|
||||
|
||||
* reset_step_clock oid=%c clock=%u : Normally, step timing is relative
|
||||
to the last step for a given stepper. This command resets the clock
|
||||
so that the next step is relative to the supplied 'clock' time. The
|
||||
host usually only sends this command at the start of a print.
|
||||
|
||||
* end_stop_home oid=%c clock=%u rest_ticks=%u pin_value=%c : This
|
||||
command is used during stepper "homing" operations. To use this
|
||||
command a 'config_end_stop' command with the same 'oid' parameter
|
||||
must have been issued during firmware configuration. When invoked,
|
||||
the firmware will sample the endstop pin every 'rest_ticks' clock
|
||||
ticks and check if it has a value equal to 'pin_value'. If the value
|
||||
matches then the movement queue for the associated stepper will be
|
||||
cleared and the stepper will come to an immediate halt. The host
|
||||
uses this command to implement homing - the host instructs the
|
||||
endstop to sample for the endstop trigger and then it issues a
|
||||
series of queue_step commands to the stepper to move it towards the
|
||||
endstop. Once the stepper hits the endstop, the trigger will be
|
||||
detected, the movement halted, and the host notified.
|
||||
|
||||
### Move queue
|
||||
|
||||
Each queue_step command utilizes an entry in the firmware "move
|
||||
queue". The firmware allocates this queue when it receives the
|
||||
"finalize_config" command, and it reports the number of available
|
||||
queue entries in "config" response messages.
|
||||
|
||||
It is the responsibility of the host to ensure that there is available
|
||||
space in the queue before sending a queue_step command. The host does
|
||||
this by calculating when each queue_step command completes and
|
||||
scheduling new queue_step commands accordingly.
|
||||
@@ -1,55 +1,49 @@
|
||||
Klipper is currently in an experimental state. These instructions
|
||||
assume the software will run on a Raspberry Pi computer in conjunction
|
||||
with OctoPrint. Klipper supports Atmel ATmega based micro-controllers
|
||||
and Arduino Due (Atmel SAM3x8e ARM micro-controllers) printers.
|
||||
These instructions assume the software will run on a Raspberry Pi
|
||||
computer in conjunction with OctoPrint. (See the
|
||||
[Beaglebone specific instructions](beaglebone.md) if using a
|
||||
Beaglebone.) It is recommended that a Raspberry Pi 2 or Raspberry Pi 3
|
||||
computer be used as the host machine.
|
||||
|
||||
It is recommended that a Raspberry Pi 2 or Raspberry Pi 3 computer be
|
||||
used as the host. The software will run on a first generation
|
||||
Raspberry Pi, but the combined load of OctoPrint, Klipper, and a web
|
||||
cam (if applicable) can overwhelm its CPU leading to print stalls.
|
||||
It should be possible to run the Klipper host software on any computer
|
||||
running a recent Linux distribution, but doing so will require Linux
|
||||
admin knowledge to translate these installation instructions to the
|
||||
particulars of that machine.
|
||||
|
||||
Klipper currently supports Atmel ATmega based micro-controllers,
|
||||
Arduino Due (Atmel SAM3x8e ARM micro-controller), and
|
||||
[Beaglebone PRU](beaglebone.md) based printers.
|
||||
|
||||
Prepping an OS image
|
||||
====================
|
||||
|
||||
Start by installing [OctoPi](https://github.com/guysoft/OctoPi) on the
|
||||
Raspberry Pi computer. Use OctoPi v0.13.0 or later - see the
|
||||
Raspberry Pi computer. Use OctoPi v0.14.0 or later - see the
|
||||
[octopi releases](https://github.com/guysoft/OctoPi/releases) for
|
||||
release information. One should verify that OctoPi boots and that the
|
||||
OctoPrint web server works. After connecting to the OctoPrint web
|
||||
page, follow the prompt to upgrade OctoPrint to v1.3.0 or later.
|
||||
page, follow the prompt to upgrade OctoPrint to v1.3.5 or later.
|
||||
|
||||
After installing OctoPi and upgrading OctoPrint, ssh into the target
|
||||
machine (ssh pi@octopi -- password is "raspberry") and run the
|
||||
following commands:
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install libncurses-dev
|
||||
sudo apt-get install avrdude gcc-avr binutils-avr avr-libc # AVR toolchain
|
||||
sudo apt-get install bossa-cli libnewlib-arm-none-eabi # ARM toolchain
|
||||
```
|
||||
|
||||
The host software (Klippy) requires a one-time setup - run as the
|
||||
regular "pi" user:
|
||||
|
||||
```
|
||||
virtualenv ~/klippy-env
|
||||
~/klippy-env/bin/pip install cffi==1.6.0 pyserial==3.2.1 greenlet==0.4.10
|
||||
```
|
||||
|
||||
Building Klipper
|
||||
================
|
||||
|
||||
To obtain Klipper, run the following command on the target machine:
|
||||
|
||||
```
|
||||
git clone https://github.com/KevinOConnor/klipper
|
||||
cd klipper/
|
||||
./klipper/scripts/install-octopi.sh
|
||||
```
|
||||
|
||||
The above will download Klipper, install some system dependencies,
|
||||
setup Klipper to run at system startup, and start the Klipper host
|
||||
software. It will require an internet connection and it may take a few
|
||||
minutes to complete.
|
||||
|
||||
Building and flashing the micro-controller
|
||||
==========================================
|
||||
|
||||
To compile the micro-controller code, start by configuring it:
|
||||
|
||||
```
|
||||
cd ~/klipper/
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
@@ -60,54 +54,39 @@ configured, run:
|
||||
make
|
||||
```
|
||||
|
||||
Ignore any warnings you may see about "misspelled signal handler" (it
|
||||
is due to a bug fixed in gcc v4.8.3).
|
||||
|
||||
Installing Klipper on an AVR micro-controller
|
||||
---------------------------------------------
|
||||
|
||||
The avrdude package can be used to install the micro-controller code
|
||||
on an AVR ATmega chip. The exact syntax of the avrdude command is
|
||||
different for each micro-controller. The following is an example
|
||||
command for atmega2560 chips:
|
||||
Finally, for common micro-controllers, the code can be flashed with:
|
||||
|
||||
```
|
||||
example-only$ avrdude -C/etc/avrdude.conf -v -patmega2560 -cwiring -P/dev/ttyACM0 -b115200 -D -Uflash:w:/home/pi/klipper/out/klipper.elf.hex:i
|
||||
sudo service klipper stop
|
||||
make flash FLASH_DEVICE=/dev/ttyACM0
|
||||
sudo service klipper start
|
||||
```
|
||||
|
||||
Installing Klipper on an Arduino Due
|
||||
------------------------------------
|
||||
Configuring Klipper
|
||||
===================
|
||||
|
||||
Klipper currently uses the Arduino Due USB programming port (it will
|
||||
not work when connected to the application USB port). The programming
|
||||
port is the USB port closest to the power supply. To flash Klipper to
|
||||
the Due connect it to the host machine and run:
|
||||
|
||||
```
|
||||
stty -F /dev/ttyACM0 1200
|
||||
bossac -i -p ttyACM0 -R -e -w -v -b ~/klipper/out/klipper.bin
|
||||
```
|
||||
|
||||
Setting up the printer configuration
|
||||
====================================
|
||||
|
||||
It is necessary to configure the printer. This is done by modifying a
|
||||
configuration file that resides on the host. Start by copying an
|
||||
example configuration and editing it. For example:
|
||||
The Klipper configuration is stored in a text file on the Raspberry
|
||||
Pi. Take a look at the example config files in the
|
||||
[config directory](../config/). The
|
||||
[example.cfg](../config/example.cfg) file contains documentation on
|
||||
command parameters and it can also be used as an initial config file
|
||||
template. However, for most printers, one of the other config files
|
||||
may be a more concise starting point. The next step is to copy and
|
||||
edit one of these config files - for example:
|
||||
|
||||
```
|
||||
cp ~/klipper/config/example.cfg ~/printer.cfg
|
||||
nano printer.cfg
|
||||
nano ~/printer.cfg
|
||||
```
|
||||
|
||||
Make sure to look at and update each setting that is appropriate for
|
||||
Make sure to review and update each setting that is appropriate for
|
||||
the hardware.
|
||||
|
||||
Configuring OctoPrint to use Klippy
|
||||
===================================
|
||||
Configuring OctoPrint to use Klipper
|
||||
====================================
|
||||
|
||||
The OctoPrint web server needs to be configured to communicate with
|
||||
the Klippy host software. Using a web-browser, login to the OctoPrint
|
||||
the Klipper host software. Using a web browser, login to the OctoPrint
|
||||
web page, and navigate to the Settings tab. Then configure the
|
||||
following items:
|
||||
|
||||
@@ -115,40 +94,35 @@ Under "Serial Connection" in "Additional serial ports" add
|
||||
"/tmp/printer". Then click "Save".
|
||||
|
||||
Enter the Settings tab again and under "Serial Connection" change the
|
||||
"Serial Port" setting to "/tmp/printer". Change the Baudrate field to
|
||||
250000 (this buad rate field is not related to the firmware baudrate
|
||||
and may be safely left at 250000). Unselect the "Not only cancel
|
||||
ongoing prints but also disconnect..." checkbox.
|
||||
"Serial Port" setting to "/tmp/printer". Unselect the "Not only cancel
|
||||
ongoing prints but also disconnect..." checkbox. Click "Save".
|
||||
|
||||
Under the "Features" tab, unselect "Enable SD support". Then click
|
||||
"Save".
|
||||
|
||||
Running the host software
|
||||
=========================
|
||||
|
||||
The host software is executed by running the following as the regular
|
||||
"pi" user:
|
||||
|
||||
```
|
||||
~/klippy-env/bin/python ~/klipper/klippy/klippy.py ~/printer.cfg -l /tmp/klippy.log < /dev/null > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
Once Klippy is running, use a web-browser and navigate to the
|
||||
OctoPrint web site. Under the "Connection" tab (on the left of the
|
||||
main page) make sure the "Serial Port" is set to "/tmp/printer" and
|
||||
From the main page, under the "Connection" section (at the top left of
|
||||
the page) make sure the "Serial Port" is set to "/tmp/printer" and
|
||||
click "Connect". (If "/tmp/printer" is not an available selection then
|
||||
try reloading the page.)
|
||||
|
||||
Once connected, navigate to the "Terminal" tab and type "status"
|
||||
(without the quotes) into the command entry box and click "Send". If
|
||||
the Klippy config file was successfully read, and the micro-controller
|
||||
was successfully found and configured, then this command will report
|
||||
that the printer is ready. Klippy reports error messages via this
|
||||
terminal tab. The "status" command can be used to re-report error
|
||||
messages.
|
||||
(without the quotes) into the command entry box and click "Send". The
|
||||
terminal window will likely report there is an error opening the
|
||||
config file - issue a "restart" command in the OctoPrint terminal to
|
||||
load the config. A "status" command will report the printer is ready
|
||||
if the Klipper config file is successfully read and the
|
||||
micro-controller is successfully found and configured. It is not
|
||||
unusual to have configuration errors during the initial setup - update
|
||||
the printer config file and issue "restart" until "status" reports the
|
||||
printer is ready.
|
||||
|
||||
In addition to common g-code commands, Klippy supports a few extended
|
||||
commands - "status" is an example of one of these commands. Use the
|
||||
"help" command to get a list of other extended commands. In
|
||||
particular, note the "restart" command - use this command to reload
|
||||
the Klippy config file after any changes.
|
||||
Klipper reports error messages via the OctoPrint terminal tab. The
|
||||
"status" command can be used to re-report error messages. The default
|
||||
Klipper startup script also places a log in **/tmp/klippy.log** which
|
||||
provides more detailed information.
|
||||
|
||||
In addition to common g-code commands, Klipper supports a few extended
|
||||
commands - "status" and "restart" are examples of these commands. Use
|
||||
the "help" command to get a list of other extended commands.
|
||||
|
||||
Contacting the developers
|
||||
=========================
|
||||
|
||||
See the [contact page](Contact.md) to ask questions or report a bug.
|
||||
|
||||
298
docs/Kinematics.md
Normal file
@@ -0,0 +1,298 @@
|
||||
This document provides an overview of how Klipper implements robot
|
||||
motion (its [kinematics](https://en.wikipedia.org/wiki/Kinematics)).
|
||||
The contents may be of interest to both developers interested in
|
||||
working on the Klipper software as well as users interested in better
|
||||
understanding the mechanics of their machines.
|
||||
|
||||
Acceleration
|
||||
============
|
||||
|
||||
Klipper implements a constant acceleration scheme whenever the print
|
||||
head changes velocity - the velocity is gradually changed to the new
|
||||
speed instead of suddenly jerking to it. Klipper always enforces
|
||||
acceleration between the tool head and the print. The filament leaving
|
||||
the extruder can be quite fragile - rapid jerks and/or extruder flow
|
||||
changes lead to poor quality and poor bed adhesion. Even when not
|
||||
extruding, if the print head is at the same level as the print then
|
||||
rapid jerking of the head can cause disruption of recently deposited
|
||||
filament. Limiting speed changes of the print head (relative to the
|
||||
print) reduces risks of disrupting the print.
|
||||
|
||||
It is also important to limit acceleration so that the stepper motors
|
||||
do not skip or put excessive stress on the machine. Klipper limits the
|
||||
torque on each stepper by virtue of limiting the acceleration of the
|
||||
print head. Enforcing acceleration at the print head naturally also
|
||||
limits the torque of the steppers that move the print head (the
|
||||
inverse is not always true).
|
||||
|
||||
Klipper implements constant acceleration. The key formula for constant
|
||||
acceleration is:
|
||||
```
|
||||
velocity(time) = start_velocity + accel*time
|
||||
```
|
||||
|
||||
Trapezoid generator
|
||||
===================
|
||||
|
||||
Klipper uses a traditional "trapezoid generator" to model the motion
|
||||
of each move - each move has a start speed, it accelerates to a
|
||||
cruising speed at constant acceleration, it cruises at a constant
|
||||
speed, and then decelerates to the end speed using constant
|
||||
acceleration.
|
||||
|
||||

|
||||
|
||||
It's called a "trapezoid generator" because a velocity diagram of the
|
||||
move looks like a trapezoid.
|
||||
|
||||
The cruising speed is always greater than or equal to both the start
|
||||
speed and the end speed. The acceleration phase may be of zero
|
||||
duration (if the start speed is equal to the cruising speed), the
|
||||
cruising phase may be of zero duration (if the move immediately starts
|
||||
decelerating after acceleration), and/or the deceleration phase may be
|
||||
of zero duration (if the end speed is equal to the cruising speed).
|
||||
|
||||

|
||||
|
||||
Look-ahead
|
||||
==========
|
||||
|
||||
The "look-ahead" system is used to determine cornering speeds between
|
||||
moves.
|
||||
|
||||
Consider the following two moves contained on an XY plane:
|
||||
|
||||

|
||||
|
||||
In the above situation it is possible to fully decelerate after the
|
||||
first move and then fully accelerate at the start of the next move,
|
||||
but that is not ideal as all that acceleration and deceleration would
|
||||
greatly increase the print time and the frequent changes in extruder
|
||||
flow would result in poor print quality.
|
||||
|
||||
To solve this, the "look-ahead" mechanism queues multiple incoming
|
||||
moves and analyzes the angles between moves to determine a reasonable
|
||||
speed that can be obtained during the "junction" between two moves. If
|
||||
the next move is nearly in the same direction then the head need only
|
||||
slow down a little (if at all).
|
||||
|
||||

|
||||
|
||||
However, if the next move forms an acute angle (the head is going to
|
||||
travel in nearly a reverse direction on the next move) then only a
|
||||
small junction speed is permitted.
|
||||
|
||||

|
||||
|
||||
The junction speeds are determined using "approximated centripetal
|
||||
acceleration". Best
|
||||
[described by the author](https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/).
|
||||
|
||||
Klipper implements look-ahead between moves that have similar extruder
|
||||
flow rates. Other moves are relatively rare and implementing
|
||||
look-ahead between them is unnecessary.
|
||||
|
||||
Key formula for look-ahead:
|
||||
```
|
||||
end_velocity^2 = start_velocity^2 + 2*accel*move_distance
|
||||
```
|
||||
|
||||
Smoothed look-ahead
|
||||
-------------------
|
||||
|
||||
Klipper also implements a mechanism for smoothing out the motions of
|
||||
short "zigzag" moves. Consider the following moves:
|
||||
|
||||

|
||||
|
||||
In the above, the frequent changes from acceleration to deceleration
|
||||
can cause the machine to vibrate which causes stress on the machine
|
||||
and increases the noise. To reduce this, Klipper tracks both regular
|
||||
move acceleration as well as a virtual "acceleration to deceleration"
|
||||
rate. Using this system, the top speed of these short "zigzag" moves
|
||||
are limited to smooth out the printer motion:
|
||||
|
||||

|
||||
|
||||
Specifically, the code calculates what the velocity of each move would
|
||||
be if it were limited to this virtual "acceleration to deceleration"
|
||||
rate (half the normal acceleration rate by default). In the above
|
||||
picture the dashed gray lines represent this virtual acceleration rate
|
||||
for the first move. If a move can not reach its full cruising speed
|
||||
using this virtual acceleration rate then its top speed is reduced to
|
||||
the maximum speed it could obtain at this virtual acceleration
|
||||
rate. For most moves the limit will be at or above the move's existing
|
||||
limits and no change in behavior is induced. For short zigzag moves,
|
||||
however, this limit reduces the top speed. Note that it does not
|
||||
change the actual acceleration within the move - the move continues to
|
||||
use the normal acceleration scheme up to its adjusted top-speed.
|
||||
|
||||
Generating steps
|
||||
================
|
||||
|
||||
Once the look-ahead process completes, the print head movement for the
|
||||
given move is fully known (time, start position, end position,
|
||||
velocity at each point) and it is possible to generate the step times
|
||||
for the move. This process is done within "kinematic classes" in the
|
||||
Klipper code. Outside of these kinematic classes, everything is
|
||||
tracked in millimeters, seconds, and in cartesian coordinate space.
|
||||
It's the task of the kinematic classes to convert from this generic
|
||||
coordinate system to the hardware specifics of the particular printer.
|
||||
|
||||
In general, the code determines each step time by first calculating
|
||||
where along the line of movement the head would be if a step is
|
||||
taken. It then calculates what time the head should be at that
|
||||
position. Determining the time along the line of movement can be done
|
||||
using the formulas for constant acceleration and constant velocity:
|
||||
|
||||
```
|
||||
time = sqrt(2*distance/accel + (start_velocity/accel)^2) - start_velocity/accel
|
||||
time = distance/cruise_velocity
|
||||
```
|
||||
|
||||
Cartesian Robots
|
||||
----------------
|
||||
|
||||
Generating steps for cartesian printers is the simplest case. The
|
||||
movement on each axis is directly related to the movement in cartesian
|
||||
space.
|
||||
|
||||
Delta Robots
|
||||
------------
|
||||
|
||||
To generate step times on Delta printers it is necessary to correlate
|
||||
the movement in cartesian space with the movement on each stepper
|
||||
tower.
|
||||
|
||||
To simplify the math, for each stepper tower, the code calculates the
|
||||
location of a "virtual tower" that is along the line of movement.
|
||||
This virtual tower is chosen at the point where the line of movement
|
||||
(extended infinitely in both directions) would be closest to the
|
||||
actual tower.
|
||||
|
||||

|
||||
|
||||
It is then possible to calculate where the head will be along the line
|
||||
of movement after each step is taken on the virtual tower.
|
||||
|
||||

|
||||
|
||||
The key formula is Pythagoras's theorem:
|
||||
```
|
||||
distance_to_tower^2 = arm_length^2 - tower_height^2
|
||||
```
|
||||
|
||||
One complexity is that if the print head passes the virtual tower
|
||||
location then the stepper direction must be reversed. In this case
|
||||
forward steps will be taken at the start of the move and reverse steps
|
||||
will be taken at the end of the move.
|
||||
|
||||
### Delta movements beyond simple XY plane ###
|
||||
|
||||
Movement calculation is more complicated if a single move contains
|
||||
both XY movement and Z movement. These moves are rare, but they must
|
||||
still be handled correctly. A virtual tower along the line of movement
|
||||
is still calculated, but in this case the tower is not at a 90 degree
|
||||
angle relative to the line of movement:
|
||||
|
||||

|
||||
|
||||
The code continues to calculate step times using the same general
|
||||
scheme as delta moves within an XY plane, but the slope of the tower
|
||||
must also be used in the calculations.
|
||||
|
||||
Should the move contain only Z movement (ie, no XY movement at all)
|
||||
then the same math is used - just in this case the tower is parallel
|
||||
to the line of movement.
|
||||
|
||||
### Stepper motor acceleration limits ###
|
||||
|
||||
With delta kinematics it is possible for a move that is accelerating
|
||||
in cartesian space to require an acceleration on a particular stepper
|
||||
motor greater than the move's acceleration. This can occur when a
|
||||
stepper arm is more horizontal than vertical and the line of movement
|
||||
passes near that stepper's tower. Although these moves could require a
|
||||
stepper motor acceleration greater than the printer's maximum
|
||||
configured move acceleration, the effective mass moved by that stepper
|
||||
would be smaller. Thus the higher stepper acceleration does not result
|
||||
in significantly higher stepper torque and it is therefore considered
|
||||
harmless.
|
||||
|
||||
However, to avoid extreme cases, Klipper enforces a maximum ceiling on
|
||||
stepper acceleration of three times the printer's configured maximum
|
||||
move acceleration. (Similarly, the maximum velocity of the stepper is
|
||||
limited to three times the maximum move velocity.) In order to enforce
|
||||
this limit, moves at the extreme edge of the build envelope (where a
|
||||
stepper arm may be nearly horizontal) will have a lower maximum
|
||||
acceleration and velocity.
|
||||
|
||||
Extruder kinematics
|
||||
-------------------
|
||||
|
||||
Klipper implements extruder motion in its own kinematic class. Since
|
||||
the timing and speed of each print head movement is fully known for
|
||||
each move, it's possible to calculate the step times for the extruder
|
||||
independently from the step time calculations of the print head
|
||||
movement.
|
||||
|
||||
Basic extruder movement is simple to calculate. The step time
|
||||
generation uses the same constant acceleration and constant velocity
|
||||
formulas that cartesian robots use.
|
||||
|
||||
### Pressure advance ###
|
||||
|
||||
Experimentation has shown that it's possible to improve the modeling
|
||||
of the extruder beyond the basic extruder formula. In the ideal case,
|
||||
as an extrusion move progresses, the same volume of filament should be
|
||||
deposited at each point along the move and there should be no volume
|
||||
extruded after the move. Unfortunately, it's common to find that the
|
||||
basic extrusion formulas cause too little filament to exit the
|
||||
extruder at the start of extrusion moves and for excess filament to
|
||||
extrude after extrusion ends. This is often referred to as "ooze".
|
||||
|
||||

|
||||
|
||||
The "pressure advance" system attempts to account for this by using a
|
||||
different model for the extruder. Instead of naively believing that
|
||||
each mm^3 of filament fed into the extruder will result in that amount
|
||||
of mm^3 immediately exiting the extruder, it uses a model based on
|
||||
pressure. Pressure increases when filament is pushed into the extruder
|
||||
(as in [Hooke's law](https://en.wikipedia.org/wiki/Hooke%27s_law)) and
|
||||
the pressure necessary to extrude is dominated by the flow rate
|
||||
through the nozzle orifice (as in
|
||||
[Poiseuille's law](https://en.wikipedia.org/wiki/Poiseuille_law)). The
|
||||
key idea is that the relationship between filament, pressure, and flow
|
||||
rate can be modeled using a linear coefficient:
|
||||
```
|
||||
extra_filament = pressure_advance_coefficient * extruder_velocity
|
||||
```
|
||||
|
||||
See the [pressure advance](Pressure_Advance.md) document for
|
||||
information on how to find this pressure advance coefficient.
|
||||
|
||||
Once configured, Klipper will push in an additional amount of filament
|
||||
during acceleration. The higher the desired filament flow rate, the
|
||||
more filament must be pushed in during acceleration to account for
|
||||
pressure. During head deceleration the extra filament is retracted
|
||||
(the extruder will have a negative velocity).
|
||||
|
||||

|
||||
|
||||
One may notice that the pressure advance algorithm can cause the
|
||||
extruder motor to make sudden velocity changes. This is tolerated
|
||||
based on the idea that the majority of the inertia in the system is in
|
||||
changing the extruder pressure. As long as the extruder pressure does
|
||||
not change rapidly the sudden changes in extruder motor velocity are
|
||||
tolerated.
|
||||
|
||||
One area where sudden velocity changes become problematic is during
|
||||
small changes in head speed due to cornering.
|
||||
|
||||

|
||||
|
||||
To prevent this, the Klipper pressure advance code utilizes the move
|
||||
look-ahead queue to detect intermittent speed changes. During a
|
||||
deceleration event the code finds the maximum upcoming head speed
|
||||
within a configurable time window. The pressure is then only adjusted
|
||||
to this found maximum. This can greatly reduce (or even completely
|
||||
eliminate) pressure changes during cornering.
|
||||
285
docs/MCU_Commands.md
Normal file
@@ -0,0 +1,285 @@
|
||||
This document provides information on the low-level micro-controller
|
||||
commands that are sent from the Klipper "host" software and processed
|
||||
by the Klipper micro-controller software. This document is not an
|
||||
authoritative reference for these commands, nor is it an exclusive
|
||||
list of all available commands.
|
||||
|
||||
This document may be useful for developers interested in understanding
|
||||
the low-level micro-controller commands.
|
||||
|
||||
See the [protocol](Protocol.md) document for more information on the
|
||||
format of commands and their transmission. The commands here are
|
||||
described using their "printf" style syntax - for those unfamiliar
|
||||
with that format, just note that where a '%...' sequence is seen it
|
||||
should be replaced with an actual integer. For example, a description
|
||||
with "count=%c" could be replaced with the text "count=10".
|
||||
|
||||
Startup Commands
|
||||
================
|
||||
|
||||
It may be necessary to take certain one-time actions to configure the
|
||||
micro-controller and its peripherals. This section lists common
|
||||
commands available for that purpose. Unlike most micro-controller
|
||||
commands, these commands run as soon as they are received and they do
|
||||
not require any particular setup.
|
||||
|
||||
Several of these commands will take a "pin=%u" parameter. The
|
||||
low-level micro-controller software uses integer encodings of the
|
||||
hardware pin numbers, but to make things more readable the host will
|
||||
translate human readable pin names (eg, "PA3") to their equivalent
|
||||
integer encodings. By convention, any parameter named "pin" or that
|
||||
has a "_pin" suffix will use pin name translation by the
|
||||
host.
|
||||
|
||||
Common startup commands:
|
||||
|
||||
* `set_digital_out pin=%u value=%c` : This command immediately
|
||||
configures the given pin as a digital out GPIO and it sets it to
|
||||
either a low level (value=0) or a high level (value=1). This command
|
||||
may be useful for configuring the initial value of LEDs and for
|
||||
configuring the initial value of stepper driver micro-stepping pins.
|
||||
|
||||
* `set_pwm_out pin=%u cycle_ticks=%u value=%hu` : This command will
|
||||
immediately configure the given pin to use hardware based
|
||||
pulse-width-modulation (PWM) with the given number of
|
||||
cycle_ticks. The "cycle_ticks" is the number of MCU clock ticks each
|
||||
power on and power off cycle should last. A cycle_ticks value of 1
|
||||
can be used to request the fastest possible cycle time. The "value"
|
||||
parameter is between 0 and 255 with 0 indicating a full off state
|
||||
and 255 indicating a full on state. This command may be useful for
|
||||
enabling CPU and nozzle cooling fans.
|
||||
|
||||
* `send_spi_message pin=%u msg=%*s` : This command can be used to
|
||||
transmit messages to a serial-peripheral-interface (SPI) component
|
||||
connected to the micro-controller. It has been used to configure the
|
||||
startup settings of AD5206 digipots. The 'pin' parameter specifies
|
||||
the chip select line to use during the transmission. The 'msg'
|
||||
indicates the binary message to transmit to the given chip.
|
||||
|
||||
Low-level micro-controller configuration
|
||||
========================================
|
||||
|
||||
Most commands in the micro-controller require an initial setup before
|
||||
they can be successfully invoked. This section provides an overview of
|
||||
the configuration process. This section and the following sections are
|
||||
likely only of interest to developers interested in the internal
|
||||
details of Klipper.
|
||||
|
||||
When the host first connects to the micro-controller it always starts
|
||||
by obtaining a data dictionary (see [protocol](Protocol.md) for more
|
||||
information). After the data dictionary is obtained the host will
|
||||
check if the micro-controller is in a "configured" state and configure
|
||||
it if not. Configuration involves the following phases:
|
||||
|
||||
* `get_config` : The host starts by checking if the micro-controller
|
||||
is already configured. The micro-controller responds to this command
|
||||
with a "config" response message. The micro-controller software
|
||||
always starts in an unconfigured state at power-on. It remains in
|
||||
this state until the host completes the configuration processes (by
|
||||
issuing a finalize_config command). If the micro-controller is
|
||||
already configured from a previous session (and is configured with
|
||||
the desired settings) then no further action is needed by the host
|
||||
and the configuration process ends successfully.
|
||||
|
||||
* `allocate_oids count=%c` : This command is issued to inform the
|
||||
micro-controller of the maximum number of object-ids (oid) that the
|
||||
host requires. It is only valid to issue this command once. An oid
|
||||
is an integer identifier allocated to each stepper, each endstop,
|
||||
and each schedulable gpio pin. The host determines in advance the
|
||||
number of oids it will require to operate the hardware and passes
|
||||
this to the micro-controller so that it may allocate sufficient
|
||||
memory to store a mapping from oid to internal object.
|
||||
|
||||
* `config_XXX oid=%c ...` : By convention any command starting with
|
||||
the "config_" prefix creates a new micro-controller object and
|
||||
assigns the given oid to it. For example, the config_digital_out
|
||||
command will configure the specified pin as a digital output GPIO
|
||||
and create an internal object that the host can use to schedule
|
||||
changes to the given GPIO. The oid parameter passed into the config
|
||||
command is selected by the host and must be between zero and the
|
||||
maximum count supplied in the allocate_oids command. The config
|
||||
commands may only be run when the micro-controller is not in a
|
||||
configured state (ie, prior to the host sending finalize_config) and
|
||||
after the allocate_oids command has been sent.
|
||||
|
||||
* `finalize_config crc=%u` : The finalize_config command transitions
|
||||
the micro-controller from an unconfigured state to a configured
|
||||
state. The crc parameter passed to the micro-controller is stored
|
||||
and provided back to the host in "config" response messages. By
|
||||
convention, the host takes a 32bit CRC of the configuration it will
|
||||
request and at the start of subsequent communication sessions it
|
||||
checks that the CRC stored in the micro-controller exactly matches
|
||||
its desired CRC. If the CRC does not match then the host knows the
|
||||
micro-controller has not been configured in the state desired by the
|
||||
host.
|
||||
|
||||
Common micro-controller objects
|
||||
-------------------------------
|
||||
|
||||
This section lists some commonly used config commands.
|
||||
|
||||
* `config_digital_out oid=%c pin=%u value=%c default_value=%c
|
||||
max_duration=%u` : This command creates an internal micro-controller
|
||||
object for the given GPIO 'pin'. The pin will be configured in
|
||||
digital output mode and set to an initial value as specified by
|
||||
'value' (0 for low, 1 for high). Creating a digital_out object
|
||||
allows the host to schedule GPIO updates for the given pin at
|
||||
specified times (see the schedule_digital_out command described
|
||||
below). Should the micro-controller software go into shutdown mode
|
||||
then all configured digital_out objects will be set to
|
||||
'default_value'. The 'max_duration' parameter is used to implement a
|
||||
safety check - if it is non-zero then it is the maximum number of
|
||||
clock ticks that the host may set the given GPIO to a non-default
|
||||
value without further updates. For example, if the default_value is
|
||||
zero and the max_duration is 16000 then if the host sets the gpio to
|
||||
a value of one then it must schedule another update to the gpio pin
|
||||
(to either zero or one) within 16000 clock ticks. This safety
|
||||
feature can be used with heater pins to ensure the host does not
|
||||
enable the heater and then go off-line.
|
||||
|
||||
* `config_pwm_out oid=%c pin=%u cycle_ticks=%u value=%hu
|
||||
default_value=%hu max_duration=%u` : This command creates an
|
||||
internal object for hardware based PWM pins that the host may
|
||||
schedule updates for. Its usage is analogous to config_digital_out -
|
||||
see the description of the 'set_pwm_out' and 'config_digital_out'
|
||||
commands for parameter description.
|
||||
|
||||
* `config_soft_pwm_out oid=%c pin=%u cycle_ticks=%u value=%c
|
||||
default_value=%c max_duration=%u` : This command creates an internal
|
||||
micro-controller object for software implemented PWM. Unlike
|
||||
hardware pwm pins, a software pwm object does not require any
|
||||
special hardware support (other than the ability to configure the
|
||||
pin as a digital output GPIO). Because the output switching is
|
||||
implemented in the micro-controller software, it is recommended that
|
||||
the cycle_ticks parameter correspond to a time of 10ms or
|
||||
greater. See the description of the 'set_pwm_out' and
|
||||
'config_digital_out' commands for parameter description.
|
||||
|
||||
* `config_analog_in oid=%c pin=%u` : This command is used to configure
|
||||
a pin in analog input sampling mode. Once configured, the pin can be
|
||||
sampled at regular interval using the query_analog_in command (see
|
||||
below).
|
||||
|
||||
* `config_stepper oid=%c step_pin=%c dir_pin=%c min_stop_interval=%u
|
||||
invert_step=%c` : This command creates an internal stepper
|
||||
object. The 'step_pin' and 'dir_pin' parameters specify the step and
|
||||
direction pins respectively; this command will configure them in
|
||||
digital output mode. The 'invert_step' parameter specifies whether a
|
||||
step occurs on a rising edge (invert_step=0) or falling edge
|
||||
(invert_step=1). The 'min_stop_interval' implements a safety
|
||||
feature - it is checked when the micro-controller finishes all moves
|
||||
for a stepper - if it is non-zero it specifies the minimum number of
|
||||
clock ticks since the last step. It is used as a check on the
|
||||
maximum stepper velocity that a stepper may have before stopping.
|
||||
|
||||
* `config_end_stop oid=%c pin=%c pull_up=%c stepper_count=%c` : This
|
||||
command creates an internal "endstop" object. It is used to specify
|
||||
the endstop pins and to enable "homing" operations (see the
|
||||
end_stop_home command below). The command will configure the
|
||||
specified pin in digital input mode. The 'pull_up' parameter
|
||||
determines whether hardware provided pullup resistors for the pin
|
||||
(if available) will be enabled. The 'stepper_count' parameter
|
||||
specifies the maximum number of steppers that this endstop may need
|
||||
to halt during a homing operation (see end_stop_home below).
|
||||
|
||||
Common commands
|
||||
===============
|
||||
|
||||
This section lists some commonly used run-time commands. It is likely
|
||||
only of interest to developers looking to gain insight into Klipper.
|
||||
|
||||
* `schedule_digital_out oid=%c clock=%u value=%c` : This command will
|
||||
schedule a change to a digital output GPIO pin at the given clock
|
||||
time. To use this command a 'config_digital_out' command with the
|
||||
same 'oid' parameter must have been issued during micro-controller
|
||||
configuration.
|
||||
|
||||
* `schedule_pwm_out oid=%c clock=%u value=%hu` : Schedules a change to
|
||||
a hardware PWM output pin. See the 'schedule_digital_out' and
|
||||
'config_pwm_out' commands for more info.
|
||||
|
||||
* `schedule_soft_pwm_out oid=%c clock=%u value=%hu` : Schedules a
|
||||
change to a software PWM output pin. See the 'schedule_digital_out'
|
||||
and 'config_soft_pwm_out' commands for more info.
|
||||
|
||||
* `query_analog_in oid=%c clock=%u sample_ticks=%u sample_count=%c
|
||||
rest_ticks=%u min_value=%hu max_value=%hu` : This command sets up a
|
||||
recurring schedule of analog input samples. To use this command a
|
||||
'config_analog_in' command with the same 'oid' parameter must have
|
||||
been issued during micro-controller configuration. The samples will
|
||||
start as of 'clock' time, it will report on the obtained value every
|
||||
'rest_ticks' clock ticks, it will over-sample 'sample_count' number
|
||||
of times, and it will pause 'sample_ticks' number of clock ticks
|
||||
between over-sample samples. The 'min_value' and 'max_value'
|
||||
parameters implement a safety feature - the micro-controller
|
||||
software will verify the sampled value (after any oversampling) is
|
||||
always between the supplied range. This is intended for use with
|
||||
pins attached to thermistors controlling heaters - it can be used to
|
||||
check that a heater is within a temperature range.
|
||||
|
||||
* `get_status` : This command causes the micro-controller to generate
|
||||
a "status" response message. The host sends this command once a
|
||||
second to obtain the value of the micro-controller clock and to
|
||||
estimate the drift between host and micro-controller clocks. It
|
||||
enables the host to accurately estimate the micro-controller clock.
|
||||
|
||||
Stepper commands
|
||||
----------------
|
||||
|
||||
* `queue_step oid=%c interval=%u count=%hu add=%hi` : This command
|
||||
schedules 'count' number of steps for the given stepper, with
|
||||
'interval' number of clock ticks between each step. The first step
|
||||
will be 'interval' number of clock ticks since the last scheduled
|
||||
step for the given stepper. If 'add' is non-zero then the interval
|
||||
will be adjusted by 'add' amount after each step. This command
|
||||
appends the given interval/count/add sequence to a per-stepper
|
||||
queue. There may be hundreds of these sequences queued during normal
|
||||
operation. New sequence are appended to the end of the queue and as
|
||||
each sequence completes its 'count' number of steps it is popped
|
||||
from the front of the queue. This system allows the micro-controller
|
||||
to queue potentially hundreds of thousands of steps - all with
|
||||
reliable and predictable schedule times.
|
||||
|
||||
* `set_next_step_dir oid=%c dir=%c` : This command specifies the value
|
||||
of the dir_pin that the next queue_step command will use.
|
||||
|
||||
* `reset_step_clock oid=%c clock=%u` : Normally, step timing is
|
||||
relative to the last step for a given stepper. This command resets
|
||||
the clock so that the next step is relative to the supplied 'clock'
|
||||
time. The host usually only sends this command at the start of a
|
||||
print.
|
||||
|
||||
* `stepper_get_position oid=%c` : This command causes the
|
||||
micro-controller to generate a "stepper_position" response message
|
||||
with the stepper's current position. The position is the total
|
||||
number of steps generated with dir=1 minus the total number of steps
|
||||
generated with dir=0.
|
||||
|
||||
* `end_stop_home oid=%c clock=%u sample_ticks=%u sample_count=%c
|
||||
rest_ticks=%u pin_value=%c` : This command is used during stepper
|
||||
"homing" operations. To use this command a 'config_end_stop' command
|
||||
with the same 'oid' parameter must have been issued during
|
||||
micro-controller configuration. When this command is invoked, the
|
||||
micro-controller will sample the endstop pin every 'rest_ticks'
|
||||
clock ticks and check if it has a value equal to 'pin_value'. If the
|
||||
value matches (and it continues to match for 'sample_count'
|
||||
additional samples spread 'sample_ticks' apart) then the movement
|
||||
queue for the associated stepper will be cleared and the stepper
|
||||
will come to an immediate halt. The host uses this command to
|
||||
implement homing - the host instructs the endstop to sample for the
|
||||
endstop trigger and then it issues a series of queue_step commands
|
||||
to move a stepper towards the endstop. Once the stepper hits the
|
||||
endstop, the trigger will be detected, the movement halted, and the
|
||||
host notified.
|
||||
|
||||
### Move queue
|
||||
|
||||
Each queue_step command utilizes an entry in the micro-controller
|
||||
"move queue". This queue is allocated when it receives the
|
||||
"finalize_config" command, and it reports the number of available
|
||||
queue entries in "config" response messages.
|
||||
|
||||
It is the responsibility of the host to ensure that there is available
|
||||
space in the queue before sending a queue_step command. The host does
|
||||
this by calculating when each queue_step command completes and
|
||||
scheduling new queue_step commands accordingly.
|
||||
@@ -1,18 +1,36 @@
|
||||
See [installation](Installation.md) for information on compiling,
|
||||
installing, and running Klipper. Read [features](Features.md) for a
|
||||
high-level description of useful capabilities. The history of releases
|
||||
is available at [releases](Releases.md).
|
||||
Welcome to the Klipper documentation. There are two parts to Klipper -
|
||||
code that runs on a micro-controller and code that runs on a "host"
|
||||
machine. The host code is intended to run on a low-cost
|
||||
general-purpose machine such as a Raspberry Pi, while the
|
||||
micro-controller code is intended to run on commodity micro-controller
|
||||
chips. Read [features](Features.md) for reasons to use Klipper. See
|
||||
[installation](Installation.md) to get started with Klipper.
|
||||
|
||||
The Klipper configuration is stored in a simple text file on the host
|
||||
machine. The [config/example.cfg](../config/example.cfg) file serves
|
||||
as a reference for the config file. The
|
||||
[Pressure Advance](Pressure_Advance.md) document contains information
|
||||
on tuning the pressure advance config.
|
||||
|
||||
The [kinematics](Kinematics.md) document provides some technical
|
||||
details on how Klipper implements motion.
|
||||
|
||||
The history of Klipper releases is available at
|
||||
[releases](Releases.md). See [contact](Contact.md) for information on
|
||||
bug reporting and general communication with the developers.
|
||||
|
||||
Developer Documentation
|
||||
=======================
|
||||
|
||||
There are also several documents available for developers interested
|
||||
in understanding how Klipper works:
|
||||
in understanding how Klipper works. Start with the
|
||||
[code overview](Code_Overview.md) document - it provides information
|
||||
on the structure and layout of the Klipper code.
|
||||
|
||||
See [code overview](Code_Overview.md) for information on the structure
|
||||
and layout of the Klipper code.
|
||||
|
||||
See [protocol](Protocol.md) for information on the messaging protocol
|
||||
between host and firmware. See also
|
||||
[firmware commands](Firmware_Commands.md) for a high-level description
|
||||
of common commands implemented in the firmware.
|
||||
See [protocol](Protocol.md) for information on the low-level messaging
|
||||
protocol between host and micro-controller. See also
|
||||
[MCU commands](MCU_Commands.md) for a description of low-level
|
||||
commands implemented in the micro-controller software.
|
||||
|
||||
See [debugging](Debugging.md) for information on how to test and debug
|
||||
Klipper.
|
||||
|
||||
94
docs/Pressure_Advance.md
Normal file
@@ -0,0 +1,94 @@
|
||||
This document provides information on tuning the "pressure advance"
|
||||
configuration variables for a particular nozzle and filament. The
|
||||
pressure advance feature can be helpful in reducing ooze. For more
|
||||
information on how pressure advance is implemented see the
|
||||
[kinematics](Kinematics.md) document.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
In order to tune the pressure advance setting the printer must be
|
||||
configured and operational. The tuning test involves printing objects
|
||||
and inspecting the differences between objects. In particular, the
|
||||
extruder
|
||||
[E steps](http://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide#E_steps)
|
||||
and
|
||||
[nozzle temperature](http://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide#Nozzle_Temperature)
|
||||
should be tuned prior to tuning pressure advance.
|
||||
|
||||
Tuning pressure advance
|
||||
=======================
|
||||
|
||||
Pressure advance does two useful things - it reduces ooze during
|
||||
non-extrude moves and it reduces blobbing during cornering. This guide
|
||||
uses the second feature (reducing blobbing during cornering) as a
|
||||
mechanism for measuring and tuning the pressure advance configuration.
|
||||
|
||||
Start by changing the extruder section of the config file so that
|
||||
pressure_advance is set to 0.0. (Make sure to issue a RESTART command
|
||||
after each update to the config file so that the new configuration
|
||||
takes effect.) Then print at least 10 layers of a large hollow square
|
||||
at high speed (eg, 100mm/s). See
|
||||
[docs/prints/square.stl](prints/square.stl) file for an STL file that
|
||||
one may use. While the object is printing, make a note of which
|
||||
direction the head is moving during external perimeters. What many
|
||||
people see here is blobbing occurring at the corners - extra filament
|
||||
at the corner in the direction the head travels followed by a possible
|
||||
lack of filament on the side immediately after that corner:
|
||||
|
||||

|
||||
|
||||
This blobbing is the result of pressure in the extruder being released
|
||||
as a blob when the head slows down to corner.
|
||||
|
||||
The next step is to set pressure_advance_lookahead_time to 0.0, slowly
|
||||
increase pressure_advance (eg, start with 0.05), and reprint the test
|
||||
object. (Be sure to issue RESTART between each config change.) The
|
||||
goal is to attempt to eliminate the blobbing during cornering. (With
|
||||
pressure advance, the extruder will retract when the head slows down,
|
||||
thus countering the pressure buildup and ideally eliminate the
|
||||
blobbing.)
|
||||
|
||||
If a test run is done with a pressure_advance setting that is too
|
||||
high, one typically sees a dimple in the corner followed by possible
|
||||
blobbing after the corner (too much filament is retracted during slow
|
||||
down and then too much filament is extruded during the following speed
|
||||
up after cornering):
|
||||
|
||||

|
||||
|
||||
The goal is to find the smallest pressure_advance value that results
|
||||
in good quality corners:
|
||||
|
||||

|
||||
|
||||
Typical pressure_advance values are between 0.05 and 0.20 (the high
|
||||
end usually only with bowden extruders).
|
||||
|
||||
It is not unusual for one corner of the test print to be consistently
|
||||
different than the other three corners. This typically occurs when the
|
||||
slicer arranges to always change Z height at that corner. If this
|
||||
occurs, then ignore that corner and tune pressure_advance using the
|
||||
other three corners.
|
||||
|
||||
Once a good pressure_advance value is found, return
|
||||
pressure_advance_lookahead_time to its default (0.010). This parameter
|
||||
controls how far in advance to check if a head slow-down is
|
||||
immediately followed by a speed-up - it reduces pointless pressure
|
||||
changes in the head. It's possible to tune this - higher values will
|
||||
decrease the number of pressure changes in the nozzle at the expense
|
||||
of permitting more blobbing during cornering. (Tuning this value is
|
||||
unlikely to impact ooze.) The default of 10ms should work well on most
|
||||
printers.
|
||||
|
||||
Although this tuning exercise directly improves the quality of
|
||||
corners, it's worth remembering that a good pressure advance
|
||||
configuration can reduce ooze throughout the print.
|
||||
|
||||
Finally, once pressure_advance is tuned in Klipper, it may still be
|
||||
useful to configure a small retract value in the slicer (eg, 0.75mm)
|
||||
and to utilize the slicer's "wipe on retract option" if available.
|
||||
These slicer settings may help counteract ooze caused by filament
|
||||
cohesion (filament pulled out of the nozzle due to the stickiness of
|
||||
the plastic). It is recommended to disable the slicer's "z-lift on
|
||||
retract" option.
|
||||
259
docs/Protocol.md
@@ -1,8 +1,9 @@
|
||||
The Klipper transmission protocol can be thought of, at a high level,
|
||||
as a series of command and response strings that are compressed,
|
||||
transmitted over a serial line, and then processed at the receiving
|
||||
side. An example series of commands in uncompressed human-readable
|
||||
format might look like:
|
||||
The Klipper messaging protocol is used for low-level communication
|
||||
between the Klipper host software and the Klipper micro-controller
|
||||
software. At a high level the protocol can be thought of as a series
|
||||
of command and response strings that are compressed, transmitted, and
|
||||
then processed at the receiving side. An example series of commands in
|
||||
uncompressed human-readable format might look like:
|
||||
|
||||
```
|
||||
set_digital_out pin=86 value=1
|
||||
@@ -12,34 +13,35 @@ queue_step oid=7 interval=7458 count=10 add=331
|
||||
queue_step oid=7 interval=11717 count=4 add=1281
|
||||
```
|
||||
|
||||
See the [firmware commands](Firmware_Commands.md) document for
|
||||
information on available commands. See the [debugging](Debugging.md)
|
||||
document for information on how to translate a G-Code file into its
|
||||
corresponding human-readable firmware commands.
|
||||
See the [mcu commands](MCU_Commands.md) document for information on
|
||||
available commands. See the [debugging](Debugging.md) document for
|
||||
information on how to translate a G-Code file into its corresponding
|
||||
human-readable micro-controller commands.
|
||||
|
||||
This page provides a high-level description of the Klipper
|
||||
transmission protocol itself. It describes how messages are declared,
|
||||
encoded in binary format (the "compression" scheme), and transmitted.
|
||||
This page provides a high-level description of the Klipper messaging
|
||||
protocol itself. It describes how messages are declared, encoded in
|
||||
binary format (the "compression" scheme), and transmitted.
|
||||
|
||||
The goal of the protocol is to enable an error-free communication
|
||||
channel between the host and firmware that is low-latency,
|
||||
low-bandwidth, and low-complexity for the firmware.
|
||||
channel between the host and micro-controller that is low-latency,
|
||||
low-bandwidth, and low-complexity for the micro-controller.
|
||||
|
||||
Firmware Interface
|
||||
==================
|
||||
Micro-controller Interface
|
||||
==========================
|
||||
|
||||
The Klipper transmission protocol can be thought of as a
|
||||
[RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) mechanism
|
||||
between firmware and host. The firmware declares the commands that the
|
||||
host may invoke along with the response messages that it can
|
||||
generate. The host uses that information to command the firmware to
|
||||
perform actions and to interpret the results.
|
||||
between micro-controller and host. The micro-controller software
|
||||
declares the commands that the host may invoke along with the response
|
||||
messages that it can generate. The host uses that information to
|
||||
command the micro-controller to perform actions and to interpret the
|
||||
results.
|
||||
|
||||
Declaring commands
|
||||
------------------
|
||||
|
||||
The firmware declares a "command" by using the DECL_COMMAND() macro in
|
||||
the C code. For example:
|
||||
The micro-controller software declares a "command" by using the
|
||||
DECL_COMMAND() macro in the C code. For example:
|
||||
|
||||
```
|
||||
DECL_COMMAND(command_set_digital_out, "set_digital_out pin=%u value=%c");
|
||||
@@ -48,11 +50,11 @@ DECL_COMMAND(command_set_digital_out, "set_digital_out pin=%u value=%c");
|
||||
The above declares a command named "set_digital_out". This allows the
|
||||
host to "invoke" this command which would cause the
|
||||
command_set_digital_out() C function to be executed in the
|
||||
firmware. The above also indicates that the command takes two integer
|
||||
parameters. When the command_set_digital_out() C code is executed, it
|
||||
will be passed an array containing these two integers - the first
|
||||
corresponding to the 'pin' and the second corresponding to the
|
||||
'value'.
|
||||
micro-controller. The above also indicates that the command takes two
|
||||
integer parameters. When the command_set_digital_out() C code is
|
||||
executed, it will be passed an array containing these two integers -
|
||||
the first corresponding to the 'pin' and the second corresponding to
|
||||
the 'value'.
|
||||
|
||||
In general, the parameters are described with printf() style syntax
|
||||
(eg, "%u"). The formatting directly corresponds to the human-readable
|
||||
@@ -63,64 +65,61 @@ documentation. In this example, the "%c" is also used as documentation
|
||||
to indicate the expected integer is 1 byte in size (the declared
|
||||
integer size does not impact the parsing or encoding).
|
||||
|
||||
At firmware compile time, the build will collect all commands declared
|
||||
with DECL_COMMAND(), determine their parameters, and arrange for them
|
||||
to be callable.
|
||||
The micro-controller build will collect all commands declared with
|
||||
DECL_COMMAND(), determine their parameters, and arrange for them to be
|
||||
callable.
|
||||
|
||||
Declaring responses
|
||||
-------------------
|
||||
|
||||
To send information from the firmware to the host a "response" is
|
||||
generated. These are both declared and transmitted using the sendf() C
|
||||
macro. For example:
|
||||
To send information from the micro-controller to the host a "response"
|
||||
is generated. These are both declared and transmitted using the
|
||||
sendf() C macro. For example:
|
||||
|
||||
```
|
||||
sendf("status clock=%u status=%c", sched_read_time(), sched_is_shutdown());
|
||||
```
|
||||
|
||||
The above transmits a "status" response message that contains two
|
||||
integer parameters ("clock" and "status"). At firmware compile time
|
||||
the build automatically finds all sendf() calls and generates encoders
|
||||
for them. The first parameter of the sendf() function describes the
|
||||
integer parameters ("clock" and "status"). The micro-controller build
|
||||
automatically finds all sendf() calls and generates encoders for
|
||||
them. The first parameter of the sendf() function describes the
|
||||
response and it is in the same format as command declarations.
|
||||
|
||||
The host can arrange to register a callback function for each
|
||||
response. So, in effect, commands allow the host to invoke C functions
|
||||
in the firmware and responses allow the firmware to invoke code in the
|
||||
host.
|
||||
in the micro-controller and responses allow the micro-controller
|
||||
software to invoke code in the host.
|
||||
|
||||
The firmware should only invoke sendf() from command or task handlers,
|
||||
and it should not be invoked from interrupts or timers. The firmware
|
||||
does not need to issue a sendf() in response to a received command, it
|
||||
is not limited in the number of times sendf() may be invoked, and it
|
||||
may invoke sendf() at any time from a task handler.
|
||||
The sendf() macro should only be invoked from command or task
|
||||
handlers, and it should not be invoked from interrupts or timers. The
|
||||
code does not need to issue a sendf() in response to a received
|
||||
command, it is not limited in the number of times sendf() may be
|
||||
invoked, and it may invoke sendf() at any time from a task handler.
|
||||
|
||||
### Output responses
|
||||
|
||||
To simplify debugging, the firmware also has an output() C
|
||||
function. For example:
|
||||
To simplify debugging, there is also has an output() C function. For
|
||||
example:
|
||||
|
||||
```
|
||||
output("The value of %u is %s with size %u.", x, buf, buf_len);
|
||||
```
|
||||
|
||||
The output() function is similar in usage to printf() - it is intended
|
||||
to generate and format arbitrary messages for human consumption. It is
|
||||
a wrapper around sendf() and as with sendf() it should not be called
|
||||
from interrupts or timers.
|
||||
to generate and format arbitrary messages for human consumption.
|
||||
|
||||
Declaring constants
|
||||
-------------------
|
||||
|
||||
The firmware can also define constants to be exported. For example,
|
||||
the following:
|
||||
Constants can also be exported. For example, the following:
|
||||
|
||||
```
|
||||
DECL_CONSTANT(SERIAL_BAUD, 250000);
|
||||
```
|
||||
|
||||
would export a constant named "SERIAL_BAUD" with a value of 250000
|
||||
from the firmware to the host.
|
||||
from the micro-controller to the host.
|
||||
|
||||
Low-level message encoding
|
||||
==========================
|
||||
@@ -132,9 +131,9 @@ the transmission system.
|
||||
Message Blocks
|
||||
--------------
|
||||
|
||||
All data sent from host to firmware and vice-versa are contained in
|
||||
"message blocks". A message block has a two byte header and a three
|
||||
byte trailer. The format of a message block is:
|
||||
All data sent from host to micro-controller and vice-versa are
|
||||
contained in "message blocks". A message block has a two byte header
|
||||
and a three byte trailer. The format of a message block is:
|
||||
|
||||
```
|
||||
<1 byte length><1 byte sequence><n-byte content><2 byte crc><1 byte sync>
|
||||
@@ -162,10 +161,11 @@ present in the message block content.
|
||||
Message Block Contents
|
||||
----------------------
|
||||
|
||||
Each message block sent from host to firmware contains a series of
|
||||
zero or more message commands in its contents. Each command starts
|
||||
with a Variable Length Quantity (VLQ) encoded integer command-id
|
||||
followed by zero or more VLQ parameters for the given command.
|
||||
Each message block sent from host to micro-controller contains a
|
||||
series of zero or more message commands in its contents. Each command
|
||||
starts with a [Variable Length Quantity](#variable-length-quantities)
|
||||
(VLQ) encoded integer command-id followed by zero or more VLQ
|
||||
parameters for the given command.
|
||||
|
||||
As an example, the following four commands might be placed in a single
|
||||
message block:
|
||||
@@ -184,21 +184,22 @@ and encoded into the following eight VLQ integers:
|
||||
```
|
||||
|
||||
In order to encode and parse the message contents, both the host and
|
||||
firmware must agree on the command ids and the number of parameters
|
||||
each command has. So, in the above example, both the host and firmware
|
||||
would know that "id_set_digital_out" is always followed by two
|
||||
parameters, and "id_get_config" and "id_get_status" have zero
|
||||
parameters. The host and firmware share a "data dictionary" that maps
|
||||
the command descriptions (eg, "set_digital_out pin=%u value=%c") to
|
||||
their integer command-ids. When processing the data, the parser will
|
||||
know to expect a specific number of VLQ encoded parameters following a
|
||||
given command id.
|
||||
micro-controller must agree on the command ids and the number of
|
||||
parameters each command has. So, in the above example, both the host
|
||||
and micro-controller would know that "id_set_digital_out" is always
|
||||
followed by two parameters, and "id_get_config" and "id_get_status"
|
||||
have zero parameters. The host and micro-controller share a "data
|
||||
dictionary" that maps the command descriptions (eg, "set_digital_out
|
||||
pin=%u value=%c") to their integer command-ids. When processing the
|
||||
data, the parser will know to expect a specific number of VLQ encoded
|
||||
parameters following a given command id.
|
||||
|
||||
The message contents for blocks sent from firmware to host follow the
|
||||
same format. The identifiers in these messages are "response ids", but
|
||||
they serve the same purpose and follow the same encoding rules. In
|
||||
practice, message blocks sent from the firmware to the host never
|
||||
contain more than one response in the message block contents.
|
||||
The message contents for blocks sent from micro-controller to host
|
||||
follow the same format. The identifiers in these messages are
|
||||
"response ids", but they serve the same purpose and follow the same
|
||||
encoding rules. In practice, message blocks sent from the
|
||||
micro-controller to the host never contain more than one response in
|
||||
the message block contents.
|
||||
|
||||
### Variable Length Quantities
|
||||
|
||||
@@ -230,60 +231,62 @@ the length as a VLQ encoded integer followed by the contents itself:
|
||||
```
|
||||
|
||||
The command descriptions found in the data dictionary allow both the
|
||||
host and firmware to know which command parameters use simple VLQ
|
||||
encoding and which parameters use string encoding.
|
||||
host and micro-controller to know which command parameters use simple
|
||||
VLQ encoding and which parameters use string encoding.
|
||||
|
||||
Data Dictionary
|
||||
===============
|
||||
|
||||
In order for meaningful communications to be established between
|
||||
firmware and host, both sides must agree on a "data dictionary". This
|
||||
data dictionary contains the integer identifiers for commands and
|
||||
responses along with their descriptions.
|
||||
micro-controller and host, both sides must agree on a "data
|
||||
dictionary". This data dictionary contains the integer identifiers for
|
||||
commands and responses along with their descriptions.
|
||||
|
||||
At compile time the firmware build uses the contents of DECL_COMMAND()
|
||||
and sendf() macros to generate the data dictionary. The build
|
||||
The micro-controller build uses the contents of DECL_COMMAND() and
|
||||
sendf() macros to generate the data dictionary. The build
|
||||
automatically assigns unique identifiers to each command and
|
||||
response. This system allows both the host and firmware code to
|
||||
seamlessly use descriptive human-readable names while still using
|
||||
response. This system allows both the host and micro-controller code
|
||||
to seamlessly use descriptive human-readable names while still using
|
||||
minimal bandwidth.
|
||||
|
||||
The host queries the data dictionary when it first connects to the
|
||||
firmware. Once the host downloads the data dictionary from the
|
||||
firmware, it uses that data dictionary to encode all commands and to
|
||||
parse all responses from the firmware. The host must therefore handle
|
||||
a dynamic data dictionary. However, to keep the firmware simple, the
|
||||
firmware always uses its static (compiled in) data dictionary.
|
||||
micro-controller. Once the host downloads the data dictionary from the
|
||||
micro-controller, it uses that data dictionary to encode all commands
|
||||
and to parse all responses from the micro-controller. The host must
|
||||
therefore handle a dynamic data dictionary. However, to keep the
|
||||
micro-controller software simple, the micro-controller always uses its
|
||||
static (compiled in) data dictionary.
|
||||
|
||||
The data dictionary is queried by sending "identify" commands to the
|
||||
firmware. The firmware will respond to each identify command with an
|
||||
"identify_response" message. Since these two commands are needed prior
|
||||
to obtaining the data dictionary, their integer ids and parameter
|
||||
types are hard-coded in both the firmware and the host. The
|
||||
"identify_response" response id is 0, the "identify" command id
|
||||
is 1. Other than having hard-coded ids the identify command and its
|
||||
response are declared and transmitted the same way as other commands
|
||||
and responses. No other command or response is hard-coded.
|
||||
micro-controller. The micro-controller will respond to each identify
|
||||
command with an "identify_response" message. Since these two commands
|
||||
are needed prior to obtaining the data dictionary, their integer ids
|
||||
and parameter types are hard-coded in both the micro-controller and
|
||||
the host. The "identify_response" response id is 0, the "identify"
|
||||
command id is 1. Other than having hard-coded ids the identify command
|
||||
and its response are declared and transmitted the same way as other
|
||||
commands and responses. No other command or response is hard-coded.
|
||||
|
||||
The format of the transmitted data dictionary itself is a zlib
|
||||
compressed JSON string. The firmware compile process generates the
|
||||
string, compresses it, and stores it in the text section of the
|
||||
firmware. The data dictionary can be much larger than the maximum
|
||||
message block size - the host downloads it by sending multiple
|
||||
identify commands requesting progressive chunks of the data
|
||||
compressed JSON string. The micro-controller build process generates
|
||||
the string, compresses it, and stores it in the text section of the
|
||||
micro-controller flash. The data dictionary can be much larger than
|
||||
the maximum message block size - the host downloads it by sending
|
||||
multiple identify commands requesting progressive chunks of the data
|
||||
dictionary. Once all chunks are obtained the host will assemble the
|
||||
chunks, uncompress the data, and parse the contents.
|
||||
|
||||
In addition to information on the communication protocol, the data
|
||||
dictionary also contains firmware version, constants (as defined by
|
||||
DECL_CONSTANT), and static strings.
|
||||
dictionary also contains the software version, constants (as defined
|
||||
by DECL_CONSTANT), and static strings.
|
||||
|
||||
Static Strings
|
||||
--------------
|
||||
|
||||
To reduce bandwidth the data dictionary also contains a set of static
|
||||
strings known to the firmware. This is useful when sending messages
|
||||
from firmware to host. For example, if the firmware were to run:
|
||||
strings known to the micro-controller. This is useful when sending
|
||||
messages from micro-controller to host. For example, if the
|
||||
micro-controller were to run:
|
||||
|
||||
```
|
||||
shutdown("Unable to handle command");
|
||||
@@ -296,22 +299,22 @@ to their associated human-readable strings.
|
||||
Message flow
|
||||
============
|
||||
|
||||
Message commands sent from host to firmware are intended to be
|
||||
error-free. The firmware will check the CRC and sequence numbers in
|
||||
each message block to ensure the commands are accurate and
|
||||
in-order. The firmware always processes message blocks in-order -
|
||||
should it receive a block out-of-order it will discard it and any
|
||||
other out-of-order blocks until it receives blocks with the correct
|
||||
sequencing.
|
||||
Message commands sent from host to micro-controller are intended to be
|
||||
error-free. The micro-controller will check the CRC and sequence
|
||||
numbers in each message block to ensure the commands are accurate and
|
||||
in-order. The micro-controller always processes message blocks
|
||||
in-order - should it receive a block out-of-order it will discard it
|
||||
and any other out-of-order blocks until it receives blocks with the
|
||||
correct sequencing.
|
||||
|
||||
The low-level host code implements an automatic retransmission system
|
||||
for lost and corrupt message blocks sent to the firmware. To
|
||||
facilitate this, the firmware transmits an "ack message block" after
|
||||
each successfully received message block. The host schedules a timeout
|
||||
after sending each block and it will retransmit should the timeout
|
||||
expire without receiving a corresponding "ack". In addition, if the
|
||||
firmware detects a corrupt or out-of-order block it may transmit a
|
||||
"nak message block" to facilitate fast retransmission.
|
||||
for lost and corrupt message blocks sent to the micro-controller. To
|
||||
facilitate this, the micro-controller transmits an "ack message block"
|
||||
after each successfully received message block. The host schedules a
|
||||
timeout after sending each block and it will retransmit should the
|
||||
timeout expire without receiving a corresponding "ack". In addition,
|
||||
if the micro-controller detects a corrupt or out-of-order block it may
|
||||
transmit a "nak message block" to facilitate fast retransmission.
|
||||
|
||||
An "ack" is a message block with empty content (ie, a 5 byte message
|
||||
block) and a sequence number greater than the last received host
|
||||
@@ -326,15 +329,15 @@ in the event of transmission latency. The timeout, retransmit,
|
||||
windowing, and ack mechanism are inspired by similar mechanisms in
|
||||
[TCP](https://en.wikipedia.org/wiki/Transmission_Control_Protocol).
|
||||
|
||||
In the other direction, message blocks sent from firmware to host are
|
||||
designed to be error-free, but they do not have assured
|
||||
In the other direction, message blocks sent from micro-controller to
|
||||
host are designed to be error-free, but they do not have assured
|
||||
transmission. (Responses should not be corrupt, but they may go
|
||||
missing.) This is done to keep the implementation in the firmware
|
||||
simple. There is no automatic retransmission system for responses -
|
||||
the high-level code is expected to be capable of handling an
|
||||
occasional missing response (usually by re-requesting the content or
|
||||
setting up a recurring schedule of response transmission). The
|
||||
sequence number field in message blocks sent to the host is always one
|
||||
greater than the last received sequence number of message blocks
|
||||
received from the host. It is not used to track sequences of response
|
||||
message blocks.
|
||||
missing.) This is done to keep the implementation in the
|
||||
micro-controller simple. There is no automatic retransmission system
|
||||
for responses - the high-level code is expected to be capable of
|
||||
handling an occasional missing response (usually by re-requesting the
|
||||
content or setting up a recurring schedule of response
|
||||
transmission). The sequence number field in message blocks sent to the
|
||||
host is always one greater than the last received sequence number of
|
||||
message blocks received from the host. It is not used to track
|
||||
sequences of response message blocks.
|
||||
|
||||
2
docs/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
Welcome to the Klipper documentation. The
|
||||
[overview document](Overview.md) is a good starting point.
|
||||
@@ -1,6 +1,46 @@
|
||||
History of Klipper releases. Please see
|
||||
[installation](Installation.md) for information on installing Klipper.
|
||||
|
||||
Klipper 0.5.0
|
||||
=============
|
||||
|
||||
Available on 20171025. Major changes in this release:
|
||||
|
||||
* Support for printers with multiple extruders.
|
||||
* Initial support for running on the Beaglebone PRU. Initial support
|
||||
for the Replicape board.
|
||||
* Initial support for running the micro-controller code in a real-time
|
||||
Linux process.
|
||||
* Support for multiple micro-controllers. (For example, one could
|
||||
control an extruder with one micro-controller and the rest of the
|
||||
printer with another.) Software clock synchronization is implemented
|
||||
to coordinate actions between micro-controllers.
|
||||
* Stepper performance improvements (20Mhz AVRs up to 189K steps per
|
||||
second).
|
||||
* Support for controlling servos and support for defining nozzle
|
||||
cooling fans.
|
||||
* Several bug fixes and code cleanups
|
||||
|
||||
Klipper 0.4.0
|
||||
=============
|
||||
|
||||
Available on 20170503. Major changes in this release:
|
||||
|
||||
* Improved installation on Raspberry Pi machines. Most of the install
|
||||
is now scripted.
|
||||
* Support for corexy kinematics
|
||||
* Documentation updates: New Kinematics document, new Pressure Advance
|
||||
tuning guide, new example config files, and more
|
||||
* Stepper performance improvements (20Mhz AVRs over 175K steps per
|
||||
second, Arduino Due over 460K)
|
||||
* Support for automatic micro-controller resets. Support for resets
|
||||
via toggling USB power on Raspberry Pi.
|
||||
* The pressure advance algorithm now works with look-ahead to reduce
|
||||
pressure changes during cornering.
|
||||
* Support for limiting the top speed of short zigzag moves
|
||||
* Support for AD595 sensors
|
||||
* Several bug fixes and code cleanups
|
||||
|
||||
Klipper 0.3.0
|
||||
=============
|
||||
|
||||
|
||||
65
docs/Todo.md
@@ -1,5 +1,5 @@
|
||||
Klipper is currently in an experimental state. There are several
|
||||
features still to be implemented. In no particular order:
|
||||
There are several features still to be implemented in Klipper. In no
|
||||
particular order:
|
||||
|
||||
Host user interaction
|
||||
=====================
|
||||
@@ -9,13 +9,6 @@ Host user interaction
|
||||
find the error) and errors written to the log can be non-obvious to
|
||||
a user.
|
||||
|
||||
* Improve startup:
|
||||
|
||||
* Provide startup scripts so that Klippy can startup at system
|
||||
bootup.
|
||||
|
||||
* Allow loading of a new config without having to restart the mcu.
|
||||
|
||||
* Improve gcode interface:
|
||||
|
||||
* Provide a better way to handle print nozzle z offsets. The M206
|
||||
@@ -28,12 +21,6 @@ Host user interaction
|
||||
|
||||
* Improve logging:
|
||||
|
||||
* Automatically roll Klippy log files. The default log file should
|
||||
have the current date in the log file name.
|
||||
|
||||
* Report the Klippy git version in log file. Log the contents of the
|
||||
config file at startup.
|
||||
|
||||
* Possibly collate and report the statistics messages in the log in a
|
||||
more friendly way.
|
||||
|
||||
@@ -44,12 +31,12 @@ Host user interaction
|
||||
Safety features
|
||||
===============
|
||||
|
||||
* Support loading a valid step range into the firmware after
|
||||
homing. This would provide a sanity check in the firmware that would
|
||||
reduce the risk of the host commanding a stepper motor past its
|
||||
valid step range. To maintain high efficiency in the firmware, the
|
||||
firmware would only need to check periodically (eg, every 100ms)
|
||||
that the stepper is in range.
|
||||
* Support loading a valid step range into the micro-controller
|
||||
software after homing. This would provide a sanity check in the
|
||||
micro-controller that would reduce the risk of the host commanding a
|
||||
stepper motor past its valid step range. To maintain high
|
||||
efficiency, the micro-controller would only need to check
|
||||
periodically (eg, every 100ms) that the stepper is in range.
|
||||
|
||||
* Possibly support periodically querying the endstop switches and use
|
||||
multiple step ranges depending on the switch state. This would
|
||||
@@ -61,45 +48,31 @@ Safety features
|
||||
can be useful to detect a sensor failure (eg, thermistor short) that
|
||||
could otherwise cause the PID to command excessive heating.
|
||||
|
||||
* Possibly implement host based checking on the ratio between extrude
|
||||
amount and head movement.
|
||||
* Enforce acceleration and speed limits on extruder stepper motor.
|
||||
|
||||
Testing features
|
||||
================
|
||||
|
||||
* Complete the host based simulator. It's possible to compile the
|
||||
firmware for a "host simulator", but that simulator doesn't do
|
||||
anything currently. It would be useful to expand the code to support
|
||||
more error checks, kinematic simulations, and improved logging.
|
||||
micro-controller for a "host simulator", but that simulator doesn't
|
||||
do anything currently. It would be useful to expand the code to
|
||||
support more error checks, kinematic simulations, and improved
|
||||
logging.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
* Document and test running the host software on a Beagle Bone Black.
|
||||
|
||||
* Add documentation describing how to perform bed-leveling accurately
|
||||
in Klipper. Improve description of stepper phase based bed leveling.
|
||||
|
||||
* Document the kinematic formulas in Klippy. Document how acceleration
|
||||
and jerk limits are enforced.
|
||||
|
||||
* Document how one can tune the pressure advance setting.
|
||||
|
||||
Hardware features
|
||||
=================
|
||||
|
||||
* Port firmware to more architectures:
|
||||
* Beagle Bone Black PRU
|
||||
* Port to additional micro-controller architectures:
|
||||
* Smoothieboard / NXP LPC1769 (ARM cortex-M3)
|
||||
* Unix based scheduling; Unix based real-time scheduling
|
||||
|
||||
* Support for additional kinematics: scara, corexy, etc.
|
||||
* Support for additional kinematics: scara, etc.
|
||||
|
||||
* Support shared motor enable GPIO lines.
|
||||
|
||||
* Support for multiple extruders.
|
||||
|
||||
* Support for bed-level probes.
|
||||
|
||||
* Possible support for touch panels attached to the micro-controller.
|
||||
@@ -108,19 +81,9 @@ Hardware features
|
||||
it would also be useful to handle panels already hardwired to the
|
||||
micro-controller.)
|
||||
|
||||
* The raspberry pi has the ability to cut power to its USB ports. This
|
||||
feature is useful for resetting micro-controllers that are powered
|
||||
over USB. It would be useful to have a high-level command interface
|
||||
in Klippy to request a micro-controller reset via this mechanism.
|
||||
|
||||
* Possibly support printers using multiple micro-controllers.
|
||||
|
||||
Misc features
|
||||
=============
|
||||
|
||||
* Possibly use cubic functions instead of quadratic functions in step
|
||||
compression code.
|
||||
|
||||
* Possibly support a "feed forward PID" that takes into account the
|
||||
amount of plastic being extruded. If the extrude rate changes
|
||||
significantly during a print it can cause heating bumps that the PID
|
||||
|
||||
94
docs/beaglebone.md
Normal file
@@ -0,0 +1,94 @@
|
||||
This document describes the process of running Klipper on a Beaglebone
|
||||
PRU.
|
||||
|
||||
Building an OS image
|
||||
====================
|
||||
|
||||
Start by installing the
|
||||
[latest Jessie IoT](https://beagleboard.org/latest-images) image
|
||||
(2017-03-19 or later). One may run the image from either a micro-SD
|
||||
card or from builtin eMMC. If using the eMMC, install it to eMMC now
|
||||
by following the instructions from the above link.
|
||||
|
||||
Then ssh into the beaglebone machine (ssh debian@beaglebone --
|
||||
password is "temppwd") and install Klipper by running the following
|
||||
commands:
|
||||
```
|
||||
git clone https://github.com/KevinOConnor/klipper
|
||||
./klipper/scripts/install-beaglebone.sh
|
||||
```
|
||||
|
||||
Install Octoprint
|
||||
=================
|
||||
|
||||
One may then install Octoprint:
|
||||
```
|
||||
git clone https://github.com/foosel/OctoPrint.git
|
||||
cd OctoPrint/
|
||||
virtualenv venv
|
||||
./venv/bin/python setup.py install
|
||||
```
|
||||
|
||||
And setup OctoPrint to start at bootup:
|
||||
```
|
||||
sudo cp ~/OctoPrint/scripts/octoprint.init /etc/init.d/octoprint
|
||||
sudo chmod +x /etc/init.d/octoprint
|
||||
sudo cp ~/OctoPrint/scripts/octoprint.default /etc/default/octoprint
|
||||
sudo update-rc.d octoprint defaults
|
||||
```
|
||||
|
||||
It is necessary to modify OctoPrint's **/etc/default/octoprint**
|
||||
configuration file. One must change the OCTOPRINT_USER user to
|
||||
"debian", change NICELEVEL to 0, uncomment the BASEDIR, CONFIGFILE,
|
||||
and DAEMON settings and change the references from "/home/pi/" to
|
||||
"/home/debian/":
|
||||
```
|
||||
sudo nano /etc/default/octoprint
|
||||
```
|
||||
|
||||
Then start the Octoprint service:
|
||||
```
|
||||
sudo systemctl start octoprint
|
||||
```
|
||||
|
||||
Make sure the octoprint web server is accessible - it should be at:
|
||||
[http://beaglebone:5000/](http://beaglebone:5000/)
|
||||
|
||||
|
||||
Building the micro-controller code
|
||||
==================================
|
||||
|
||||
To compile the Klipper micro-controller code, start by configuring it
|
||||
for the "Beaglebone PRU":
|
||||
```
|
||||
cd ~/klipper/
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
To build and install the new micro-controller code, run:
|
||||
```
|
||||
sudo service klipper stop
|
||||
make flash
|
||||
sudo service klipper start
|
||||
```
|
||||
|
||||
For the Replicape, it is also necessary to compile and install the
|
||||
micro-controller code for a Linux host process. Run "make menuconfig"
|
||||
a second time and configure it for a "Linux process":
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
|
||||
Then install this micro-controller code as well:
|
||||
```
|
||||
sudo service klipper stop
|
||||
make flash
|
||||
sudo service klipper start
|
||||
```
|
||||
|
||||
Remaining configuration
|
||||
=======================
|
||||
|
||||
Complete the installation by configuring Klipper and Octoprint
|
||||
following the instructions in
|
||||
[the main installation document](Installation.md#configuring-klipper).
|
||||
BIN
docs/img/corner-blob.jpg
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
docs/img/corner-dimple.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/corner-good.jpg
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
184
docs/img/corner.svg
Normal file
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="54.904114mm"
|
||||
height="6.0860338mm"
|
||||
viewBox="0 0 194.54213 21.564687"
|
||||
id="svg3506"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="corner.svg">
|
||||
<defs
|
||||
id="defs3508">
|
||||
<marker
|
||||
inkscape:stockid="DiamondL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="DiamondL"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4399"
|
||||
d="M 0,-7.0710768 -7.0710894,0 0,7.0710589 7.0710462,0 0,-7.0710768 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="scale(0.8,0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Lend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4341"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker4596"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4598"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4329"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.49"
|
||||
inkscape:cx="95.030833"
|
||||
inkscape:cy="-0.17370789"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:window-width="1068"
|
||||
inkscape:window-height="478"
|
||||
inkscape:window-x="378"
|
||||
inkscape:window-y="113"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid6021"
|
||||
spacingx="9.9999997"
|
||||
spacingy="10.000001"
|
||||
originx="0.89299989"
|
||||
originy="-30.954583" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3511">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-253.40821,-436.43703)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
|
||||
d="m 345.38554,440.31401 96.88541,12.96764"
|
||||
id="path3514"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)"
|
||||
d="m 253.63788,454.62572 89.78715,-13.91164"
|
||||
id="path3514-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="189.09824"
|
||||
y="482.48389"
|
||||
id="text12656-9"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0.98759291,-0.15703579,0.15703579,0.98759291,0,0)"
|
||||
inkscape:transform-center-x="1.3563414"
|
||||
inkscape:transform-center-y="-5.7099754"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5552"
|
||||
x="189.09824"
|
||||
y="482.48389">move 1</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="427.95532"
|
||||
y="379.5321"
|
||||
id="text12656-9-8"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0.98949457,0.14457001,-0.14457001,0.98949457,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5554"
|
||||
x="427.95532"
|
||||
y="379.5321">move 2</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/img/corner.svg.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
273
docs/img/delta-tower.svg
Normal file
@@ -0,0 +1,273 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="delta-tower.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6618"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6620"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="marker6500"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path6502"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1;fill:#4b4b4b;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible;"
|
||||
id="marker6082"
|
||||
refX="0.0"
|
||||
refY="0.0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="scale(0.4) rotate(180) translate(10,0)"
|
||||
style="fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1;fill:#4b4b4b;fill-opacity:1"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
id="path6084" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible;"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path5747"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1;fill:#4b4b4b;fill-opacity:1"
|
||||
transform="scale(0.4) rotate(180) translate(10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path5744"
|
||||
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||
style="fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1;fill:#4b4b4b;fill-opacity:1"
|
||||
transform="scale(0.4) translate(10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6082-9"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6084-2" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.75"
|
||||
inkscape:cx="172.24895"
|
||||
inkscape:cy="45.708959"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="928"
|
||||
inkscape:window-height="628"
|
||||
inkscape:window-x="162"
|
||||
inkscape:window-y="50"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<ellipse
|
||||
style="fill:#4d4d4d;stroke:#4b4b4b;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1, 0.5, 1;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4255"
|
||||
cx="353.79568"
|
||||
cy="327.87662"
|
||||
rx="4.2857141"
|
||||
ry="4.8571429" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000006;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="M 181.50998,381.87664 359.22427,275.01949"
|
||||
id="path5510"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="371.22427"
|
||||
y="354.44806"
|
||||
id="text6058"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6060"
|
||||
x="371.22427"
|
||||
y="354.44806">stepper</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="371.22427"
|
||||
y="366.94806"
|
||||
id="tspan9337">tower</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="166.65283"
|
||||
y="304.16235"
|
||||
id="text6062"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6064"
|
||||
x="166.65283"
|
||||
y="304.16235">line of movement</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.0000006px;line-height:125%;font-family:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;-inkscape-font-specification:'DejaVu Sans, Normal';font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;"
|
||||
x="252.93855"
|
||||
y="384.16235"
|
||||
id="text6066"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6068"
|
||||
x="252.93855"
|
||||
y="384.16235">move</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000006;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#marker6618)"
|
||||
d="m 382.65284,344.73378 c -0.19273,-13.52091 -9.87887,-14.83602 -20.57143,-14.85715"
|
||||
id="path6070"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker6500)"
|
||||
d="m 254.65284,374.44806 c 3.39239,-12.86009 -2.06023,-20.09154 -15.42857,-22.28571"
|
||||
id="path6072"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6082)"
|
||||
d="m 257.50997,296.16234 c 31.05376,-3.13332 32.38959,-1.23784 42.28572,10.28572"
|
||||
id="path6074"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)"
|
||||
d="m 219.61431,358.80126 57.78715,-34.48307"
|
||||
id="path3514-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:0.50000003;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 351.22427,323.59092 -20.57143,-32"
|
||||
id="path3358"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="244.36713"
|
||||
y="257.30521"
|
||||
id="text4460"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4462"
|
||||
x="244.36713"
|
||||
y="257.30521">virtual tower</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="244.36713"
|
||||
y="269.80521"
|
||||
id="tspan5867">location</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6082-9)"
|
||||
d="m 310.90661,257.14111 c 21.29088,8.40268 18.35244,16.28958 20.57143,29.7143"
|
||||
id="path6074-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/delta-tower.svg.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
208
docs/img/lookahead-slow.svg
Normal file
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="lookahead-slow.svg"
|
||||
inkscape:export-filename="/home/kevin/src/reprap/firmware/klipper/docs/img/lookahead-slow.svg.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path4329"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.15"
|
||||
inkscape:cx="3.4198125"
|
||||
inkscape:cy="101.26451"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="149"
|
||||
inkscape:window-y="422"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.62366331px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179.63013,351.45141 16.05677,-60.94328 43.25999,0 16.53759,47.11348"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.7558428px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 254.72406,337.27551 22.65101,-77.77178 43.89917,0 24.69858,91.73948"
|
||||
id="path3361-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="195.45749"
|
||||
y="286.52051"
|
||||
id="text12656-9"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7078"
|
||||
x="195.45749"
|
||||
y="286.52051">move A</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="280.5639"
|
||||
y="254.60564"
|
||||
id="text12656-9-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7080"
|
||||
x="280.5639"
|
||||
y="254.60564">move B</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
|
||||
d="m 74.333855,283.02668 -147.83244,52.19984"
|
||||
id="path3514"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)"
|
||||
d="m -79.633985,251.6122 154.13499,31.81455"
|
||||
id="path3514-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="36.7374"
|
||||
y="252.31848"
|
||||
id="text12656-9-1"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0.96753827,0.25272454,-0.25272454,0.96753827,0,0)"
|
||||
inkscape:transform-center-x="-0.91382951"
|
||||
inkscape:transform-center-y="-4.9145266"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7074"
|
||||
x="36.7374"
|
||||
y="252.31848">move A</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-126.26086"
|
||||
y="304.35226"
|
||||
id="text12656-9-8"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0.93839918,-0.34555314,0.34555314,0.93839918,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7076"
|
||||
x="-126.26086"
|
||||
y="304.35226">move B</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
BIN
docs/img/lookahead-slow.svg.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
136
docs/img/lookahead.svg
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="lookahead.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.94"
|
||||
inkscape:cx="116.54041"
|
||||
inkscape:cy="45.708959"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="149"
|
||||
inkscape:window-y="422"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.62366331px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179.63013,351.45141 16.05677,-60.94328 61.3451,0 4.83546,8.81561"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.7558428px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 261.70791,300.17937 13.5395,-40.67564 59.85662,0 24.69858,91.73948"
|
||||
id="path3361-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="200.77664"
|
||||
y="286.52051"
|
||||
id="text12656-9"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5532"
|
||||
x="200.77664"
|
||||
y="286.52051">move 1</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="280.5639"
|
||||
y="255.66946"
|
||||
id="text12656-9-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5534"
|
||||
x="280.5639"
|
||||
y="255.66946">move 2</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
BIN
docs/img/lookahead.svg.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
207
docs/img/ooze.svg
Normal file
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="98.140816mm"
|
||||
height="63.537022mm"
|
||||
viewBox="0 0 347.74305 225.13119"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="ooze.svg"
|
||||
inkscape:export-filename="/home/kevin/src/reprap/firmware/klipper/docs/img/ooze.svg.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.94"
|
||||
inkscape:cx="199.68782"
|
||||
inkscape:cy="58.510649"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="266"
|
||||
inkscape:window-y="106"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436"
|
||||
originx="-1.5695746e-05"
|
||||
originy="109.79552" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22431,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-346.84067"
|
||||
y="139.75046"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12733"
|
||||
x="-346.84067"
|
||||
y="139.75046">head velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 179.63013,351.45141 16.05677,-60.94328 120.91957,-1.06383 16.53759,62.00711"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.78742969px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 332.99641,351.24986 16.72764,-63.00287 133.24331,0"
|
||||
id="path3361-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="200.11789"
|
||||
y="284.45413"
|
||||
id="text12656"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12658"
|
||||
x="200.11789"
|
||||
y="284.45413">extrude move</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="356.50089"
|
||||
y="283.39032"
|
||||
id="text12660"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12662"
|
||||
x="356.50089"
|
||||
y="283.39032">non-extrude move</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 152.72419,471.73218 1.06383,102.12766 327.12768,-0.53192 -7.97872,5.85107"
|
||||
id="path3347-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="436.76678"
|
||||
y="586.62585"
|
||||
id="text3349-3"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351-4"
|
||||
x="436.76678"
|
||||
y="586.62585">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-551.11292"
|
||||
y="125.37186"
|
||||
id="text3353-0"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4197"
|
||||
x="-551.11292"
|
||||
y="125.37186">actual</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4199"
|
||||
x="-551.11292"
|
||||
y="140.99686">filament</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 152.72419,471.73218 -5.31915,8.51063"
|
||||
id="path3359-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 182.35432,572.94905 c 17.46262,-43.80215 52.67413,-56.54375 92.65253,-60.94329 l 48.57915,-1.06383 c 24.916,55.4715 110.00504,59.23318 151.64398,62.00712"
|
||||
id="path3361-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150.87843,360.66993 1.06383,102.12766 327.12768,-0.53192 -7.97872,5.85107"
|
||||
id="path3347-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-444.27307"
|
||||
y="124.17301"
|
||||
id="text3353-6"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4193"
|
||||
x="-444.27307"
|
||||
y="124.17301">desired</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4195"
|
||||
x="-444.27307"
|
||||
y="139.79802">filament</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150.87843,360.66993 -5.31915,8.51063"
|
||||
id="path3359-8"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 180.50856,461.8868 16.05678,-60.94329 120.91958,-1.06383 16.53759,62.00712"
|
||||
id="path3361-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
BIN
docs/img/ooze.svg.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
207
docs/img/pressure-advance.svg
Normal file
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="102.61139mm"
|
||||
height="99.266594mm"
|
||||
viewBox="0 0 363.58366 351.73204"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="pressure-advance.svg"
|
||||
inkscape:export-filename="/home/kevin/src/reprap/firmware/klipper/docs/img/pressure-advance.svg.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.97968464"
|
||||
inkscape:cx="147.06528"
|
||||
inkscape:cy="169.55487"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="419"
|
||||
inkscape:window-y="273"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436"
|
||||
originx="17.089787"
|
||||
originy="126.61899" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-118.13451,-140.19216)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150.47693,249.82417 0.58688,123.9666 m 0,-21.42857 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-334.86746"
|
||||
y="122.91875"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12816"
|
||||
x="-334.86746"
|
||||
y="122.91875">extruder</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12818"
|
||||
x="-334.86746"
|
||||
y="138.54375">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 316.60647,311.78473 16.53759,62.00711 m -154.57776,-52.12767 16.05677,-60.94328 1.06383,29.78724 m 0,0 120.91957,-1.06383 0,22.34043"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 148.75077,140.45716 1.06383,102.12766 327.12769,-0.53192 -7.97872,5.85107"
|
||||
id="path3347-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-237.05733"
|
||||
y="140.25932"
|
||||
id="text3353-2"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12733-6"
|
||||
x="-237.05733"
|
||||
y="140.25932">head velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 148.75077,140.45716 -5.31915,8.51063"
|
||||
id="path3359-7"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 178.3809,241.67403 16.05679,-60.94329 120.91958,-1.06383 16.53759,62.00712"
|
||||
id="path3361-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.78742969px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 331.74721,241.47248 16.72764,-63.00288 133.24332,0"
|
||||
id="path3361-7-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="198.86868"
|
||||
y="174.67674"
|
||||
id="text12656-9"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12658-8"
|
||||
x="198.86868"
|
||||
y="174.67674">extrude move</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="355.25165"
|
||||
y="173.61293"
|
||||
id="text12660-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12662-2"
|
||||
x="355.25165"
|
||||
y="173.61293">non-extrude move</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 151.94226,384.0742 1.06383,102.12766 327.12769,-0.53192 -7.97872,5.85107"
|
||||
id="path3347-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-467.65732"
|
||||
y="122.73453"
|
||||
id="text3353-1"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12868"
|
||||
x="-467.65732"
|
||||
y="122.73453">extruder</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12870"
|
||||
x="-467.65732"
|
||||
y="138.35953">pressure</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 151.94226,384.0742 -5.31915,8.51063"
|
||||
id="path3359-5"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 181.57239,485.29107 16.05679,-60.94329 120.91958,-1.06383 16.53759,62.00712"
|
||||
id="path3361-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="438.03586"
|
||||
y="499.53384"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="438.03586"
|
||||
y="499.53384">time</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
BIN
docs/img/pressure-advance.svg.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
180
docs/img/pressure-cornering.svg
Normal file
@@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="102.04809mm"
|
||||
height="61.494076mm"
|
||||
viewBox="0 0 361.58771 217.8924"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="pressure-cornering.svg"
|
||||
inkscape:export-filename="/home/kevin/src/reprap/firmware/klipper/docs/img/pressure-cornering.svg.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98"
|
||||
inkscape:cx="110.74341"
|
||||
inkscape:cy="35.715236"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="656"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436"
|
||||
originx="17.089805"
|
||||
originy="-7.2206491" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-118.13449,-140.19216)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-334.86746"
|
||||
y="122.91875"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12816"
|
||||
x="-334.86746"
|
||||
y="122.91875">extruder</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12818"
|
||||
x="-334.86746"
|
||||
y="138.54375">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 178.5663,321.66417 16.05677,-60.94328 1.06383,29.78724 m 0,0 117.21969,-0.77616 5.99519,7.3871 5.9952,-6.89861 131.50541,-0.77616"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 148.75077,140.45716 1.06383,102.12766 327.12769,-0.53192 -7.97872,5.85107"
|
||||
id="path3347-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.50000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-237.05733"
|
||||
y="140.25932"
|
||||
id="text3353-2"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12733-6"
|
||||
x="-237.05733"
|
||||
y="140.25932">head velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 148.75077,140.45716 -5.31915,8.51063"
|
||||
id="path3359-7"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00000024;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 178.3809,241.67403 16.05679,-60.94329 120.91958,-1.06383 4.29269,10.98671"
|
||||
id="path3361-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 319.50231,193.5133 4.48274,-15.0437 133.24332,0"
|
||||
id="path3361-7-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="198.86868"
|
||||
y="174.67674"
|
||||
id="text12656-9"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12891"
|
||||
x="198.86868"
|
||||
y="174.67674">first extrude</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.25px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="355.25165"
|
||||
y="173.61293"
|
||||
id="text12660-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan12893"
|
||||
x="355.25165"
|
||||
y="173.61293">second extrude</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 2, 1, 2;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 314.05288,288.86296 0,17.34694 5.10204,17.34694 -1.02041,-48.97959 7.14286,-17.34694 0,31.63265"
|
||||
id="path12895"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="435.80841"
|
||||
y="366.8262"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="435.80841"
|
||||
y="366.8262">time</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
BIN
docs/img/pressure-cornering.svg.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
132
docs/img/smoothed.svg
Normal file
@@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="smoothed.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.75"
|
||||
inkscape:cx="167.32577"
|
||||
inkscape:cy="45.708959"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="840"
|
||||
inkscape:window-height="628"
|
||||
inkscape:window-x="162"
|
||||
inkscape:window-y="50"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 165.50998,351.59092 15.42857,-35.42857 35.71428,0 11.71429,36.57143"
|
||||
id="path3362"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.07142997px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 227.83578,352.85633 15.76217,-47.37239 22.00135,0 5.58242,17.6933"
|
||||
id="path3362-3"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.21482575px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 271.35434,323.73296 11.15454,-30.54076 19.27119,-0.11152 8.73883,29.51288"
|
||||
id="path3362-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.01949775px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 311.26144,322.49329 11.63197,-21.06975 15.33656,0 13.51652,50.22357"
|
||||
id="path3362-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.00000012, 1.00000006, 0.50000003, 1.00000006;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 165.50999,351.59092 33.14285,-34.28571 29.14286,34.28571"
|
||||
id="path3426"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
BIN
docs/img/smoothed.svg.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
102
docs/img/trapezoid.svg
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="trapezoid.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.94"
|
||||
inkscape:cx="294.7319"
|
||||
inkscape:cy="45.708959"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="113"
|
||||
inkscape:window-y="31"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179.78723,351.29837 41.48937,-60.63829 158.51063,0 37.23405,60.63829"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
BIN
docs/img/trapezoid.svg.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
182
docs/img/trapezoids.svg
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="trapezoids.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.67"
|
||||
inkscape:cx="164.48301"
|
||||
inkscape:cy="76.011989"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1091"
|
||||
inkscape:window-height="588"
|
||||
inkscape:window-x="113"
|
||||
inkscape:window-y="31"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.62366331px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 179.63013,351.45141 16.05677,-60.94328 61.3451,0 6.96312,32.21987"
|
||||
id="path3361"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.7558428px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 263.83557,321.45597 11.41184,-61.95224 59.85662,0 9.80496,29.88826"
|
||||
id="path3361-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.7558428px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 344.93248,289.80297 59.85663,0 15.12411,61.95225"
|
||||
id="path3361-7-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 263.86975,321.69491 0.37453,30.52957"
|
||||
id="path3412"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 345.25947,289.31929 0.14909,62.46104"
|
||||
id="path3414"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 222.45831,289.77329 -0.0598,62.07666"
|
||||
id="path3416"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:20.00000119px;line-height:125%;font-family:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;-inkscape-font-specification:'DejaVu Sans, Normal';font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;"
|
||||
x="193.73491"
|
||||
y="338.70944"
|
||||
id="text3418"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3420"
|
||||
x="193.73491"
|
||||
y="338.70944">1</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.00000191px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="236.28812"
|
||||
y="338.70947"
|
||||
id="text3422"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3434"
|
||||
x="236.28812"
|
||||
y="338.70947">2</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.00000191px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="294.79874"
|
||||
y="335.51794"
|
||||
id="text3426"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3428"
|
||||
x="294.79874"
|
||||
y="335.51794">3</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:20.00000119px;line-height:125%;font-family:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;-inkscape-font-specification:'DejaVu Sans, Normal';font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;"
|
||||
x="367.13916"
|
||||
y="337.6456"
|
||||
id="text3430"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3432"
|
||||
x="367.13916"
|
||||
y="337.6456">4</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
BIN
docs/img/trapezoids.svg.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
241
docs/img/virtual-tower.svg
Normal file
@@ -0,0 +1,241 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64.619751mm"
|
||||
height="27.45583mm"
|
||||
viewBox="0 0 228.96762 97.284438"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="virtual-tower.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6618"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6620"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker6500"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path6502"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path5747"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path5744"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6082"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6084" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.75"
|
||||
inkscape:cx="169.7719"
|
||||
inkscape:cy="-4.0565054"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="922"
|
||||
inkscape:window-height="628"
|
||||
inkscape:window-x="162"
|
||||
inkscape:window-y="50"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436"
|
||||
originx="-14.085234"
|
||||
originy="-95.286988" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-149.30952,-172.73378)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 176.36712,256.16235 200.00001,-0.57143"
|
||||
id="path5510"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-254.01012"
|
||||
y="369.20834"
|
||||
id="text6058"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01833576,-0.99983189,0.99983189,-0.01833576,0,0)"
|
||||
inkscape:transform-center-x="-8.0000002"
|
||||
inkscape:transform-center-y="12.571429"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan10365"
|
||||
x="-254.01012"
|
||||
y="369.20834">virtual tower</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="148.36713"
|
||||
y="269.87662"
|
||||
id="text6062"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6064"
|
||||
x="148.36713"
|
||||
y="269.87662">line of movement</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="237.50998"
|
||||
y="251.01949"
|
||||
id="text6066"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6068"
|
||||
x="237.50998"
|
||||
y="251.01949">move</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:7.00000048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 358.08141,255.01949 0,-82.28571"
|
||||
id="path10351"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 220.36713,255.01949 357.50998,173.30521"
|
||||
id="path10373"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="200.36713"
|
||||
y="187.59093"
|
||||
id="text10375"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan10377"
|
||||
x="200.36713"
|
||||
y="187.59093">virtual arm</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6082)"
|
||||
d="m 261.76375,182.85539 c 22.73118,-0.70136 26.45506,3.7437 40.00001,18.28573"
|
||||
id="path6074"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)"
|
||||
d="m 219.61431,255.37268 70.93001,0.37408"
|
||||
id="path3514-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
BIN
docs/img/virtual-tower.svg.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
241
docs/img/xy+z-tower.svg
Normal file
@@ -0,0 +1,241 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64.619751mm"
|
||||
height="27.45583mm"
|
||||
viewBox="0 0 228.96762 97.284438"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="xy+z-tower.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6618"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6620"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker6500"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path6502"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path5747"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path5744"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6082"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#4b4b4b;fill-opacity:1;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
|
||||
id="path6084" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.75"
|
||||
inkscape:cx="169.7719"
|
||||
inkscape:cy="-4.0565054"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="922"
|
||||
inkscape:window-height="628"
|
||||
inkscape:window-x="162"
|
||||
inkscape:window-y="50"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436"
|
||||
originx="-14.085234"
|
||||
originy="-95.286988" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-149.30952,-172.73378)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 176.36712,256.16235 200.00001,-0.57143"
|
||||
id="path5510"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-119.27526"
|
||||
y="434.37329"
|
||||
id="text6058"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(0.32454206,-0.94587127,0.94587127,0.32454206,0,0)"
|
||||
inkscape:transform-center-x="-3.9400734"
|
||||
inkscape:transform-center-y="14.789029"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan10365"
|
||||
x="-119.27526"
|
||||
y="434.37329">virtual tower</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="148.36713"
|
||||
y="269.87662"
|
||||
id="text6062"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6064"
|
||||
x="148.36713"
|
||||
y="269.87662">line of movement</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="237.50998"
|
||||
y="251.01949"
|
||||
id="text6066"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6068"
|
||||
x="237.50998"
|
||||
y="251.01949">move</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:7.00000048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 354.70239,255.76769 28.12779,-77.32895"
|
||||
id="path10351"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 220.36713,255.01949 380.93855,178.44807"
|
||||
id="path10373"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.00000095px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="200.36713"
|
||||
y="187.59093"
|
||||
id="text10375"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan10377"
|
||||
x="200.36713"
|
||||
y="187.59093">virtual arm</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#4b4b4b;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker6082)"
|
||||
d="m 261.76375,182.85539 c 22.73118,-0.70136 26.45506,3.7437 40.00001,18.28573"
|
||||
id="path6074"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-1)"
|
||||
d="m 219.61431,255.37268 70.93001,0.37408"
|
||||
id="path3514-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
BIN
docs/img/xy+z-tower.svg.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
126
docs/img/zigzag.svg
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="97.22496mm"
|
||||
height="32.550285mm"
|
||||
viewBox="0 0 344.49789 115.33566"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="zigzag.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.75"
|
||||
inkscape:cx="167.32577"
|
||||
inkscape:cy="45.708959"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="840"
|
||||
inkscape:window-height="628"
|
||||
inkscape:window-x="160"
|
||||
inkscape:window-y="35"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showborder="false"
|
||||
inkscape:snap-global="false"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3436" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-135.22429,-249.96955)">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 1.06383,102.12765 327.12765,-0.53192 -7.97871,5.85107"
|
||||
id="path3347"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="434.04257"
|
||||
y="365.1282"
|
||||
id="text3349"
|
||||
sodipodi:linespacing="100%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3351"
|
||||
x="434.04257"
|
||||
y="365.1282">time</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.5px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
x="-313.86618"
|
||||
y="140.27856"
|
||||
id="text3353"
|
||||
sodipodi:linespacing="125%"
|
||||
transform="matrix(-0.01601372,-0.99987177,0.99987177,-0.01601372,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3355"
|
||||
x="-313.86618"
|
||||
y="140.27856">velocity</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 150,250.23455 -5.31915,8.51063"
|
||||
id="path3359"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 165.50998,351.59092 24.57143,-73.14286 23.42857,73.14286"
|
||||
id="path3362"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 214.08032,352.31321 24.57144,-73.14287 13.14286,43.42858"
|
||||
id="path3362-3"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.09757805px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 252.42963,323.78006 17.64919,-60.34771 14.22362,59.20485"
|
||||
id="path3362-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 284.36604,322.59892 14.85715,-44.00001 3.71429,0 16.85715,73.14287"
|
||||
id="path3362-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
BIN
docs/img/zigzag.svg.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
16
docs/prints/square.scad
Normal file
@@ -0,0 +1,16 @@
|
||||
// Test square
|
||||
//
|
||||
// Generate STL using OpenSCAD:
|
||||
// openscad square.scad -o square.stl
|
||||
|
||||
square_width = 5;
|
||||
square_size = 60;
|
||||
square_height = 5;
|
||||
|
||||
difference() {
|
||||
cube([square_size, square_size, square_height]);
|
||||
translate([square_width, square_width, -1])
|
||||
cube([square_size-2*square_width, square_size-2*square_width, square_height+2]);
|
||||
translate([-.5, square_size/2 - 4, -1])
|
||||
cube([1, 2, square_height+2]);
|
||||
}
|
||||
338
docs/prints/square.stl
Normal file
@@ -0,0 +1,338 @@
|
||||
solid OpenSCAD_Model
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 0 26 5
|
||||
vertex 0 26 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 -0 0
|
||||
outer loop
|
||||
vertex 0 26 5
|
||||
vertex 0 0 0
|
||||
vertex 0 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 28 0
|
||||
vertex 0 60 5
|
||||
vertex 0 60 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 -0 0
|
||||
outer loop
|
||||
vertex 0 60 5
|
||||
vertex 0 28 0
|
||||
vertex 0 28 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 60 60 5
|
||||
vertex 55 55 5
|
||||
vertex 60 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 60 60 5
|
||||
vertex 5 55 5
|
||||
vertex 55 55 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 5 55 5
|
||||
vertex 0.5 28 5
|
||||
vertex 5 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 0 1
|
||||
outer loop
|
||||
vertex 0 60 5
|
||||
vertex 5 55 5
|
||||
vertex 60 60 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 28 5
|
||||
vertex 5 55 5
|
||||
vertex 0 60 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 5 55 5
|
||||
vertex 0 28 5
|
||||
vertex 0.5 28 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 0 1
|
||||
outer loop
|
||||
vertex 55 5 5
|
||||
vertex 60 0 5
|
||||
vertex 55 55 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 0 1
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 60 0 5
|
||||
vertex 55 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 0 1
|
||||
outer loop
|
||||
vertex 0.5 26 5
|
||||
vertex 5 5 5
|
||||
vertex 0.5 28 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 0 1
|
||||
outer loop
|
||||
vertex 0 26 5
|
||||
vertex 5 5 5
|
||||
vertex 0.5 26 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 0 0 5
|
||||
vertex 60 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 0 5
|
||||
vertex 5 5 5
|
||||
vertex 0 26 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 -0 0
|
||||
outer loop
|
||||
vertex 60 0 5
|
||||
vertex 60 60 0
|
||||
vertex 60 60 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 60 60 0
|
||||
vertex 60 0 5
|
||||
vertex 60 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 60 60 0
|
||||
vertex 0 60 5
|
||||
vertex 60 60 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 60 5
|
||||
vertex 60 60 0
|
||||
vertex 0 60 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 60 0 0
|
||||
vertex 55 5 0
|
||||
vertex 60 60 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 60 0 0
|
||||
vertex 5 5 0
|
||||
vertex 55 5 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0.5 28 0
|
||||
vertex 5 5 0
|
||||
vertex 0.5 26 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 5 5 0
|
||||
vertex 60 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 26 0
|
||||
vertex 5 5 0
|
||||
vertex 0 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5 5 0
|
||||
vertex 0 26 0
|
||||
vertex 0.5 26 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 55 55 0
|
||||
vertex 60 60 0
|
||||
vertex 55 5 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5 55 0
|
||||
vertex 60 60 0
|
||||
vertex 55 55 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5 5 0
|
||||
vertex 0.5 28 0
|
||||
vertex 5 55 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 28 0
|
||||
vertex 5 55 0
|
||||
vertex 0.5 28 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5 55 0
|
||||
vertex 0 60 0
|
||||
vertex 60 60 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 60 0
|
||||
vertex 5 55 0
|
||||
vertex 0 28 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 0 0
|
||||
vertex 60 0 5
|
||||
vertex 0 0 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 60 0 5
|
||||
vertex 0 0 0
|
||||
vertex 60 0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 -0 0
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 5 55 0
|
||||
vertex 5 55 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 5 55 0
|
||||
vertex 5 5 5
|
||||
vertex 5 5 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 55 5 0
|
||||
vertex 55 55 5
|
||||
vertex 55 55 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 -0 0
|
||||
outer loop
|
||||
vertex 55 55 5
|
||||
vertex 55 5 0
|
||||
vertex 55 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 5 55 0
|
||||
vertex 55 55 5
|
||||
vertex 5 55 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 55 55 5
|
||||
vertex 5 55 0
|
||||
vertex 55 55 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 55 5 0
|
||||
vertex 5 5 5
|
||||
vertex 55 5 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 5 5 5
|
||||
vertex 55 5 0
|
||||
vertex 5 5 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0.5 26 0
|
||||
vertex 0.5 28 5
|
||||
vertex 0.5 28 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 -0 0
|
||||
outer loop
|
||||
vertex 0.5 28 5
|
||||
vertex 0.5 26 0
|
||||
vertex 0.5 26 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 28 0
|
||||
vertex 0.5 28 5
|
||||
vertex 0 28 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 0.5 28 5
|
||||
vertex 0 28 0
|
||||
vertex 0.5 28 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 0.5 26 0
|
||||
vertex 0 26 5
|
||||
vertex 0.5 26 5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 26 5
|
||||
vertex 0.5 26 0
|
||||
vertex 0 26 0
|
||||
endloop
|
||||
endfacet
|
||||
endsolid OpenSCAD_Model
|
||||
@@ -9,25 +9,26 @@ import stepper, homing
|
||||
StepList = (0, 1, 2)
|
||||
|
||||
class CartKinematics:
|
||||
def __init__(self, printer, config):
|
||||
self.steppers = [stepper.PrinterStepper(
|
||||
def __init__(self, toolhead, printer, config):
|
||||
self.steppers = [stepper.PrinterHomingStepper(
|
||||
printer, config.getsection('stepper_' + n), n)
|
||||
for n in ['x', 'y', 'z']]
|
||||
self.max_z_velocity = config.getfloat('max_z_velocity', 9999999.9)
|
||||
self.max_z_accel = config.getfloat('max_z_accel', 9999999.9)
|
||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||
self.max_z_velocity = config.getfloat(
|
||||
'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
|
||||
self.max_z_accel = config.getfloat(
|
||||
'max_z_accel', max_accel, above=0., maxval=max_accel)
|
||||
self.need_motor_enable = True
|
||||
self.limits = [(1.0, -1.0)] * 3
|
||||
def set_max_jerk(self, max_xy_halt_velocity, max_accel):
|
||||
self.steppers[0].set_max_jerk(max_xy_halt_velocity, max_accel)
|
||||
self.steppers[1].set_max_jerk(max_xy_halt_velocity, max_accel)
|
||||
self.steppers[2].set_max_jerk(0., self.max_z_accel)
|
||||
def build_config(self):
|
||||
for stepper in self.steppers:
|
||||
stepper.build_config()
|
||||
# Setup stepper max halt velocity
|
||||
max_halt_velocity = toolhead.get_max_axis_halt()
|
||||
self.steppers[0].set_max_jerk(max_halt_velocity, max_accel)
|
||||
self.steppers[1].set_max_jerk(max_halt_velocity, max_accel)
|
||||
self.steppers[2].set_max_jerk(
|
||||
min(max_halt_velocity, self.max_z_velocity), max_accel)
|
||||
def set_position(self, newpos):
|
||||
for i in StepList:
|
||||
s = self.steppers[i]
|
||||
s.mcu_stepper.set_position(int(newpos[i]*s.inv_step_dist + 0.5))
|
||||
self.steppers[i].mcu_stepper.set_position(newpos[i])
|
||||
def home(self, homing_state):
|
||||
# Each axis is homed independently and in order
|
||||
for axis in homing_state.get_axes():
|
||||
@@ -45,36 +46,36 @@ class CartKinematics:
|
||||
rpos = s.position_endstop + s.homing_retract_dist
|
||||
r2pos = rpos + s.homing_retract_dist
|
||||
# Initial homing
|
||||
homing_speed = s.get_homing_speed()
|
||||
homepos = [None, None, None, None]
|
||||
homepos[axis] = s.position_endstop
|
||||
coord = [None, None, None, None]
|
||||
coord[axis] = pos
|
||||
homing_state.home(list(coord), homepos, [s], s.homing_speed)
|
||||
homing_state.home(list(coord), homepos, [s], homing_speed)
|
||||
# Retract
|
||||
coord[axis] = rpos
|
||||
homing_state.retract(list(coord), s.homing_speed)
|
||||
homing_state.retract(list(coord), homing_speed)
|
||||
# Home again
|
||||
coord[axis] = r2pos
|
||||
homing_state.home(
|
||||
list(coord), homepos, [s], s.homing_speed/2.0, second_home=True)
|
||||
list(coord), homepos, [s], homing_speed/2.0, second_home=True)
|
||||
# Set final homed position
|
||||
coord[axis] = s.position_endstop + s.get_homed_offset()*s.step_dist
|
||||
coord[axis] = s.position_endstop + s.get_homed_offset()
|
||||
homing_state.set_homed_position(coord)
|
||||
def motor_off(self, move_time):
|
||||
def query_endstops(self, print_time, query_flags):
|
||||
return homing.query_endstops(print_time, query_flags, self.steppers)
|
||||
def motor_off(self, print_time):
|
||||
self.limits = [(1.0, -1.0)] * 3
|
||||
for stepper in self.steppers:
|
||||
stepper.motor_enable(move_time, 0)
|
||||
stepper.motor_enable(print_time, 0)
|
||||
self.need_motor_enable = True
|
||||
def _check_motor_enable(self, move_time, move):
|
||||
def _check_motor_enable(self, print_time, move):
|
||||
need_motor_enable = False
|
||||
for i in StepList:
|
||||
if move.axes_d[i]:
|
||||
self.steppers[i].motor_enable(move_time, 1)
|
||||
self.steppers[i].motor_enable(print_time, 1)
|
||||
need_motor_enable |= self.steppers[i].need_motor_enable
|
||||
self.need_motor_enable = need_motor_enable
|
||||
def query_endstops(self, print_time):
|
||||
endstops = [(s, s.query_endstop(print_time)) for s in self.steppers]
|
||||
return [(s.name, es.query_endstop_wait()) for s, es in endstops]
|
||||
def _check_endstops(self, move):
|
||||
end_pos = move.end_pos
|
||||
for i in StepList:
|
||||
@@ -99,49 +100,36 @@ class CartKinematics:
|
||||
z_ratio = move.move_d / abs(move.axes_d[2])
|
||||
move.limit_speed(
|
||||
self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
|
||||
def move(self, move_time, move):
|
||||
def move(self, print_time, move):
|
||||
if self.need_motor_enable:
|
||||
self._check_motor_enable(move_time, move)
|
||||
inv_accel = 1. / move.accel
|
||||
inv_cruise_v = 1. / move.cruise_v
|
||||
self._check_motor_enable(print_time, move)
|
||||
for i in StepList:
|
||||
if not move.axes_d[i]:
|
||||
axis_d = move.axes_d[i]
|
||||
if not axis_d:
|
||||
continue
|
||||
mcu_stepper = self.steppers[i].mcu_stepper
|
||||
mcu_time = mcu_stepper.print_to_mcu_time(move_time)
|
||||
step_pos = mcu_stepper.commanded_position
|
||||
inv_step_dist = self.steppers[i].inv_step_dist
|
||||
step_offset = step_pos - move.start_pos[i] * inv_step_dist
|
||||
steps = move.axes_d[i] * inv_step_dist
|
||||
move_step_d = move.move_d / abs(steps)
|
||||
move_time = print_time
|
||||
start_pos = move.start_pos[i]
|
||||
axis_r = abs(axis_d) / move.move_d
|
||||
accel = move.accel * axis_r
|
||||
cruise_v = move.cruise_v * axis_r
|
||||
|
||||
# Acceleration steps
|
||||
accel_multiplier = 2.0 * move_step_d * inv_accel
|
||||
if move.accel_r:
|
||||
#t = sqrt(2*pos/accel + (start_v/accel)**2) - start_v/accel
|
||||
accel_time_offset = move.start_v * inv_accel
|
||||
accel_sqrt_offset = accel_time_offset**2
|
||||
accel_steps = move.accel_r * steps
|
||||
count = mcu_stepper.step_sqrt(
|
||||
mcu_time - accel_time_offset, accel_steps, step_offset
|
||||
, accel_sqrt_offset, accel_multiplier)
|
||||
step_offset += count - accel_steps
|
||||
mcu_time += move.accel_t
|
||||
accel_d = move.accel_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, accel_d, move.start_v * axis_r, accel)
|
||||
start_pos += accel_d
|
||||
move_time += move.accel_t
|
||||
# Cruising steps
|
||||
if move.cruise_r:
|
||||
#t = pos/cruise_v
|
||||
cruise_multiplier = move_step_d * inv_cruise_v
|
||||
cruise_steps = move.cruise_r * steps
|
||||
count = mcu_stepper.step_factor(
|
||||
mcu_time, cruise_steps, step_offset, cruise_multiplier)
|
||||
step_offset += count - cruise_steps
|
||||
mcu_time += move.cruise_t
|
||||
cruise_d = move.cruise_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, cruise_d, cruise_v, 0.)
|
||||
start_pos += cruise_d
|
||||
move_time += move.cruise_t
|
||||
# Deceleration steps
|
||||
if move.decel_r:
|
||||
#t = cruise_v/accel - sqrt((cruise_v/accel)**2 - 2*pos/accel)
|
||||
decel_time_offset = move.cruise_v * inv_accel
|
||||
decel_sqrt_offset = decel_time_offset**2
|
||||
decel_steps = move.decel_r * steps
|
||||
count = mcu_stepper.step_sqrt(
|
||||
mcu_time + decel_time_offset, decel_steps, step_offset
|
||||
, decel_sqrt_offset, -accel_multiplier)
|
||||
decel_d = move.decel_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, decel_d, cruise_v, -accel)
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
# Wrapper around C helper code
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import os, logging
|
||||
import cffi
|
||||
|
||||
COMPILE_CMD = "gcc -Wall -g -O -shared -fPIC -o %s %s"
|
||||
|
||||
######################################################################
|
||||
# c_helper.so compiling
|
||||
######################################################################
|
||||
|
||||
COMPILE_CMD = "gcc -Wall -g -O2 -shared -fPIC -o %s %s"
|
||||
SOURCE_FILES = ['stepcompress.c', 'serialqueue.c', 'pyhelper.c']
|
||||
DEST_LIB = "c_helper.so"
|
||||
OTHER_FILES = ['list.h', 'serialqueue.h', 'pyhelper.h']
|
||||
@@ -16,31 +21,24 @@ defs_stepcompress = """
|
||||
, uint32_t queue_step_msgid, uint32_t set_next_step_dir_msgid
|
||||
, uint32_t invert_sdir, uint32_t oid);
|
||||
void stepcompress_free(struct stepcompress *sc);
|
||||
void stepcompress_push(struct stepcompress *sc, double step_clock
|
||||
int stepcompress_reset(struct stepcompress *sc, uint64_t last_step_clock);
|
||||
int stepcompress_set_homing(struct stepcompress *sc, uint64_t homing_clock);
|
||||
int stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len);
|
||||
|
||||
int32_t stepcompress_push(struct stepcompress *sc, double step_clock
|
||||
, int32_t sdir);
|
||||
int32_t stepcompress_push_factor(struct stepcompress *sc
|
||||
, double steps, double step_offset
|
||||
, double clock_offset, double factor);
|
||||
int32_t stepcompress_push_sqrt(struct stepcompress *sc
|
||||
, double steps, double step_offset
|
||||
, double clock_offset, double sqrt_offset, double factor);
|
||||
int32_t stepcompress_push_delta_const(struct stepcompress *sc
|
||||
, double clock_offset, double dist, double start_pos
|
||||
, double inv_velocity, double step_dist, double height
|
||||
, double closestxy_d, double closest_height2, double movez_r);
|
||||
int32_t stepcompress_push_delta_accel(struct stepcompress *sc
|
||||
, double clock_offset, double dist, double start_pos
|
||||
, double accel_multiplier, double step_dist, double height
|
||||
, double closestxy_d, double closest_height2, double movez_r);
|
||||
void stepcompress_reset(struct stepcompress *sc, uint64_t last_step_clock);
|
||||
void stepcompress_queue_msg(struct stepcompress *sc
|
||||
, uint32_t *data, int len);
|
||||
uint32_t stepcompress_get_errors(struct stepcompress *sc);
|
||||
int32_t stepcompress_push_const(struct stepcompress *sc, double clock_offset
|
||||
, double step_offset, double steps, double start_sv, double accel);
|
||||
int32_t stepcompress_push_delta(struct stepcompress *sc
|
||||
, double clock_offset, double move_sd, double start_sv, double accel
|
||||
, double height, double startxy_sd, double arm_d, double movez_r);
|
||||
|
||||
struct steppersync *steppersync_alloc(struct serialqueue *sq
|
||||
, struct stepcompress **sc_list, int sc_num, int move_num);
|
||||
void steppersync_free(struct steppersync *ss);
|
||||
void steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||
void steppersync_set_time(struct steppersync *ss
|
||||
, double time_offset, double mcu_freq);
|
||||
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||
"""
|
||||
|
||||
defs_serialqueue = """
|
||||
@@ -63,9 +61,8 @@ defs_serialqueue = """
|
||||
, uint64_t min_clock, uint64_t req_clock);
|
||||
void serialqueue_pull(struct serialqueue *sq, struct pull_queue_message *pqm);
|
||||
void serialqueue_set_baud_adjust(struct serialqueue *sq, double baud_adjust);
|
||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_clock
|
||||
, double last_ack_time, uint64_t last_ack_clock);
|
||||
void serialqueue_flush_ready(struct serialqueue *sq);
|
||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
||||
, double last_clock_time, uint64_t last_clock);
|
||||
void serialqueue_get_stats(struct serialqueue *sq, char *buf, int len);
|
||||
int serialqueue_extract_old(struct serialqueue *sq, int sentq
|
||||
, struct pull_queue_message *q, int max);
|
||||
@@ -73,6 +70,7 @@ defs_serialqueue = """
|
||||
|
||||
defs_pyhelper = """
|
||||
void set_python_logging_callback(void (*func)(const char *));
|
||||
double get_monotonic(void);
|
||||
"""
|
||||
|
||||
# Return the list of file modification times
|
||||
@@ -88,14 +86,14 @@ def get_mtimes(srcdir, filelist):
|
||||
return out
|
||||
|
||||
# Check if the code needs to be compiled
|
||||
def check_build_code(srcdir):
|
||||
src_times = get_mtimes(srcdir, SOURCE_FILES + OTHER_FILES)
|
||||
obj_times = get_mtimes(srcdir, [DEST_LIB])
|
||||
def check_build_code(srcdir, target, sources, cmd, other_files=[]):
|
||||
src_times = get_mtimes(srcdir, sources + other_files)
|
||||
obj_times = get_mtimes(srcdir, [target])
|
||||
if not obj_times or max(src_times) > min(obj_times):
|
||||
logging.info("Building C code module")
|
||||
srcfiles = [os.path.join(srcdir, fname) for fname in SOURCE_FILES]
|
||||
destlib = os.path.join(srcdir, DEST_LIB)
|
||||
os.system(COMPILE_CMD % (destlib, ' '.join(srcfiles)))
|
||||
logging.info("Building C code module %s", target)
|
||||
srcfiles = [os.path.join(srcdir, fname) for fname in sources]
|
||||
destlib = os.path.join(srcdir, target)
|
||||
os.system(cmd % (destlib, ' '.join(srcfiles)))
|
||||
|
||||
FFI_main = None
|
||||
FFI_lib = None
|
||||
@@ -106,7 +104,8 @@ def get_ffi():
|
||||
global FFI_main, FFI_lib, pyhelper_logging_callback
|
||||
if FFI_lib is None:
|
||||
srcdir = os.path.dirname(os.path.realpath(__file__))
|
||||
check_build_code(srcdir)
|
||||
check_build_code(srcdir, DEST_LIB, SOURCE_FILES, COMPILE_CMD
|
||||
, OTHER_FILES)
|
||||
FFI_main = cffi.FFI()
|
||||
FFI_main.cdef(defs_stepcompress)
|
||||
FFI_main.cdef(defs_serialqueue)
|
||||
@@ -119,3 +118,20 @@ def get_ffi():
|
||||
"void(const char *)", logging_callback)
|
||||
FFI_lib.set_python_logging_callback(pyhelper_logging_callback)
|
||||
return FFI_main, FFI_lib
|
||||
|
||||
|
||||
######################################################################
|
||||
# hub-ctrl hub power controller
|
||||
######################################################################
|
||||
|
||||
HC_COMPILE_CMD = "gcc -Wall -g -O2 -o %s %s -lusb"
|
||||
HC_SOURCE_FILES = ['hub-ctrl.c']
|
||||
HC_SOURCE_DIR = '../lib/hub-ctrl'
|
||||
HC_TARGET = "hub-ctrl"
|
||||
HC_CMD = "sudo %s/hub-ctrl -h 0 -P 2 -p %d"
|
||||
|
||||
def run_hub_ctrl(enable_power):
|
||||
srcdir = os.path.dirname(os.path.realpath(__file__))
|
||||
hubdir = os.path.join(srcdir, HC_SOURCE_DIR)
|
||||
check_build_code(hubdir, HC_TARGET, HC_SOURCE_FILES, HC_COMPILE_CMD)
|
||||
os.system(HC_CMD % (hubdir, enable_power))
|
||||
|
||||
296
klippy/chipmisc.py
Normal file
@@ -0,0 +1,296 @@
|
||||
# Code to configure miscellaneous chips
|
||||
#
|
||||
# Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import pins, mcu
|
||||
|
||||
|
||||
######################################################################
|
||||
# Statically configured output pins
|
||||
######################################################################
|
||||
|
||||
class PrinterStaticDigitalOut:
|
||||
def __init__(self, printer, config):
|
||||
pin_list = [pin.strip() for pin in config.get('pins').split(',')]
|
||||
for pin_desc in pin_list:
|
||||
mcu_pin = pins.setup_pin(printer, 'digital_out', pin_desc)
|
||||
mcu_pin.setup_static()
|
||||
|
||||
class PrinterStaticPWM:
|
||||
def __init__(self, printer, config):
|
||||
mcu_pwm = pins.setup_pin(printer, 'pwm', config.get('pin'))
|
||||
mcu_pwm.setup_max_duration(0.)
|
||||
hard_pwm = config.getint('hard_pwm', None, minval=1)
|
||||
if hard_pwm is None:
|
||||
mcu_pwm.setup_cycle_time(config.getfloat(
|
||||
'cycle_time', 0.100, above=0.))
|
||||
else:
|
||||
mcu_pwm.setup_hard_pwm(hard_pwm)
|
||||
scale = config.getfloat('scale', 1., above=0.)
|
||||
value = config.getfloat('value', minval=0., maxval=scale)
|
||||
mcu_pwm.setup_static_pwm(value / scale)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Servos
|
||||
######################################################################
|
||||
|
||||
SERVO_MIN_TIME = 0.100
|
||||
SERVO_SIGNAL_PERIOD = 0.020
|
||||
|
||||
class PrinterServo:
|
||||
def __init__(self, printer, config):
|
||||
self.mcu_servo = pins.setup_pin(printer, 'pwm', config.get('pin'))
|
||||
self.mcu_servo.setup_max_duration(0.)
|
||||
self.mcu_servo.setup_cycle_time(SERVO_SIGNAL_PERIOD)
|
||||
self.min_width = config.getfloat(
|
||||
'minimum_pulse_width', .001, above=0., below=SERVO_SIGNAL_PERIOD)
|
||||
self.max_width = config.getfloat(
|
||||
'maximum_pulse_width', .002
|
||||
, above=self.min_width, below=SERVO_SIGNAL_PERIOD)
|
||||
self.max_angle = config.getfloat('maximum_servo_angle', 180.)
|
||||
self.angle_to_width = (self.max_width - self.min_width) / self.max_angle
|
||||
self.width_to_value = 1. / SERVO_SIGNAL_PERIOD
|
||||
self.last_value = self.last_value_time = 0.
|
||||
def set_pwm(self, print_time, value):
|
||||
if value == self.last_value:
|
||||
return
|
||||
print_time = max(self.last_value_time + SERVO_MIN_TIME, print_time)
|
||||
self.mcu_servo.set_pwm(print_time, value)
|
||||
self.last_value = value
|
||||
self.last_value_time = print_time
|
||||
# External commands
|
||||
def set_angle(self, print_time, angle):
|
||||
angle = max(0., min(self.max_angle, angle))
|
||||
width = self.min_width + angle * self.angle_to_width
|
||||
self.set_pwm(print_time, width * self.width_to_value)
|
||||
def set_pulse_width(self, print_time, width):
|
||||
width = max(self.min_width, min(self.max_width, width))
|
||||
self.set_pwm(print_time, width * self.width_to_value)
|
||||
|
||||
def get_printer_servo(printer, name):
|
||||
return printer.objects.get('servo ' + name)
|
||||
|
||||
|
||||
######################################################################
|
||||
# AD5206 digipot
|
||||
######################################################################
|
||||
|
||||
class ad5206:
|
||||
def __init__(self, printer, config):
|
||||
enable_pin_params = pins.get_printer_pins(printer).parse_pin_desc(
|
||||
config.get('enable_pin'))
|
||||
self.mcu = enable_pin_params['chip']
|
||||
self.pin = enable_pin_params['pin']
|
||||
self.mcu.add_config_object(self)
|
||||
scale = config.getfloat('scale', 1., above=0.)
|
||||
self.channels = [None] * 6
|
||||
for i in range(len(self.channels)):
|
||||
val = config.getfloat('channel_%d' % (i+1,), None,
|
||||
minval=0., maxval=scale)
|
||||
if val is not None:
|
||||
self.channels[i] = int(val * 256. / scale + .5)
|
||||
def build_config(self):
|
||||
for i, val in enumerate(self.channels):
|
||||
if val is not None:
|
||||
self.mcu.add_config_cmd(
|
||||
"send_spi_message pin=%s msg=%02x%02x" % (self.pin, i, val))
|
||||
|
||||
|
||||
######################################################################
|
||||
# Replicape board
|
||||
######################################################################
|
||||
|
||||
REPLICAPE_MAX_CURRENT = 3.84
|
||||
REPLICAPE_SHIFT_REGISTER_BUS = 1
|
||||
REPLICAPE_SHIFT_REGISTER_DEVICE = 1
|
||||
REPLICAPE_PCA9685_BUS = 2
|
||||
REPLICAPE_PCA9685_ADDRESS = 0x70
|
||||
REPLICAPE_PCA9685_CYCLE_TIME = .001
|
||||
|
||||
class pca9685_pwm:
|
||||
def __init__(self, replicape, channel, pin_params):
|
||||
self._replicape = replicape
|
||||
self._channel = channel
|
||||
if pin_params['type'] not in ['digital_out', 'pwm']:
|
||||
raise pins.error("Pin type not supported on replicape")
|
||||
self._mcu = replicape.host_mcu
|
||||
self._mcu.add_config_object(self)
|
||||
self._bus = REPLICAPE_PCA9685_BUS
|
||||
self._address = REPLICAPE_PCA9685_ADDRESS
|
||||
self._cycle_time = REPLICAPE_PCA9685_CYCLE_TIME
|
||||
self._max_duration = 2.
|
||||
self._oid = None
|
||||
self._invert = pin_params['invert']
|
||||
self._shutdown_value = float(self._invert)
|
||||
self._last_clock = 0
|
||||
self._pwm_max = 0.
|
||||
self._cmd_queue = self._mcu.alloc_command_queue()
|
||||
self._set_cmd = None
|
||||
self._static_value = None
|
||||
def get_mcu(self):
|
||||
return self._mcu
|
||||
def setup_max_duration(self, max_duration):
|
||||
self._max_duration = max_duration
|
||||
def setup_cycle_time(self, cycle_time):
|
||||
pass
|
||||
def setup_hard_pwm(self, hard_cycle_ticks):
|
||||
if hard_cycle_ticks:
|
||||
raise pins.error("pca9685 does not support hard_pwm parameter")
|
||||
def setup_static_pwm(self, value):
|
||||
if self._invert:
|
||||
value = 1. - value
|
||||
self._static_value = max(0., min(1., value))
|
||||
def setup_shutdown_value(self, value):
|
||||
if self._invert:
|
||||
value = 1. - value
|
||||
self._shutdown_value = max(0., min(1., value))
|
||||
if self._shutdown_value:
|
||||
self._replicape.note_enable_on_shutdown()
|
||||
def build_config(self):
|
||||
self._pwm_max = self._mcu.get_constant_float("PCA9685_MAX")
|
||||
cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
|
||||
if self._static_value is not None:
|
||||
value = int(self._static_value * self._pwm_max + 0.5)
|
||||
self._mcu.add_config_cmd(
|
||||
"set_pca9685_out bus=%d addr=%d channel=%d"
|
||||
" cycle_ticks=%d value=%d" % (
|
||||
self._bus, self._address, self._channel,
|
||||
cycle_ticks, value))
|
||||
return
|
||||
self._oid = self._mcu.create_oid()
|
||||
self._mcu.add_config_cmd(
|
||||
"config_pca9685 oid=%d bus=%d addr=%d channel=%d cycle_ticks=%d"
|
||||
" value=%d default_value=%d max_duration=%d" % (
|
||||
self._oid, self._bus, self._address, self._channel, cycle_ticks,
|
||||
self._invert * self._pwm_max,
|
||||
self._shutdown_value * self._pwm_max,
|
||||
self._mcu.seconds_to_clock(self._max_duration)))
|
||||
self._set_cmd = self._mcu.lookup_command(
|
||||
"schedule_pca9685_out oid=%c clock=%u value=%hu")
|
||||
def set_pwm(self, print_time, value):
|
||||
clock = self._mcu.print_time_to_clock(print_time)
|
||||
if self._invert:
|
||||
value = 1. - value
|
||||
value = int(max(0., min(1., value)) * self._pwm_max + 0.5)
|
||||
self._replicape.note_enable(print_time, self._channel, not not value)
|
||||
msg = self._set_cmd.encode(self._oid, clock, value)
|
||||
self._mcu.send(msg, minclock=self._last_clock, reqclock=clock
|
||||
, cq=self._cmd_queue)
|
||||
self._last_clock = clock
|
||||
def set_digital(self, print_time, value):
|
||||
if value:
|
||||
self.set_pwm(print_time, 1.)
|
||||
else:
|
||||
self.set_pwm(print_time, 0.)
|
||||
|
||||
class ReplicapeDACEnable:
|
||||
def __init__(self, replicape, channel, pin_params):
|
||||
if pin_params['type'] != 'digital_out':
|
||||
raise pins.error("Replicape virtual enable pin must be digital_out")
|
||||
if pin_params['invert']:
|
||||
raise pins.error("Replicape virtual enable pin can not be inverted")
|
||||
self.mcu = replicape.host_mcu
|
||||
self.value = replicape.stepper_dacs[channel]
|
||||
self.pwm = pca9685_pwm(replicape, channel, pin_params)
|
||||
self.last = 0
|
||||
def get_mcu(self):
|
||||
return self.mcu
|
||||
def setup_max_duration(self, max_duration):
|
||||
self.pwm.setup_max_duration(max_duration)
|
||||
def set_digital(self, print_time, value):
|
||||
if value:
|
||||
self.pwm.set_pwm(print_time, self.value)
|
||||
else:
|
||||
self.pwm.set_pwm(print_time, 0.)
|
||||
self.last = value
|
||||
def get_last_setting(self):
|
||||
return self.last
|
||||
|
||||
ReplicapeStepConfig = {
|
||||
'disable': None,
|
||||
'1': (1<<7)|(1<<5), '2': (1<<7)|(1<<5)|(1<<6), 'spread2': (1<<5),
|
||||
'4': (1<<7)|(1<<5)|(1<<4), '16': (1<<7)|(1<<5)|(1<<6)|(1<<4),
|
||||
'spread4': (1<<5)|(1<<4), 'spread16': (1<<7), 'stealth4': (1<<7)|(1<<6),
|
||||
'stealth16': 0
|
||||
}
|
||||
|
||||
class Replicape:
|
||||
def __init__(self, printer, config):
|
||||
pins.get_printer_pins(printer).register_chip('replicape', self)
|
||||
revisions = {'B3': 'B3'}
|
||||
config.getchoice('revision', revisions)
|
||||
self.host_mcu = mcu.get_printer_mcu(printer, config.get('host_mcu'))
|
||||
# Setup enable pin
|
||||
self.mcu_enable = pins.setup_pin(
|
||||
printer, 'digital_out', config.get('enable_pin', '!P9_41'))
|
||||
self.mcu_enable.setup_max_duration(0.)
|
||||
self.enabled_channels = {}
|
||||
# Setup power pins
|
||||
self.pins = {
|
||||
"power_e": (pca9685_pwm, 5), "power_h": (pca9685_pwm, 3),
|
||||
"power_hotbed": (pca9685_pwm, 4),
|
||||
"power_fan0": (pca9685_pwm, 7), "power_fan1": (pca9685_pwm, 8),
|
||||
"power_fan2": (pca9685_pwm, 9), "power_fan3": (pca9685_pwm, 10) }
|
||||
# Setup stepper config
|
||||
self.stepper_dacs = {}
|
||||
shift_registers = [1] * 5
|
||||
for port, name in enumerate('xyzeh'):
|
||||
prefix = 'stepper_%s_' % (name,)
|
||||
sc = config.getchoice(
|
||||
prefix + 'microstep_mode', ReplicapeStepConfig, 'disable')
|
||||
if sc is None:
|
||||
continue
|
||||
if config.getboolean(prefix + 'chopper_off_time_high', False):
|
||||
sc |= 1<<3
|
||||
if config.getboolean(prefix + 'chopper_hysteresis_high', False):
|
||||
sc |= 1<<2
|
||||
if config.getboolean(prefix + 'chopper_blank_time_high', True):
|
||||
sc |= 1<<1
|
||||
shift_registers[port] = sc
|
||||
channel = port + 11
|
||||
cur = config.getfloat(
|
||||
prefix + 'current', above=0., maxval=REPLICAPE_MAX_CURRENT)
|
||||
self.stepper_dacs[channel] = cur / REPLICAPE_MAX_CURRENT
|
||||
self.pins[prefix + 'enable'] = (ReplicapeDACEnable, channel)
|
||||
shift_registers.reverse()
|
||||
self.host_mcu.add_config_cmd("send_spi bus=%d dev=%d msg=%s" % (
|
||||
REPLICAPE_SHIFT_REGISTER_BUS, REPLICAPE_SHIFT_REGISTER_DEVICE,
|
||||
"".join(["%02x" % (x,) for x in shift_registers])))
|
||||
def note_enable_on_shutdown(self):
|
||||
self.mcu_enable.setup_shutdown_value(1)
|
||||
def note_enable(self, print_time, channel, is_enable):
|
||||
if is_enable:
|
||||
is_off = not self.enabled_channels
|
||||
self.enabled_channels[channel] = 1
|
||||
if is_off:
|
||||
self.mcu_enable.set_digital(print_time, 1)
|
||||
elif channel in self.enabled_channels:
|
||||
del self.enabled_channels[channel]
|
||||
if not self.enabled_channels:
|
||||
self.mcu_enable.set_digital(print_time, 0)
|
||||
def setup_pin(self, pin_params):
|
||||
pin = pin_params['pin']
|
||||
if pin not in self.pins:
|
||||
raise pins.error("Unknown replicape pin %s" % (pin,))
|
||||
pclass, channel = self.pins[pin]
|
||||
return pclass(self, channel, pin_params)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Setup
|
||||
######################################################################
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
if config.has_section('replicape'):
|
||||
printer.add_object('replicape', Replicape(
|
||||
printer, config.getsection('replicape')))
|
||||
for s in config.get_prefix_sections('static_digital_output '):
|
||||
printer.add_object(s.section, PrinterStaticDigitalOut(printer, s))
|
||||
for s in config.get_prefix_sections('static_pwm_output '):
|
||||
printer.add_object(s.section, PrinterStaticPWM(printer, s))
|
||||
for s in config.get_prefix_sections('servo '):
|
||||
printer.add_object(s.section, PrinterServo(printer, s))
|
||||
for s in config.get_prefix_sections('ad5206 '):
|
||||
printer.add_object(s.section, ad5206(printer, s))
|
||||
212
klippy/clocksync.py
Normal file
@@ -0,0 +1,212 @@
|
||||
# Micro-controller clock synchronization
|
||||
#
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, threading, math
|
||||
|
||||
COMM_TIMEOUT = 3.5
|
||||
RTT_AGE = .000010 / (60. * 60.)
|
||||
DECAY = 1. / (2. * 60.)
|
||||
TRANSMIT_EXTRA = .001
|
||||
|
||||
class ClockSync:
|
||||
def __init__(self, reactor):
|
||||
self.reactor = reactor
|
||||
self.serial = None
|
||||
self.status_timer = self.reactor.register_timer(self._status_event)
|
||||
self.status_cmd = None
|
||||
self.mcu_freq = 1.
|
||||
self.last_clock = 0
|
||||
self.clock_est = (0., 0., 0.)
|
||||
# Minimum round-trip-time tracking
|
||||
self.min_half_rtt = 999999999.9
|
||||
self.min_rtt_time = 0.
|
||||
# Linear regression of mcu clock and system sent_time
|
||||
self.time_avg = self.time_variance = 0.
|
||||
self.clock_avg = self.clock_covariance = 0.
|
||||
self.prediction_variance = 0.
|
||||
self.last_prediction_time = 0.
|
||||
def connect(self, serial):
|
||||
self.serial = serial
|
||||
msgparser = serial.msgparser
|
||||
self.mcu_freq = msgparser.get_constant_float('CLOCK_FREQ')
|
||||
# Load initial clock and frequency
|
||||
uptime_msg = msgparser.create_command('get_uptime')
|
||||
params = serial.send_with_response(uptime_msg, 'uptime')
|
||||
self.last_clock = (params['high'] << 32) | params['clock']
|
||||
self.clock_avg = self.last_clock
|
||||
self.time_avg = params['#sent_time']
|
||||
self.clock_est = (self.time_avg, self.clock_avg, self.mcu_freq)
|
||||
self.prediction_variance = (.001 * self.mcu_freq)**2
|
||||
# Enable periodic get_status timer
|
||||
self.status_cmd = msgparser.create_command('get_status')
|
||||
for i in range(8):
|
||||
params = serial.send_with_response(self.status_cmd, 'status')
|
||||
self._handle_status(params)
|
||||
self.reactor.pause(0.100)
|
||||
serial.register_callback(self._handle_status, 'status')
|
||||
self.reactor.update_timer(self.status_timer, self.reactor.NOW)
|
||||
def connect_file(self, serial, pace=False):
|
||||
self.serial = serial
|
||||
self.mcu_freq = serial.msgparser.get_constant_float('CLOCK_FREQ')
|
||||
self.clock_est = (0., 0., self.mcu_freq)
|
||||
freq = 1000000000000.
|
||||
if pace:
|
||||
freq = self.mcu_freq
|
||||
serial.set_clock_est(freq, self.reactor.monotonic(), 0)
|
||||
# MCU clock querying (status callback invoked from background thread)
|
||||
def _status_event(self, eventtime):
|
||||
self.serial.send(self.status_cmd)
|
||||
return eventtime + 1.0
|
||||
def _handle_status(self, params):
|
||||
# Extend clock to 64bit
|
||||
last_clock = self.last_clock
|
||||
clock = (last_clock & ~0xffffffff) | params['clock']
|
||||
if clock < last_clock:
|
||||
clock += 0x100000000
|
||||
self.last_clock = clock
|
||||
# Check if this is the best round-trip-time seen so far
|
||||
sent_time = params['#sent_time']
|
||||
if not sent_time:
|
||||
return
|
||||
receive_time = params['#receive_time']
|
||||
half_rtt = .5 * (receive_time - sent_time)
|
||||
aged_rtt = (sent_time - self.min_rtt_time) * RTT_AGE
|
||||
if half_rtt < self.min_half_rtt + aged_rtt:
|
||||
self.min_half_rtt = half_rtt
|
||||
self.min_rtt_time = sent_time
|
||||
logging.debug("new minimum rtt %.3f: hrtt=%.6f freq=%d",
|
||||
sent_time, half_rtt, self.clock_est[2])
|
||||
# Filter out samples that are extreme outliers
|
||||
exp_clock = ((sent_time - self.time_avg) * self.clock_est[2]
|
||||
+ self.clock_avg)
|
||||
clock_diff2 = (clock - exp_clock)**2
|
||||
if (clock_diff2 > 25. * self.prediction_variance
|
||||
and clock_diff2 > (.000500 * self.mcu_freq)**2):
|
||||
if clock > exp_clock and sent_time < self.last_prediction_time + 10.:
|
||||
logging.debug("Ignoring clock sample %.3f:"
|
||||
" freq=%d diff=%d stddev=%.3f",
|
||||
sent_time, self.clock_est[2], clock - exp_clock,
|
||||
math.sqrt(self.prediction_variance))
|
||||
return
|
||||
logging.info("Resetting prediction variance %.3f:"
|
||||
" freq=%d diff=%d stddev=%.3f",
|
||||
sent_time, self.clock_est[2], clock - exp_clock,
|
||||
math.sqrt(self.prediction_variance))
|
||||
self.prediction_variance = (.001 * self.mcu_freq)**2
|
||||
else:
|
||||
self.last_prediction_time = sent_time
|
||||
self.prediction_variance = (
|
||||
(1. - DECAY) * (self.prediction_variance + clock_diff2 * DECAY))
|
||||
# Add clock and sent_time to linear regression
|
||||
diff_sent_time = sent_time - self.time_avg
|
||||
self.time_avg += DECAY * diff_sent_time
|
||||
self.time_variance = (1. - DECAY) * (
|
||||
self.time_variance + diff_sent_time**2 * DECAY)
|
||||
diff_clock = clock - self.clock_avg
|
||||
self.clock_avg += DECAY * diff_clock
|
||||
self.clock_covariance = (1. - DECAY) * (
|
||||
self.clock_covariance + diff_sent_time * diff_clock * DECAY)
|
||||
# Update prediction from linear regression
|
||||
new_freq = self.clock_covariance / self.time_variance
|
||||
pred_stddev = math.sqrt(self.prediction_variance)
|
||||
self.serial.set_clock_est(new_freq, self.time_avg + TRANSMIT_EXTRA,
|
||||
int(self.clock_avg - 3. * pred_stddev))
|
||||
self.clock_est = (self.time_avg - self.min_half_rtt,
|
||||
self.clock_avg, new_freq)
|
||||
#logging.debug("regr %.3f: freq=%.3f d=%d(%.3f)",
|
||||
# sent_time, new_freq, clock - exp_clock, pred_stddev)
|
||||
# clock frequency conversions
|
||||
def print_time_to_clock(self, print_time):
|
||||
return int(print_time * self.mcu_freq)
|
||||
def clock_to_print_time(self, clock):
|
||||
return clock / self.mcu_freq
|
||||
def get_adjusted_freq(self):
|
||||
return self.mcu_freq
|
||||
# system time conversions
|
||||
def get_clock(self, eventtime):
|
||||
sample_time, clock, freq = self.clock_est
|
||||
return int(clock + (eventtime - sample_time) * freq)
|
||||
def estimated_print_time(self, eventtime):
|
||||
return self.clock_to_print_time(self.get_clock(eventtime))
|
||||
# misc commands
|
||||
def clock32_to_clock64(self, clock32):
|
||||
last_clock = self.last_clock
|
||||
clock_diff = (last_clock - clock32) & 0xffffffff
|
||||
if clock_diff & 0x80000000:
|
||||
return last_clock + 0x100000000 - clock_diff
|
||||
return last_clock - clock_diff
|
||||
def is_active(self, eventtime):
|
||||
print_time = self.estimated_print_time(eventtime)
|
||||
last_clock_print_time = self.clock_to_print_time(self.last_clock)
|
||||
return print_time < last_clock_print_time + COMM_TIMEOUT
|
||||
def dump_debug(self):
|
||||
sample_time, clock, freq = self.clock_est
|
||||
return ("clocksync state: mcu_freq=%d last_clock=%d"
|
||||
" clock_est=(%.3f %d %.3f) min_half_rtt=%.6f min_rtt_time=%.3f"
|
||||
" time_avg=%.3f(%.3f) clock_avg=%.3f(%.3f)"
|
||||
" pred_variance=%.3f" % (
|
||||
self.mcu_freq, self.last_clock, sample_time, clock, freq,
|
||||
self.min_half_rtt, self.min_rtt_time,
|
||||
self.time_avg, self.time_variance,
|
||||
self.clock_avg, self.clock_covariance,
|
||||
self.prediction_variance))
|
||||
def stats(self, eventtime):
|
||||
sample_time, clock, freq = self.clock_est
|
||||
return "freq=%d" % (freq,)
|
||||
def calibrate_clock(self, print_time, eventtime):
|
||||
return (0., self.mcu_freq)
|
||||
|
||||
# Clock syncing code for secondary MCUs (whose clocks are sync'ed to a
|
||||
# primary MCU)
|
||||
class SecondarySync(ClockSync):
|
||||
def __init__(self, reactor, main_sync):
|
||||
ClockSync.__init__(self, reactor)
|
||||
self.main_sync = main_sync
|
||||
self.clock_adj = (0., 1.)
|
||||
def connect(self, serial):
|
||||
ClockSync.connect(self, serial)
|
||||
self.clock_adj = (0., self.mcu_freq)
|
||||
curtime = self.reactor.monotonic()
|
||||
main_print_time = self.main_sync.estimated_print_time(curtime)
|
||||
local_print_time = self.estimated_print_time(curtime)
|
||||
self.clock_adj = (main_print_time - local_print_time, self.mcu_freq)
|
||||
self.calibrate_clock(0., curtime)
|
||||
def connect_file(self, serial, pace=False):
|
||||
ClockSync.connect_file(self, serial, pace)
|
||||
self.clock_adj = (0., self.mcu_freq)
|
||||
# clock frequency conversions
|
||||
def print_time_to_clock(self, print_time):
|
||||
adjusted_offset, adjusted_freq = self.clock_adj
|
||||
return int((print_time - adjusted_offset) * adjusted_freq)
|
||||
def clock_to_print_time(self, clock):
|
||||
adjusted_offset, adjusted_freq = self.clock_adj
|
||||
return clock / adjusted_freq + adjusted_offset
|
||||
def get_adjusted_freq(self):
|
||||
adjusted_offset, adjusted_freq = self.clock_adj
|
||||
return adjusted_freq
|
||||
# misc commands
|
||||
def dump_debug(self):
|
||||
adjusted_offset, adjusted_freq = self.clock_adj
|
||||
return "%s clock_adj=(%.3f %.3f)" % (
|
||||
ClockSync.dump_debug(self), adjusted_offset, adjusted_freq)
|
||||
def stats(self, eventtime):
|
||||
adjusted_offset, adjusted_freq = self.clock_adj
|
||||
return "%s adj=%d" % (ClockSync.stats(self, eventtime), adjusted_freq)
|
||||
def calibrate_clock(self, print_time, eventtime):
|
||||
ser_time, ser_clock, ser_freq = self.main_sync.clock_est
|
||||
main_mcu_freq = self.main_sync.mcu_freq
|
||||
|
||||
main_clock = (eventtime - ser_time) * ser_freq + ser_clock
|
||||
print_time = max(print_time, main_clock / main_mcu_freq)
|
||||
main_sync_clock = (print_time + 4.) * main_mcu_freq
|
||||
sync_time = ser_time + (main_sync_clock - ser_clock) / ser_freq
|
||||
|
||||
print_clock = self.print_time_to_clock(print_time)
|
||||
sync_clock = self.get_clock(sync_time)
|
||||
adjusted_freq = .25 * (sync_clock - print_clock)
|
||||
adjusted_offset = print_time - print_clock / adjusted_freq
|
||||
|
||||
self.clock_adj = (adjusted_offset, adjusted_freq)
|
||||
return self.clock_adj
|
||||
@@ -1,12 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
# Script to implement a test console with firmware over serial port
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import sys, optparse, os, re, logging
|
||||
import reactor, serialhdl, pins, util, msgproto, clocksync
|
||||
|
||||
import reactor, serialhdl, pins, util, msgproto
|
||||
help_txt = """
|
||||
This is a debugging console for the Klipper micro-controller.
|
||||
In addition to mcu commands, the following artificial commands are
|
||||
available:
|
||||
PINS : Load pin name aliases (eg, "PINS arduino")
|
||||
DELAY : Send a command at a clock time (eg, "DELAY 9999 get_uptime")
|
||||
FLOOD : Send a command many times (eg, "FLOOD 22 .01 get_uptime")
|
||||
SUPPRESS : Suppress a response message (eg, "SUPPRESS stats")
|
||||
SET : Create a local variable (eg, "SET myvar 123.4")
|
||||
STATS : Report serial statistics
|
||||
LIST : List available mcu commands, local commands, and local variables
|
||||
HELP : Show this text
|
||||
All commands also support evaluation by enclosing an expression in { }.
|
||||
For example, "reset_step_clock oid=4 clock={clock + freq}". In addition
|
||||
to user defined variables (via the SET command) the following builtin
|
||||
variables may be used in expressions:
|
||||
clock : The current mcu clock time (as estimated by the host)
|
||||
freq : The mcu clock frequency
|
||||
"""
|
||||
|
||||
re_eval = re.compile(r'\{(?P<eval>[^}]*)\}')
|
||||
|
||||
@@ -14,59 +33,132 @@ class KeyboardReader:
|
||||
def __init__(self, ser, reactor):
|
||||
self.ser = ser
|
||||
self.reactor = reactor
|
||||
self.clocksync = clocksync.ClockSync(self.reactor)
|
||||
self.fd = sys.stdin.fileno()
|
||||
util.set_nonblock(self.fd)
|
||||
self.mcu_freq = 0
|
||||
self.pins = None
|
||||
self.data = ""
|
||||
reactor.register_fd(self.fd, self.process_kbd)
|
||||
self.connect_timer = reactor.register_timer(self.connect, reactor.NOW)
|
||||
self.local_commands = { "PINS": self.set_pin_map, "SET": self.set_var }
|
||||
self.local_commands = {
|
||||
"PINS": self.command_PINS, "SET": self.command_SET,
|
||||
"DELAY": self.command_DELAY, "FLOOD": self.command_FLOOD,
|
||||
"SUPPRESS": self.command_SUPPRESS, "STATS": self.command_STATS,
|
||||
"LIST": self.command_LIST, "HELP": self.command_HELP,
|
||||
}
|
||||
self.eval_globals = {}
|
||||
def connect(self, eventtime):
|
||||
self.output(help_txt)
|
||||
self.output("="*20 + " attempting to connect " + "="*20)
|
||||
self.ser.connect()
|
||||
self.clocksync.connect(self.ser)
|
||||
self.ser.handle_default = self.handle_default
|
||||
self.mcu_freq = self.ser.msgparser.get_constant_float('CLOCK_FREQ')
|
||||
mcu = self.ser.msgparser.get_constant('MCU')
|
||||
self.pins = pins.get_pin_map(mcu)
|
||||
self.reactor.unregister_timer(self.connect_timer)
|
||||
self.output("="*20 + " connected " + "="*20)
|
||||
return self.reactor.NEVER
|
||||
def output(self, msg):
|
||||
sys.stdout.write("%s\n" % (msg,))
|
||||
sys.stdout.flush()
|
||||
def handle_default(self, params):
|
||||
self.output(self.ser.msgparser.format_params(params))
|
||||
def handle_suppress(self, params):
|
||||
pass
|
||||
def update_evals(self, eventtime):
|
||||
f = int(self.ser.msgparser.config.get('CLOCK_FREQ', 1))
|
||||
c = self.ser.get_clock(eventtime)
|
||||
self.eval_globals['freq'] = f
|
||||
self.eval_globals['clock'] = int(c)
|
||||
def set_pin_map(self, parts):
|
||||
mcu = self.ser.msgparser.config['MCU']
|
||||
self.pins = pins.map_pins(parts[1], mcu)
|
||||
def set_var(self, parts):
|
||||
self.eval_globals['freq'] = self.mcu_freq
|
||||
self.eval_globals['clock'] = self.clocksync.get_clock(eventtime)
|
||||
def command_PINS(self, parts):
|
||||
mcu = self.ser.msgparser.get_constant('MCU')
|
||||
self.pins = pins.get_pin_map(mcu, parts[1])
|
||||
def command_SET(self, parts):
|
||||
val = parts[2]
|
||||
try:
|
||||
val = int(val)
|
||||
except ValueError:
|
||||
try:
|
||||
val = float(val)
|
||||
except ValueError:
|
||||
pass
|
||||
self.eval_globals[parts[1]] = val
|
||||
def lookup_pin(self, value):
|
||||
if self.pins is None:
|
||||
self.pins = pins.mcu_to_pins(self.ser.msgparser.config['MCU'])
|
||||
return self.pins[value]
|
||||
def command_DELAY(self, parts):
|
||||
try:
|
||||
val = int(parts[1])
|
||||
except ValueError as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return
|
||||
try:
|
||||
msg = self.ser.msgparser.create_command(' '.join(parts[2:]))
|
||||
except msgproto.error as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return
|
||||
self.ser.send(msg, minclock=val)
|
||||
def command_FLOOD(self, parts):
|
||||
try:
|
||||
count = int(parts[1])
|
||||
delay = float(parts[2])
|
||||
except ValueError as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return
|
||||
try:
|
||||
msg = self.ser.msgparser.create_command(' '.join(parts[3:]))
|
||||
except msgproto.error as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return
|
||||
delay_clock = int(delay * self.mcu_freq)
|
||||
msg_clock = int(self.clocksync.get_clock(self.reactor.monotonic())
|
||||
+ self.mcu_freq * .200)
|
||||
for i in range(count):
|
||||
next_clock = msg_clock + delay_clock
|
||||
self.ser.send(msg, minclock=msg_clock, reqclock=next_clock)
|
||||
msg_clock = next_clock
|
||||
def command_SUPPRESS(self, parts):
|
||||
oid = None
|
||||
try:
|
||||
name = parts[1]
|
||||
if len(parts) > 2:
|
||||
oid = int(parts[2])
|
||||
except ValueError as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return
|
||||
self.ser.register_callback(self.handle_suppress, name, oid)
|
||||
def command_STATS(self, parts):
|
||||
curtime = self.reactor.monotonic()
|
||||
self.output(' '.join([self.ser.stats(curtime),
|
||||
self.clocksync.stats(curtime)]))
|
||||
def command_LIST(self, parts):
|
||||
self.update_evals(self.reactor.monotonic())
|
||||
mp = self.ser.msgparser
|
||||
out = "Available mcu commands:"
|
||||
out += "\n ".join([""] + sorted([
|
||||
mp.messages_by_id[i].msgformat for i in mp.command_ids]))
|
||||
out += "\nAvailable artificial commands:"
|
||||
out += "\n ".join([""] + [n for n in sorted(self.local_commands)])
|
||||
out += "\nAvailable local variables:"
|
||||
out += "\n ".join([""] + ["%s: %s" % (k, v)
|
||||
for k, v in sorted(self.eval_globals.items())])
|
||||
self.output(out)
|
||||
def command_HELP(self, parts):
|
||||
self.output(help_txt)
|
||||
def translate(self, line, eventtime):
|
||||
evalparts = re_eval.split(line)
|
||||
if len(evalparts) > 1:
|
||||
self.update_evals(eventtime)
|
||||
try:
|
||||
for i in range(1, len(evalparts), 2):
|
||||
evalparts[i] = str(eval(evalparts[i], self.eval_globals))
|
||||
e = eval(evalparts[i], dict(self.eval_globals))
|
||||
if type(e) == type(0.):
|
||||
e = int(e)
|
||||
evalparts[i] = str(e)
|
||||
except:
|
||||
print "Unable to evaluate: ", line
|
||||
self.output("Unable to evaluate: %s" % (line,))
|
||||
return None
|
||||
line = ''.join(evalparts)
|
||||
print "Eval:", line
|
||||
if self.pins is None and self.ser.msgparser.config:
|
||||
self.pins = pins.mcu_to_pins(self.ser.msgparser.config['MCU'])
|
||||
self.output("Eval: %s" % (line,))
|
||||
if self.pins is not None:
|
||||
try:
|
||||
line = pins.update_command(line, self.pins).strip()
|
||||
except:
|
||||
print "Unable to map pin: ", line
|
||||
self.output("Unable to map pin: %s" % (line,))
|
||||
return None
|
||||
if line:
|
||||
parts = line.split()
|
||||
@@ -75,8 +167,8 @@ class KeyboardReader:
|
||||
return None
|
||||
try:
|
||||
msg = self.ser.msgparser.create_command(line)
|
||||
except msgproto.error, e:
|
||||
print "Error:", e
|
||||
except msgproto.error as e:
|
||||
self.output("Error: %s" % (str(e),))
|
||||
return None
|
||||
return msg
|
||||
def process_kbd(self, eventtime):
|
||||
|
||||
150
klippy/corexy.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# Code for handling the kinematics of corexy robots
|
||||
#
|
||||
# Copyright (C) 2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, math
|
||||
import stepper, homing
|
||||
|
||||
StepList = (0, 1, 2)
|
||||
|
||||
class CoreXYKinematics:
|
||||
def __init__(self, toolhead, printer, config):
|
||||
self.steppers = [stepper.PrinterHomingStepper(
|
||||
printer, config.getsection('stepper_' + n), n)
|
||||
for n in ['x', 'y', 'z']]
|
||||
self.steppers[0].mcu_endstop.add_stepper(self.steppers[1].mcu_stepper)
|
||||
self.steppers[1].mcu_endstop.add_stepper(self.steppers[0].mcu_stepper)
|
||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||
self.max_z_velocity = config.getfloat(
|
||||
'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
|
||||
self.max_z_accel = config.getfloat(
|
||||
'max_z_accel', max_accel, above=0., maxval=max_accel)
|
||||
self.need_motor_enable = True
|
||||
self.limits = [(1.0, -1.0)] * 3
|
||||
# Setup stepper max halt velocity
|
||||
max_halt_velocity = toolhead.get_max_axis_halt()
|
||||
max_xy_halt_velocity = max_halt_velocity * math.sqrt(2.)
|
||||
self.steppers[0].set_max_jerk(max_xy_halt_velocity, max_accel)
|
||||
self.steppers[1].set_max_jerk(max_xy_halt_velocity, max_accel)
|
||||
self.steppers[2].set_max_jerk(
|
||||
min(max_halt_velocity, self.max_z_velocity), self.max_z_accel)
|
||||
def set_position(self, newpos):
|
||||
pos = (newpos[0] + newpos[1], newpos[0] - newpos[1], newpos[2])
|
||||
for i in StepList:
|
||||
self.steppers[i].mcu_stepper.set_position(pos[i])
|
||||
def home(self, homing_state):
|
||||
# Each axis is homed independently and in order
|
||||
for axis in homing_state.get_axes():
|
||||
s = self.steppers[axis]
|
||||
self.limits[axis] = (s.position_min, s.position_max)
|
||||
# Determine moves
|
||||
if s.homing_positive_dir:
|
||||
pos = s.position_endstop - 1.5*(
|
||||
s.position_endstop - s.position_min)
|
||||
rpos = s.position_endstop - s.homing_retract_dist
|
||||
r2pos = rpos - s.homing_retract_dist
|
||||
else:
|
||||
pos = s.position_endstop + 1.5*(
|
||||
s.position_max - s.position_endstop)
|
||||
rpos = s.position_endstop + s.homing_retract_dist
|
||||
r2pos = rpos + s.homing_retract_dist
|
||||
# Initial homing
|
||||
homing_speed = s.get_homing_speed()
|
||||
homepos = [None, None, None, None]
|
||||
homepos[axis] = s.position_endstop
|
||||
coord = [None, None, None, None]
|
||||
coord[axis] = pos
|
||||
homing_state.home(list(coord), homepos, [s], homing_speed)
|
||||
# Retract
|
||||
coord[axis] = rpos
|
||||
homing_state.retract(list(coord), homing_speed)
|
||||
# Home again
|
||||
coord[axis] = r2pos
|
||||
homing_state.home(
|
||||
list(coord), homepos, [s], homing_speed/2.0, second_home=True)
|
||||
if axis == 2:
|
||||
# Support endstop phase detection on Z axis
|
||||
coord[axis] = s.position_endstop + s.get_homed_offset()
|
||||
homing_state.set_homed_position(coord)
|
||||
def query_endstops(self, print_time, query_flags):
|
||||
return homing.query_endstops(print_time, query_flags, self.steppers)
|
||||
def motor_off(self, print_time):
|
||||
self.limits = [(1.0, -1.0)] * 3
|
||||
for stepper in self.steppers:
|
||||
stepper.motor_enable(print_time, 0)
|
||||
self.need_motor_enable = True
|
||||
def _check_motor_enable(self, print_time, move):
|
||||
if move.axes_d[0] or move.axes_d[1]:
|
||||
self.steppers[0].motor_enable(print_time, 1)
|
||||
self.steppers[1].motor_enable(print_time, 1)
|
||||
if move.axes_d[2]:
|
||||
self.steppers[2].motor_enable(print_time, 1)
|
||||
need_motor_enable = False
|
||||
for i in StepList:
|
||||
need_motor_enable |= self.steppers[i].need_motor_enable
|
||||
self.need_motor_enable = need_motor_enable
|
||||
def _check_endstops(self, move):
|
||||
end_pos = move.end_pos
|
||||
for i in StepList:
|
||||
if (move.axes_d[i]
|
||||
and (end_pos[i] < self.limits[i][0]
|
||||
or end_pos[i] > self.limits[i][1])):
|
||||
if self.limits[i][0] > self.limits[i][1]:
|
||||
raise homing.EndstopMoveError(
|
||||
end_pos, "Must home axis first")
|
||||
raise homing.EndstopMoveError(end_pos)
|
||||
def check_move(self, move):
|
||||
limits = self.limits
|
||||
xpos, ypos = move.end_pos[:2]
|
||||
if (xpos < limits[0][0] or xpos > limits[0][1]
|
||||
or ypos < limits[1][0] or ypos > limits[1][1]):
|
||||
self._check_endstops(move)
|
||||
if not move.axes_d[2]:
|
||||
# Normal XY move - use defaults
|
||||
return
|
||||
# Move with Z - update velocity and accel for slower Z axis
|
||||
self._check_endstops(move)
|
||||
z_ratio = move.move_d / abs(move.axes_d[2])
|
||||
move.limit_speed(
|
||||
self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
|
||||
def move(self, print_time, move):
|
||||
if self.need_motor_enable:
|
||||
self._check_motor_enable(print_time, move)
|
||||
sxp = move.start_pos[0]
|
||||
syp = move.start_pos[1]
|
||||
move_start_pos = (sxp + syp, sxp - syp, move.start_pos[2])
|
||||
exp = move.end_pos[0]
|
||||
eyp = move.end_pos[1]
|
||||
axes_d = ((exp + eyp) - move_start_pos[0],
|
||||
(exp - eyp) - move_start_pos[1], move.axes_d[2])
|
||||
for i in StepList:
|
||||
axis_d = axes_d[i]
|
||||
if not axis_d:
|
||||
continue
|
||||
mcu_stepper = self.steppers[i].mcu_stepper
|
||||
move_time = print_time
|
||||
start_pos = move_start_pos[i]
|
||||
axis_r = abs(axis_d) / move.move_d
|
||||
accel = move.accel * axis_r
|
||||
cruise_v = move.cruise_v * axis_r
|
||||
|
||||
# Acceleration steps
|
||||
if move.accel_r:
|
||||
accel_d = move.accel_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, accel_d, move.start_v * axis_r, accel)
|
||||
start_pos += accel_d
|
||||
move_time += move.accel_t
|
||||
# Cruising steps
|
||||
if move.cruise_r:
|
||||
cruise_d = move.cruise_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, cruise_d, cruise_v, 0.)
|
||||
start_pos += cruise_d
|
||||
move_time += move.cruise_t
|
||||
# Deceleration steps
|
||||
if move.decel_r:
|
||||
decel_d = move.decel_r * axis_d
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, decel_d, cruise_v, -accel)
|
||||
263
klippy/delta.py
@@ -1,6 +1,6 @@
|
||||
# Code for handling the kinematics of linear delta robots
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import math, logging
|
||||
@@ -8,40 +8,60 @@ import stepper, homing
|
||||
|
||||
StepList = (0, 1, 2)
|
||||
|
||||
# Slow moves once the ratio of tower to XY movement exceeds SLOW_RATIO
|
||||
SLOW_RATIO = 3.
|
||||
|
||||
class DeltaKinematics:
|
||||
def __init__(self, printer, config):
|
||||
self.steppers = [stepper.PrinterStepper(
|
||||
def __init__(self, toolhead, printer, config):
|
||||
self.steppers = [stepper.PrinterHomingStepper(
|
||||
printer, config.getsection('stepper_' + n), n)
|
||||
for n in ['a', 'b', 'c']]
|
||||
self.need_motor_enable = True
|
||||
self.max_z_velocity = config.getfloat('max_z_velocity', 9999999.9)
|
||||
radius = config.getfloat('delta_radius')
|
||||
arm_length = config.getfloat('delta_arm_length')
|
||||
self.need_motor_enable = self.need_home = True
|
||||
radius = config.getfloat('delta_radius', above=0.)
|
||||
arm_length = config.getfloat('delta_arm_length', above=radius)
|
||||
self.arm_length2 = arm_length**2
|
||||
self.max_xy2 = min(radius, arm_length - radius)**2
|
||||
self.limit_xy2 = -1.
|
||||
tower_height_at_zeros = math.sqrt(self.arm_length2 - radius**2)
|
||||
self.max_z = self.steppers[0].position_max
|
||||
self.max_z = min([s.position_endstop for s in self.steppers])
|
||||
self.limit_z = self.max_z - (arm_length - tower_height_at_zeros)
|
||||
sin = lambda angle: math.sin(math.radians(angle))
|
||||
cos = lambda angle: math.cos(math.radians(angle))
|
||||
self.towers = [
|
||||
(cos(210.)*radius, sin(210.)*radius),
|
||||
(cos(330.)*radius, sin(330.)*radius),
|
||||
(cos(90.)*radius, sin(90.)*radius)]
|
||||
def set_max_jerk(self, max_xy_halt_velocity, max_accel):
|
||||
# XXX - this sets conservative values
|
||||
for stepper in self.steppers:
|
||||
stepper.set_max_jerk(max_xy_halt_velocity, max_accel)
|
||||
def build_config(self):
|
||||
for stepper in self.steppers:
|
||||
stepper.build_config()
|
||||
logging.info(
|
||||
"Delta max build height %.2fmm (radius tapered above %.2fmm)" % (
|
||||
self.max_z, self.limit_z))
|
||||
# Setup stepper max halt velocity
|
||||
self.max_velocity, self.max_accel = toolhead.get_max_velocity()
|
||||
self.max_z_velocity = config.getfloat(
|
||||
'max_z_velocity', self.max_velocity,
|
||||
above=0., maxval=self.max_velocity)
|
||||
max_halt_velocity = toolhead.get_max_axis_halt()
|
||||
for s in self.steppers:
|
||||
s.set_max_jerk(max_halt_velocity, self.max_accel)
|
||||
# Determine tower locations in cartesian space
|
||||
angles = [config.getsection('stepper_a').getfloat('angle', 210.),
|
||||
config.getsection('stepper_b').getfloat('angle', 330.),
|
||||
config.getsection('stepper_c').getfloat('angle', 90.)]
|
||||
self.towers = [(math.cos(math.radians(angle)) * radius,
|
||||
math.sin(math.radians(angle)) * radius)
|
||||
for angle in angles]
|
||||
# Find the point where an XY move could result in excessive
|
||||
# tower movement
|
||||
half_min_step_dist = min([s.step_dist for s in self.steppers]) * .5
|
||||
def ratio_to_dist(ratio):
|
||||
return (ratio * math.sqrt(self.arm_length2 / (ratio**2 + 1.)
|
||||
- half_min_step_dist**2)
|
||||
+ half_min_step_dist)
|
||||
self.slow_xy2 = (ratio_to_dist(SLOW_RATIO) - radius)**2
|
||||
self.very_slow_xy2 = (ratio_to_dist(2. * SLOW_RATIO) - radius)**2
|
||||
self.max_xy2 = min(radius, arm_length - radius,
|
||||
ratio_to_dist(4. * SLOW_RATIO) - radius)**2
|
||||
logging.info(
|
||||
"Delta max build radius %.2fmm (moves slowed past %.2fmm and %.2fmm)"
|
||||
% (math.sqrt(self.max_xy2), math.sqrt(self.slow_xy2),
|
||||
math.sqrt(self.very_slow_xy2)))
|
||||
self.set_position([0., 0., 0.])
|
||||
def _cartesian_to_actuator(self, coord):
|
||||
return [int((math.sqrt(self.arm_length2
|
||||
return [math.sqrt(self.arm_length2
|
||||
- (self.towers[i][0] - coord[0])**2
|
||||
- (self.towers[i][1] - coord[1])**2) + coord[2])
|
||||
* self.steppers[i].inv_step_dist + 0.5)
|
||||
- (self.towers[i][1] - coord[1])**2) + coord[2]
|
||||
for i in StepList]
|
||||
def _actuator_to_cartesian(self, pos):
|
||||
# Based on code from Smoothieware
|
||||
@@ -78,164 +98,129 @@ class DeltaKinematics:
|
||||
pos = self._cartesian_to_actuator(newpos)
|
||||
for i in StepList:
|
||||
self.steppers[i].mcu_stepper.set_position(pos[i])
|
||||
self.limit_xy2 = -1.
|
||||
def home(self, homing_state):
|
||||
# All axes are homed simultaneously
|
||||
homing_state.set_axes([0, 1, 2])
|
||||
s = self.steppers[0] # Assume homing parameters same for all steppers
|
||||
self.limit_xy2 = self.max_xy2
|
||||
s = self.steppers[0] # Assume homing speed same for all steppers
|
||||
self.need_home = False
|
||||
# Initial homing
|
||||
homepos = [0., 0., s.position_endstop, None]
|
||||
homing_speed = s.get_homing_speed()
|
||||
homepos = [0., 0., self.max_z, None]
|
||||
coord = list(homepos)
|
||||
coord[2] = -1.5 * math.sqrt(self.arm_length2-self.max_xy2)
|
||||
homing_state.home(list(coord), homepos, self.steppers, s.homing_speed)
|
||||
homing_state.home(list(coord), homepos, self.steppers, homing_speed)
|
||||
# Retract
|
||||
coord[2] = homepos[2] - s.homing_retract_dist
|
||||
homing_state.retract(list(coord), s.homing_speed)
|
||||
homing_state.retract(list(coord), homing_speed)
|
||||
# Home again
|
||||
coord[2] -= s.homing_retract_dist
|
||||
homing_state.home(list(coord), homepos, self.steppers
|
||||
, s.homing_speed/2.0, second_home=True)
|
||||
, homing_speed/2.0, second_home=True)
|
||||
# Set final homed position
|
||||
coord = [(s.mcu_stepper.commanded_position + s.get_homed_offset())
|
||||
* s.step_dist
|
||||
for s in self.steppers]
|
||||
homing_state.set_homed_position(self._actuator_to_cartesian(coord))
|
||||
def motor_off(self, move_time):
|
||||
spos = self._cartesian_to_actuator(homepos)
|
||||
spos = [spos[i] + self.steppers[i].position_endstop - self.max_z
|
||||
+ self.steppers[i].get_homed_offset()
|
||||
for i in StepList]
|
||||
homing_state.set_homed_position(self._actuator_to_cartesian(spos))
|
||||
def query_endstops(self, print_time, query_flags):
|
||||
return homing.query_endstops(print_time, query_flags, self.steppers)
|
||||
def motor_off(self, print_time):
|
||||
self.limit_xy2 = -1.
|
||||
for stepper in self.steppers:
|
||||
stepper.motor_enable(move_time, 0)
|
||||
self.need_motor_enable = True
|
||||
def _check_motor_enable(self, move_time):
|
||||
stepper.motor_enable(print_time, 0)
|
||||
self.need_motor_enable = self.need_home = True
|
||||
def _check_motor_enable(self, print_time):
|
||||
for i in StepList:
|
||||
self.steppers[i].motor_enable(move_time, 1)
|
||||
self.steppers[i].motor_enable(print_time, 1)
|
||||
self.need_motor_enable = False
|
||||
def query_endstops(self, print_time):
|
||||
endstops = [(s, s.query_endstop(print_time)) for s in self.steppers]
|
||||
return [(s.name, es.query_endstop_wait()) for s, es in endstops]
|
||||
def check_move(self, move):
|
||||
end_pos = move.end_pos
|
||||
xy2 = end_pos[0]**2 + end_pos[1]**2
|
||||
if xy2 > self.limit_xy2 or end_pos[2] < 0.:
|
||||
if self.limit_xy2 < 0.:
|
||||
if xy2 <= self.limit_xy2 and not move.axes_d[2]:
|
||||
# Normal XY move
|
||||
return
|
||||
if self.need_home:
|
||||
raise homing.EndstopMoveError(end_pos, "Must home first")
|
||||
raise homing.EndstopMoveError(end_pos)
|
||||
limit_xy2 = self.max_xy2
|
||||
if end_pos[2] > self.limit_z:
|
||||
if end_pos[2] > self.max_z or xy2 > (self.max_z - end_pos[2])**2:
|
||||
limit_xy2 = min(limit_xy2, (self.max_z - end_pos[2])**2)
|
||||
if xy2 > limit_xy2 or end_pos[2] < 0. or end_pos[2] > self.max_z:
|
||||
raise homing.EndstopMoveError(end_pos)
|
||||
if move.axes_d[2]:
|
||||
move.limit_speed(self.max_z_velocity, 9999999.9)
|
||||
def move(self, move_time, move):
|
||||
move.limit_speed(self.max_z_velocity, move.accel)
|
||||
limit_xy2 = -1.
|
||||
# Limit the speed/accel of this move if is is at the extreme
|
||||
# end of the build envelope
|
||||
extreme_xy2 = max(xy2, move.start_pos[0]**2 + move.start_pos[1]**2)
|
||||
if extreme_xy2 > self.slow_xy2:
|
||||
r = 0.5
|
||||
if extreme_xy2 > self.very_slow_xy2:
|
||||
r = 0.25
|
||||
max_velocity = self.max_velocity
|
||||
if move.axes_d[2]:
|
||||
max_velocity = self.max_z_velocity
|
||||
move.limit_speed(max_velocity * r, self.max_accel * r)
|
||||
limit_xy2 = -1.
|
||||
self.limit_xy2 = min(limit_xy2, self.slow_xy2)
|
||||
def move(self, print_time, move):
|
||||
if self.need_motor_enable:
|
||||
self._check_motor_enable(print_time)
|
||||
axes_d = move.axes_d
|
||||
move_d = movexy_d = move.move_d
|
||||
move_d = move.move_d
|
||||
movexy_r = 1.
|
||||
movez_r = 0.
|
||||
inv_movexy_d = 1. / movexy_d
|
||||
inv_movexy_d = 1. / move_d
|
||||
if not axes_d[0] and not axes_d[1]:
|
||||
if not axes_d[2]:
|
||||
return
|
||||
# Z only move
|
||||
movez_r = axes_d[2] * inv_movexy_d
|
||||
movexy_d = movexy_r = inv_movexy_d = 0.
|
||||
movexy_r = inv_movexy_d = 0.
|
||||
elif axes_d[2]:
|
||||
# XY+Z move
|
||||
movexy_d = math.sqrt(axes_d[0]**2 + axes_d[1]**2)
|
||||
movexy_r = movexy_d * inv_movexy_d
|
||||
movez_r = axes_d[2] * inv_movexy_d
|
||||
inv_movexy_d = 1. / movexy_d
|
||||
|
||||
if self.need_motor_enable:
|
||||
self._check_motor_enable(move_time)
|
||||
|
||||
origx, origy, origz = move.start_pos[:3]
|
||||
|
||||
accel_t = move.accel_t
|
||||
cruise_end_t = accel_t + move.cruise_t
|
||||
accel = move.accel
|
||||
cruise_v = move.cruise_v
|
||||
accel_d = move.accel_r * move_d
|
||||
cruise_end_d = accel_d + move.cruise_r * move_d
|
||||
|
||||
inv_cruise_v = 1. / move.cruise_v
|
||||
inv_accel = 1. / move.accel
|
||||
accel_time_offset = move.start_v * inv_accel
|
||||
accel_multiplier = 2.0 * inv_accel
|
||||
accel_offset = move.start_v**2 * 0.5 * inv_accel
|
||||
decel_time_offset = move.cruise_v * inv_accel + cruise_end_t
|
||||
decel_offset = move.cruise_v**2 * 0.5 * inv_accel + cruise_end_d
|
||||
cruise_d = move.cruise_r * move_d
|
||||
decel_d = move.decel_r * move_d
|
||||
|
||||
for i in StepList:
|
||||
# Find point on line of movement closest to tower
|
||||
# Calculate a virtual tower along the line of movement at
|
||||
# the point closest to this stepper's tower.
|
||||
towerx_d = self.towers[i][0] - origx
|
||||
towery_d = self.towers[i][1] - origy
|
||||
closestxy_d = (towerx_d*axes_d[0] + towery_d*axes_d[1])*inv_movexy_d
|
||||
tangentxy_d2 = towerx_d**2 + towery_d**2 - closestxy_d**2
|
||||
closest_height2 = self.arm_length2 - tangentxy_d2
|
||||
|
||||
# Calculate accel/cruise/decel portions of move
|
||||
reversexy_d = closestxy_d + math.sqrt(closest_height2)*movez_r
|
||||
accel_up_d = cruise_up_d = decel_up_d = 0.
|
||||
accel_down_d = cruise_down_d = decel_down_d = 0.
|
||||
if reversexy_d <= 0.:
|
||||
accel_down_d = accel_d
|
||||
cruise_down_d = cruise_end_d
|
||||
decel_down_d = move_d
|
||||
elif reversexy_d >= movexy_d:
|
||||
accel_up_d = accel_d
|
||||
cruise_up_d = cruise_end_d
|
||||
decel_up_d = move_d
|
||||
elif reversexy_d < accel_d * movexy_r:
|
||||
accel_up_d = reversexy_d * move_d * inv_movexy_d
|
||||
accel_down_d = accel_d
|
||||
cruise_down_d = cruise_end_d
|
||||
decel_down_d = move_d
|
||||
elif reversexy_d < cruise_end_d * movexy_r:
|
||||
accel_up_d = accel_d
|
||||
cruise_up_d = reversexy_d * move_d * inv_movexy_d
|
||||
cruise_down_d = cruise_end_d
|
||||
decel_down_d = move_d
|
||||
else:
|
||||
accel_up_d = accel_d
|
||||
cruise_up_d = cruise_end_d
|
||||
decel_up_d = reversexy_d * move_d * inv_movexy_d
|
||||
decel_down_d = move_d
|
||||
vt_startxy_d = (towerx_d*axes_d[0] + towery_d*axes_d[1])*inv_movexy_d
|
||||
tangentxy_d2 = towerx_d**2 + towery_d**2 - vt_startxy_d**2
|
||||
vt_arm_d = math.sqrt(self.arm_length2 - tangentxy_d2)
|
||||
vt_startz = origz
|
||||
|
||||
# Generate steps
|
||||
mcu_stepper = self.steppers[i].mcu_stepper
|
||||
mcu_time = mcu_stepper.print_to_mcu_time(move_time)
|
||||
step_pos = mcu_stepper.commanded_position
|
||||
step_dist = self.steppers[i].step_dist
|
||||
height = step_pos*step_dist - origz
|
||||
if accel_up_d > 0.:
|
||||
count = mcu_stepper.step_delta_accel(
|
||||
mcu_time - accel_time_offset, accel_up_d,
|
||||
accel_offset, accel_multiplier, step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
height += count * step_dist
|
||||
if cruise_up_d > 0.:
|
||||
count = mcu_stepper.step_delta_const(
|
||||
mcu_time + accel_t, cruise_up_d,
|
||||
-accel_d, inv_cruise_v, step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
height += count * step_dist
|
||||
if decel_up_d > 0.:
|
||||
count = mcu_stepper.step_delta_accel(
|
||||
mcu_time + decel_time_offset, decel_up_d,
|
||||
-decel_offset, -accel_multiplier, step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
height += count * step_dist
|
||||
if accel_down_d > 0.:
|
||||
count = mcu_stepper.step_delta_accel(
|
||||
mcu_time - accel_time_offset, accel_down_d,
|
||||
accel_offset, accel_multiplier, -step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
height += count * step_dist
|
||||
if cruise_down_d > 0.:
|
||||
count = mcu_stepper.step_delta_const(
|
||||
mcu_time + accel_t, cruise_down_d,
|
||||
-accel_d, inv_cruise_v, -step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
height += count * step_dist
|
||||
if decel_down_d > 0.:
|
||||
count = mcu_stepper.step_delta_accel(
|
||||
mcu_time + decel_time_offset, decel_down_d,
|
||||
-decel_offset, -accel_multiplier, -step_dist,
|
||||
height, closestxy_d, closest_height2, movez_r)
|
||||
move_time = print_time
|
||||
if accel_d:
|
||||
mcu_stepper.step_delta(
|
||||
move_time, accel_d, move.start_v, accel,
|
||||
vt_startz, vt_startxy_d, vt_arm_d, movez_r)
|
||||
vt_startz += accel_d * movez_r
|
||||
vt_startxy_d -= accel_d * movexy_r
|
||||
move_time += move.accel_t
|
||||
if cruise_d:
|
||||
mcu_stepper.step_delta(
|
||||
move_time, cruise_d, cruise_v, 0.,
|
||||
vt_startz, vt_startxy_d, vt_arm_d, movez_r)
|
||||
vt_startz += cruise_d * movez_r
|
||||
vt_startxy_d -= cruise_d * movexy_r
|
||||
move_time += move.cruise_t
|
||||
if decel_d:
|
||||
mcu_stepper.step_delta(
|
||||
move_time, decel_d, cruise_v, -accel,
|
||||
vt_startz, vt_startxy_d, vt_arm_d, movez_r)
|
||||
|
||||
|
||||
######################################################################
|
||||
|
||||
@@ -3,45 +3,144 @@
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
import math, logging
|
||||
import stepper, heater, homing
|
||||
|
||||
EXTRUDE_DIFF_IGNORE = 1.02
|
||||
|
||||
class PrinterExtruder:
|
||||
def __init__(self, printer, config):
|
||||
self.config = config
|
||||
self.heater = heater.PrinterHeater(printer, config)
|
||||
self.stepper = stepper.PrinterStepper(printer, config, 'extruder')
|
||||
self.pressure_advance = config.getfloat('pressure_advance', 0.)
|
||||
self.max_e_velocity = config.getfloat('max_velocity')
|
||||
self.max_e_accel = config.getfloat('max_accel')
|
||||
self.nozzle_diameter = config.getfloat('nozzle_diameter', above=0.)
|
||||
filament_diameter = config.getfloat(
|
||||
'filament_diameter', minval=self.nozzle_diameter)
|
||||
self.filament_area = math.pi * (filament_diameter * .5)**2
|
||||
max_cross_section = config.getfloat(
|
||||
'max_extrude_cross_section', 4. * self.nozzle_diameter**2
|
||||
, above=0.)
|
||||
self.max_extrude_ratio = max_cross_section / self.filament_area
|
||||
toolhead = printer.objects['toolhead']
|
||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||
self.max_e_velocity = self.config.getfloat(
|
||||
'max_extrude_only_velocity', max_velocity * self.max_extrude_ratio
|
||||
, above=0.)
|
||||
self.max_e_accel = self.config.getfloat(
|
||||
'max_extrude_only_accel', max_accel * self.max_extrude_ratio
|
||||
, above=0.)
|
||||
self.stepper.set_max_jerk(9999999.9, 9999999.9)
|
||||
self.max_e_dist = config.getfloat(
|
||||
'max_extrude_only_distance', 50., minval=0.)
|
||||
self.activate_gcode = config.get('activate_gcode', '')
|
||||
self.deactivate_gcode = config.get('deactivate_gcode', '')
|
||||
self.pressure_advance = config.getfloat(
|
||||
'pressure_advance', 0., minval=0.)
|
||||
self.pressure_advance_lookahead_time = 0.
|
||||
if self.pressure_advance:
|
||||
self.pressure_advance_lookahead_time = config.getfloat(
|
||||
'pressure_advance_lookahead_time', 0.010, minval=0.)
|
||||
self.need_motor_enable = True
|
||||
self.extrude_pos = 0.
|
||||
def build_config(self):
|
||||
self.heater.build_config()
|
||||
self.stepper.set_max_jerk(9999999.9, 9999999.9)
|
||||
self.stepper.build_config()
|
||||
def motor_off(self, move_time):
|
||||
self.stepper.motor_enable(move_time, 0)
|
||||
def get_heater(self):
|
||||
return self.heater
|
||||
def set_active(self, print_time, is_active):
|
||||
return self.extrude_pos
|
||||
def get_activate_gcode(self, is_active):
|
||||
if is_active:
|
||||
return self.activate_gcode
|
||||
return self.deactivate_gcode
|
||||
def motor_off(self, print_time):
|
||||
self.stepper.motor_enable(print_time, 0)
|
||||
self.need_motor_enable = True
|
||||
def check_move(self, move):
|
||||
move.extrude_r = move.axes_d[3] / move.move_d
|
||||
move.extrude_max_corner_v = 0.
|
||||
if not self.heater.can_extrude:
|
||||
raise homing.EndstopMoveError(
|
||||
move.end_pos, "Extrude below minimum temp")
|
||||
if (not move.do_calc_junction
|
||||
and not move.axes_d[0] and not move.axes_d[1]
|
||||
and not move.axes_d[2]):
|
||||
# Extrude only move - limit accel and velocity
|
||||
move.limit_speed(self.max_e_velocity, self.max_e_accel)
|
||||
def move(self, move_time, move):
|
||||
raise homing.EndstopError(
|
||||
"Extrude below minimum temp\n"
|
||||
"See the 'min_extrude_temp' config option for details")
|
||||
if not move.is_kinematic_move or move.extrude_r < 0.:
|
||||
# Extrude only move (or retraction move) - limit accel and velocity
|
||||
if abs(move.axes_d[3]) > self.max_e_dist:
|
||||
raise homing.EndstopError(
|
||||
"Extrude only move too long (%.3fmm vs %.3fmm)\n"
|
||||
"See the 'max_extrude_only_distance' config"
|
||||
" option for details" % (move.axes_d[3], self.max_e_dist))
|
||||
inv_extrude_r = 1. / abs(move.extrude_r)
|
||||
move.limit_speed(self.max_e_velocity * inv_extrude_r
|
||||
, self.max_e_accel * inv_extrude_r)
|
||||
elif (move.extrude_r > self.max_extrude_ratio
|
||||
and move.axes_d[3] > self.nozzle_diameter*self.max_extrude_ratio):
|
||||
area = move.axes_d[3] * self.filament_area / move.move_d
|
||||
logging.debug("Overextrude: %s vs %s (area=%.3f dist=%.3f)",
|
||||
move.extrude_r, self.max_extrude_ratio,
|
||||
area, move.move_d)
|
||||
raise homing.EndstopError(
|
||||
"Move exceeds maximum extrusion (%.3fmm^2 vs %.3fmm^2)\n"
|
||||
"See the 'max_extrude_cross_section' config option for details"
|
||||
% (area, self.max_extrude_ratio * self.filament_area))
|
||||
def calc_junction(self, prev_move, move):
|
||||
extrude = move.axes_d[3]
|
||||
prev_extrude = prev_move.axes_d[3]
|
||||
if extrude or prev_extrude:
|
||||
if not extrude or not prev_extrude:
|
||||
# Extrude move to non-extrude move - disable lookahead
|
||||
return 0.
|
||||
if ((move.extrude_r > prev_move.extrude_r * EXTRUDE_DIFF_IGNORE
|
||||
or prev_move.extrude_r > move.extrude_r * EXTRUDE_DIFF_IGNORE)
|
||||
and abs(move.move_d * prev_move.extrude_r - extrude) >= .001):
|
||||
# Extrude ratio between moves is too different
|
||||
return 0.
|
||||
move.extrude_r = prev_move.extrude_r
|
||||
return move.max_cruise_v2
|
||||
def lookahead(self, moves, flush_count, lazy):
|
||||
lookahead_t = self.pressure_advance_lookahead_time
|
||||
if not lookahead_t:
|
||||
return flush_count
|
||||
# Calculate max_corner_v - the speed the head will accelerate
|
||||
# to after cornering.
|
||||
for i in range(flush_count):
|
||||
move = moves[i]
|
||||
if not move.decel_t:
|
||||
continue
|
||||
cruise_v = move.cruise_v
|
||||
max_corner_v = 0.
|
||||
sum_t = lookahead_t
|
||||
for j in range(i+1, flush_count):
|
||||
fmove = moves[j]
|
||||
if not fmove.max_start_v2:
|
||||
break
|
||||
if fmove.cruise_v > max_corner_v:
|
||||
if (not max_corner_v
|
||||
and not fmove.accel_t and not fmove.cruise_t):
|
||||
# Start timing after any full decel moves
|
||||
continue
|
||||
if sum_t >= fmove.accel_t:
|
||||
max_corner_v = fmove.cruise_v
|
||||
else:
|
||||
max_corner_v = max(
|
||||
max_corner_v, fmove.start_v + fmove.accel * sum_t)
|
||||
if max_corner_v >= cruise_v:
|
||||
break
|
||||
sum_t -= fmove.accel_t + fmove.cruise_t + fmove.decel_t
|
||||
if sum_t <= 0.:
|
||||
break
|
||||
else:
|
||||
if lazy:
|
||||
return i
|
||||
move.extrude_max_corner_v = max_corner_v
|
||||
return flush_count
|
||||
def move(self, print_time, move):
|
||||
if self.need_motor_enable:
|
||||
self.stepper.motor_enable(move_time, 1)
|
||||
self.stepper.motor_enable(print_time, 1)
|
||||
self.need_motor_enable = False
|
||||
axis_d = move.axes_d[3]
|
||||
extrude_r = abs(axis_d) / move.move_d
|
||||
inv_accel = 1. / (move.accel * extrude_r)
|
||||
|
||||
start_v = move.start_v * extrude_r
|
||||
cruise_v = move.cruise_v * extrude_r
|
||||
end_v = move.end_v * extrude_r
|
||||
axis_r = abs(axis_d) / move.move_d
|
||||
accel = move.accel * axis_r
|
||||
start_v = move.start_v * axis_r
|
||||
cruise_v = move.cruise_v * axis_r
|
||||
end_v = move.end_v * axis_r
|
||||
accel_t, cruise_t, decel_t = move.accel_t, move.cruise_t, move.decel_t
|
||||
accel_d = move.accel_r * axis_d
|
||||
cruise_d = move.cruise_r * axis_d
|
||||
@@ -55,25 +154,19 @@ class PrinterExtruder:
|
||||
if (axis_d >= 0. and (move.axes_d[0] or move.axes_d[1])
|
||||
and self.pressure_advance):
|
||||
# Increase accel_d and start_v when accelerating
|
||||
move_extrude_r = move.extrude_r
|
||||
pressure_advance = self.pressure_advance * move.extrude_r
|
||||
prev_pressure_d = start_pos - move.start_pos[3]
|
||||
if accel_t:
|
||||
npd = move.cruise_v * move_extrude_r * self.pressure_advance
|
||||
if accel_d:
|
||||
npd = move.cruise_v * pressure_advance
|
||||
extra_accel_d = npd - prev_pressure_d
|
||||
if extra_accel_d > 0.:
|
||||
accel_d += extra_accel_d
|
||||
start_v += extra_accel_d / accel_t
|
||||
prev_pressure_d += extra_accel_d
|
||||
# Update decel and retract parameters when decelerating
|
||||
if decel_t:
|
||||
if move.corner_min:
|
||||
npd = move.corner_max*move_extrude_r * self.pressure_advance
|
||||
extra_decel_d = prev_pressure_d - npd
|
||||
if move.end_v > move.corner_min:
|
||||
extra_decel_d *= ((move.cruise_v - move.end_v)
|
||||
/ (move.cruise_v - move.corner_min))
|
||||
else:
|
||||
npd = move.end_v * move_extrude_r * self.pressure_advance
|
||||
emcv = move.extrude_max_corner_v
|
||||
if decel_d and emcv < move.cruise_v:
|
||||
npd = max(emcv, move.end_v) * pressure_advance
|
||||
extra_decel_d = prev_pressure_d - npd
|
||||
if extra_decel_d > 0.:
|
||||
extra_decel_v = extra_decel_d / decel_t
|
||||
@@ -87,7 +180,7 @@ class PrinterExtruder:
|
||||
decel_t = decel_d = 0.
|
||||
elif end_v < 0.:
|
||||
# Split decel phase into decel and retraction
|
||||
retract_t = -end_v * inv_accel
|
||||
retract_t = -end_v / accel
|
||||
retract_d = -end_v * 0.5 * retract_t
|
||||
decel_t -= retract_t
|
||||
decel_d = decel_v * 0.5 * decel_t
|
||||
@@ -96,53 +189,72 @@ class PrinterExtruder:
|
||||
decel_d -= extra_decel_d
|
||||
|
||||
# Prepare for steps
|
||||
inv_step_dist = self.stepper.inv_step_dist
|
||||
step_dist = self.stepper.step_dist
|
||||
mcu_stepper = self.stepper.mcu_stepper
|
||||
mcu_time = mcu_stepper.print_to_mcu_time(move_time)
|
||||
step_pos = mcu_stepper.commanded_position
|
||||
step_offset = step_pos - start_pos * inv_step_dist
|
||||
move_time = print_time
|
||||
|
||||
# Acceleration steps
|
||||
accel_multiplier = 2.0 * step_dist * inv_accel
|
||||
if accel_d:
|
||||
#t = sqrt(2*pos/accel + (start_v/accel)**2) - start_v/accel
|
||||
accel_time_offset = start_v * inv_accel
|
||||
accel_sqrt_offset = accel_time_offset**2
|
||||
accel_steps = accel_d * inv_step_dist
|
||||
count = mcu_stepper.step_sqrt(
|
||||
mcu_time - accel_time_offset, accel_steps, step_offset
|
||||
, accel_sqrt_offset, accel_multiplier)
|
||||
step_offset += count - accel_steps
|
||||
mcu_time += accel_t
|
||||
mcu_stepper.step_const(move_time, start_pos, accel_d, start_v, accel)
|
||||
start_pos += accel_d
|
||||
move_time += accel_t
|
||||
# Cruising steps
|
||||
if cruise_d:
|
||||
#t = pos/cruise_v
|
||||
cruise_multiplier = step_dist / cruise_v
|
||||
cruise_steps = cruise_d * inv_step_dist
|
||||
count = mcu_stepper.step_factor(
|
||||
mcu_time, cruise_steps, step_offset, cruise_multiplier)
|
||||
step_offset += count - cruise_steps
|
||||
mcu_time += cruise_t
|
||||
mcu_stepper.step_const(move_time, start_pos, cruise_d, cruise_v, 0.)
|
||||
start_pos += cruise_d
|
||||
move_time += cruise_t
|
||||
# Deceleration steps
|
||||
if decel_d:
|
||||
#t = cruise_v/accel - sqrt((cruise_v/accel)**2 - 2*pos/accel)
|
||||
decel_time_offset = decel_v * inv_accel
|
||||
decel_sqrt_offset = decel_time_offset**2
|
||||
decel_steps = decel_d * inv_step_dist
|
||||
count = mcu_stepper.step_sqrt(
|
||||
mcu_time + decel_time_offset, decel_steps, step_offset
|
||||
, decel_sqrt_offset, -accel_multiplier)
|
||||
step_offset += count - decel_steps
|
||||
mcu_time += decel_t
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, decel_d, decel_v, -accel)
|
||||
start_pos += decel_d
|
||||
move_time += decel_t
|
||||
# Retraction steps
|
||||
if retract_d:
|
||||
#t = sqrt(2*pos/accel + (start_v/accel)**2) - start_v/accel
|
||||
accel_time_offset = retract_v * inv_accel
|
||||
accel_sqrt_offset = accel_time_offset**2
|
||||
accel_steps = -retract_d * inv_step_dist
|
||||
count = mcu_stepper.step_sqrt(
|
||||
mcu_time - accel_time_offset, accel_steps, step_offset
|
||||
, accel_sqrt_offset, accel_multiplier)
|
||||
mcu_stepper.step_const(
|
||||
move_time, start_pos, -retract_d, retract_v, accel)
|
||||
start_pos -= retract_d
|
||||
self.extrude_pos = start_pos
|
||||
|
||||
self.extrude_pos = start_pos + accel_d + cruise_d + decel_d - retract_d
|
||||
# Dummy extruder class used when a printer has no extruder at all
|
||||
class DummyExtruder:
|
||||
def set_active(self, print_time, is_active):
|
||||
return 0.
|
||||
def motor_off(self, move_time):
|
||||
pass
|
||||
def check_move(self, move):
|
||||
raise homing.EndstopMoveError(
|
||||
move.end_pos, "Extrude when no extruder present")
|
||||
def calc_junction(self, prev_move, move):
|
||||
return move.max_cruise_v2
|
||||
def lookahead(self, moves, flush_count, lazy):
|
||||
return flush_count
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
for i in range(99):
|
||||
section = 'extruder%d' % (i,)
|
||||
if not config.has_section(section):
|
||||
if not i and config.has_section('extruder'):
|
||||
printer.add_object('extruder0', PrinterExtruder(
|
||||
printer, config.getsection('extruder')))
|
||||
continue
|
||||
break
|
||||
printer.add_object(section, PrinterExtruder(
|
||||
printer, config.getsection(section)))
|
||||
|
||||
def get_printer_extruders(printer):
|
||||
out = []
|
||||
for i in range(99):
|
||||
extruder = printer.objects.get('extruder%d' % (i,))
|
||||
if extruder is None:
|
||||
break
|
||||
out.append(extruder)
|
||||
return out
|
||||
|
||||
def get_printer_heater(printer, name):
|
||||
if name == 'heater_bed' and name in printer.objects:
|
||||
return printer.objects[name]
|
||||
if name == 'extruder':
|
||||
name = 'extruder0'
|
||||
if name.startswith('extruder') and name in printer.objects:
|
||||
return printer.objects[name].get_heater()
|
||||
raise printer.config_error("Unknown heater '%s'" % (name,))
|
||||
|
||||
@@ -1,35 +1,63 @@
|
||||
# Printer fan support
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import extruder, pins
|
||||
|
||||
FAN_MIN_TIME = 0.1
|
||||
PWM_CYCLE_TIME = 0.010
|
||||
|
||||
class PrinterFan:
|
||||
def __init__(self, printer, config):
|
||||
self.printer = printer
|
||||
self.config = config
|
||||
self.mcu_fan = None
|
||||
self.last_fan_value = 0
|
||||
self.last_fan_value = 0.
|
||||
self.last_fan_time = 0.
|
||||
self.kick_start_time = config.getfloat('kick_start_time', 0.1)
|
||||
def build_config(self):
|
||||
pin = self.config.get('pin')
|
||||
hard_pwm = self.config.getint('hard_pwm', 128)
|
||||
self.mcu_fan = self.printer.mcu.create_pwm(pin, hard_pwm, 0)
|
||||
# External commands
|
||||
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
||||
self.kick_start_time = config.getfloat('kick_start_time', 0.1, minval=0.)
|
||||
self.mcu_fan = pins.setup_pin(printer, 'pwm', config.get('pin'))
|
||||
self.mcu_fan.setup_max_duration(0.)
|
||||
self.mcu_fan.setup_cycle_time(PWM_CYCLE_TIME)
|
||||
self.mcu_fan.setup_hard_pwm(config.getint('hard_pwm', 0))
|
||||
def set_speed(self, print_time, value):
|
||||
value = max(0, min(255, int(value*255. + 0.5)))
|
||||
value = max(0., min(self.max_power, value))
|
||||
if value == self.last_fan_value:
|
||||
return
|
||||
mcu_time = self.mcu_fan.print_to_mcu_time(print_time)
|
||||
mcu_time = max(self.last_fan_time + FAN_MIN_TIME, mcu_time)
|
||||
if (value and value < 255
|
||||
print_time = max(self.last_fan_time + FAN_MIN_TIME, print_time)
|
||||
if (value and value < self.max_power
|
||||
and not self.last_fan_value and self.kick_start_time):
|
||||
# Run fan at full speed for specified kick_start_time
|
||||
self.mcu_fan.set_pwm(mcu_time, 255)
|
||||
mcu_time += self.kick_start_time
|
||||
self.mcu_fan.set_pwm(mcu_time, value)
|
||||
self.last_fan_time = mcu_time
|
||||
self.mcu_fan.set_pwm(print_time, self.max_power)
|
||||
print_time += self.kick_start_time
|
||||
self.mcu_fan.set_pwm(print_time, value)
|
||||
self.last_fan_time = print_time
|
||||
self.last_fan_value = value
|
||||
|
||||
class PrinterHeaterFan:
|
||||
def __init__(self, printer, config):
|
||||
self.fan = PrinterFan(printer, config)
|
||||
self.mcu = printer.objects['mcu']
|
||||
heater = config.get("heater", "extruder0")
|
||||
self.heater = extruder.get_printer_heater(printer, heater)
|
||||
self.heater_temp = config.getfloat("heater_temp", 50.0)
|
||||
max_power = self.fan.max_power
|
||||
self.fan_speed = config.getfloat(
|
||||
"fan_speed", max_power, minval=0., maxval=max_power)
|
||||
self.fan.mcu_fan.setup_shutdown_value(max_power)
|
||||
printer.reactor.register_timer(self.callback, printer.reactor.NOW)
|
||||
def callback(self, eventtime):
|
||||
current_temp, target_temp = self.heater.get_temp(eventtime)
|
||||
if not current_temp and not target_temp and not self.fan.last_fan_time:
|
||||
# Printer still starting
|
||||
return eventtime + 1.
|
||||
power = 0.
|
||||
if target_temp or current_temp > self.heater_temp:
|
||||
power = self.fan_speed
|
||||
print_time = self.mcu.estimated_print_time(eventtime) + FAN_MIN_TIME
|
||||
self.fan.set_speed(print_time, power)
|
||||
return eventtime + 1.
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
if config.has_section('fan'):
|
||||
printer.add_object('fan', PrinterFan(printer, config.getsection('fan')))
|
||||
for s in config.get_prefix_sections('heater_fan '):
|
||||
printer.add_object(s.section, PrinterHeaterFan(printer, s))
|
||||
|
||||
415
klippy/gcode.py
@@ -3,140 +3,150 @@
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import os, re, logging, collections, time
|
||||
import homing
|
||||
import os, re, logging, collections
|
||||
import homing, extruder, chipmisc
|
||||
|
||||
# Parse out incoming GCode and find and translate head movements
|
||||
class GCodeParser:
|
||||
RETRY_TIME = 0.100
|
||||
def __init__(self, printer, fd, is_fileinput=False):
|
||||
def __init__(self, printer, fd):
|
||||
self.printer = printer
|
||||
self.fd = fd
|
||||
self.is_fileinput = is_fileinput
|
||||
# Input handling
|
||||
self.reactor = printer.reactor
|
||||
self.is_processing_data = False
|
||||
self.is_fileinput = not not printer.get_start_args().get("debuginput")
|
||||
self.fd_handle = None
|
||||
if not is_fileinput:
|
||||
if not self.is_fileinput:
|
||||
self.fd_handle = self.reactor.register_fd(self.fd, self.process_data)
|
||||
self.input_commands = [""]
|
||||
self.partial_input = ""
|
||||
self.bytes_read = 0
|
||||
self.input_log = collections.deque([], 50)
|
||||
# Command handling
|
||||
self.gcode_handlers = {}
|
||||
self.is_printer_ready = False
|
||||
self.gcode_handlers = {}
|
||||
self.build_handlers()
|
||||
self.need_ack = False
|
||||
self.toolhead = self.heater_nozzle = self.heater_bed = self.fan = None
|
||||
self.toolhead = self.fan = self.extruder = None
|
||||
self.heaters = []
|
||||
self.speed = 25.0
|
||||
self.absolutecoord = self.absoluteextrude = True
|
||||
self.base_position = [0.0, 0.0, 0.0, 0.0]
|
||||
self.last_position = [0.0, 0.0, 0.0, 0.0]
|
||||
self.homing_add = [0.0, 0.0, 0.0, 0.0]
|
||||
self.axis2pos = {'X': 0, 'Y': 1, 'Z': 2, 'E': 3}
|
||||
self.build_handlers()
|
||||
def build_config(self):
|
||||
self.toolhead = self.printer.objects['toolhead']
|
||||
self.heater_nozzle = None
|
||||
extruder = self.printer.objects.get('extruder')
|
||||
if extruder:
|
||||
self.heater_nozzle = extruder.heater
|
||||
self.heater_bed = self.printer.objects.get('heater_bed')
|
||||
self.fan = self.printer.objects.get('fan')
|
||||
def build_handlers(self):
|
||||
handlers = ['G1', 'G4', 'G20', 'G21', 'G28', 'G90', 'G91', 'G92',
|
||||
'M18', 'M82', 'M83', 'M105', 'M110', 'M112', 'M114', 'M206',
|
||||
'HELP', 'QUERY_ENDSTOPS', 'RESTART', 'CLEAR_SHUTDOWN',
|
||||
'STATUS']
|
||||
if self.heater_nozzle is not None:
|
||||
handlers.extend(['M104', 'M109', 'PID_TUNE'])
|
||||
if self.heater_bed is not None:
|
||||
handlers.extend(['M140', 'M190'])
|
||||
if self.fan is not None:
|
||||
handlers.extend(['M106', 'M107'])
|
||||
handlers = self.all_handlers
|
||||
if not self.is_printer_ready:
|
||||
handlers = [h for h in handlers
|
||||
if getattr(self, 'cmd_'+h+'_when_not_ready', False)]
|
||||
self.gcode_handlers = dict((h, getattr(self, 'cmd_'+h))
|
||||
for h in handlers)
|
||||
for h, f in self.gcode_handlers.items():
|
||||
gcode_handlers = { h: getattr(self, 'cmd_'+h) for h in handlers }
|
||||
for h, f in list(gcode_handlers.items()):
|
||||
aliases = getattr(self, 'cmd_'+h+'_aliases', [])
|
||||
self.gcode_handlers.update(dict([(a, f) for a in aliases]))
|
||||
gcode_handlers.update({ a: f for a in aliases })
|
||||
self.gcode_handlers = gcode_handlers
|
||||
def stats(self, eventtime):
|
||||
return "gcodein=%d" % (self.bytes_read,)
|
||||
def set_printer_ready(self, is_ready):
|
||||
if self.is_printer_ready == is_ready:
|
||||
return
|
||||
self.is_printer_ready = is_ready
|
||||
def connect(self):
|
||||
self.is_printer_ready = True
|
||||
self.build_handlers()
|
||||
if is_ready and self.is_fileinput and self.fd_handle is None:
|
||||
# Lookup printer components
|
||||
self.toolhead = self.printer.objects.get('toolhead')
|
||||
extruders = extruder.get_printer_extruders(self.printer)
|
||||
if extruders:
|
||||
self.extruder = extruders[0]
|
||||
self.toolhead.set_extruder(self.extruder)
|
||||
self.heaters = [ e.get_heater() for e in extruders ]
|
||||
self.heaters.append(self.printer.objects.get('heater_bed'))
|
||||
self.fan = self.printer.objects.get('fan')
|
||||
if self.is_fileinput and self.fd_handle is None:
|
||||
self.fd_handle = self.reactor.register_fd(self.fd, self.process_data)
|
||||
def do_shutdown(self):
|
||||
if not self.is_printer_ready:
|
||||
return
|
||||
self.is_printer_ready = False
|
||||
self.build_handlers()
|
||||
self.dump_debug()
|
||||
if self.is_fileinput:
|
||||
self.printer.request_exit()
|
||||
def motor_heater_off(self):
|
||||
if self.toolhead is not None:
|
||||
if self.toolhead is None:
|
||||
return
|
||||
self.toolhead.motor_off()
|
||||
if self.heater_nozzle is not None:
|
||||
self.heater_nozzle.set_temp(0., 0.)
|
||||
if self.heater_bed is not None:
|
||||
self.heater_bed.set_temp(0., 0.)
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
for heater in self.heaters:
|
||||
if heater is not None:
|
||||
heater.set_temp(print_time, 0.)
|
||||
if self.fan is not None:
|
||||
self.fan.set_speed(print_time, 0.)
|
||||
def dump_debug(self):
|
||||
logging.info("Dumping gcode input %d blocks" % (
|
||||
out = []
|
||||
out.append("Dumping gcode input %d blocks" % (
|
||||
len(self.input_log),))
|
||||
for eventtime, data in self.input_log:
|
||||
logging.info("Read %f: %s" % (eventtime, repr(data)))
|
||||
out.append("Read %f: %s" % (eventtime, repr(data)))
|
||||
logging.info("\n".join(out))
|
||||
# Parse input into commands
|
||||
args_r = re.compile('([a-zA-Z_]+|[a-zA-Z*])')
|
||||
def process_commands(self, eventtime):
|
||||
while len(self.input_commands) > 1:
|
||||
line = self.input_commands.pop(0)
|
||||
args_r = re.compile('([A-Z_]+|[A-Z*])')
|
||||
def process_commands(self, commands, need_ack=True):
|
||||
prev_need_ack = self.need_ack
|
||||
for line in commands:
|
||||
# Ignore comments and leading/trailing spaces
|
||||
line = origline = line.strip()
|
||||
cpos = line.find(';')
|
||||
if cpos >= 0:
|
||||
line = line[:cpos]
|
||||
# Break command into parts
|
||||
parts = self.args_r.split(line)[1:]
|
||||
params = dict((parts[i].upper(), parts[i+1].strip())
|
||||
for i in range(0, len(parts), 2))
|
||||
parts = self.args_r.split(line.upper())[1:]
|
||||
params = { parts[i]: parts[i+1].strip()
|
||||
for i in range(0, len(parts), 2) }
|
||||
params['#original'] = origline
|
||||
if parts and parts[0].upper() == 'N':
|
||||
if parts and parts[0] == 'N':
|
||||
# Skip line number at start of command
|
||||
del parts[:2]
|
||||
if not parts:
|
||||
self.cmd_default(params)
|
||||
continue
|
||||
params['#command'] = cmd = parts[0].upper() + parts[1].strip()
|
||||
params['#command'] = cmd = parts[0] + parts[1].strip()
|
||||
# Invoke handler for command
|
||||
self.need_ack = True
|
||||
self.need_ack = need_ack
|
||||
handler = self.gcode_handlers.get(cmd, self.cmd_default)
|
||||
try:
|
||||
handler(params)
|
||||
except error as e:
|
||||
self.respond_error(str(e))
|
||||
except:
|
||||
logging.exception("Exception in command handler")
|
||||
self.toolhead.force_shutdown()
|
||||
self.respond_error('Internal error on command:"%s"' % (cmd,))
|
||||
msg = 'Internal error on command:"%s"' % (cmd,)
|
||||
logging.exception(msg)
|
||||
self.printer.invoke_shutdown(msg)
|
||||
self.respond_error(msg)
|
||||
self.ack()
|
||||
self.need_ack = prev_need_ack
|
||||
def process_data(self, eventtime):
|
||||
data = os.read(self.fd, 4096)
|
||||
self.input_log.append((eventtime, data))
|
||||
self.bytes_read += len(data)
|
||||
lines = data.split('\n')
|
||||
lines[0] = self.input_commands.pop() + lines[0]
|
||||
self.input_commands.extend(lines)
|
||||
lines[0] = self.partial_input + lines[0]
|
||||
self.partial_input = lines.pop()
|
||||
if self.is_processing_data:
|
||||
if len(lines) <= 1:
|
||||
if not self.is_fileinput and not lines:
|
||||
return
|
||||
if not self.is_fileinput and lines[0].strip().upper() == 'M112':
|
||||
self.cmd_M112({})
|
||||
self.reactor.unregister_fd(self.fd_handle)
|
||||
self.fd_handle = None
|
||||
return
|
||||
self.is_processing_data = True
|
||||
self.process_commands(eventtime)
|
||||
self.is_processing_data = False
|
||||
if self.fd_handle is None:
|
||||
if not self.is_fileinput and lines[0].strip().upper() == 'M112':
|
||||
self.cmd_M112({})
|
||||
while self.is_processing_data:
|
||||
eventtime = self.reactor.pause(eventtime + 0.100)
|
||||
self.fd_handle = self.reactor.register_fd(self.fd, self.process_data)
|
||||
self.is_processing_data = True
|
||||
self.process_commands(lines)
|
||||
if not data and self.is_fileinput:
|
||||
self.motor_heater_off()
|
||||
self.printer.request_exit_eof()
|
||||
if self.toolhead is not None:
|
||||
self.toolhead.wait_moves()
|
||||
self.printer.request_exit()
|
||||
self.is_processing_data = False
|
||||
# Response handling
|
||||
def ack(self, msg=None):
|
||||
if not self.need_ack or self.is_fileinput:
|
||||
@@ -147,45 +157,110 @@ class GCodeParser:
|
||||
os.write(self.fd, "ok\n")
|
||||
self.need_ack = False
|
||||
def respond(self, msg):
|
||||
logging.debug(msg)
|
||||
if self.is_fileinput:
|
||||
return
|
||||
os.write(self.fd, msg+"\n")
|
||||
def respond_info(self, msg):
|
||||
logging.debug(msg)
|
||||
lines = [l.strip() for l in msg.strip().split('\n')]
|
||||
self.respond("// " + "\n// ".join(lines))
|
||||
def respond_error(self, msg):
|
||||
logging.warning(msg)
|
||||
lines = msg.strip().split('\n')
|
||||
if len(lines) > 1:
|
||||
self.respond_info("\n".join(lines[:-1]))
|
||||
self.respond('!! %s' % (lines[-1].strip(),))
|
||||
# Parameter parsing helpers
|
||||
def get_int(self, name, params, default=None):
|
||||
if name in params:
|
||||
try:
|
||||
return int(params[name])
|
||||
except ValueError:
|
||||
raise error("Error on '%s': unable to parse %s" % (
|
||||
params['#original'], params[name]))
|
||||
if default is not None:
|
||||
return default
|
||||
raise error("Error on '%s': missing %s" % (params['#original'], name))
|
||||
def get_float(self, name, params, default=None):
|
||||
if name in params:
|
||||
try:
|
||||
return float(params[name])
|
||||
except ValueError:
|
||||
raise error("Error on '%s': unable to parse %s" % (
|
||||
params['#original'], params[name]))
|
||||
if default is not None:
|
||||
return default
|
||||
raise error("Error on '%s': missing %s" % (params['#original'], name))
|
||||
extended_r = re.compile(
|
||||
r'^\s*(?:N[0-9]+\s*)?'
|
||||
r'(?P<cmd>[a-zA-Z_][a-zA-Z_]+)(?:\s+|$)'
|
||||
r'(?P<args>[^#*;]*?)'
|
||||
r'\s*(?:[#*;].*)?$')
|
||||
def get_extended_params(self, params):
|
||||
m = self.extended_r.match(params['#original'])
|
||||
if m is None:
|
||||
# Not an "extended" command
|
||||
return params
|
||||
eargs = m.group('args')
|
||||
try:
|
||||
eparams = [earg.split('=', 1) for earg in eargs.split()]
|
||||
eparams = { k.upper(): v for k, v in eparams }
|
||||
eparams.update({k: params[k] for k in params if k.startswith('#')})
|
||||
return eparams
|
||||
except ValueError as e:
|
||||
raise error("Malformed command '%s'" % (params['#original'],))
|
||||
# Temperature wrappers
|
||||
def get_temp(self):
|
||||
if not self.is_printer_ready:
|
||||
return "T:0"
|
||||
# T:XXX /YYY B:XXX /YYY
|
||||
def get_temp(self, eventtime):
|
||||
# Tn:XXX /YYY B:XXX /YYY
|
||||
out = []
|
||||
if self.heater_nozzle:
|
||||
cur, target = self.heater_nozzle.get_temp()
|
||||
out.append("T:%.1f /%.1f" % (cur, target))
|
||||
if self.heater_bed:
|
||||
cur, target = self.heater_bed.get_temp()
|
||||
out.append("B:%.1f /%.1f" % (cur, target))
|
||||
for i, heater in enumerate(self.heaters):
|
||||
if heater is not None:
|
||||
cur, target = heater.get_temp(eventtime)
|
||||
name = "B"
|
||||
if i < len(self.heaters) - 1:
|
||||
name = "T%d" % (i,)
|
||||
out.append("%s:%.1f /%.1f" % (name, cur, target))
|
||||
if not out:
|
||||
return "T:0"
|
||||
return " ".join(out)
|
||||
def bg_temp(self, heater):
|
||||
if self.is_fileinput:
|
||||
return
|
||||
eventtime = time.time()
|
||||
eventtime = self.reactor.monotonic()
|
||||
while self.is_printer_ready and heater.check_busy(eventtime):
|
||||
self.toolhead.reset_motor_off_time(eventtime)
|
||||
self.respond(self.get_temp())
|
||||
eventtime = self.reactor.pause(eventtime + 1.)
|
||||
def set_temp(self, heater, params, wait=False):
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
temp = float(params.get('S', '0'))
|
||||
self.respond(self.get_temp(eventtime))
|
||||
eventtime = self.reactor.pause(eventtime + 1.)
|
||||
def set_temp(self, params, is_bed=False, wait=False):
|
||||
temp = self.get_float('S', params, 0.)
|
||||
heater = None
|
||||
if is_bed:
|
||||
heater = self.heaters[-1]
|
||||
elif 'T' in params:
|
||||
heater_index = self.get_int('T', params)
|
||||
if heater_index >= 0 and heater_index < len(self.heaters) - 1:
|
||||
heater = self.heaters[heater_index]
|
||||
elif self.extruder is not None:
|
||||
heater = self.extruder.get_heater()
|
||||
if heater is None:
|
||||
if temp > 0.:
|
||||
self.respond_error("Heater not configured")
|
||||
return
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
try:
|
||||
heater.set_temp(print_time, temp)
|
||||
except heater.error as e:
|
||||
self.respond_error(str(e))
|
||||
return
|
||||
if wait:
|
||||
self.bg_temp(heater)
|
||||
def set_fan_speed(self, speed):
|
||||
if self.fan is None:
|
||||
if speed and not self.is_fileinput:
|
||||
self.respond_info("Fan not configured")
|
||||
return
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
self.fan.set_speed(print_time, speed)
|
||||
# Individual command handlers
|
||||
def cmd_default(self, params):
|
||||
if not self.is_printer_ready:
|
||||
@@ -195,39 +270,75 @@ class GCodeParser:
|
||||
if not cmd:
|
||||
logging.debug(params['#original'])
|
||||
return
|
||||
self.respond('echo:Unknown command:"%s"' % (cmd,))
|
||||
if cmd[0] == 'T' and len(cmd) > 1 and cmd[1].isdigit():
|
||||
# Tn command has to be handled specially
|
||||
self.cmd_Tn(params)
|
||||
return
|
||||
self.respond_info('Unknown command:"%s"' % (cmd,))
|
||||
def cmd_Tn(self, params):
|
||||
# Select Tool
|
||||
index = self.get_int('T', params)
|
||||
extruders = extruder.get_printer_extruders(self.printer)
|
||||
if self.extruder is None or index < 0 or index >= len(extruders):
|
||||
self.respond_error("Extruder %d not configured" % (index,))
|
||||
return
|
||||
e = extruders[index]
|
||||
if self.extruder is e:
|
||||
return
|
||||
deactivate_gcode = self.extruder.get_activate_gcode(False)
|
||||
self.process_commands(deactivate_gcode.split('\n'), need_ack=False)
|
||||
try:
|
||||
self.toolhead.set_extruder(e)
|
||||
except homing.EndstopError as e:
|
||||
self.respond_error(str(e))
|
||||
return
|
||||
self.extruder = e
|
||||
self.last_position = self.toolhead.get_position()
|
||||
activate_gcode = self.extruder.get_activate_gcode(True)
|
||||
self.process_commands(activate_gcode.split('\n'), need_ack=False)
|
||||
all_handlers = [
|
||||
'G1', 'G4', 'G20', 'G28', 'G90', 'G91', 'G92',
|
||||
'M82', 'M83', 'M18', 'M105', 'M104', 'M109', 'M112', 'M114', 'M115',
|
||||
'M140', 'M190', 'M106', 'M107', 'M206', 'M400',
|
||||
'IGNORE', 'QUERY_ENDSTOPS', 'PID_TUNE', 'SET_SERVO',
|
||||
'RESTART', 'FIRMWARE_RESTART', 'ECHO', 'STATUS', 'HELP']
|
||||
cmd_G1_aliases = ['G0']
|
||||
def cmd_G1(self, params):
|
||||
# Move
|
||||
try:
|
||||
for a, p in self.axis2pos.items():
|
||||
if a in params:
|
||||
v = float(params[a])
|
||||
if not self.absolutecoord or (p>2 and not self.absoluteextrude):
|
||||
if (not self.absolutecoord
|
||||
or (p>2 and not self.absoluteextrude)):
|
||||
# value relative to position of last move
|
||||
self.last_position[p] += v
|
||||
else:
|
||||
# value relative to base coordinate position
|
||||
self.last_position[p] = v + self.base_position[p]
|
||||
if 'F' in params:
|
||||
self.speed = float(params['F']) / 60.
|
||||
speed = float(params['F']) / 60.
|
||||
if speed <= 0.:
|
||||
raise ValueError()
|
||||
self.speed = speed
|
||||
except ValueError as e:
|
||||
self.last_position = self.toolhead.get_position()
|
||||
raise error("Unable to parse move '%s'" % (params['#original'],))
|
||||
try:
|
||||
self.toolhead.move(self.last_position, self.speed)
|
||||
except homing.EndstopError, e:
|
||||
except homing.EndstopError as e:
|
||||
self.respond_error(str(e))
|
||||
self.last_position = self.toolhead.get_position()
|
||||
def cmd_G4(self, params):
|
||||
# Dwell
|
||||
if 'S' in params:
|
||||
delay = float(params['S'])
|
||||
delay = self.get_float('S', params)
|
||||
else:
|
||||
delay = float(params.get('P', '0')) / 1000.
|
||||
delay = self.get_float('P', params, 0.) / 1000.
|
||||
self.toolhead.dwell(delay)
|
||||
def cmd_G20(self, params):
|
||||
# Set units to inches
|
||||
self.respond_error('Machine does not support G20 (inches) command')
|
||||
def cmd_G21(self, params):
|
||||
# Set units to millimeters
|
||||
pass
|
||||
def cmd_G28(self, params):
|
||||
# Move to origin
|
||||
axes = []
|
||||
@@ -241,7 +352,7 @@ class GCodeParser:
|
||||
homing_state.set_no_verify_retract()
|
||||
try:
|
||||
self.toolhead.home(homing_state)
|
||||
except homing.EndstopError, e:
|
||||
except homing.EndstopError as e:
|
||||
self.toolhead.motor_off()
|
||||
self.respond_error(str(e))
|
||||
return
|
||||
@@ -257,12 +368,11 @@ class GCodeParser:
|
||||
self.absolutecoord = False
|
||||
def cmd_G92(self, params):
|
||||
# Set position
|
||||
mcount = 0
|
||||
for a, p in self.axis2pos.items():
|
||||
if a in params:
|
||||
self.base_position[p] = self.last_position[p] - float(params[a])
|
||||
mcount += 1
|
||||
if not mcount:
|
||||
offsets = { p: self.get_float(a, params)
|
||||
for a, p in self.axis2pos.items() if a in params }
|
||||
for p, offset in offsets.items():
|
||||
self.base_position[p] = self.last_position[p] - offset
|
||||
if not offsets:
|
||||
self.base_position = list(self.last_position)
|
||||
def cmd_M82(self, params):
|
||||
# Use absolute distances for extrusion
|
||||
@@ -277,52 +387,60 @@ class GCodeParser:
|
||||
cmd_M105_when_not_ready = True
|
||||
def cmd_M105(self, params):
|
||||
# Get Extruder Temperature
|
||||
self.ack(self.get_temp())
|
||||
self.ack(self.get_temp(self.reactor.monotonic()))
|
||||
def cmd_M104(self, params):
|
||||
# Set Extruder Temperature
|
||||
self.set_temp(self.heater_nozzle, params)
|
||||
self.set_temp(params)
|
||||
def cmd_M109(self, params):
|
||||
# Set Extruder Temperature and Wait
|
||||
self.set_temp(self.heater_nozzle, params, wait=True)
|
||||
cmd_M110_when_not_ready = True
|
||||
def cmd_M110(self, params):
|
||||
# Set Current Line Number
|
||||
pass
|
||||
self.set_temp(params, wait=True)
|
||||
def cmd_M112(self, params):
|
||||
# Emergency Stop
|
||||
self.toolhead.force_shutdown()
|
||||
self.printer.invoke_shutdown("Shutdown due to M112 command")
|
||||
cmd_M114_when_not_ready = True
|
||||
def cmd_M114(self, params):
|
||||
# Get Current Position
|
||||
if self.toolhead is None:
|
||||
self.cmd_default(params)
|
||||
return
|
||||
kinpos = self.toolhead.get_position()
|
||||
self.respond("X:%.3f Y:%.3f Z:%.3f E:%.3f Count X:%.3f Y:%.3f Z:%.3f" % (
|
||||
raw_pos = self.toolhead.query_endstops("get_mcu_position")
|
||||
self.respond("X:%.3f Y:%.3f Z:%.3f E:%.3f Count %s" % (
|
||||
self.last_position[0], self.last_position[1],
|
||||
self.last_position[2], self.last_position[3],
|
||||
kinpos[0], kinpos[1], kinpos[2]))
|
||||
" ".join(["%s:%d" % (n.upper(), p) for n, p in raw_pos])))
|
||||
cmd_M115_when_not_ready = True
|
||||
def cmd_M115(self, params):
|
||||
# Get Firmware Version and Capabilities
|
||||
software_version = self.printer.get_start_args().get('software_version')
|
||||
kw = {"FIRMWARE_NAME": "Klipper", "FIRMWARE_VERSION": software_version}
|
||||
self.ack(" ".join(["%s:%s" % (k, v) for k, v in kw.items()]))
|
||||
def cmd_M140(self, params):
|
||||
# Set Bed Temperature
|
||||
self.set_temp(self.heater_bed, params)
|
||||
self.set_temp(params, is_bed=True)
|
||||
def cmd_M190(self, params):
|
||||
# Set Bed Temperature and Wait
|
||||
self.set_temp(self.heater_bed, params, wait=True)
|
||||
self.set_temp(params, is_bed=True, wait=True)
|
||||
def cmd_M106(self, params):
|
||||
# Set fan speed
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
self.fan.set_speed(print_time, float(params.get('S', '255')) / 255.)
|
||||
self.set_fan_speed(self.get_float('S', params, 255.) / 255.)
|
||||
def cmd_M107(self, params):
|
||||
# Turn fan off
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
self.fan.set_speed(print_time, 0)
|
||||
self.set_fan_speed(0.)
|
||||
def cmd_M206(self, params):
|
||||
# Set home offset
|
||||
for a, p in self.axis2pos.items():
|
||||
if a in params:
|
||||
v = float(params[a])
|
||||
self.base_position[p] += self.homing_add[p] - v
|
||||
self.homing_add[p] = v
|
||||
offsets = { p: self.get_float(a, params)
|
||||
for a, p in self.axis2pos.items() if a in params }
|
||||
for p, offset in offsets.items():
|
||||
self.base_position[p] += self.homing_add[p] - offset
|
||||
self.homing_add[p] = offset
|
||||
def cmd_M400(self, params):
|
||||
# Wait for current moves to finish
|
||||
self.toolhead.wait_moves()
|
||||
cmd_IGNORE_when_not_ready = True
|
||||
cmd_IGNORE_aliases = ["G21", "M110", "M21"]
|
||||
def cmd_IGNORE(self, params):
|
||||
# Commands that are just silently accepted
|
||||
pass
|
||||
cmd_QUERY_ENDSTOPS_help = "Report on the status of each endstop"
|
||||
cmd_QUERY_ENDSTOPS_aliases = ["M119"]
|
||||
def cmd_QUERY_ENDSTOPS(self, params):
|
||||
@@ -331,7 +449,7 @@ class GCodeParser:
|
||||
return
|
||||
try:
|
||||
res = self.toolhead.query_endstops()
|
||||
except self.printer.mcu.error, e:
|
||||
except homing.EndstopError as e:
|
||||
self.respond_error(str(e))
|
||||
return
|
||||
self.respond(" ".join(["%s:%s" % (name, ["open", "TRIGGERED"][not not t])
|
||||
@@ -340,23 +458,47 @@ class GCodeParser:
|
||||
cmd_PID_TUNE_aliases = ["M303"]
|
||||
def cmd_PID_TUNE(self, params):
|
||||
# Run PID tuning
|
||||
heater = int(params.get('E', '0'))
|
||||
heater = {0: self.heater_nozzle, -1: self.heater_bed}[heater]
|
||||
temp = float(params.get('S', '60'))
|
||||
heater_index = self.get_int('E', params, 0)
|
||||
if (heater_index < -1 or heater_index >= len(self.heaters) - 1
|
||||
or self.heaters[heater_index] is None):
|
||||
self.respond_error("Heater not configured")
|
||||
heater = self.heaters[heater_index]
|
||||
temp = self.get_float('S', params)
|
||||
heater.start_auto_tune(temp)
|
||||
self.bg_temp(heater)
|
||||
cmd_CLEAR_SHUTDOWN_when_not_ready = True
|
||||
cmd_CLEAR_SHUTDOWN_help = "Clear a firmware shutdown and restart"
|
||||
def cmd_CLEAR_SHUTDOWN(self, params):
|
||||
if self.toolhead is None:
|
||||
self.cmd_default(params)
|
||||
cmd_SET_SERVO_help = "Set servo angle"
|
||||
def cmd_SET_SERVO(self, params):
|
||||
params = self.get_extended_params(params)
|
||||
name = params.get('SERVO')
|
||||
if name is None:
|
||||
raise error("Error on '%s': missing SERVO" % (params['#original'],))
|
||||
s = chipmisc.get_printer_servo(self.printer, name)
|
||||
if s is None:
|
||||
raise error("Servo not configured")
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
if 'WIDTH' in params:
|
||||
s.set_pulse_width(print_time, self.get_float('WIDTH', params))
|
||||
return
|
||||
self.printer.mcu.clear_shutdown()
|
||||
self.printer.request_restart()
|
||||
s.set_angle(print_time, self.get_float('ANGLE', params))
|
||||
def prep_restart(self):
|
||||
if self.is_printer_ready:
|
||||
self.respond_info("Preparing to restart...")
|
||||
self.motor_heater_off()
|
||||
self.toolhead.dwell(0.500)
|
||||
self.toolhead.wait_moves()
|
||||
cmd_RESTART_when_not_ready = True
|
||||
cmd_RESTART_help = "Reload config file and restart host software"
|
||||
def cmd_RESTART(self, params):
|
||||
self.printer.request_restart()
|
||||
self.prep_restart()
|
||||
self.printer.request_exit('restart')
|
||||
cmd_FIRMWARE_RESTART_when_not_ready = True
|
||||
cmd_FIRMWARE_RESTART_help = "Restart firmware, host, and reload config"
|
||||
def cmd_FIRMWARE_RESTART(self, params):
|
||||
self.prep_restart()
|
||||
self.printer.request_exit('firmware_restart')
|
||||
cmd_ECHO_when_not_ready = True
|
||||
def cmd_ECHO(self, params):
|
||||
self.respond_info(params['#original'])
|
||||
cmd_STATUS_when_not_ready = True
|
||||
cmd_STATUS_help = "Report the printer status"
|
||||
def cmd_STATUS(self, params):
|
||||
@@ -371,8 +513,11 @@ class GCodeParser:
|
||||
if not self.is_printer_ready:
|
||||
cmdhelp.append("Printer is not ready - not all commands available.")
|
||||
cmdhelp.append("Available extended commands:")
|
||||
for cmd in self.gcode_handlers:
|
||||
for cmd in sorted(self.gcode_handlers):
|
||||
desc = getattr(self, 'cmd_'+cmd+'_help', None)
|
||||
if desc is not None:
|
||||
cmdhelp.append("%-10s: %s" % (cmd, desc))
|
||||
self.respond_info("\n".join(cmdhelp))
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
271
klippy/heater.py
@@ -1,109 +1,191 @@
|
||||
# Printer heater support
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import math, logging, threading
|
||||
import pins
|
||||
|
||||
# Mapping from name to Steinhart-Hart coefficients
|
||||
Thermistors = {
|
||||
"EPCOS 100K B57560G104F": (
|
||||
0.000722136308968056, 0.000216766566488498, 8.92935804531095e-08),
|
||||
"ATC Semitec 104GT-2": (
|
||||
0.000809651054275124, 0.000211636030735685, 7.07420883993973e-08),
|
||||
|
||||
######################################################################
|
||||
# Sensors
|
||||
######################################################################
|
||||
|
||||
KELVIN_TO_CELCIUS = -273.15
|
||||
|
||||
# Thermistor calibrated with three temp measurements
|
||||
class Thermistor:
|
||||
def __init__(self, config, params):
|
||||
self.pullup = config.getfloat('pullup_resistor', 4700., above=0.)
|
||||
# Calculate Steinhart-Hart coefficents from temp measurements
|
||||
inv_t1 = 1. / (params['t1'] - KELVIN_TO_CELCIUS)
|
||||
inv_t2 = 1. / (params['t2'] - KELVIN_TO_CELCIUS)
|
||||
inv_t3 = 1. / (params['t3'] - KELVIN_TO_CELCIUS)
|
||||
ln_r1 = math.log(params['r1'])
|
||||
ln_r2 = math.log(params['r2'])
|
||||
ln_r3 = math.log(params['r3'])
|
||||
ln3_r1, ln3_r2, ln3_r3 = ln_r1**3, ln_r2**3, ln_r3**3
|
||||
|
||||
inv_t12, inv_t13 = inv_t1 - inv_t2, inv_t1 - inv_t3
|
||||
ln_r12, ln_r13 = ln_r1 - ln_r2, ln_r1 - ln_r3
|
||||
ln3_r12, ln3_r13 = ln3_r1 - ln3_r2, ln3_r1 - ln3_r3
|
||||
|
||||
self.c3 = ((inv_t12 - inv_t13 * ln_r12 / ln_r13)
|
||||
/ (ln3_r12 - ln3_r13 * ln_r12 / ln_r13))
|
||||
self.c2 = (inv_t12 - self.c3 * ln3_r12) / ln_r12
|
||||
self.c1 = inv_t1 - self.c2 * ln_r1 - self.c3 * ln3_r1
|
||||
def calc_temp(self, adc):
|
||||
r = self.pullup * adc / (1.0 - adc)
|
||||
ln_r = math.log(r)
|
||||
inv_t = self.c1 + self.c2 * ln_r + self.c3 * ln_r**3
|
||||
return 1.0/inv_t + KELVIN_TO_CELCIUS
|
||||
def calc_adc(self, temp):
|
||||
inv_t = 1. / (temp - KELVIN_TO_CELCIUS)
|
||||
if self.c3:
|
||||
y = (self.c1 - inv_t) / (2. * self.c3)
|
||||
x = math.sqrt((self.c2 / (3. * self.c3))**3 + y**2)
|
||||
ln_r = math.pow(x - y, 1./3.) - math.pow(x + y, 1./3.)
|
||||
else:
|
||||
ln_r = (inv_t - self.c1) / self.c2
|
||||
r = math.exp(ln_r)
|
||||
return r / (self.pullup + r)
|
||||
|
||||
# Thermistor calibrated from one temp measurement and its beta
|
||||
class ThermistorBeta(Thermistor):
|
||||
def __init__(self, config, params):
|
||||
self.pullup = config.getfloat('pullup_resistor', 4700., above=0.)
|
||||
# Calculate Steinhart-Hart coefficents from beta
|
||||
inv_t1 = 1. / (params['t1'] - KELVIN_TO_CELCIUS)
|
||||
ln_r1 = math.log(params['r1'])
|
||||
self.c3 = 0.
|
||||
self.c2 = 1. / params['beta']
|
||||
self.c1 = inv_t1 - self.c2 * ln_r1
|
||||
|
||||
# Linear style conversion chips calibrated with two temp measurements
|
||||
class Linear:
|
||||
def __init__(self, config, params):
|
||||
adc_voltage = config.getfloat('adc_voltage', 5., above=0.)
|
||||
slope = (params['t2'] - params['t1']) / (params['v2'] - params['v1'])
|
||||
self.gain = adc_voltage * slope
|
||||
self.offset = params['t1'] - params['v1'] * slope
|
||||
def calc_temp(self, adc):
|
||||
return adc * self.gain + self.offset
|
||||
def calc_adc(self, temp):
|
||||
return (temp - self.offset) / self.gain
|
||||
|
||||
# Available sensors
|
||||
Sensors = {
|
||||
"EPCOS 100K B57560G104F": {
|
||||
'class': Thermistor, 't1': 25., 'r1': 100000.,
|
||||
't2': 150., 'r2': 1641.9, 't3': 250., 'r3': 226.15 },
|
||||
"ATC Semitec 104GT-2": {
|
||||
'class': Thermistor, 't1': 20., 'r1': 126800.,
|
||||
't2': 150., 'r2': 1360., 't3': 300., 'r3': 80.65 },
|
||||
"NTC 100K beta 3950": {
|
||||
'class': ThermistorBeta, 't1': 25., 'r1': 100000., 'beta': 3950. },
|
||||
"AD595": { 'class': Linear, 't1': 25., 'v1': .25, 't2': 300., 'v2': 3.022 },
|
||||
}
|
||||
|
||||
|
||||
######################################################################
|
||||
# Heater
|
||||
######################################################################
|
||||
|
||||
SAMPLE_TIME = 0.001
|
||||
SAMPLE_COUNT = 8
|
||||
REPORT_TIME = 0.300
|
||||
KELVIN_TO_CELCIUS = -273.15
|
||||
PWM_CYCLE_TIME = 0.100
|
||||
MAX_HEAT_TIME = 5.0
|
||||
AMBIENT_TEMP = 25.
|
||||
PWM_MAX = 255
|
||||
PID_PARAM_BASE = 255.
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
class PrinterHeater:
|
||||
error = error
|
||||
def __init__(self, printer, config):
|
||||
self.printer = printer
|
||||
self.config = config
|
||||
self.mcu_pwm = self.mcu_adc = None
|
||||
self.thermistor_c = config.getchoice('thermistor_type', Thermistors)
|
||||
self.pullup_r = config.getfloat('pullup_resistor', 4700.)
|
||||
self.min_extrude_temp = config.getfloat('min_extrude_temp', 170.)
|
||||
self.can_extrude = (self.min_extrude_temp <= 0.)
|
||||
self.name = config.section
|
||||
sensor_params = config.getchoice('sensor_type', Sensors)
|
||||
self.sensor = sensor_params['class'](config, sensor_params)
|
||||
self.min_temp = config.getfloat('min_temp', minval=0.)
|
||||
self.max_temp = config.getfloat('max_temp', above=self.min_temp)
|
||||
self.min_extrude_temp = config.getfloat(
|
||||
'min_extrude_temp', 170., minval=self.min_temp, maxval=self.max_temp)
|
||||
self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.)
|
||||
self.lock = threading.Lock()
|
||||
self.last_temp = 0.
|
||||
self.last_temp_time = 0.
|
||||
self.target_temp = 0.
|
||||
self.control = None
|
||||
algos = {'watermark': ControlBangBang, 'pid': ControlPID}
|
||||
algo = config.getchoice('control', algos)
|
||||
heater_pin = config.get('heater_pin')
|
||||
if algo is ControlBangBang and self.max_power == 1.:
|
||||
self.mcu_pwm = pins.setup_pin(printer, 'digital_out', heater_pin)
|
||||
else:
|
||||
self.mcu_pwm = pins.setup_pin(printer, 'pwm', heater_pin)
|
||||
self.mcu_pwm.setup_cycle_time(PWM_CYCLE_TIME)
|
||||
self.mcu_pwm.setup_max_duration(MAX_HEAT_TIME)
|
||||
self.mcu_adc = pins.setup_pin(printer, 'adc', config.get('sensor_pin'))
|
||||
adc_range = [self.sensor.calc_adc(self.min_temp),
|
||||
self.sensor.calc_adc(self.max_temp)]
|
||||
self.mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT,
|
||||
minval=min(adc_range), maxval=max(adc_range))
|
||||
self.mcu_adc.setup_adc_callback(REPORT_TIME, self.adc_callback)
|
||||
is_fileoutput = self.mcu_adc.get_mcu().is_fileoutput()
|
||||
self.can_extrude = self.min_extrude_temp <= 0. or is_fileoutput
|
||||
self.control = algo(self, config)
|
||||
# pwm caching
|
||||
self.next_pwm_time = 0.
|
||||
self.last_pwm_value = 0
|
||||
def build_config(self):
|
||||
heater_pin = self.config.get('heater_pin')
|
||||
thermistor_pin = self.config.get('thermistor_pin')
|
||||
self.mcu_pwm = self.printer.mcu.create_pwm(heater_pin, 0, MAX_HEAT_TIME)
|
||||
self.mcu_adc = self.printer.mcu.create_adc(thermistor_pin)
|
||||
min_adc = self.calc_adc(self.config.getfloat('max_temp'))
|
||||
max_adc = self.calc_adc(self.config.getfloat('min_temp'))
|
||||
self.mcu_adc.set_minmax(
|
||||
SAMPLE_TIME, SAMPLE_COUNT, minval=min_adc, maxval=max_adc)
|
||||
self.mcu_adc.set_adc_callback(REPORT_TIME, self.adc_callback)
|
||||
algos = {'watermark': ControlBangBang, 'pid': ControlPID}
|
||||
self.control = self.config.getchoice('control', algos)(self, self.config)
|
||||
if self.printer.mcu.is_fileoutput():
|
||||
self.can_extrude = True
|
||||
def set_pwm(self, read_time, value):
|
||||
if value:
|
||||
if self.target_temp <= 0.:
|
||||
return
|
||||
if (read_time < self.next_pwm_time
|
||||
and abs(value - self.last_pwm_value) < 15):
|
||||
return
|
||||
elif not self.last_pwm_value:
|
||||
value = 0.
|
||||
if ((read_time < self.next_pwm_time or not self.last_pwm_value)
|
||||
and abs(value - self.last_pwm_value) < 0.05):
|
||||
# No significant change in value - can suppress update
|
||||
return
|
||||
pwm_time = read_time + REPORT_TIME + SAMPLE_TIME*SAMPLE_COUNT
|
||||
self.next_pwm_time = pwm_time + 0.75 * MAX_HEAT_TIME
|
||||
self.last_pwm_value = value
|
||||
logging.debug("pwm=%d@%.3f (%.3f)" % (value, read_time, pwm_time))
|
||||
logging.debug("%s: pwm=%.3f@%.3f (from %.3f@%.3f [%.3f])",
|
||||
self.name, value, pwm_time,
|
||||
self.last_temp, self.last_temp_time, self.target_temp)
|
||||
self.mcu_pwm.set_pwm(pwm_time, value)
|
||||
# Temperature calculation
|
||||
def calc_temp(self, adc):
|
||||
r = self.pullup_r * adc / (1.0 - adc)
|
||||
ln_r = math.log(r)
|
||||
c1, c2, c3 = self.thermistor_c
|
||||
temp_inv = c1 + c2*ln_r + c3*math.pow(ln_r, 3)
|
||||
return 1.0/temp_inv + KELVIN_TO_CELCIUS
|
||||
def calc_adc(self, temp):
|
||||
if temp is None:
|
||||
return None
|
||||
c1, c2, c3 = self.thermistor_c
|
||||
temp -= KELVIN_TO_CELCIUS
|
||||
temp_inv = 1./temp
|
||||
y = (c1 - temp_inv) / (2*c3)
|
||||
x = math.sqrt(math.pow(c2 / (3.*c3), 3.) + math.pow(y, 2.))
|
||||
r = math.exp(math.pow(x-y, 1./3.) - math.pow(x+y, 1./3.))
|
||||
return r / (self.pullup_r + r)
|
||||
def adc_callback(self, read_time, read_value):
|
||||
temp = self.calc_temp(read_value)
|
||||
temp = self.sensor.calc_temp(read_value)
|
||||
with self.lock:
|
||||
self.last_temp = temp
|
||||
self.last_temp_time = read_time
|
||||
self.can_extrude = (temp >= self.min_extrude_temp)
|
||||
self.control.adc_callback(read_time, temp)
|
||||
#logging.debug("temp: %.3f %f = %f" % (read_time, read_value, temp))
|
||||
#logging.debug("temp: %.3f %f = %f", read_time, read_value, temp)
|
||||
# External commands
|
||||
def set_temp(self, print_time, degrees):
|
||||
if degrees and (degrees < self.min_temp or degrees > self.max_temp):
|
||||
raise error("Requested temperature (%.1f) out of range (%.1f:%.1f)"
|
||||
% (degrees, self.min_temp, self.max_temp))
|
||||
with self.lock:
|
||||
self.target_temp = degrees
|
||||
def get_temp(self):
|
||||
def get_temp(self, eventtime):
|
||||
print_time = self.mcu_adc.get_mcu().estimated_print_time(eventtime) - 5.
|
||||
with self.lock:
|
||||
if self.last_temp_time < print_time:
|
||||
return 0., self.target_temp
|
||||
return self.last_temp, self.target_temp
|
||||
def check_busy(self, eventtime):
|
||||
with self.lock:
|
||||
return self.control.check_busy(eventtime)
|
||||
def start_auto_tune(self, temp):
|
||||
def start_auto_tune(self, degrees):
|
||||
if degrees and (degrees < self.min_temp or degrees > self.max_temp):
|
||||
raise error("Requested temperature (%.1f) out of range (%.1f:%.1f)"
|
||||
% (degrees, self.min_temp, self.max_temp))
|
||||
with self.lock:
|
||||
self.control = ControlAutoTune(self, self.control, temp)
|
||||
self.control = ControlAutoTune(self, self.control)
|
||||
self.target_temp = degrees
|
||||
def finish_auto_tune(self, old_control):
|
||||
self.control = old_control
|
||||
self.target_temp = 0
|
||||
|
||||
|
||||
######################################################################
|
||||
@@ -113,7 +195,7 @@ class PrinterHeater:
|
||||
class ControlBangBang:
|
||||
def __init__(self, heater, config):
|
||||
self.heater = heater
|
||||
self.max_delta = config.getfloat('max_delta', 2.0)
|
||||
self.max_delta = config.getfloat('max_delta', 2.0, above=0.)
|
||||
self.heating = False
|
||||
def adc_callback(self, read_time, temp):
|
||||
if self.heating and temp >= self.heater.target_temp+self.max_delta:
|
||||
@@ -121,9 +203,9 @@ class ControlBangBang:
|
||||
elif not self.heating and temp <= self.heater.target_temp-self.max_delta:
|
||||
self.heating = True
|
||||
if self.heating:
|
||||
self.heater.set_pwm(read_time, PWM_MAX)
|
||||
self.heater.set_pwm(read_time, self.heater.max_power)
|
||||
else:
|
||||
self.heater.set_pwm(read_time, 0)
|
||||
self.heater.set_pwm(read_time, 0.)
|
||||
def check_busy(self, eventtime):
|
||||
return self.heater.last_temp < self.heater.target_temp-self.max_delta
|
||||
|
||||
@@ -135,11 +217,11 @@ class ControlBangBang:
|
||||
class ControlPID:
|
||||
def __init__(self, heater, config):
|
||||
self.heater = heater
|
||||
self.Kp = config.getfloat('pid_Kp')
|
||||
self.Ki = config.getfloat('pid_Ki')
|
||||
self.Kd = config.getfloat('pid_Kd')
|
||||
self.min_deriv_time = config.getfloat('pid_deriv_time', 2.)
|
||||
imax = config.getint('pid_integral_max', PWM_MAX)
|
||||
self.Kp = config.getfloat('pid_Kp') / PID_PARAM_BASE
|
||||
self.Ki = config.getfloat('pid_Ki') / PID_PARAM_BASE
|
||||
self.Kd = config.getfloat('pid_Kd') / PID_PARAM_BASE
|
||||
self.min_deriv_time = config.getfloat('pid_deriv_time', 2., above=0.)
|
||||
imax = config.getfloat('pid_integral_max', heater.max_power, minval=0.)
|
||||
self.temp_integ_max = imax / self.Ki
|
||||
self.prev_temp = AMBIENT_TEMP
|
||||
self.prev_temp_time = 0.
|
||||
@@ -159,10 +241,10 @@ class ControlPID:
|
||||
temp_integ = self.prev_temp_integ + temp_err * time_diff
|
||||
temp_integ = max(0., min(self.temp_integ_max, temp_integ))
|
||||
# Calculate output
|
||||
co = int(self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv)
|
||||
#logging.debug("pid: %f@%.3f -> diff=%f deriv=%f err=%f integ=%f co=%d" % (
|
||||
# temp, read_time, temp_diff, temp_deriv, temp_err, temp_integ, co))
|
||||
bounded_co = max(0, min(PWM_MAX, co))
|
||||
co = self.Kp*temp_err + self.Ki*temp_integ - self.Kd*temp_deriv
|
||||
#logging.debug("pid: %f@%.3f -> diff=%f deriv=%f err=%f integ=%f co=%d",
|
||||
# temp, read_time, temp_diff, temp_deriv, temp_err, temp_integ, co)
|
||||
bounded_co = max(0., min(self.heater.max_power, co))
|
||||
self.heater.set_pwm(read_time, bounded_co)
|
||||
# Store state for next measurement
|
||||
self.prev_temp = temp
|
||||
@@ -182,28 +264,28 @@ class ControlPID:
|
||||
TUNE_PID_DELTA = 5.0
|
||||
|
||||
class ControlAutoTune:
|
||||
def __init__(self, heater, old_control, target_temp):
|
||||
def __init__(self, heater, old_control):
|
||||
self.heater = heater
|
||||
self.old_control = old_control
|
||||
self.target_temp = target_temp
|
||||
self.heating = False
|
||||
self.peaks = []
|
||||
self.peak = 0.
|
||||
self.peak_time = 0.
|
||||
def adc_callback(self, read_time, temp):
|
||||
if self.heating and temp >= self.target_temp:
|
||||
if self.heating and temp >= self.heater.target_temp:
|
||||
self.heating = False
|
||||
self.check_peaks()
|
||||
elif not self.heating and temp <= self.target_temp - TUNE_PID_DELTA:
|
||||
elif (not self.heating
|
||||
and temp <= self.heater.target_temp - TUNE_PID_DELTA):
|
||||
self.heating = True
|
||||
self.check_peaks()
|
||||
if self.heating:
|
||||
self.heater.set_pwm(read_time, PWM_MAX)
|
||||
self.heater.set_pwm(read_time, self.heater.max_power)
|
||||
if temp < self.peak:
|
||||
self.peak = temp
|
||||
self.peak_time = read_time
|
||||
else:
|
||||
self.heater.set_pwm(read_time, 0)
|
||||
self.heater.set_pwm(read_time, 0.)
|
||||
if temp > self.peak:
|
||||
self.peak = temp
|
||||
self.peak_time = read_time
|
||||
@@ -217,8 +299,8 @@ class ControlAutoTune:
|
||||
return
|
||||
temp_diff = self.peaks[-1][0] - self.peaks[-2][0]
|
||||
time_diff = self.peaks[-1][1] - self.peaks[-3][1]
|
||||
pwm_diff = PWM_MAX - 0
|
||||
Ku = 4. * (2. * pwm_diff) / (abs(temp_diff) * math.pi)
|
||||
max_power = self.heater.max_power
|
||||
Ku = 4. * (2. * max_power) / (abs(temp_diff) * math.pi)
|
||||
Tu = time_diff
|
||||
|
||||
Kp = 0.6 * Ku
|
||||
@@ -226,12 +308,13 @@ class ControlAutoTune:
|
||||
Td = 0.125 * Tu
|
||||
Ki = Kp / Ti
|
||||
Kd = Kp * Td
|
||||
logging.info("Autotune: raw=%f/%d Ku=%f Tu=%f Kp=%f Ki=%f Kd=%f" % (
|
||||
temp_diff, pwm_diff, Ku, Tu, Kp, Ki, Kd))
|
||||
logging.info("Autotune: raw=%f/%f Ku=%f Tu=%f Kp=%f Ki=%f Kd=%f",
|
||||
temp_diff, max_power, Ku, Tu, Kp * PID_PARAM_BASE,
|
||||
Ki * PID_PARAM_BASE, Kd * PID_PARAM_BASE)
|
||||
def check_busy(self, eventtime):
|
||||
if self.heating or len(self.peaks) < 12:
|
||||
return True
|
||||
self.heater.control = self.old_control
|
||||
self.heater.finish_auto_tune(self.old_control)
|
||||
return False
|
||||
|
||||
|
||||
@@ -240,10 +323,9 @@ class ControlAutoTune:
|
||||
######################################################################
|
||||
|
||||
class ControlBumpTest:
|
||||
def __init__(self, heater, old_control, target_temp):
|
||||
def __init__(self, heater, old_control):
|
||||
self.heater = heater
|
||||
self.old_control = old_control
|
||||
self.target_temp = target_temp
|
||||
self.temp_samples = {}
|
||||
self.pwm_samples = {}
|
||||
self.state = 0
|
||||
@@ -253,18 +335,18 @@ class ControlBumpTest:
|
||||
def adc_callback(self, read_time, temp):
|
||||
self.temp_samples[read_time] = temp
|
||||
if not self.state:
|
||||
self.set_pwm(read_time, 0)
|
||||
self.set_pwm(read_time, 0.)
|
||||
if len(self.temp_samples) >= 20:
|
||||
self.state += 1
|
||||
elif self.state == 1:
|
||||
if temp < self.target_temp:
|
||||
self.set_pwm(read_time, PWM_MAX)
|
||||
if temp < self.heater.target_temp:
|
||||
self.set_pwm(read_time, self.heater.max_power)
|
||||
return
|
||||
self.set_pwm(read_time, 0)
|
||||
self.set_pwm(read_time, 0.)
|
||||
self.state += 1
|
||||
elif self.state == 2:
|
||||
self.set_pwm(read_time, 0)
|
||||
if temp <= (self.target_temp + AMBIENT_TEMP) / 2.:
|
||||
self.set_pwm(read_time, 0.)
|
||||
if temp <= (self.heater.target_temp + AMBIENT_TEMP) / 2.:
|
||||
self.dump_stats()
|
||||
self.state += 1
|
||||
def dump_stats(self):
|
||||
@@ -276,5 +358,10 @@ class ControlBumpTest:
|
||||
def check_busy(self, eventtime):
|
||||
if self.state < 3:
|
||||
return True
|
||||
self.heater.control = self.old_control
|
||||
self.heater.finish_auto_tune(self.old_control)
|
||||
return False
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
if config.has_section('heater_bed'):
|
||||
printer.add_object('heater_bed', PrinterHeater(
|
||||
printer, config.getsection('heater_bed')))
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# Code for state tracking during homing operations
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
|
||||
HOMING_DELAY = 0.250
|
||||
ENDSTOP_SAMPLE_TIME = .000015
|
||||
ENDSTOP_SAMPLE_COUNT = 4
|
||||
|
||||
class Homing:
|
||||
def __init__(self, toolhead, changed_axes):
|
||||
self.toolhead = toolhead
|
||||
@@ -29,21 +33,24 @@ class Homing:
|
||||
# Alter kinematics class to think printer is at forcepos
|
||||
self.toolhead.set_position(self._fill_coord(forcepos))
|
||||
# Start homing and issue move
|
||||
if not second_home:
|
||||
self.toolhead.dwell(HOMING_DELAY)
|
||||
print_time = self.toolhead.get_last_move_time()
|
||||
endstops = []
|
||||
for s in steppers:
|
||||
es = s.enable_endstop_checking(print_time, s.step_dist / speed)
|
||||
endstops.append((s, es, s.mcu_stepper.get_mcu_position()))
|
||||
s.mcu_endstop.home_start(print_time, ENDSTOP_SAMPLE_TIME,
|
||||
ENDSTOP_SAMPLE_COUNT, s.step_dist / speed)
|
||||
endstops.append((s, s.mcu_stepper.get_mcu_position()))
|
||||
self.toolhead.move(self._fill_coord(movepos), speed)
|
||||
move_end_print_time = self.toolhead.get_last_move_time()
|
||||
self.toolhead.reset_print_time()
|
||||
for s, es, last_pos in endstops:
|
||||
es.home_finalize(es.print_to_mcu_time(move_end_print_time))
|
||||
self.toolhead.reset_print_time(print_time)
|
||||
for s, last_pos in endstops:
|
||||
s.mcu_endstop.home_finalize(move_end_print_time)
|
||||
# Wait for endstops to trigger
|
||||
for s, es, last_pos in endstops:
|
||||
for s, last_pos in endstops:
|
||||
try:
|
||||
es.home_wait()
|
||||
except es.error, e:
|
||||
s.mcu_endstop.home_wait()
|
||||
except s.mcu_endstop.error as e:
|
||||
raise EndstopError("Failed to home stepper %s: %s" % (
|
||||
s.name, str(e)))
|
||||
post_home_pos = s.mcu_stepper.get_mcu_position()
|
||||
@@ -53,6 +60,21 @@ class Homing:
|
||||
def set_homed_position(self, pos):
|
||||
self.toolhead.set_position(self._fill_coord(pos))
|
||||
|
||||
def query_endstops(print_time, query_flags, steppers):
|
||||
if query_flags == "get_mcu_position":
|
||||
# Only the commanded position is requested
|
||||
return [(s.name.upper(), s.mcu_stepper.get_mcu_position())
|
||||
for s in steppers]
|
||||
for s in steppers:
|
||||
s.mcu_endstop.query_endstop(print_time)
|
||||
out = []
|
||||
for s in steppers:
|
||||
try:
|
||||
out.append((s.name, s.mcu_endstop.query_endstop_wait()))
|
||||
except s.mcu_endstop.error as e:
|
||||
raise EndstopError(str(e))
|
||||
return out
|
||||
|
||||
class EndstopError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
326
klippy/klippy.py
@@ -1,11 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
# Main code for host side printer firmware
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import sys, optparse, ConfigParser, logging, time, threading
|
||||
import gcode, toolhead, util, mcu, fan, heater, extruder, reactor, queuelogger
|
||||
import util, reactor, queuelogger, msgproto, gcode
|
||||
import pins, mcu, chipmisc, toolhead, extruder, heater, fan
|
||||
|
||||
message_ready = "Printer is ready"
|
||||
|
||||
message_startup = """
|
||||
The klippy host software is attempting to connect. Please
|
||||
@@ -19,17 +22,26 @@ command to reload the config and restart the host software.
|
||||
Printer is halted
|
||||
"""
|
||||
|
||||
message_protocol_error = """
|
||||
This type of error is frequently caused by running an older
|
||||
version of the firmware on the micro-controller (fix by
|
||||
recompiling and flashing the firmware).
|
||||
Once the underlying issue is corrected, use the "RESTART"
|
||||
command to reload the config and restart the host software.
|
||||
Protocol error connecting to printer
|
||||
"""
|
||||
|
||||
message_mcu_connect_error = """
|
||||
This is an unrecoverable error. Please manually restart the
|
||||
micro-controller and then issue the "RESTART" command to
|
||||
restart the host software.
|
||||
Once the underlying issue is corrected, use the
|
||||
"FIRMWARE_RESTART" command to reset the firmware, reload the
|
||||
config, and restart the host software.
|
||||
Error configuring printer
|
||||
"""
|
||||
|
||||
message_shutdown = """
|
||||
Once the underlying issue is corrected, the "CLEAR_SHUTDOWN"
|
||||
command can be used to clear the firmware flag and restart
|
||||
the host software.
|
||||
Once the underlying issue is corrected, use the
|
||||
"FIRMWARE_RESTART" command to reset the firmware, reload the
|
||||
config, and restart the host software.
|
||||
Printer is shutdown
|
||||
"""
|
||||
|
||||
@@ -40,26 +52,47 @@ class ConfigWrapper:
|
||||
def __init__(self, printer, section):
|
||||
self.printer = printer
|
||||
self.section = section
|
||||
def get_wrapper(self, parser, option, default):
|
||||
def get_wrapper(self, parser, option, default
|
||||
, minval=None, maxval=None, above=None, below=None):
|
||||
if (default is not self.sentinel
|
||||
and not self.printer.fileconfig.has_option(self.section, option)):
|
||||
return default
|
||||
self.printer.all_config_options[
|
||||
(self.section.lower(), option.lower())] = 1
|
||||
try:
|
||||
return parser(self.section, option)
|
||||
except self.error, e:
|
||||
v = parser(self.section, option)
|
||||
except self.error as e:
|
||||
raise
|
||||
except:
|
||||
raise self.error("Unable to parse option '%s' in section '%s'" % (
|
||||
option, self.section))
|
||||
if minval is not None and v < minval:
|
||||
raise self.error(
|
||||
"Option '%s' in section '%s' must have minimum of %s" % (
|
||||
option, self.section, minval))
|
||||
if maxval is not None and v > maxval:
|
||||
raise self.error(
|
||||
"Option '%s' in section '%s' must have maximum of %s" % (
|
||||
option, self.section, maxval))
|
||||
if above is not None and v <= above:
|
||||
raise self.error(
|
||||
"Option '%s' in section '%s' must be above %s" % (
|
||||
option, self.section, above))
|
||||
if below is not None and v >= below:
|
||||
raise self.error(
|
||||
"Option '%s' in section '%s' must be below %s" % (
|
||||
option, self.section, below))
|
||||
return v
|
||||
def get(self, option, default=sentinel):
|
||||
return self.get_wrapper(self.printer.fileconfig.get, option, default)
|
||||
def getint(self, option, default=sentinel):
|
||||
return self.get_wrapper(self.printer.fileconfig.getint, option, default)
|
||||
def getfloat(self, option, default=sentinel):
|
||||
def getint(self, option, default=sentinel, minval=None, maxval=None):
|
||||
return self.get_wrapper(
|
||||
self.printer.fileconfig.getfloat, option, default)
|
||||
self.printer.fileconfig.getint, option, default, minval, maxval)
|
||||
def getfloat(self, option, default=sentinel
|
||||
, minval=None, maxval=None, above=None, below=None):
|
||||
return self.get_wrapper(
|
||||
self.printer.fileconfig.getfloat, option, default
|
||||
, minval, maxval, above, below)
|
||||
def getboolean(self, option, default=sentinel):
|
||||
return self.get_wrapper(
|
||||
self.printer.fileconfig.getboolean, option, default)
|
||||
@@ -72,134 +105,159 @@ class ConfigWrapper:
|
||||
return choices[c]
|
||||
def getsection(self, section):
|
||||
return ConfigWrapper(self.printer, section)
|
||||
def has_section(self, section):
|
||||
return self.printer.fileconfig.has_section(section)
|
||||
def get_prefix_sections(self, prefix):
|
||||
return [self.getsection(s) for s in self.printer.fileconfig.sections()
|
||||
if s.startswith(prefix)]
|
||||
|
||||
class ConfigLogger():
|
||||
def __init__(self, cfg, bglogger):
|
||||
self.lines = ["===== Config file ====="]
|
||||
cfg.write(self)
|
||||
self.lines.append("=======================")
|
||||
data = "\n".join(self.lines)
|
||||
logging.info(data)
|
||||
bglogger.set_rollover_info("config", data)
|
||||
def write(self, data):
|
||||
self.lines.append(data.strip())
|
||||
|
||||
class Printer:
|
||||
def __init__(self, conffile, input_fd, is_fileinput=False):
|
||||
self.conffile = conffile
|
||||
config_error = ConfigParser.Error
|
||||
def __init__(self, input_fd, bglogger, start_args):
|
||||
self.bglogger = bglogger
|
||||
self.start_args = start_args
|
||||
if bglogger is not None:
|
||||
bglogger.set_rollover_info("config", None)
|
||||
self.reactor = reactor.Reactor()
|
||||
self.gcode = gcode.GCodeParser(self, input_fd, is_fileinput)
|
||||
self.stats_timer = self.reactor.register_timer(self.stats)
|
||||
self.objects = {}
|
||||
self.gcode = gcode.GCodeParser(self, input_fd)
|
||||
self.stats_timer = self.reactor.register_timer(self._stats)
|
||||
self.connect_timer = self.reactor.register_timer(
|
||||
self.connect, self.reactor.NOW)
|
||||
self._connect, self.reactor.NOW)
|
||||
self.all_config_options = {}
|
||||
self.need_dump_debug = False
|
||||
self.state_message = message_startup
|
||||
self.debugoutput = self.dictionary = None
|
||||
self.is_shutdown = False
|
||||
self.async_shutdown_msg = ""
|
||||
self.run_result = None
|
||||
self.fileconfig = None
|
||||
self.mcu = None
|
||||
self.objects = {}
|
||||
def set_fileoutput(self, debugoutput, dictionary):
|
||||
self.debugoutput = debugoutput
|
||||
self.dictionary = dictionary
|
||||
def stats(self, eventtime):
|
||||
if self.need_dump_debug:
|
||||
# Call dump_debug here so it is executed in the main thread
|
||||
self.gcode.dump_debug()
|
||||
self.need_dump_debug = False
|
||||
self.mcus = []
|
||||
def get_start_args(self):
|
||||
return self.start_args
|
||||
def _stats(self, eventtime, force_output=False):
|
||||
toolhead = self.objects.get('toolhead')
|
||||
if toolhead is None:
|
||||
return eventtime + 1.
|
||||
is_active = toolhead.check_active(eventtime)
|
||||
if not is_active and not force_output:
|
||||
return eventtime + 1.
|
||||
out = []
|
||||
out.append(self.gcode.stats(eventtime))
|
||||
toolhead = self.objects.get('toolhead')
|
||||
if toolhead is not None:
|
||||
out.append(toolhead.stats(eventtime))
|
||||
if self.mcu is not None:
|
||||
out.append(self.mcu.stats(eventtime))
|
||||
logging.info("Stats %.0f: %s" % (eventtime, ' '.join(out)))
|
||||
for m in self.mcus:
|
||||
out.append(m.stats(eventtime))
|
||||
logging.info("Stats %.1f: %s", eventtime, ' '.join(out))
|
||||
return eventtime + 1.
|
||||
def load_config(self):
|
||||
def add_object(self, name, obj):
|
||||
self.objects[name] = obj
|
||||
def _load_config(self):
|
||||
self.fileconfig = ConfigParser.RawConfigParser()
|
||||
res = self.fileconfig.read(self.conffile)
|
||||
config_file = self.start_args['config_file']
|
||||
res = self.fileconfig.read(config_file)
|
||||
if not res:
|
||||
raise ConfigParser.Error("Unable to open config file %s" % (
|
||||
self.conffile,))
|
||||
self.mcu = mcu.MCU(self, ConfigWrapper(self, 'mcu'))
|
||||
if self.fileconfig.has_section('fan'):
|
||||
self.objects['fan'] = fan.PrinterFan(
|
||||
self, ConfigWrapper(self, 'fan'))
|
||||
if self.fileconfig.has_section('extruder'):
|
||||
self.objects['extruder'] = extruder.PrinterExtruder(
|
||||
self, ConfigWrapper(self, 'extruder'))
|
||||
if self.fileconfig.has_section('heater_bed'):
|
||||
self.objects['heater_bed'] = heater.PrinterHeater(
|
||||
self, ConfigWrapper(self, 'heater_bed'))
|
||||
self.objects['toolhead'] = toolhead.ToolHead(
|
||||
self, ConfigWrapper(self, 'printer'))
|
||||
def build_config(self):
|
||||
for oname in sorted(self.objects.keys()):
|
||||
self.objects[oname].build_config()
|
||||
self.gcode.build_config()
|
||||
self.mcu.build_config()
|
||||
def validate_config(self):
|
||||
valid_sections = dict([(s, 1) for s, o in self.all_config_options])
|
||||
raise self.config_error("Unable to open config file %s" % (
|
||||
config_file,))
|
||||
if self.bglogger is not None:
|
||||
ConfigLogger(self.fileconfig, self.bglogger)
|
||||
# Create printer components
|
||||
config = ConfigWrapper(self, 'printer')
|
||||
for m in [pins, mcu, chipmisc, toolhead, extruder, heater, fan]:
|
||||
m.add_printer_objects(self, config)
|
||||
self.mcus = mcu.get_printer_mcus(self)
|
||||
# Validate that there are no undefined parameters in the config file
|
||||
valid_sections = { s: 1 for s, o in self.all_config_options }
|
||||
for section in self.fileconfig.sections():
|
||||
section = section.lower()
|
||||
if section not in valid_sections:
|
||||
raise ConfigParser.Error("Unknown config file section '%s'" % (
|
||||
raise self.config_error("Unknown config file section '%s'" % (
|
||||
section,))
|
||||
for option in self.fileconfig.options(section):
|
||||
option = option.lower()
|
||||
if (section, option) not in self.all_config_options:
|
||||
raise ConfigParser.Error(
|
||||
raise self.config_error(
|
||||
"Unknown option '%s' in section '%s'" % (
|
||||
option, section))
|
||||
def connect(self, eventtime):
|
||||
def _connect(self, eventtime):
|
||||
self.reactor.unregister_timer(self.connect_timer)
|
||||
try:
|
||||
self.load_config()
|
||||
if self.debugoutput is None:
|
||||
self._load_config()
|
||||
for m in self.mcus:
|
||||
m.connect()
|
||||
self.gcode.connect()
|
||||
self.state_message = message_ready
|
||||
if self.start_args.get('debugoutput') is None:
|
||||
self.reactor.update_timer(self.stats_timer, self.reactor.NOW)
|
||||
else:
|
||||
self.mcu.connect_file(self.debugoutput, self.dictionary)
|
||||
self.mcu.connect()
|
||||
self.build_config()
|
||||
self.validate_config()
|
||||
self.gcode.set_printer_ready(True)
|
||||
self.state_message = "Printer is ready"
|
||||
except ConfigParser.Error, e:
|
||||
except (self.config_error, pins.error) as e:
|
||||
logging.exception("Config error")
|
||||
self.state_message = "%s%s" % (str(e), message_restart)
|
||||
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
|
||||
except mcu.error, e:
|
||||
except msgproto.error as e:
|
||||
logging.exception("Protocol error")
|
||||
self.state_message = "%s%s" % (str(e), message_protocol_error)
|
||||
except mcu.error as e:
|
||||
logging.exception("MCU error during connect")
|
||||
self.state_message = "%s%s" % (str(e), message_mcu_connect_error)
|
||||
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
|
||||
except:
|
||||
logging.exception("Unhandled exception during connect")
|
||||
self.state_message = "Internal error during connect.%s" % (
|
||||
message_restart)
|
||||
self.reactor.update_timer(self.stats_timer, self.reactor.NEVER)
|
||||
self.reactor.unregister_timer(self.connect_timer)
|
||||
message_restart,)
|
||||
return self.reactor.NEVER
|
||||
def run(self):
|
||||
systime = time.time()
|
||||
monotime = self.reactor.monotonic()
|
||||
logging.info("Start printer at %s (%.1f %.1f)",
|
||||
time.asctime(time.localtime(systime)), systime, monotime)
|
||||
while 1:
|
||||
# Enter main reactor loop
|
||||
try:
|
||||
self.reactor.run()
|
||||
except:
|
||||
logging.exception("Unhandled exception during run")
|
||||
return
|
||||
return self.run_result
|
||||
return "exit"
|
||||
# Check restart flags
|
||||
run_result = self.run_result
|
||||
try:
|
||||
if run_result == 'shutdown':
|
||||
self.invoke_shutdown(self.async_shutdown_msg, True)
|
||||
continue
|
||||
self._stats(self.reactor.monotonic(), force_output=True)
|
||||
for m in self.mcus:
|
||||
if run_result == 'firmware_restart':
|
||||
m.microcontroller_restart()
|
||||
m.disconnect()
|
||||
except:
|
||||
logging.exception("Unhandled exception during post run")
|
||||
return run_result
|
||||
def get_state_message(self):
|
||||
return self.state_message
|
||||
def note_shutdown(self, msg):
|
||||
if self.state_message == 'Running':
|
||||
self.need_dump_debug = True
|
||||
self.state_message = "Firmware shutdown: %s%s" % (
|
||||
msg, message_shutdown)
|
||||
self.gcode.set_printer_ready(False)
|
||||
def note_mcu_error(self, msg):
|
||||
def invoke_shutdown(self, msg, is_mcu_shutdown=False):
|
||||
if self.is_shutdown:
|
||||
return
|
||||
self.is_shutdown = True
|
||||
if is_mcu_shutdown:
|
||||
self.state_message = "%s%s" % (msg, message_shutdown)
|
||||
else:
|
||||
self.state_message = "%s%s" % (msg, message_restart)
|
||||
self.gcode.set_printer_ready(False)
|
||||
self.gcode.motor_heater_off()
|
||||
def disconnect(self):
|
||||
try:
|
||||
if self.mcu is not None:
|
||||
self.stats(time.time())
|
||||
self.mcu.disconnect()
|
||||
except:
|
||||
logging.exception("Unhandled exception during disconnect")
|
||||
def request_restart(self):
|
||||
self.run_result = "restart"
|
||||
self.reactor.end()
|
||||
def request_exit_eof(self):
|
||||
self.run_result = "exit_eof"
|
||||
for m in self.mcus:
|
||||
m.do_shutdown()
|
||||
self.gcode.do_shutdown()
|
||||
toolhead = self.objects.get('toolhead')
|
||||
if toolhead is not None:
|
||||
toolhead.do_shutdown()
|
||||
def invoke_async_shutdown(self, msg):
|
||||
self.async_shutdown_msg = msg
|
||||
self.request_exit("shutdown")
|
||||
def request_exit(self, result="exit"):
|
||||
self.run_result = result
|
||||
self.reactor.end()
|
||||
|
||||
|
||||
@@ -207,18 +265,19 @@ class Printer:
|
||||
# Startup
|
||||
######################################################################
|
||||
|
||||
def read_dictionary(filename):
|
||||
dfile = open(filename, 'rb')
|
||||
dictionary = dfile.read()
|
||||
dfile.close()
|
||||
return dictionary
|
||||
def arg_dictionary(option, opt_str, value, parser):
|
||||
key, fname = "dictionary", value
|
||||
if '=' in value:
|
||||
mcu_name, fname = value.split('=', 1)
|
||||
key = "dictionary_" + mcu_name
|
||||
if parser.values.dictionary is None:
|
||||
parser.values.dictionary = {}
|
||||
parser.values.dictionary[key] = fname
|
||||
|
||||
def main():
|
||||
usage = "%prog [options] <config file>"
|
||||
opts = optparse.OptionParser(usage)
|
||||
opts.add_option("-o", "--debugoutput", dest="outputfile",
|
||||
help="write output to file instead of to serial port")
|
||||
opts.add_option("-i", "--debuginput", dest="inputfile",
|
||||
opts.add_option("-i", "--debuginput", dest="debuginput",
|
||||
help="read commands from file instead of from tty port")
|
||||
opts.add_option("-I", "--input-tty", dest="inputtty", default='/tmp/printer',
|
||||
help="input tty name (default is /tmp/printer)")
|
||||
@@ -226,47 +285,54 @@ def main():
|
||||
help="write log to file instead of stderr")
|
||||
opts.add_option("-v", action="store_true", dest="verbose",
|
||||
help="enable debug messages")
|
||||
opts.add_option("-d", dest="read_dictionary",
|
||||
opts.add_option("-o", "--debugoutput", dest="debugoutput",
|
||||
help="write output to file instead of to serial port")
|
||||
opts.add_option("-d", "--dictionary", dest="dictionary", type="string",
|
||||
action="callback", callback=arg_dictionary,
|
||||
help="file to read for mcu protocol dictionary")
|
||||
options, args = opts.parse_args()
|
||||
if len(args) != 1:
|
||||
opts.error("Incorrect number of arguments")
|
||||
conffile = args[0]
|
||||
start_args = {'config_file': args[0], 'start_reason': 'startup'}
|
||||
|
||||
input_fd = debuginput = debugoutput = bglogger = None
|
||||
input_fd = bglogger = None
|
||||
|
||||
debuglevel = logging.INFO
|
||||
if options.verbose:
|
||||
debuglevel = logging.DEBUG
|
||||
if options.inputfile:
|
||||
debuginput = open(options.inputfile, 'rb')
|
||||
if options.debuginput:
|
||||
start_args['debuginput'] = options.debuginput
|
||||
debuginput = open(options.debuginput, 'rb')
|
||||
input_fd = debuginput.fileno()
|
||||
else:
|
||||
input_fd = util.create_pty(options.inputtty)
|
||||
if options.outputfile:
|
||||
debugoutput = open(options.outputfile, 'wb')
|
||||
if options.debugoutput:
|
||||
start_args['debugoutput'] = options.debugoutput
|
||||
start_args.update(options.dictionary)
|
||||
if options.logfile:
|
||||
bglogger = queuelogger.setup_bg_logging(options.logfile, debuglevel)
|
||||
else:
|
||||
logging.basicConfig(level=debuglevel)
|
||||
logging.info("Starting Klippy...")
|
||||
start_args['software_version'] = util.get_git_version()
|
||||
if bglogger is not None:
|
||||
lines = ["Args: %s" % (sys.argv,),
|
||||
"Git version: %s" % (repr(start_args['software_version']),),
|
||||
"CPU: %s" % (util.get_cpu_info(),),
|
||||
"Python: %s" % (repr(sys.version),)]
|
||||
lines = "\n".join(lines)
|
||||
logging.info(lines)
|
||||
bglogger.set_rollover_info('versions', lines)
|
||||
|
||||
# Start firmware
|
||||
# Start Printer() class
|
||||
while 1:
|
||||
is_fileinput = debuginput is not None
|
||||
printer = Printer(conffile, input_fd, is_fileinput)
|
||||
if debugoutput:
|
||||
proto_dict = read_dictionary(options.read_dictionary)
|
||||
printer.set_fileoutput(debugoutput, proto_dict)
|
||||
printer = Printer(input_fd, bglogger, start_args)
|
||||
res = printer.run()
|
||||
if res == 'restart':
|
||||
printer.disconnect()
|
||||
if res == 'exit':
|
||||
break
|
||||
time.sleep(1.)
|
||||
logging.info("Restarting printer")
|
||||
continue
|
||||
elif res == 'exit_eof':
|
||||
printer.disconnect()
|
||||
break
|
||||
start_args['start_reason'] = res
|
||||
|
||||
if bglogger is not None:
|
||||
bglogger.stop()
|
||||
|
||||
1008
klippy/mcu.py
@@ -1,6 +1,6 @@
|
||||
# Protocol definitions for firmware communication
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import json, zlib, logging
|
||||
@@ -106,8 +106,8 @@ class MessageFormat:
|
||||
self.name = parts[0]
|
||||
argparts = [arg.split('=') for arg in parts[1:]]
|
||||
self.param_types = [MessageTypes[fmt] for name, fmt in argparts]
|
||||
self.param_names = [name for name, fmt in argparts]
|
||||
self.name_to_type = dict(zip(self.param_names, self.param_types))
|
||||
self.param_names = [(name, MessageTypes[fmt]) for name, fmt in argparts]
|
||||
self.name_to_type = dict(self.param_names)
|
||||
def encode(self, *params):
|
||||
out = []
|
||||
out.append(self.msgid)
|
||||
@@ -117,26 +117,24 @@ class MessageFormat:
|
||||
def encode_by_name(self, **params):
|
||||
out = []
|
||||
out.append(self.msgid)
|
||||
for name, t in zip(self.param_names, self.param_types):
|
||||
for name, t in self.param_names:
|
||||
t.encode(out, params[name])
|
||||
return out
|
||||
def parse(self, s, pos):
|
||||
pos += 1
|
||||
out = {}
|
||||
for t, name in zip(self.param_types, self.param_names):
|
||||
for name, t in self.param_names:
|
||||
v, pos = t.parse(s, pos)
|
||||
out[name] = v
|
||||
return out, pos
|
||||
def dump(self, s, pos):
|
||||
pos += 1
|
||||
def format_params(self, params):
|
||||
out = []
|
||||
for t in self.param_types:
|
||||
v, pos = t.parse(s, pos)
|
||||
for name, t in self.param_names:
|
||||
v = params[name]
|
||||
if not t.is_int:
|
||||
v = repr(v)
|
||||
out.append(v)
|
||||
outmsg = self.debugformat % tuple(out)
|
||||
return outmsg, pos
|
||||
return self.debugformat % tuple(out)
|
||||
|
||||
class OutputFormat:
|
||||
name = '#output'
|
||||
@@ -164,17 +162,13 @@ class OutputFormat:
|
||||
out = []
|
||||
for t in self.param_types:
|
||||
v, pos = t.parse(s, pos)
|
||||
if not t.is_int:
|
||||
v = repr(v)
|
||||
out.append(v)
|
||||
outmsg = self.debugformat % tuple(out)
|
||||
return {'#msg': outmsg}, pos
|
||||
def dump(self, s, pos):
|
||||
pos += 1
|
||||
out = []
|
||||
for t in self.param_types:
|
||||
v, pos = t.parse(s, pos)
|
||||
out.append(v)
|
||||
outmsg = self.debugformat % tuple(out)
|
||||
return outmsg, pos
|
||||
def format_params(self, params):
|
||||
return "#output %s" % (params['#msg'],)
|
||||
|
||||
class UnknownFormat:
|
||||
name = '#unknown'
|
||||
@@ -182,13 +176,17 @@ class UnknownFormat:
|
||||
msgid = s[pos]
|
||||
msg = str(bytearray(s))
|
||||
return {'#msgid': msgid, '#msg': msg}, len(s)-MESSAGE_TRAILER_SIZE
|
||||
def format_params(self, params):
|
||||
return "#unknown %s" % (repr(params['#msg']),)
|
||||
|
||||
class MessageParser:
|
||||
error = error
|
||||
def __init__(self):
|
||||
self.unknown = UnknownFormat()
|
||||
self.command_ids = []
|
||||
self.messages_by_id = {}
|
||||
self.messages_by_name = {}
|
||||
self.static_strings = []
|
||||
self.static_strings = {}
|
||||
self.config = {}
|
||||
self.version = ""
|
||||
self.raw_identify_data = ""
|
||||
@@ -210,7 +208,7 @@ class MessageParser:
|
||||
msgcrc = s[msglen-MESSAGE_TRAILER_CRC:msglen-MESSAGE_TRAILER_CRC+2]
|
||||
crc = crc16_ccitt(s[:msglen-MESSAGE_TRAILER_SIZE])
|
||||
if crc != msgcrc:
|
||||
#logging.debug("got crc %s vs %s" % (repr(crc), repr(msgcrc)))
|
||||
#logging.debug("got crc %s vs %s", repr(crc), repr(msgcrc))
|
||||
return -1
|
||||
return msglen
|
||||
def dump(self, s):
|
||||
@@ -220,11 +218,20 @@ class MessageParser:
|
||||
while 1:
|
||||
msgid = s[pos]
|
||||
mid = self.messages_by_id.get(msgid, self.unknown)
|
||||
params, pos = mid.dump(s, pos)
|
||||
out.append("%s" % (params,))
|
||||
params, pos = mid.parse(s, pos)
|
||||
out.append(mid.format_params(params))
|
||||
if pos >= len(s)-MESSAGE_TRAILER_SIZE:
|
||||
break
|
||||
return out
|
||||
def format_params(self, params):
|
||||
name = params.get('#name')
|
||||
mid = self.messages_by_name.get(name)
|
||||
if mid is not None:
|
||||
return mid.format_params(params)
|
||||
msg = params.get('#msg')
|
||||
if msg is not None:
|
||||
return "%s %s" % (name, msg)
|
||||
return str(params)
|
||||
def parse(self, s):
|
||||
msgid = s[MESSAGE_HEADER_SIZE]
|
||||
mid = self.messages_by_id.get(msgid, self.unknown)
|
||||
@@ -234,7 +241,7 @@ class MessageParser:
|
||||
params['#name'] = mid.name
|
||||
static_string_id = params.get('static_string_id')
|
||||
if static_string_id is not None:
|
||||
params['#msg'] = self.static_strings[static_string_id]
|
||||
params['#msg'] = self.static_strings.get(static_string_id, "?")
|
||||
return params
|
||||
def encode(self, seq, cmd):
|
||||
msglen = MESSAGE_MIN + len(cmd)
|
||||
@@ -246,7 +253,7 @@ class MessageParser:
|
||||
def _parse_buffer(self, value):
|
||||
tval = int(value, 16)
|
||||
out = []
|
||||
for i in range(len(value)/2):
|
||||
for i in range(len(value) // 2):
|
||||
out.append(tval & 0xff)
|
||||
tval >>= 8
|
||||
out.reverse()
|
||||
@@ -297,14 +304,34 @@ class MessageParser:
|
||||
self.messages_by_id[msgid] = msg
|
||||
self.messages_by_name[msg.name] = msg
|
||||
def process_identify(self, data, decompress=True):
|
||||
try:
|
||||
if decompress:
|
||||
data = zlib.decompress(data)
|
||||
self.raw_identify_data = data
|
||||
data = json.loads(data)
|
||||
messages = data.get('messages')
|
||||
commands = data.get('commands')
|
||||
self.command_ids = commands
|
||||
responses = data.get('responses')
|
||||
self._init_messages(messages, commands+responses)
|
||||
self.static_strings = data.get('static_strings', [])
|
||||
static_strings = data.get('static_strings', {})
|
||||
self.static_strings = { int(k): v for k, v in static_strings.items() }
|
||||
self.config.update(data.get('config', {}))
|
||||
self.version = data.get('version', '')
|
||||
except error as e:
|
||||
raise
|
||||
except Exception as e:
|
||||
logging.exception("process_identify error")
|
||||
raise error("Error during identify: %s" % (str(e),))
|
||||
def get_constant(self, name):
|
||||
try:
|
||||
return self.config[name]
|
||||
except KeyError:
|
||||
raise error("Firmware constant '%s' not found" % (name,))
|
||||
def get_constant_float(self, name):
|
||||
try:
|
||||
return float(self.config[name])
|
||||
except ValueError:
|
||||
raise error("Firmware constant '%s' not a float" % (name,))
|
||||
except KeyError:
|
||||
raise error("Firmware constant '%s' not found" % (name,))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
# Script to parse a serial port data dump
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
|
||||
151
klippy/pins.py
@@ -1,11 +1,15 @@
|
||||
# Pin name to pin number definitions
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
import re
|
||||
|
||||
|
||||
######################################################################
|
||||
# Hardware pin names
|
||||
######################################################################
|
||||
|
||||
def port_pins(port_count, bit_count=8):
|
||||
pins = {}
|
||||
for port in range(port_count):
|
||||
@@ -16,25 +20,26 @@ def port_pins(port_count, bit_count=8):
|
||||
pins['P%c%d' % (portchr, portbit)] = port * bit_count + portbit
|
||||
return pins
|
||||
|
||||
PINS_atmega164 = port_pins(4)
|
||||
PINS_atmega1280 = port_pins(12)
|
||||
def named_pins(fmt, port_count, bit_count=32):
|
||||
return { fmt % (port, portbit) : port * bit_count + portbit
|
||||
for port in range(port_count)
|
||||
for portbit in range(bit_count) }
|
||||
|
||||
def beaglebone_pins():
|
||||
gpios = named_pins("gpio%d_%d", 4)
|
||||
gpios.update({"AIN%d" % i: i+4*32 for i in range(8)})
|
||||
return gpios
|
||||
|
||||
MCU_PINS = {
|
||||
"atmega168": PINS_atmega164, "atmega644p": PINS_atmega164,
|
||||
"at90usb1286": port_pins(5),
|
||||
"atmega1280": PINS_atmega1280, "atmega2560": PINS_atmega1280,
|
||||
"sam3x8e": port_pins(4, 32)
|
||||
"atmega168": port_pins(4), "atmega328": port_pins(4),
|
||||
"atmega644p": port_pins(4), "atmega1284p": port_pins(4),
|
||||
"at90usb1286": port_pins(6),
|
||||
"atmega1280": port_pins(12), "atmega2560": port_pins(12),
|
||||
"sam3x8e": port_pins(4, 32),
|
||||
"pru": beaglebone_pins(),
|
||||
"linux": {"analog%d" % i: i for i in range(8)}, # XXX
|
||||
}
|
||||
|
||||
def mcu_to_pins(mcu):
|
||||
return MCU_PINS.get(mcu, {})
|
||||
|
||||
re_pin = re.compile(r'(?P<prefix>[ _]pin=)(?P<name>[^ ]*)')
|
||||
def update_command(cmd, pmap):
|
||||
def fixup(m):
|
||||
return m.group('prefix') + str(pmap[m.group('name')])
|
||||
return re_pin.sub(fixup, cmd)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Arduino mappings
|
||||
@@ -96,12 +101,116 @@ Arduino_from_mcu = {
|
||||
"sam3x8e": (Arduino_Due, Arduino_Due_analog),
|
||||
}
|
||||
|
||||
def map_pins(name, mcu):
|
||||
pins = MCU_PINS.get(mcu, {})
|
||||
if name == 'arduino':
|
||||
dpins, apins = Arduino_from_mcu.get(mcu, [])
|
||||
def update_map_arduino(pins, mcu):
|
||||
dpins, apins = Arduino_from_mcu.get(mcu, ([], []))
|
||||
for i in range(len(dpins)):
|
||||
pins['ar' + str(i)] = pins[dpins[i]]
|
||||
for i in range(len(apins)):
|
||||
pins['analog%d' % (i,)] = pins[apins[i]]
|
||||
|
||||
|
||||
######################################################################
|
||||
# Beaglebone mappings
|
||||
######################################################################
|
||||
|
||||
beagleboneblack_mappings = {
|
||||
'P8_3': 'gpio1_6', 'P8_4': 'gpio1_7', 'P8_5': 'gpio1_2',
|
||||
'P8_6': 'gpio1_3', 'P8_7': 'gpio2_2', 'P8_8': 'gpio2_3',
|
||||
'P8_9': 'gpio2_5', 'P8_10': 'gpio2_4', 'P8_11': 'gpio1_13',
|
||||
'P8_12': 'gpio1_12', 'P8_13': 'gpio0_23', 'P8_14': 'gpio0_26',
|
||||
'P8_15': 'gpio1_15', 'P8_16': 'gpio1_14', 'P8_17': 'gpio0_27',
|
||||
'P8_18': 'gpio2_1', 'P8_19': 'gpio0_22', 'P8_20': 'gpio1_31',
|
||||
'P8_21': 'gpio1_30', 'P8_22': 'gpio1_5', 'P8_23': 'gpio1_4',
|
||||
'P8_24': 'gpio1_1', 'P8_25': 'gpio1_0', 'P8_26': 'gpio1_29',
|
||||
'P8_27': 'gpio2_22', 'P8_28': 'gpio2_24', 'P8_29': 'gpio2_23',
|
||||
'P8_30': 'gpio2_25', 'P8_31': 'gpio0_10', 'P8_32': 'gpio0_11',
|
||||
'P8_33': 'gpio0_9', 'P8_34': 'gpio2_17', 'P8_35': 'gpio0_8',
|
||||
'P8_36': 'gpio2_16', 'P8_37': 'gpio2_14', 'P8_38': 'gpio2_15',
|
||||
'P8_39': 'gpio2_12', 'P8_40': 'gpio2_13', 'P8_41': 'gpio2_10',
|
||||
'P8_42': 'gpio2_11', 'P8_43': 'gpio2_8', 'P8_44': 'gpio2_9',
|
||||
'P8_45': 'gpio2_6', 'P8_46': 'gpio2_7', 'P9_11': 'gpio0_30',
|
||||
'P9_12': 'gpio1_28', 'P9_13': 'gpio0_31', 'P9_14': 'gpio1_18',
|
||||
'P9_15': 'gpio1_16', 'P9_16': 'gpio1_19', 'P9_17': 'gpio0_5',
|
||||
'P9_18': 'gpio0_4', 'P9_19': 'gpio0_13', 'P9_20': 'gpio0_12',
|
||||
'P9_21': 'gpio0_3', 'P9_22': 'gpio0_2', 'P9_23': 'gpio1_17',
|
||||
'P9_24': 'gpio0_15', 'P9_25': 'gpio3_21', 'P9_26': 'gpio0_14',
|
||||
'P9_27': 'gpio3_19', 'P9_28': 'gpio3_17', 'P9_29': 'gpio3_15',
|
||||
'P9_30': 'gpio3_16', 'P9_31': 'gpio3_14', 'P9_41': 'gpio0_20',
|
||||
'P9_42': 'gpio3_20', 'P9_43': 'gpio0_7', 'P9_44': 'gpio3_18',
|
||||
|
||||
'P9_33': 'AIN4', 'P9_35': 'AIN6', 'P9_36': 'AIN5', 'P9_37': 'AIN2',
|
||||
'P9_38': 'AIN3', 'P9_39': 'AIN0', 'P9_40': 'AIN1',
|
||||
}
|
||||
|
||||
def update_map_beaglebone(pins, mcu):
|
||||
for pin, gpio in beagleboneblack_mappings.items():
|
||||
pins[pin] = pins[gpio]
|
||||
|
||||
|
||||
######################################################################
|
||||
# Command translation
|
||||
######################################################################
|
||||
|
||||
# Obtains the pin mappings
|
||||
def get_pin_map(mcu, mapping_name=None):
|
||||
pins = dict(MCU_PINS.get(mcu, {}))
|
||||
if mapping_name == 'arduino':
|
||||
update_map_arduino(pins, mcu)
|
||||
elif mapping_name == 'beaglebone':
|
||||
update_map_beaglebone(pins, mcu)
|
||||
return pins
|
||||
|
||||
# Translate pin names in a firmware command
|
||||
re_pin = re.compile(r'(?P<prefix>[ _]pin=)(?P<name>[^ ]*)')
|
||||
def update_command(cmd, pmap):
|
||||
def pin_fixup(m):
|
||||
return m.group('prefix') + str(pmap[m.group('name')])
|
||||
return re_pin.sub(pin_fixup, cmd)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Pin to chip mapping
|
||||
######################################################################
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
class PrinterPins:
|
||||
error = error
|
||||
def __init__(self):
|
||||
self.chips = {}
|
||||
def parse_pin_desc(self, pin_desc, can_invert=False, can_pullup=False):
|
||||
pullup = invert = 0
|
||||
if can_pullup and pin_desc.startswith('^'):
|
||||
pullup = 1
|
||||
pin_desc = pin_desc[1:].strip()
|
||||
if can_invert and pin_desc.startswith('!'):
|
||||
invert = 1
|
||||
pin_desc = pin_desc[1:].strip()
|
||||
if ':' not in pin_desc:
|
||||
chip_name, pin = 'mcu', pin_desc
|
||||
else:
|
||||
chip_name, pin = [s.strip() for s in pin_desc.split(':', 1)]
|
||||
if chip_name not in self.chips:
|
||||
raise error("Unknown pin chip name '%s'" % (chip_name,))
|
||||
return {'chip': self.chips[chip_name], 'pin': pin,
|
||||
'invert': invert, 'pullup': pullup}
|
||||
def register_chip(self, chip_name, chip):
|
||||
chip_name = chip_name.strip()
|
||||
if chip_name in self.chips:
|
||||
raise error("Duplicate chip name '%s'" % (chip_name,))
|
||||
self.chips[chip_name] = chip
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
printer.add_object('pins', PrinterPins())
|
||||
|
||||
def get_printer_pins(printer):
|
||||
return printer.objects['pins']
|
||||
|
||||
def setup_pin(printer, pin_type, pin_desc):
|
||||
ppins = get_printer_pins(printer)
|
||||
can_invert = pin_type in ['stepper', 'endstop', 'digital_out', 'pwm']
|
||||
can_pullup = pin_type == 'endstop'
|
||||
pin_params = ppins.parse_pin_desc(pin_desc, can_invert, can_pullup)
|
||||
pin_params['type'] = pin_type
|
||||
return pin_params['chip'].setup_pin(pin_params)
|
||||
|
||||
@@ -9,17 +9,20 @@
|
||||
#include <stdint.h> // uint8_t
|
||||
#include <stdio.h> // fprintf
|
||||
#include <string.h> // strerror
|
||||
#include <sys/time.h> // gettimeofday
|
||||
#include <time.h> // struct timespec
|
||||
#include "pyhelper.h" // get_time
|
||||
#include "pyhelper.h" // get_monotonic
|
||||
|
||||
// Return the current system time as a double
|
||||
// Return the monotonic system time as a double
|
||||
double
|
||||
get_time(void)
|
||||
get_monotonic(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.;
|
||||
struct timespec ts;
|
||||
int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (ret) {
|
||||
report_errno("clock_gettime", ret);
|
||||
return 0.;
|
||||
}
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec * .000000001;
|
||||
}
|
||||
|
||||
// Fill a 'struct timespec' with a system time stored in a double
|
||||
@@ -66,7 +69,7 @@ report_errno(char *where, int rc)
|
||||
}
|
||||
|
||||
// Return a hex character for a given number
|
||||
#define GETHEX(x) ((x) < 10 ? '0' + (x) : 'e' + (x) - 10)
|
||||
#define GETHEX(x) ((x) < 10 ? '0' + (x) : 'a' + (x) - 10)
|
||||
|
||||
// Translate a binary string into an ASCII string with escape sequences
|
||||
char *
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#ifndef PYHELPER_H
|
||||
#define PYHELPER_H
|
||||
|
||||
double get_time(void);
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
double get_monotonic(void);
|
||||
struct timespec fill_time(double time);
|
||||
void set_python_logging_callback(void (*func)(const char *));
|
||||
void errorf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Code to implement asynchronous logging from a background thread
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, threading, Queue
|
||||
import logging, logging.handlers, threading, Queue, time
|
||||
|
||||
# Class to forward all messages through a queue to a background thread
|
||||
class QueueHandler(logging.Handler):
|
||||
@@ -21,27 +21,39 @@ class QueueHandler(logging.Handler):
|
||||
self.handleError(record)
|
||||
|
||||
# Class to poll a queue in a background thread and log each message
|
||||
class QueueListener(object):
|
||||
def __init__(self, handler):
|
||||
self.handler = handler
|
||||
self.queue = Queue.Queue()
|
||||
self.thread = threading.Thread(target=self._bg_thread)
|
||||
self.thread.start()
|
||||
class QueueListener(logging.handlers.TimedRotatingFileHandler):
|
||||
def __init__(self, filename):
|
||||
logging.handlers.TimedRotatingFileHandler.__init__(
|
||||
self, filename, when='midnight', backupCount=5)
|
||||
self.bg_queue = Queue.Queue()
|
||||
self.bg_thread = threading.Thread(target=self._bg_thread)
|
||||
self.bg_thread.start()
|
||||
self.rollover_info = {}
|
||||
def _bg_thread(self):
|
||||
while 1:
|
||||
record = self.queue.get(True)
|
||||
record = self.bg_queue.get(True)
|
||||
if record is None:
|
||||
break
|
||||
self.handler.handle(record)
|
||||
self.handle(record)
|
||||
def stop(self):
|
||||
self.queue.put_nowait(None)
|
||||
self.thread.join()
|
||||
self.bg_queue.put_nowait(None)
|
||||
self.bg_thread.join()
|
||||
def set_rollover_info(self, name, info):
|
||||
self.rollover_info[name] = info
|
||||
def doRollover(self):
|
||||
logging.handlers.TimedRotatingFileHandler.doRollover(self)
|
||||
lines = [self.rollover_info[name]
|
||||
for name in sorted(self.rollover_info)
|
||||
if self.rollover_info[name]]
|
||||
lines.append(
|
||||
"=============== Log rollover at %s ===============" % (
|
||||
time.asctime(),))
|
||||
self.emit(logging.makeLogRecord(
|
||||
{'msg': "\n".join(lines), 'level': logging.INFO}))
|
||||
|
||||
def setup_bg_logging(filename, debuglevel):
|
||||
logoutput = open(filename, 'wb')
|
||||
handler = logging.StreamHandler(logoutput)
|
||||
ql = QueueListener(handler)
|
||||
qh = QueueHandler(ql.queue)
|
||||
ql = QueueListener(filename)
|
||||
qh = QueueHandler(ql.bg_queue)
|
||||
root = logging.getLogger()
|
||||
root.addHandler(qh)
|
||||
root.setLevel(debuglevel)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# File descriptor and timer event helper
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import select, time, math
|
||||
import select, math, time
|
||||
import greenlet
|
||||
import chelper
|
||||
|
||||
class ReactorTimer:
|
||||
def __init__(self, callback, waketime):
|
||||
@@ -33,6 +34,7 @@ class SelectReactor:
|
||||
self._process = False
|
||||
self._g_dispatch = None
|
||||
self._greenlets = []
|
||||
self.monotonic = chelper.get_ffi()[1].get_monotonic
|
||||
# Timers
|
||||
def _note_time(self, t):
|
||||
nexttime = t.waketime
|
||||
@@ -67,11 +69,19 @@ class SelectReactor:
|
||||
self._note_time(t)
|
||||
if eventtime >= self._next_timer:
|
||||
return 0.
|
||||
return min(1., max(.001, self._next_timer - time.time()))
|
||||
return min(1., max(.001, self._next_timer - self.monotonic()))
|
||||
# Greenlets
|
||||
def _sys_pause(self, waketime):
|
||||
# Pause using system sleep for when reactor not running
|
||||
delay = waketime - self.monotonic()
|
||||
if delay > 0.:
|
||||
time.sleep(delay)
|
||||
return self.monotonic()
|
||||
def pause(self, waketime):
|
||||
g = greenlet.getcurrent()
|
||||
if g is not self._g_dispatch:
|
||||
if self._g_dispatch is None:
|
||||
return self._sys_pause(waketime)
|
||||
return self._g_dispatch.switch(waketime)
|
||||
if self._greenlets:
|
||||
g_next = self._greenlets.pop()
|
||||
@@ -95,20 +105,21 @@ class SelectReactor:
|
||||
self._fds.pop(self._fds.index(handler))
|
||||
# Main loop
|
||||
def _dispatch_loop(self):
|
||||
self._process = True
|
||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||
eventtime = time.time()
|
||||
eventtime = self.monotonic()
|
||||
while self._process:
|
||||
timeout = self._check_timers(eventtime)
|
||||
res = select.select(self._fds, [], [], timeout)
|
||||
eventtime = time.time()
|
||||
eventtime = self.monotonic()
|
||||
for fd in res[0]:
|
||||
fd.callback(eventtime)
|
||||
if g_dispatch is not self._g_dispatch:
|
||||
self._end_greenlet(g_dispatch)
|
||||
eventtime = self.monotonic()
|
||||
break
|
||||
self._g_dispatch = None
|
||||
def run(self):
|
||||
self._process = True
|
||||
g_next = ReactorGreenlet(run=self._dispatch_loop)
|
||||
g_next.switch()
|
||||
def end(self):
|
||||
@@ -134,17 +145,17 @@ class PollReactor(SelectReactor):
|
||||
self._fds = fds
|
||||
# Main loop
|
||||
def _dispatch_loop(self):
|
||||
self._process = True
|
||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||
eventtime = time.time()
|
||||
eventtime = self.monotonic()
|
||||
while self._process:
|
||||
timeout = int(math.ceil(self._check_timers(eventtime) * 1000.))
|
||||
res = self._poll.poll(timeout)
|
||||
eventtime = time.time()
|
||||
timeout = self._check_timers(eventtime)
|
||||
res = self._poll.poll(int(math.ceil(timeout * 1000.)))
|
||||
eventtime = self.monotonic()
|
||||
for fd, event in res:
|
||||
self._fds[fd](eventtime)
|
||||
if g_dispatch is not self._g_dispatch:
|
||||
self._end_greenlet(g_dispatch)
|
||||
eventtime = self.monotonic()
|
||||
break
|
||||
self._g_dispatch = None
|
||||
|
||||
@@ -168,17 +179,17 @@ class EPollReactor(SelectReactor):
|
||||
self._fds = fds
|
||||
# Main loop
|
||||
def _dispatch_loop(self):
|
||||
self._process = True
|
||||
self._g_dispatch = g_dispatch = greenlet.getcurrent()
|
||||
eventtime = time.time()
|
||||
eventtime = self.monotonic()
|
||||
while self._process:
|
||||
timeout = self._check_timers(eventtime)
|
||||
res = self._epoll.poll(timeout)
|
||||
eventtime = time.time()
|
||||
eventtime = self.monotonic()
|
||||
for fd, event in res:
|
||||
self._fds[fd](eventtime)
|
||||
if g_dispatch is not self._g_dispatch:
|
||||
self._end_greenlet(g_dispatch)
|
||||
eventtime = self.monotonic()
|
||||
break
|
||||
self._g_dispatch = None
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# Serial port management for firmware communication
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import time, logging, threading
|
||||
import logging, threading
|
||||
import serial
|
||||
|
||||
import msgproto, chelper, util
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
class SerialReader:
|
||||
BITS_PER_BYTE = 10.
|
||||
def __init__(self, reactor, serialport, baud):
|
||||
@@ -22,23 +25,15 @@ class SerialReader:
|
||||
self.serialqueue = None
|
||||
self.default_cmd_queue = self.alloc_command_queue()
|
||||
self.stats_buf = self.ffi_main.new('char[4096]')
|
||||
# MCU time/clock tracking
|
||||
self.last_ack_time = self.last_ack_rtt_time = 0.
|
||||
self.last_ack_clock = self.last_ack_rtt_clock = 0
|
||||
self.est_clock = 0.
|
||||
# Threading
|
||||
self.lock = threading.Lock()
|
||||
self.background_thread = None
|
||||
# Message handlers
|
||||
self.status_timer = self.reactor.register_timer(
|
||||
self._status_event, self.reactor.NOW)
|
||||
self.status_cmd = None
|
||||
handlers = {
|
||||
'#unknown': self.handle_unknown,
|
||||
'#output': self.handle_output, 'status': self.handle_status,
|
||||
'#unknown': self.handle_unknown, '#output': self.handle_output,
|
||||
'shutdown': self.handle_output, 'is_shutdown': self.handle_output
|
||||
}
|
||||
self.handlers = dict(((k, None), v) for k, v in handlers.items())
|
||||
self.handlers = { (k, None): v for k, v in handlers.items() }
|
||||
def _bg_thread(self):
|
||||
response = self.ffi_main.new('struct pull_queue_message *')
|
||||
while 1:
|
||||
@@ -49,8 +44,8 @@ class SerialReader:
|
||||
params = self.msgparser.parse(response.msg[0:count])
|
||||
params['#sent_time'] = response.sent_time
|
||||
params['#receive_time'] = response.receive_time
|
||||
with self.lock:
|
||||
hdl = (params['#name'], params.get('oid'))
|
||||
with self.lock:
|
||||
hdl = self.handlers.get(hdl, self.handle_default)
|
||||
try:
|
||||
hdl(params)
|
||||
@@ -60,13 +55,18 @@ class SerialReader:
|
||||
# Initial connection
|
||||
logging.info("Starting serial connect")
|
||||
while 1:
|
||||
starttime = time.time()
|
||||
starttime = self.reactor.monotonic()
|
||||
try:
|
||||
self.ser = serial.Serial(self.serialport, self.baud, timeout=0)
|
||||
except OSError, e:
|
||||
logging.warn("Unable to open port: %s" % (e,))
|
||||
if self.baud:
|
||||
self.ser = serial.Serial(
|
||||
self.serialport, self.baud, timeout=0)
|
||||
else:
|
||||
self.ser = open(self.serialport, 'rb+')
|
||||
except (OSError, IOError, serial.SerialException) as e:
|
||||
logging.warn("Unable to open port: %s", e)
|
||||
self.reactor.pause(starttime + 5.)
|
||||
continue
|
||||
if self.baud:
|
||||
stk500v2_leave(self.ser, self.reactor)
|
||||
self.serialqueue = self.ffi_lib.serialqueue_alloc(
|
||||
self.ser.fileno(), 0)
|
||||
@@ -78,59 +78,45 @@ class SerialReader:
|
||||
if identify_data is None:
|
||||
logging.warn("Timeout on serial connect")
|
||||
self.disconnect()
|
||||
self.ser.close()
|
||||
self.ser = None
|
||||
continue
|
||||
break
|
||||
msgparser = msgproto.MessageParser()
|
||||
msgparser.process_identify(identify_data)
|
||||
self.msgparser = msgparser
|
||||
self.register_callback(self.handle_unknown, '#unknown')
|
||||
logging.info("Loaded %d commands (%s)" % (
|
||||
len(msgparser.messages_by_id), msgparser.version))
|
||||
# Setup for runtime
|
||||
logging.info("Loaded %d commands (%s)",
|
||||
len(msgparser.messages_by_id), msgparser.version)
|
||||
logging.info("MCU config: %s", " ".join(
|
||||
["%s=%s" % (k, v) for k, v in msgparser.config.items()]))
|
||||
# Setup baud adjust
|
||||
mcu_baud = float(msgparser.config.get('SERIAL_BAUD', 0.))
|
||||
if mcu_baud:
|
||||
baud_adjust = self.BITS_PER_BYTE / mcu_baud
|
||||
self.ffi_lib.serialqueue_set_baud_adjust(
|
||||
self.serialqueue, baud_adjust)
|
||||
get_status = msgparser.lookup_command('get_status')
|
||||
self.status_cmd = get_status.encode()
|
||||
def connect_file(self, debugoutput, dictionary, pace=False):
|
||||
self.ser = debugoutput
|
||||
self.msgparser.process_identify(dictionary, decompress=False)
|
||||
est_clock = 1000000000000.
|
||||
if pace:
|
||||
est_clock = float(self.msgparser.config['CLOCK_FREQ'])
|
||||
self.serialqueue = self.ffi_lib.serialqueue_alloc(self.ser.fileno(), 1)
|
||||
self.est_clock = est_clock
|
||||
self.last_ack_time = time.time()
|
||||
self.last_ack_clock = 0
|
||||
def set_clock_est(self, freq, last_time, last_clock):
|
||||
self.ffi_lib.serialqueue_set_clock_est(
|
||||
self.serialqueue, self.est_clock, self.last_ack_time
|
||||
, self.last_ack_clock)
|
||||
self.serialqueue, freq, last_time, last_clock)
|
||||
def disconnect(self):
|
||||
if self.serialqueue is None:
|
||||
return
|
||||
if self.serialqueue is not None:
|
||||
self.ffi_lib.serialqueue_exit(self.serialqueue)
|
||||
if self.background_thread is not None:
|
||||
self.background_thread.join()
|
||||
self.ffi_lib.serialqueue_free(self.serialqueue)
|
||||
self.background_thread = self.serialqueue = None
|
||||
if self.ser is not None:
|
||||
self.ser.close()
|
||||
self.ser = None
|
||||
def stats(self, eventtime):
|
||||
if self.serialqueue is None:
|
||||
return ""
|
||||
sqstats = self.ffi_lib.serialqueue_get_stats(
|
||||
self.ffi_lib.serialqueue_get_stats(
|
||||
self.serialqueue, self.stats_buf, len(self.stats_buf))
|
||||
sqstats = self.ffi_main.string(self.stats_buf)
|
||||
tstats = " est_clock=%.3f last_ack_time=%.3f last_ack_clock=%d" % (
|
||||
self.est_clock, self.last_ack_time, self.last_ack_clock)
|
||||
return sqstats + tstats
|
||||
def _status_event(self, eventtime):
|
||||
if self.status_cmd is None:
|
||||
return eventtime + 0.1
|
||||
self.send(self.status_cmd)
|
||||
return eventtime + 1.0
|
||||
return self.ffi_main.string(self.stats_buf)
|
||||
# Serial response callbacks
|
||||
def register_callback(self, callback, name, oid=None):
|
||||
with self.lock:
|
||||
@@ -138,21 +124,6 @@ class SerialReader:
|
||||
def unregister_callback(self, name, oid=None):
|
||||
with self.lock:
|
||||
del self.handlers[name, oid]
|
||||
# Clock tracking
|
||||
def get_clock(self, eventtime):
|
||||
with self.lock:
|
||||
return int(self.last_ack_clock
|
||||
+ (eventtime - self.last_ack_time) * self.est_clock)
|
||||
def translate_clock(self, raw_clock):
|
||||
with self.lock:
|
||||
last_ack_clock = self.last_ack_clock
|
||||
clock_diff = (last_ack_clock - raw_clock) & 0xffffffff
|
||||
if clock_diff & 0x80000000:
|
||||
return last_ack_clock + 0x100000000 - clock_diff
|
||||
return last_ack_clock - clock_diff
|
||||
def get_last_clock(self):
|
||||
with self.lock:
|
||||
return self.last_ack_clock, self.last_ack_time
|
||||
# Command sending
|
||||
def send(self, cmd, minclock=0, reqclock=0, cq=None):
|
||||
if cq is None:
|
||||
@@ -162,80 +133,64 @@ class SerialReader:
|
||||
def encode_and_send(self, data, minclock, reqclock, cq):
|
||||
self.ffi_lib.serialqueue_encode_and_send(
|
||||
self.serialqueue, cq, data, len(data), minclock, reqclock)
|
||||
def send_with_response(self, cmd, name):
|
||||
src = SerialRetryCommand(self, cmd, name)
|
||||
def send_with_response(self, cmd, name, oid=None):
|
||||
src = SerialRetryCommand(self, cmd, name, oid)
|
||||
return src.get_response()
|
||||
def send_flush(self):
|
||||
self.ffi_lib.serialqueue_flush_ready(self.serialqueue)
|
||||
def alloc_command_queue(self):
|
||||
return self.ffi_main.gc(self.ffi_lib.serialqueue_alloc_commandqueue(),
|
||||
self.ffi_lib.serialqueue_free_commandqueue)
|
||||
# Dumping debug lists
|
||||
def dump_debug(self):
|
||||
out = []
|
||||
out.append("Dumping serial stats: %s" % (
|
||||
self.stats(self.reactor.monotonic()),))
|
||||
sdata = self.ffi_main.new('struct pull_queue_message[1024]')
|
||||
rdata = self.ffi_main.new('struct pull_queue_message[1024]')
|
||||
scount = self.ffi_lib.serialqueue_extract_old(
|
||||
self.serialqueue, 1, sdata, len(sdata))
|
||||
rcount = self.ffi_lib.serialqueue_extract_old(
|
||||
self.serialqueue, 0, rdata, len(rdata))
|
||||
logging.info("Dumping send queue %d messages" % (scount,))
|
||||
out.append("Dumping send queue %d messages" % (scount,))
|
||||
for i in range(scount):
|
||||
msg = sdata[i]
|
||||
cmds = self.msgparser.dump(msg.msg[0:msg.len])
|
||||
logging.info("Sent %d %f %f %d: %s" % (
|
||||
out.append("Sent %d %f %f %d: %s" % (
|
||||
i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds)))
|
||||
logging.info("Dumping receive queue %d messages" % (rcount,))
|
||||
out.append("Dumping receive queue %d messages" % (rcount,))
|
||||
for i in range(rcount):
|
||||
msg = rdata[i]
|
||||
cmds = self.msgparser.dump(msg.msg[0:msg.len])
|
||||
logging.info("Receive: %d %f %f %d: %s" % (
|
||||
out.append("Receive: %d %f %f %d: %s" % (
|
||||
i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds)))
|
||||
return '\n'.join(out)
|
||||
# Default message handlers
|
||||
def handle_status(self, params):
|
||||
with self.lock:
|
||||
# Update last_ack_time / last_ack_clock
|
||||
ack_clock = (self.last_ack_clock & ~0xffffffff) | params['clock']
|
||||
if ack_clock < self.last_ack_clock:
|
||||
ack_clock += 0x100000000
|
||||
sent_time = params['#sent_time']
|
||||
self.last_ack_time = receive_time = params['#receive_time']
|
||||
self.last_ack_clock = ack_clock
|
||||
# Update est_clock (if applicable)
|
||||
if receive_time > self.last_ack_rtt_time + 1. and sent_time:
|
||||
if self.last_ack_rtt_time:
|
||||
timedelta = receive_time - self.last_ack_rtt_time
|
||||
clockdelta = ack_clock - self.last_ack_rtt_clock
|
||||
estclock = clockdelta / timedelta
|
||||
if estclock > self.est_clock and self.est_clock:
|
||||
self.est_clock = (self.est_clock * 63. + estclock) / 64.
|
||||
else:
|
||||
self.est_clock = estclock
|
||||
self.last_ack_rtt_time = sent_time
|
||||
self.last_ack_rtt_clock = ack_clock
|
||||
self.ffi_lib.serialqueue_set_clock_est(
|
||||
self.serialqueue, self.est_clock, receive_time, ack_clock)
|
||||
def handle_unknown(self, params):
|
||||
logging.warn("Unknown message type %d: %s" % (
|
||||
params['#msgid'], repr(params['#msg'])))
|
||||
logging.warn("Unknown message type %d: %s",
|
||||
params['#msgid'], repr(params['#msg']))
|
||||
def handle_output(self, params):
|
||||
logging.info("%s: %s" % (params['#name'], params['#msg']))
|
||||
logging.info("%s: %s", params['#name'], params['#msg'])
|
||||
def handle_default(self, params):
|
||||
logging.warn("got %s" % (params,))
|
||||
logging.warn("got %s", params)
|
||||
def __del__(self):
|
||||
self.disconnect()
|
||||
|
||||
# Class to retry sending of a query command until a given response is received
|
||||
class SerialRetryCommand:
|
||||
TIMEOUT_TIME = 5.0
|
||||
RETRY_TIME = 0.500
|
||||
def __init__(self, serial, cmd, name):
|
||||
def __init__(self, serial, cmd, name, oid=None):
|
||||
self.serial = serial
|
||||
self.cmd = cmd
|
||||
self.name = name
|
||||
self.oid = oid
|
||||
self.response = None
|
||||
self.min_query_time = time.time()
|
||||
self.serial.register_callback(self.handle_callback, self.name)
|
||||
self.min_query_time = self.serial.reactor.monotonic()
|
||||
self.serial.register_callback(self.handle_callback, self.name, self.oid)
|
||||
self.send_timer = self.serial.reactor.register_timer(
|
||||
self.send_event, self.serial.reactor.NOW)
|
||||
def unregister(self):
|
||||
self.serial.unregister_callback(self.name, self.oid)
|
||||
self.serial.reactor.unregister_timer(self.send_timer)
|
||||
def send_event(self, eventtime):
|
||||
if self.response is not None:
|
||||
return self.serial.reactor.NEVER
|
||||
@@ -246,11 +201,13 @@ class SerialRetryCommand:
|
||||
if last_sent_time >= self.min_query_time:
|
||||
self.response = params
|
||||
def get_response(self):
|
||||
eventtime = time.time()
|
||||
eventtime = self.serial.reactor.monotonic()
|
||||
while self.response is None:
|
||||
eventtime = self.serial.reactor.pause(eventtime + 0.05)
|
||||
self.serial.unregister_callback(self.name)
|
||||
self.serial.reactor.unregister_timer(self.send_timer)
|
||||
if eventtime > self.min_query_time + self.TIMEOUT_TIME:
|
||||
self.unregister()
|
||||
raise error("Timeout on wait for '%s' response" % (self.name,))
|
||||
self.unregister()
|
||||
return self.response
|
||||
|
||||
# Code to start communication and download message type dictionary
|
||||
@@ -267,7 +224,7 @@ class SerialBootStrap:
|
||||
self.send_timer = self.serial.reactor.register_timer(
|
||||
self.send_event, self.serial.reactor.NOW)
|
||||
def get_identify_data(self, timeout):
|
||||
eventtime = time.time()
|
||||
eventtime = self.serial.reactor.monotonic()
|
||||
while not self.is_done and eventtime <= timeout:
|
||||
eventtime = self.serial.reactor.pause(eventtime + 0.05)
|
||||
self.serial.unregister_callback('identify_response')
|
||||
@@ -292,8 +249,8 @@ class SerialBootStrap:
|
||||
self.serial.send(imsg)
|
||||
return eventtime + self.RETRY_TIME
|
||||
def handle_unknown(self, params):
|
||||
logging.debug("Unknown message %d (len %d) while identifying" % (
|
||||
params['#msgid'], len(params['#msg'])))
|
||||
logging.debug("Unknown message %d (len %d) while identifying",
|
||||
params['#msgid'], len(params['#msg']))
|
||||
|
||||
# Attempt to place an AVR stk500v2 style programmer into normal mode
|
||||
def stk500v2_leave(ser, reactor):
|
||||
@@ -305,10 +262,23 @@ def stk500v2_leave(ser, reactor):
|
||||
ser.read(1)
|
||||
# Send stk500v2 leave programmer sequence
|
||||
ser.baudrate = 115200
|
||||
reactor.pause(time.time() + 0.100)
|
||||
reactor.pause(reactor.monotonic() + 0.100)
|
||||
ser.read(4096)
|
||||
ser.write('\x1b\x01\x00\x01\x0e\x11\x04')
|
||||
reactor.pause(time.time() + 0.050)
|
||||
reactor.pause(reactor.monotonic() + 0.050)
|
||||
res = ser.read(4096)
|
||||
logging.debug("Got %s from stk500v2" % (repr(res),))
|
||||
logging.debug("Got %s from stk500v2", repr(res))
|
||||
ser.baudrate = origbaud
|
||||
|
||||
# Attempt an arduino style reset on a serial port
|
||||
def arduino_reset(serialport, reactor):
|
||||
# First try opening the port at a different baud
|
||||
ser = serial.Serial(serialport, 2400, timeout=0)
|
||||
ser.read(1)
|
||||
reactor.pause(reactor.monotonic() + 0.100)
|
||||
# Then toggle DTR
|
||||
ser.dtr = True
|
||||
reactor.pause(reactor.monotonic() + 0.100)
|
||||
ser.dtr = False
|
||||
reactor.pause(reactor.monotonic() + 0.100)
|
||||
ser.close()
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// clock times, prioritizes commands, and handles retransmissions. A
|
||||
// background thread is launched to do this work and minimize latency.
|
||||
|
||||
#include <fcntl.h> // fcntl
|
||||
#include <math.h> // ceil
|
||||
#include <poll.h> // poll
|
||||
#include <pthread.h> // pthread_mutex_lock
|
||||
@@ -23,7 +24,7 @@
|
||||
#include <termios.h> // tcflush
|
||||
#include <unistd.h> // pipe
|
||||
#include "list.h" // list_add_tail
|
||||
#include "pyhelper.h" // get_time
|
||||
#include "pyhelper.h" // get_monotonic
|
||||
#include "serialqueue.h" // struct queue_message
|
||||
|
||||
|
||||
@@ -102,14 +103,12 @@ pollreactor_add_timer(struct pollreactor *pr, int pos, void *callback)
|
||||
pr->timers[pos].waketime = PR_NEVER;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Return the last schedule wake-up time for a timer
|
||||
static double
|
||||
pollreactor_get_timer(struct pollreactor *pr, int pos)
|
||||
{
|
||||
return pr->timers[pos].waketime;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set the wake-up time for a given timer
|
||||
static void
|
||||
@@ -149,11 +148,11 @@ static void
|
||||
pollreactor_run(struct pollreactor *pr)
|
||||
{
|
||||
pr->must_exit = 0;
|
||||
double eventtime = get_time();
|
||||
double eventtime = get_monotonic();
|
||||
while (! pr->must_exit) {
|
||||
int timeout = pollreactor_check_timers(pr, eventtime);
|
||||
int ret = poll(pr->fds, pr->num_fds, timeout);
|
||||
eventtime = get_time();
|
||||
eventtime = get_monotonic();
|
||||
if (ret > 0) {
|
||||
int i;
|
||||
for (i=0; i<pr->num_fds; i++)
|
||||
@@ -180,6 +179,22 @@ pollreactor_is_exit(struct pollreactor *pr)
|
||||
return pr->must_exit;
|
||||
}
|
||||
|
||||
static int
|
||||
set_non_blocking(int fd)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
report_errno("fcntl getfl", flags);
|
||||
return -1;
|
||||
}
|
||||
int ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (ret < 0) {
|
||||
report_errno("fcntl setfl", flags);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Serial protocol helpers
|
||||
@@ -344,19 +359,18 @@ struct serialqueue {
|
||||
int receive_waiting;
|
||||
// Baud / clock tracking
|
||||
double baud_adjust, idle_time;
|
||||
double est_clock, last_ack_time;
|
||||
uint64_t last_ack_clock;
|
||||
double est_freq, last_clock_time;
|
||||
uint64_t last_clock;
|
||||
double last_receive_sent_time;
|
||||
// Retransmit support
|
||||
uint64_t send_seq, receive_seq;
|
||||
uint64_t retransmit_seq, rtt_sample_seq;
|
||||
uint64_t ignore_nak_seq, retransmit_seq, rtt_sample_seq;
|
||||
struct list_head sent_queue;
|
||||
double srtt, rttvar, rto;
|
||||
// Pending transmission message queues
|
||||
struct list_head pending_queues;
|
||||
int ready_bytes, stalled_bytes;
|
||||
int ready_bytes, stalled_bytes, need_ack_bytes;
|
||||
uint64_t need_kick_clock;
|
||||
int can_delay_writes;
|
||||
// Received messages
|
||||
struct list_head receive_queue;
|
||||
// Debugging
|
||||
@@ -375,7 +389,6 @@ struct serialqueue {
|
||||
|
||||
#define MIN_RTO 0.025
|
||||
#define MAX_RTO 5.000
|
||||
#define MAX_SERIAL_BUFFER 0.050
|
||||
#define MIN_REQTIME_DELTA 0.250
|
||||
#define IDLE_QUERY_TIME 1.0
|
||||
|
||||
@@ -428,23 +441,31 @@ static void
|
||||
update_receive_seq(struct serialqueue *sq, double eventtime, uint64_t rseq)
|
||||
{
|
||||
// Remove from sent queue
|
||||
int ack_count = rseq - sq->receive_seq;
|
||||
uint64_t sent_seq = sq->receive_seq;
|
||||
while (!list_empty(&sq->sent_queue) && ack_count--) {
|
||||
for (;;) {
|
||||
struct queue_message *sent = list_first_entry(
|
||||
&sq->sent_queue, struct queue_message, node);
|
||||
if (rseq == ++sent_seq)
|
||||
sq->last_receive_sent_time = sent->receive_time;
|
||||
if (list_empty(&sq->sent_queue)) {
|
||||
// Got an ack for a message not sent; must be connection init
|
||||
sq->send_seq = rseq;
|
||||
sq->last_receive_sent_time = 0.;
|
||||
break;
|
||||
}
|
||||
sq->need_ack_bytes -= sent->len;
|
||||
list_del(&sent->node);
|
||||
debug_queue_add(&sq->old_sent, sent);
|
||||
sent_seq++;
|
||||
if (rseq == sent_seq) {
|
||||
// Found sent message corresponding with the received sequence
|
||||
sq->last_receive_sent_time = sent->receive_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sq->receive_seq = rseq;
|
||||
if (rseq > sq->send_seq)
|
||||
sq->send_seq = rseq;
|
||||
pollreactor_update_timer(&sq->pr, SQPT_COMMAND, PR_NOW);
|
||||
|
||||
// Update retransmit info
|
||||
if (sq->rtt_sample_seq && rseq >= sq->rtt_sample_seq
|
||||
if (sq->rtt_sample_seq && rseq > sq->rtt_sample_seq
|
||||
&& sq->last_receive_sent_time) {
|
||||
// RFC6298 rtt calculations
|
||||
double delta = eventtime - sq->last_receive_sent_time;
|
||||
@@ -488,15 +509,18 @@ handle_message(struct serialqueue *sq, double eventtime, int len)
|
||||
if (rseq != sq->receive_seq)
|
||||
// New sequence number
|
||||
update_receive_seq(sq, eventtime, rseq);
|
||||
else if (len == MESSAGE_MIN && rseq > sq->retransmit_seq)
|
||||
else if (len == MESSAGE_MIN && rseq > sq->ignore_nak_seq
|
||||
&& !list_empty(&sq->sent_queue))
|
||||
// Duplicate sequence number in an empty message is a nak
|
||||
pollreactor_update_timer(&sq->pr, SQPT_RETRANSMIT, PR_NOW);
|
||||
|
||||
if (len > MESSAGE_MIN) {
|
||||
// Add message to receive queue
|
||||
struct queue_message *qm = message_fill(sq->input_buf, len);
|
||||
qm->sent_time = sq->last_receive_sent_time;
|
||||
qm->receive_time = eventtime;
|
||||
qm->sent_time = (rseq > sq->retransmit_seq
|
||||
? sq->last_receive_sent_time : 0.);
|
||||
qm->receive_time = get_monotonic(); // must be time post read()
|
||||
qm->receive_time -= sq->baud_adjust * len;
|
||||
list_add_tail(&qm->node, &sq->receive_queue);
|
||||
check_wake_receive(sq);
|
||||
}
|
||||
@@ -561,12 +585,14 @@ retransmit_event(struct serialqueue *sq, double eventtime)
|
||||
|
||||
// Retransmit all pending messages
|
||||
uint8_t buf[MESSAGE_MAX * MESSAGE_SEQ_MASK + 1];
|
||||
int buflen = 0;
|
||||
int buflen = 0, first_buflen = 0;
|
||||
buf[buflen++] = MESSAGE_SYNC;
|
||||
struct queue_message *qm;
|
||||
list_for_each_entry(qm, &sq->sent_queue, node) {
|
||||
memcpy(&buf[buflen], qm->msg, qm->len);
|
||||
buflen += qm->len;
|
||||
if (!first_buflen)
|
||||
first_buflen = qm->len + 1;
|
||||
}
|
||||
ret = write(sq->serial_fd, buf, buflen);
|
||||
if (ret < 0)
|
||||
@@ -574,13 +600,23 @@ retransmit_event(struct serialqueue *sq, double eventtime)
|
||||
sq->bytes_retransmit += buflen;
|
||||
|
||||
// Update rto
|
||||
if (pollreactor_get_timer(&sq->pr, SQPT_RETRANSMIT) == PR_NOW) {
|
||||
// Retransmit due to nak
|
||||
sq->ignore_nak_seq = sq->receive_seq;
|
||||
if (sq->receive_seq < sq->retransmit_seq)
|
||||
// Second nak for this retransmit - don't allow third
|
||||
sq->ignore_nak_seq = sq->retransmit_seq;
|
||||
} else {
|
||||
// Retransmit due to timeout
|
||||
sq->rto *= 2.0;
|
||||
if (sq->rto > MAX_RTO)
|
||||
sq->rto = MAX_RTO;
|
||||
sq->ignore_nak_seq = sq->send_seq;
|
||||
}
|
||||
sq->retransmit_seq = sq->send_seq;
|
||||
sq->rtt_sample_seq = 0;
|
||||
sq->idle_time = eventtime + buflen * sq->baud_adjust;
|
||||
double waketime = sq->idle_time + sq->rto;
|
||||
double waketime = eventtime + first_buflen * sq->baud_adjust + sq->rto;
|
||||
|
||||
pthread_mutex_unlock(&sq->lock);
|
||||
return waketime;
|
||||
@@ -643,9 +679,10 @@ build_and_send_command(struct serialqueue *sq, double eventtime)
|
||||
if (list_empty(&sq->sent_queue))
|
||||
pollreactor_update_timer(&sq->pr, SQPT_RETRANSMIT
|
||||
, sq->idle_time + sq->rto);
|
||||
sq->send_seq++;
|
||||
if (!sq->rtt_sample_seq)
|
||||
sq->rtt_sample_seq = sq->send_seq;
|
||||
sq->send_seq++;
|
||||
sq->need_ack_bytes += out->len;
|
||||
list_add_tail(&out->node, &sq->sent_queue);
|
||||
}
|
||||
|
||||
@@ -653,10 +690,8 @@ build_and_send_command(struct serialqueue *sq, double eventtime)
|
||||
static double
|
||||
check_send_command(struct serialqueue *sq, double eventtime)
|
||||
{
|
||||
if (eventtime < sq->idle_time - MAX_SERIAL_BUFFER)
|
||||
// Serial port already busy
|
||||
return sq->idle_time - MAX_SERIAL_BUFFER;
|
||||
if (sq->send_seq - sq->receive_seq >= MESSAGE_SEQ_MASK
|
||||
if ((sq->send_seq - sq->receive_seq >= MESSAGE_SEQ_MASK
|
||||
|| (sq->need_ack_bytes - 2*MESSAGE_MAX) * sq->baud_adjust > sq->srtt)
|
||||
&& sq->receive_seq != (uint64_t)-1)
|
||||
// Need an ack before more messages can be sent
|
||||
return PR_NEVER;
|
||||
@@ -664,8 +699,9 @@ check_send_command(struct serialqueue *sq, double eventtime)
|
||||
// Check for stalled messages now ready
|
||||
double idletime = eventtime > sq->idle_time ? eventtime : sq->idle_time;
|
||||
idletime += MESSAGE_MIN * sq->baud_adjust;
|
||||
double timedelta = idletime - sq->last_ack_time;
|
||||
uint64_t ack_clock = (uint64_t)(timedelta * sq->est_clock) + sq->last_ack_clock;
|
||||
double timedelta = idletime - sq->last_clock_time;
|
||||
uint64_t ack_clock = ((uint64_t)(timedelta * sq->est_freq)
|
||||
+ sq->last_clock);
|
||||
uint64_t min_stalled_clock = MAX_CLOCK, min_ready_clock = MAX_CLOCK;
|
||||
struct command_queue *cq;
|
||||
list_for_each_entry(cq, &sq->pending_queues, node) {
|
||||
@@ -695,22 +731,20 @@ check_send_command(struct serialqueue *sq, double eventtime)
|
||||
// Check for messages to send
|
||||
if (sq->ready_bytes >= MESSAGE_PAYLOAD_MAX)
|
||||
return PR_NOW;
|
||||
if (! sq->can_delay_writes) {
|
||||
if (! sq->est_freq) {
|
||||
if (sq->ready_bytes)
|
||||
return PR_NOW;
|
||||
if (sq->est_clock)
|
||||
sq->can_delay_writes = 1;
|
||||
sq->need_kick_clock = MAX_CLOCK;
|
||||
return PR_NEVER;
|
||||
}
|
||||
uint64_t reqclock_delta = MIN_REQTIME_DELTA * sq->est_clock;
|
||||
uint64_t reqclock_delta = MIN_REQTIME_DELTA * sq->est_freq;
|
||||
if (min_ready_clock <= ack_clock + reqclock_delta)
|
||||
return PR_NOW;
|
||||
uint64_t wantclock = min_ready_clock - reqclock_delta;
|
||||
if (min_stalled_clock < wantclock)
|
||||
wantclock = min_stalled_clock;
|
||||
sq->need_kick_clock = wantclock;
|
||||
return idletime + (wantclock - ack_clock) / sq->est_clock;
|
||||
return idletime + (wantclock - ack_clock) / sq->est_freq;
|
||||
}
|
||||
|
||||
// Callback timer to send data to the serial port
|
||||
@@ -761,6 +795,9 @@ serialqueue_alloc(int serial_fd, int write_only)
|
||||
pollreactor_add_fd(&sq->pr, SQPF_PIPE, sq->pipe_fds[0], kick_event);
|
||||
pollreactor_add_timer(&sq->pr, SQPT_RETRANSMIT, retransmit_event);
|
||||
pollreactor_add_timer(&sq->pr, SQPT_COMMAND, command_event);
|
||||
set_non_blocking(serial_fd);
|
||||
set_non_blocking(sq->pipe_fds[0]);
|
||||
set_non_blocking(sq->pipe_fds[1]);
|
||||
|
||||
// Retransmit setup
|
||||
sq->send_seq = 1;
|
||||
@@ -976,26 +1013,16 @@ serialqueue_set_baud_adjust(struct serialqueue *sq, double baud_adjust)
|
||||
// Set the estimated clock rate of the mcu on the other end of the
|
||||
// serial port
|
||||
void
|
||||
serialqueue_set_clock_est(struct serialqueue *sq, double est_clock
|
||||
, double last_ack_time, uint64_t last_ack_clock)
|
||||
serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
||||
, double last_clock_time, uint64_t last_clock)
|
||||
{
|
||||
pthread_mutex_lock(&sq->lock);
|
||||
sq->est_clock = est_clock;
|
||||
sq->last_ack_time = last_ack_time;
|
||||
sq->last_ack_clock = last_ack_clock;
|
||||
sq->est_freq = est_freq;
|
||||
sq->last_clock_time = last_clock_time;
|
||||
sq->last_clock = last_clock;
|
||||
pthread_mutex_unlock(&sq->lock);
|
||||
}
|
||||
|
||||
// Flush all messages in a "ready" state
|
||||
void
|
||||
serialqueue_flush_ready(struct serialqueue *sq)
|
||||
{
|
||||
pthread_mutex_lock(&sq->lock);
|
||||
sq->can_delay_writes = 0;
|
||||
pthread_mutex_unlock(&sq->lock);
|
||||
kick_bg_thread(sq);
|
||||
}
|
||||
|
||||
// Return a string buffer containing statistics for the serial port
|
||||
void
|
||||
serialqueue_get_stats(struct serialqueue *sq, char *buf, int len)
|
||||
|
||||
@@ -59,9 +59,8 @@ void serialqueue_encode_and_send(struct serialqueue *sq, struct command_queue *c
|
||||
, uint64_t min_clock, uint64_t req_clock);
|
||||
void serialqueue_pull(struct serialqueue *sq, struct pull_queue_message *pqm);
|
||||
void serialqueue_set_baud_adjust(struct serialqueue *sq, double baud_adjust);
|
||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_clock
|
||||
, double last_ack_time, uint64_t last_ack_clock);
|
||||
void serialqueue_flush_ready(struct serialqueue *sq);
|
||||
void serialqueue_set_clock_est(struct serialqueue *sq, double est_freq
|
||||
, double last_clock_time, uint64_t last_clock);
|
||||
void serialqueue_get_stats(struct serialqueue *sq, char *buf, int len);
|
||||
int serialqueue_extract_old(struct serialqueue *sq, int sentq
|
||||
, struct pull_queue_message *q, int max);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Stepper pulse schedule compression
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
//
|
||||
@@ -28,55 +28,18 @@
|
||||
|
||||
struct stepcompress {
|
||||
// Buffer management
|
||||
uint64_t *queue, *queue_end, *queue_pos, *queue_next;
|
||||
uint32_t *queue, *queue_end, *queue_pos, *queue_next;
|
||||
// Internal tracking
|
||||
uint32_t max_error;
|
||||
// Error checking
|
||||
uint32_t errors;
|
||||
double mcu_time_offset, mcu_freq;
|
||||
// Message generation
|
||||
uint64_t last_step_clock;
|
||||
uint64_t last_step_clock, homing_clock;
|
||||
struct list_head msg_queue;
|
||||
uint32_t queue_step_msgid, set_next_step_dir_msgid, oid;
|
||||
int sdir, invert_sdir;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Queue management
|
||||
****************************************************************/
|
||||
|
||||
// Shuffle the internal queue to avoid having to allocate more ram
|
||||
static void
|
||||
clean_queue(struct stepcompress *sc)
|
||||
{
|
||||
int in_use = sc->queue_next - sc->queue_pos;
|
||||
memmove(sc->queue, sc->queue_pos, in_use * sizeof(*sc->queue));
|
||||
sc->queue_pos = sc->queue;
|
||||
sc->queue_next = sc->queue + in_use;
|
||||
}
|
||||
|
||||
// Expand the internal queue of step times
|
||||
static void
|
||||
expand_queue(struct stepcompress *sc, int count)
|
||||
{
|
||||
int alloc = sc->queue_end - sc->queue;
|
||||
if (count + sc->queue_next - sc->queue_pos <= alloc) {
|
||||
clean_queue(sc);
|
||||
return;
|
||||
}
|
||||
int pos = sc->queue_pos - sc->queue;
|
||||
int next = sc->queue_next - sc->queue;
|
||||
if (!alloc)
|
||||
alloc = QUEUE_START_SIZE;
|
||||
while (next + count > alloc)
|
||||
alloc *= 2;
|
||||
sc->queue = realloc(sc->queue, alloc * sizeof(*sc->queue));
|
||||
sc->queue_end = sc->queue + alloc;
|
||||
sc->queue_pos = sc->queue + pos;
|
||||
sc->queue_next = sc->queue + next;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Step compression
|
||||
****************************************************************/
|
||||
@@ -102,10 +65,10 @@ struct points {
|
||||
// Given a requested step time, return the minimum and maximum
|
||||
// acceptable times
|
||||
static inline struct points
|
||||
minmax_point(struct stepcompress *sc, uint64_t *pos)
|
||||
minmax_point(struct stepcompress *sc, uint32_t *pos)
|
||||
{
|
||||
uint32_t prevpoint = pos > sc->queue_pos ? *(pos-1) - sc->last_step_clock : 0;
|
||||
uint32_t point = *pos - sc->last_step_clock;
|
||||
uint32_t lsc = sc->last_step_clock, point = *pos - lsc;
|
||||
uint32_t prevpoint = pos > sc->queue_pos ? *(pos-1) - lsc : 0;
|
||||
uint32_t max_error = (point - prevpoint) / 2;
|
||||
if (max_error > sc->max_error)
|
||||
max_error = sc->max_error;
|
||||
@@ -128,6 +91,9 @@ struct step_move {
|
||||
static struct step_move
|
||||
compress_bisect_add(struct stepcompress *sc)
|
||||
{
|
||||
uint32_t *qlast = sc->queue_next;
|
||||
if (qlast > sc->queue_pos + 65535)
|
||||
qlast = sc->queue_pos + 65535;
|
||||
struct points point = minmax_point(sc, sc->queue_pos);
|
||||
int32_t outer_mininterval = point.minp, outer_maxinterval = point.maxp;
|
||||
int32_t add = 0, minadd = -0x8000, maxadd = 0x7fff;
|
||||
@@ -142,10 +108,7 @@ compress_bisect_add(struct stepcompress *sc)
|
||||
int32_t nextcount = 1;
|
||||
for (;;) {
|
||||
nextcount++;
|
||||
if (nextcount > bestcount
|
||||
&& (&sc->queue_pos[nextcount-1] >= sc->queue_next
|
||||
|| sc->queue_pos[nextcount-1] >= sc->last_step_clock+(3<<28)
|
||||
|| nextcount > 65535)) {
|
||||
if (&sc->queue_pos[nextcount-1] >= qlast) {
|
||||
int32_t count = nextcount - 1;
|
||||
return (struct step_move){ interval, count, add };
|
||||
}
|
||||
@@ -174,6 +137,9 @@ compress_bisect_add(struct stepcompress *sc)
|
||||
zerointerval = interval;
|
||||
zerocount = count;
|
||||
}
|
||||
if (count > 0x200)
|
||||
// No 'add' will improve sequence; avoid integer overflow
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if a greater or lesser add could extend the sequence
|
||||
@@ -221,27 +187,19 @@ compress_bisect_add(struct stepcompress *sc)
|
||||
* Step compress checking
|
||||
****************************************************************/
|
||||
|
||||
#define ERROR_RET -989898989
|
||||
|
||||
// Verify that a given 'step_move' matches the actual step times
|
||||
static void
|
||||
static int
|
||||
check_line(struct stepcompress *sc, struct step_move move)
|
||||
{
|
||||
if (!CHECK_LINES)
|
||||
return;
|
||||
if (move.count == 1) {
|
||||
if (move.interval != (uint32_t)(*sc->queue_pos - sc->last_step_clock)
|
||||
|| *sc->queue_pos < sc->last_step_clock) {
|
||||
errorf("Count 1 point out of range: %d %d %d"
|
||||
, move.interval, move.count, move.add);
|
||||
sc->errors++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
int err = 0;
|
||||
if (!move.count || (!move.interval && !move.add)
|
||||
return 0;
|
||||
if (!move.count || (!move.interval && !move.add && move.count > 1)
|
||||
|| move.interval >= 0x80000000) {
|
||||
errorf("Point out of range: %d %d %d"
|
||||
, move.interval, move.count, move.add);
|
||||
err++;
|
||||
errorf("stepcompress o=%d i=%d c=%d a=%d: Invalid sequence"
|
||||
, sc->oid, move.interval, move.count, move.add);
|
||||
return ERROR_RET;
|
||||
}
|
||||
uint32_t interval = move.interval, p = 0;
|
||||
uint16_t i;
|
||||
@@ -249,18 +207,21 @@ check_line(struct stepcompress *sc, struct step_move move)
|
||||
struct points point = minmax_point(sc, sc->queue_pos + i);
|
||||
p += interval;
|
||||
if (p < point.minp || p > point.maxp) {
|
||||
errorf("Point %d of %d: %d not in %d:%d"
|
||||
, i+1, move.count, p, point.minp, point.maxp);
|
||||
err++;
|
||||
errorf("stepcompress o=%d i=%d c=%d a=%d: Point %d: %d not in %d:%d"
|
||||
, sc->oid, move.interval, move.count, move.add
|
||||
, i+1, p, point.minp, point.maxp);
|
||||
return ERROR_RET;
|
||||
}
|
||||
if (interval >= 0x80000000) {
|
||||
errorf("Point %d of %d: interval overflow %d"
|
||||
, i+1, move.count, interval);
|
||||
err++;
|
||||
errorf("stepcompress o=%d i=%d c=%d a=%d:"
|
||||
" Point %d: interval overflow %d"
|
||||
, sc->oid, move.interval, move.count, move.add
|
||||
, i+1, interval);
|
||||
return ERROR_RET;
|
||||
}
|
||||
interval += move.add;
|
||||
}
|
||||
sc->errors += err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,22 +229,6 @@ check_line(struct stepcompress *sc, struct step_move move)
|
||||
* Step compress interface
|
||||
****************************************************************/
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
|
||||
// Wrapper around sqrt() to handle small negative numbers
|
||||
static double
|
||||
_safe_sqrt(double v)
|
||||
{
|
||||
if (v > -0.001)
|
||||
// Due to floating point truncation, it's possible to get a
|
||||
// small negative number - treat it as zero.
|
||||
return 0.;
|
||||
return sqrt(v);
|
||||
}
|
||||
static inline double safe_sqrt(double v) {
|
||||
return likely(v >= 0.) ? sqrt(v) : _safe_sqrt(v);
|
||||
}
|
||||
|
||||
// Allocate a new 'stepcompress' object
|
||||
struct stepcompress *
|
||||
stepcompress_alloc(uint32_t max_error, uint32_t queue_step_msgid
|
||||
@@ -314,28 +259,28 @@ stepcompress_free(struct stepcompress *sc)
|
||||
}
|
||||
|
||||
// Convert previously scheduled steps into commands for the mcu
|
||||
static void
|
||||
static int
|
||||
stepcompress_flush(struct stepcompress *sc, uint64_t move_clock)
|
||||
{
|
||||
if (sc->queue_pos >= sc->queue_next)
|
||||
return;
|
||||
while (move_clock > sc->last_step_clock) {
|
||||
return 0;
|
||||
while (sc->last_step_clock < move_clock) {
|
||||
struct step_move move = compress_bisect_add(sc);
|
||||
check_line(sc, move);
|
||||
int ret = check_line(sc, move);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uint32_t msg[5] = {
|
||||
sc->queue_step_msgid, sc->oid, move.interval, move.count, move.add
|
||||
};
|
||||
struct queue_message *qm = message_alloc_and_encode(msg, 5);
|
||||
qm->min_clock = qm->req_clock = sc->last_step_clock;
|
||||
if (move.count == 1 && sc->last_step_clock + (1<<27) < *sc->queue_pos) {
|
||||
// Be careful with 32bit overflow
|
||||
sc->last_step_clock = qm->req_clock = *sc->queue_pos;
|
||||
} else {
|
||||
uint32_t addfactor = move.count*(move.count-1)/2;
|
||||
int32_t addfactor = move.count*(move.count-1)/2;
|
||||
uint32_t ticks = move.add*addfactor + move.interval*move.count;
|
||||
sc->last_step_clock += ticks;
|
||||
}
|
||||
if (sc->homing_clock)
|
||||
// When homing, all steps should be sent prior to homing_clock
|
||||
qm->min_clock = qm->req_clock = sc->homing_clock;
|
||||
list_add_tail(&qm->node, &sc->msg_queue);
|
||||
|
||||
if (sc->queue_pos + move.count >= sc->queue_next) {
|
||||
@@ -344,232 +289,415 @@ stepcompress_flush(struct stepcompress *sc, uint64_t move_clock)
|
||||
}
|
||||
sc->queue_pos += move.count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate a queue_step for a step far in the future from the last step
|
||||
static int
|
||||
stepcompress_flush_far(struct stepcompress *sc, uint64_t abs_step_clock)
|
||||
{
|
||||
uint32_t msg[5] = {
|
||||
sc->queue_step_msgid, sc->oid, abs_step_clock - sc->last_step_clock, 1, 0
|
||||
};
|
||||
struct queue_message *qm = message_alloc_and_encode(msg, 5);
|
||||
qm->min_clock = sc->last_step_clock;
|
||||
sc->last_step_clock = qm->req_clock = abs_step_clock;
|
||||
if (sc->homing_clock)
|
||||
// When homing, all steps should be sent prior to homing_clock
|
||||
qm->min_clock = qm->req_clock = sc->homing_clock;
|
||||
list_add_tail(&qm->node, &sc->msg_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send the set_next_step_dir command
|
||||
static void
|
||||
static int
|
||||
set_next_step_dir(struct stepcompress *sc, int sdir)
|
||||
{
|
||||
if (sc->sdir == sdir)
|
||||
return 0;
|
||||
sc->sdir = sdir;
|
||||
stepcompress_flush(sc, UINT64_MAX);
|
||||
int ret = stepcompress_flush(sc, UINT64_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
uint32_t msg[3] = {
|
||||
sc->set_next_step_dir_msgid, sc->oid, sdir ^ sc->invert_sdir
|
||||
};
|
||||
struct queue_message *qm = message_alloc_and_encode(msg, 3);
|
||||
qm->req_clock = sc->last_step_clock;
|
||||
qm->req_clock = sc->homing_clock ?: sc->last_step_clock;
|
||||
list_add_tail(&qm->node, &sc->msg_queue);
|
||||
}
|
||||
|
||||
// Check if the internal queue needs to be expanded, and expand if so
|
||||
static inline void
|
||||
check_expand(struct stepcompress *sc, int sdir, int count)
|
||||
{
|
||||
if (sdir != sc->sdir)
|
||||
set_next_step_dir(sc, sdir);
|
||||
if (sc->queue_next + count > sc->queue_end)
|
||||
expand_queue(sc, count);
|
||||
}
|
||||
|
||||
// Schedule a step event at the specified step_clock time
|
||||
void
|
||||
stepcompress_push(struct stepcompress *sc, double step_clock, int32_t sdir)
|
||||
{
|
||||
sdir = !!sdir;
|
||||
check_expand(sc, sdir, 1);
|
||||
step_clock += 0.5;
|
||||
*sc->queue_next++ = step_clock;
|
||||
}
|
||||
|
||||
// Schedule 'steps' number of steps with a constant time between steps
|
||||
// using the formula: step_clock = clock_offset + step_num*factor
|
||||
int32_t
|
||||
stepcompress_push_factor(struct stepcompress *sc
|
||||
, double steps, double step_offset
|
||||
, double clock_offset, double factor)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
int sdir = 1;
|
||||
if (steps < 0) {
|
||||
sdir = 0;
|
||||
steps = -steps;
|
||||
step_offset = -step_offset;
|
||||
}
|
||||
int count = steps + .5 - step_offset;
|
||||
if (count <= 0 || count > 1000000) {
|
||||
if (count && steps)
|
||||
errorf("push_factor invalid count %d %f %f %f %f"
|
||||
, sc->oid, steps, step_offset, clock_offset, factor);
|
||||
return 0;
|
||||
}
|
||||
check_expand(sc, sdir, count);
|
||||
|
||||
// Calculate each step time
|
||||
uint64_t *qn = sc->queue_next, *end = &qn[count];
|
||||
clock_offset += 0.5;
|
||||
double pos = step_offset + .5;
|
||||
while (qn < end) {
|
||||
*qn++ = clock_offset + pos*factor;
|
||||
pos += 1.0;
|
||||
}
|
||||
sc->queue_next = qn;
|
||||
return sdir ? count : -count;
|
||||
}
|
||||
|
||||
// Schedule 'steps' number of steps using the formula:
|
||||
// step_clock = clock_offset + sqrt(step_num*factor + sqrt_offset)
|
||||
int32_t
|
||||
stepcompress_push_sqrt(struct stepcompress *sc, double steps, double step_offset
|
||||
, double clock_offset, double sqrt_offset, double factor)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
int sdir = 1;
|
||||
if (steps < 0) {
|
||||
sdir = 0;
|
||||
steps = -steps;
|
||||
step_offset = -step_offset;
|
||||
}
|
||||
int count = steps + .5 - step_offset;
|
||||
if (count <= 0 || count > 1000000) {
|
||||
if (count && steps)
|
||||
errorf("push_sqrt invalid count %d %f %f %f %f %f"
|
||||
, sc->oid, steps, step_offset, clock_offset, sqrt_offset
|
||||
, factor);
|
||||
return 0;
|
||||
}
|
||||
check_expand(sc, sdir, count);
|
||||
|
||||
// Calculate each step time
|
||||
uint64_t *qn = sc->queue_next, *end = &qn[count];
|
||||
clock_offset += 0.5;
|
||||
double pos = step_offset + .5 + sqrt_offset/factor;
|
||||
while (qn < end) {
|
||||
double v = safe_sqrt(pos*factor);
|
||||
*qn++ = clock_offset + (factor >= 0. ? v : -v);
|
||||
pos += 1.0;
|
||||
}
|
||||
sc->queue_next = qn;
|
||||
return sdir ? count : -count;
|
||||
}
|
||||
|
||||
// Schedule 'count' number of steps using the delta kinematic const speed
|
||||
int32_t
|
||||
stepcompress_push_delta_const(
|
||||
struct stepcompress *sc, double clock_offset, double dist, double start_pos
|
||||
, double inv_velocity, double step_dist
|
||||
, double height, double closestxy_d, double closest_height2, double movez_r)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
double movexy_r = movez_r ? sqrt(1. - movez_r*movez_r) : 1.;
|
||||
double reldist = closestxy_d - movexy_r*dist;
|
||||
double end_height = safe_sqrt(closest_height2 - reldist*reldist);
|
||||
int count = (end_height - height + movez_r*dist) / step_dist + .5;
|
||||
if (count <= 0 || count > 1000000) {
|
||||
if (count)
|
||||
errorf("push_delta_const invalid count %d %d %f %f %f %f %f %f %f %f"
|
||||
, sc->oid, count, clock_offset, dist, step_dist, start_pos
|
||||
, closest_height2, height, movez_r, inv_velocity);
|
||||
return 0;
|
||||
}
|
||||
check_expand(sc, step_dist > 0., count);
|
||||
|
||||
// Calculate each step time
|
||||
uint64_t *qn = sc->queue_next, *end = &qn[count];
|
||||
clock_offset += 0.5;
|
||||
start_pos += movexy_r*closestxy_d;
|
||||
height += .5 * step_dist;
|
||||
if (!movez_r) {
|
||||
// Optmized case for common XY only moves (no Z movement)
|
||||
while (qn < end) {
|
||||
double v = safe_sqrt(closest_height2 - height*height);
|
||||
double pos = start_pos + (step_dist > 0. ? -v : v);
|
||||
*qn++ = clock_offset + pos * inv_velocity;
|
||||
height += step_dist;
|
||||
}
|
||||
} else if (!movexy_r) {
|
||||
// Optmized case for Z only moves
|
||||
double v = (step_dist > 0. ? -end_height : end_height);
|
||||
while (qn < end) {
|
||||
double pos = start_pos + movez_r*height + v;
|
||||
*qn++ = clock_offset + pos * inv_velocity;
|
||||
height += step_dist;
|
||||
}
|
||||
} else {
|
||||
// General case (handles XY+Z moves)
|
||||
while (qn < end) {
|
||||
double relheight = movexy_r*height - movez_r*closestxy_d;
|
||||
double v = safe_sqrt(closest_height2 - relheight*relheight);
|
||||
double pos = start_pos + movez_r*height + (step_dist > 0. ? -v : v);
|
||||
*qn++ = clock_offset + pos * inv_velocity;
|
||||
height += step_dist;
|
||||
}
|
||||
}
|
||||
sc->queue_next = qn;
|
||||
return step_dist > 0. ? count : -count;
|
||||
}
|
||||
|
||||
// Schedule 'count' number of steps using delta kinematic acceleration
|
||||
int32_t
|
||||
stepcompress_push_delta_accel(
|
||||
struct stepcompress *sc, double clock_offset, double dist, double start_pos
|
||||
, double accel_multiplier, double step_dist
|
||||
, double height, double closestxy_d, double closest_height2, double movez_r)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
double movexy_r = movez_r ? sqrt(1. - movez_r*movez_r) : 1.;
|
||||
double reldist = closestxy_d - movexy_r*dist;
|
||||
double end_height = safe_sqrt(closest_height2 - reldist*reldist);
|
||||
int count = (end_height - height + movez_r*dist) / step_dist + .5;
|
||||
if (count <= 0 || count > 1000000) {
|
||||
if (count)
|
||||
errorf("push_delta_accel invalid count %d %d %f %f %f %f %f %f %f %f"
|
||||
, sc->oid, count, clock_offset, dist, step_dist, start_pos
|
||||
, closest_height2, height, movez_r, accel_multiplier);
|
||||
return 0;
|
||||
}
|
||||
check_expand(sc, step_dist > 0., count);
|
||||
|
||||
// Calculate each step time
|
||||
uint64_t *qn = sc->queue_next, *end = &qn[count];
|
||||
clock_offset += 0.5;
|
||||
start_pos += movexy_r*closestxy_d;
|
||||
height += .5 * step_dist;
|
||||
while (qn < end) {
|
||||
double relheight = movexy_r*height - movez_r*closestxy_d;
|
||||
double v = safe_sqrt(closest_height2 - relheight*relheight);
|
||||
double pos = start_pos + movez_r*height + (step_dist > 0. ? -v : v);
|
||||
v = safe_sqrt(pos * accel_multiplier);
|
||||
*qn++ = clock_offset + (accel_multiplier >= 0. ? v : -v);
|
||||
height += step_dist;
|
||||
}
|
||||
sc->queue_next = qn;
|
||||
return step_dist > 0. ? count : -count;
|
||||
}
|
||||
|
||||
// Reset the internal state of the stepcompress object
|
||||
void
|
||||
int
|
||||
stepcompress_reset(struct stepcompress *sc, uint64_t last_step_clock)
|
||||
{
|
||||
stepcompress_flush(sc, UINT64_MAX);
|
||||
int ret = stepcompress_flush(sc, UINT64_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
sc->last_step_clock = last_step_clock;
|
||||
sc->sdir = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Indicate the stepper is in homing mode (or done homing if zero)
|
||||
int
|
||||
stepcompress_set_homing(struct stepcompress *sc, uint64_t homing_clock)
|
||||
{
|
||||
int ret = stepcompress_flush(sc, UINT64_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
sc->homing_clock = homing_clock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Queue an mcu command to go out in order with stepper commands
|
||||
void
|
||||
int
|
||||
stepcompress_queue_msg(struct stepcompress *sc, uint32_t *data, int len)
|
||||
{
|
||||
stepcompress_flush(sc, UINT64_MAX);
|
||||
int ret = stepcompress_flush(sc, UINT64_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
struct queue_message *qm = message_alloc_and_encode(data, len);
|
||||
qm->req_clock = sc->last_step_clock;
|
||||
qm->req_clock = sc->homing_clock ?: sc->last_step_clock;
|
||||
list_add_tail(&qm->node, &sc->msg_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the count of internal errors found
|
||||
uint32_t
|
||||
stepcompress_get_errors(struct stepcompress *sc)
|
||||
// Set the conversion rate of 'print_time' to mcu clock
|
||||
static void
|
||||
stepcompress_set_time(struct stepcompress *sc
|
||||
, double time_offset, double mcu_freq)
|
||||
{
|
||||
return sc->errors;
|
||||
sc->mcu_time_offset = time_offset;
|
||||
sc->mcu_freq = mcu_freq;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Queue management
|
||||
****************************************************************/
|
||||
|
||||
struct queue_append {
|
||||
struct stepcompress *sc;
|
||||
uint32_t *qnext, *qend, last_step_clock_32;
|
||||
double clock_offset;
|
||||
};
|
||||
|
||||
// Maximium clock delta between messages in the queue
|
||||
#define CLOCK_DIFF_MAX (3<<28)
|
||||
|
||||
// Create a cursor for inserting clock times into the queue
|
||||
static inline struct queue_append
|
||||
queue_append_start(struct stepcompress *sc, double print_time, double adjust)
|
||||
{
|
||||
double print_clock = (print_time - sc->mcu_time_offset) * sc->mcu_freq;
|
||||
return (struct queue_append) {
|
||||
.sc = sc, .qnext = sc->queue_next, .qend = sc->queue_end,
|
||||
.last_step_clock_32 = sc->last_step_clock,
|
||||
.clock_offset = (print_clock - (double)sc->last_step_clock) + adjust };
|
||||
}
|
||||
|
||||
// Finalize a cursor created with queue_append_start()
|
||||
static inline void
|
||||
queue_append_finish(struct queue_append qa)
|
||||
{
|
||||
qa.sc->queue_next = qa.qnext;
|
||||
}
|
||||
|
||||
// Slow path for queue_append()
|
||||
static int
|
||||
queue_append_slow(struct stepcompress *sc, double rel_sc)
|
||||
{
|
||||
uint64_t abs_step_clock = (uint64_t)rel_sc + sc->last_step_clock;
|
||||
if (abs_step_clock >= sc->last_step_clock + CLOCK_DIFF_MAX) {
|
||||
// Avoid integer overflow on steps far in the future
|
||||
int ret = stepcompress_flush(sc, abs_step_clock - CLOCK_DIFF_MAX + 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (abs_step_clock >= sc->last_step_clock + CLOCK_DIFF_MAX)
|
||||
return stepcompress_flush_far(sc, abs_step_clock);
|
||||
}
|
||||
|
||||
if (sc->queue_next - sc->queue_pos > 65535 + 2000) {
|
||||
// No point in keeping more than 64K steps in memory
|
||||
int ret = stepcompress_flush(sc, *(sc->queue_next - 65535));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int in_use = sc->queue_next - sc->queue_pos;
|
||||
if (sc->queue_pos > sc->queue) {
|
||||
// Shuffle the internal queue to avoid having to allocate more ram
|
||||
memmove(sc->queue, sc->queue_pos, in_use * sizeof(*sc->queue));
|
||||
} else {
|
||||
// Expand the internal queue of step times
|
||||
int alloc = sc->queue_end - sc->queue;
|
||||
if (!alloc)
|
||||
alloc = QUEUE_START_SIZE;
|
||||
while (in_use >= alloc)
|
||||
alloc *= 2;
|
||||
sc->queue = realloc(sc->queue, alloc * sizeof(*sc->queue));
|
||||
sc->queue_end = sc->queue + alloc;
|
||||
}
|
||||
sc->queue_pos = sc->queue;
|
||||
sc->queue_next = sc->queue + in_use;
|
||||
*sc->queue_next++ = abs_step_clock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add a clock time to the queue (flushing the queue if needed)
|
||||
static inline int
|
||||
queue_append(struct queue_append *qa, double step_clock)
|
||||
{
|
||||
double rel_sc = step_clock + qa->clock_offset;
|
||||
if (likely(!(qa->qnext >= qa->qend || rel_sc >= (double)CLOCK_DIFF_MAX))) {
|
||||
*qa->qnext++ = qa->last_step_clock_32 + (uint32_t)rel_sc;
|
||||
return 0;
|
||||
}
|
||||
// Call queue_append_slow() to handle queue expansion and integer overflow
|
||||
struct stepcompress *sc = qa->sc;
|
||||
uint64_t old_last_step_clock = sc->last_step_clock;
|
||||
sc->queue_next = qa->qnext;
|
||||
int ret = queue_append_slow(sc, rel_sc);
|
||||
if (ret)
|
||||
return ret;
|
||||
qa->qnext = sc->queue_next;
|
||||
qa->qend = sc->queue_end;
|
||||
qa->last_step_clock_32 = sc->last_step_clock;
|
||||
qa->clock_offset -= sc->last_step_clock - old_last_step_clock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Motion to step conversions
|
||||
****************************************************************/
|
||||
|
||||
// Common suffixes: _sd is step distance (a unit length the same
|
||||
// distance the stepper moves on each step), _sv is step velocity (in
|
||||
// units of step distance per time), _sd2 is step distance squared, _r
|
||||
// is ratio (scalar usually between 0.0 and 1.0). Times are in
|
||||
// seconds and acceleration is in units of step distance per second
|
||||
// squared.
|
||||
|
||||
// Wrapper around sqrt() to handle small negative numbers
|
||||
static double
|
||||
_safe_sqrt(double v)
|
||||
{
|
||||
// Due to floating point truncation, it's possible to get a small
|
||||
// negative number - treat it as zero.
|
||||
if (v < -0.001)
|
||||
errorf("safe_sqrt of %.9f", v);
|
||||
return 0.;
|
||||
}
|
||||
static inline double safe_sqrt(double v) {
|
||||
return likely(v >= 0.) ? sqrt(v) : _safe_sqrt(v);
|
||||
}
|
||||
|
||||
// Schedule a step event at the specified step_clock time
|
||||
int32_t
|
||||
stepcompress_push(struct stepcompress *sc, double print_time, int32_t sdir)
|
||||
{
|
||||
int ret = set_next_step_dir(sc, !!sdir);
|
||||
if (ret)
|
||||
return ret;
|
||||
struct queue_append qa = queue_append_start(sc, print_time, 0.5);
|
||||
ret = queue_append(&qa, 0.);
|
||||
if (ret)
|
||||
return ret;
|
||||
queue_append_finish(qa);
|
||||
return sdir ? 1 : -1;
|
||||
}
|
||||
|
||||
// Schedule 'steps' number of steps at constant acceleration. If
|
||||
// acceleration is zero (ie, constant velocity) it uses the formula:
|
||||
// step_time = print_time + step_num/start_sv
|
||||
// Otherwise it uses the formula:
|
||||
// step_time = (print_time + sqrt(2*step_num/accel + (start_sv/accel)**2)
|
||||
// - start_sv/accel)
|
||||
int32_t
|
||||
stepcompress_push_const(
|
||||
struct stepcompress *sc, double print_time
|
||||
, double step_offset, double steps, double start_sv, double accel)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
int sdir = 1;
|
||||
if (steps < 0) {
|
||||
sdir = 0;
|
||||
steps = -steps;
|
||||
step_offset = -step_offset;
|
||||
}
|
||||
int count = steps + .5 - step_offset;
|
||||
if (count <= 0 || count > 10000000) {
|
||||
if (count && steps) {
|
||||
errorf("push_const invalid count %d %f %f %f %f %f"
|
||||
, sc->oid, print_time, step_offset, steps
|
||||
, start_sv, accel);
|
||||
return ERROR_RET;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int ret = set_next_step_dir(sc, sdir);
|
||||
if (ret)
|
||||
return ret;
|
||||
int res = sdir ? count : -count;
|
||||
|
||||
// Calculate each step time
|
||||
if (!accel) {
|
||||
// Move at constant velocity (zero acceleration)
|
||||
struct queue_append qa = queue_append_start(sc, print_time, .5);
|
||||
double inv_cruise_sv = sc->mcu_freq / start_sv;
|
||||
double pos = (step_offset + .5) * inv_cruise_sv;
|
||||
while (count--) {
|
||||
ret = queue_append(&qa, pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
pos += inv_cruise_sv;
|
||||
}
|
||||
queue_append_finish(qa);
|
||||
} else {
|
||||
// Move with constant acceleration
|
||||
double inv_accel = 1. / accel;
|
||||
double accel_time = start_sv * inv_accel * sc->mcu_freq;
|
||||
struct queue_append qa = queue_append_start(
|
||||
sc, print_time, 0.5 - accel_time);
|
||||
double accel_multiplier = 2. * inv_accel * sc->mcu_freq * sc->mcu_freq;
|
||||
double pos = (step_offset + .5)*accel_multiplier + accel_time*accel_time;
|
||||
while (count--) {
|
||||
double v = safe_sqrt(pos);
|
||||
int ret = queue_append(&qa, accel_multiplier >= 0. ? v : -v);
|
||||
if (ret)
|
||||
return ret;
|
||||
pos += accel_multiplier;
|
||||
}
|
||||
queue_append_finish(qa);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Schedule steps using delta kinematics
|
||||
static int32_t
|
||||
_stepcompress_push_delta(
|
||||
struct stepcompress *sc, int sdir
|
||||
, double print_time, double move_sd, double start_sv, double accel
|
||||
, double height, double startxy_sd, double arm_sd, double movez_r)
|
||||
{
|
||||
// Calculate number of steps to take
|
||||
double movexy_r = movez_r ? sqrt(1. - movez_r*movez_r) : 1.;
|
||||
double arm_sd2 = arm_sd * arm_sd;
|
||||
double endxy_sd = startxy_sd - movexy_r*move_sd;
|
||||
double end_height = safe_sqrt(arm_sd2 - endxy_sd*endxy_sd);
|
||||
int count = (end_height + movez_r*move_sd - height) * (sdir ? 1. : -1.) + .5;
|
||||
if (count <= 0 || count > 10000000) {
|
||||
if (count) {
|
||||
errorf("push_delta invalid count %d %d %f %f %f %f %f %f %f %f"
|
||||
, sc->oid, count, print_time, move_sd, start_sv, accel
|
||||
, height, startxy_sd, arm_sd, movez_r);
|
||||
return ERROR_RET;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int ret = set_next_step_dir(sc, sdir);
|
||||
if (ret)
|
||||
return ret;
|
||||
int res = sdir ? count : -count;
|
||||
|
||||
// Calculate each step time
|
||||
height += (sdir ? .5 : -.5);
|
||||
if (!accel) {
|
||||
// Move at constant velocity (zero acceleration)
|
||||
struct queue_append qa = queue_append_start(sc, print_time, .5);
|
||||
double inv_cruise_sv = sc->mcu_freq / start_sv;
|
||||
if (!movez_r) {
|
||||
// Optimized case for common XY only moves (no Z movement)
|
||||
while (count--) {
|
||||
double v = safe_sqrt(arm_sd2 - height*height);
|
||||
double pos = startxy_sd + (sdir ? -v : v);
|
||||
int ret = queue_append(&qa, pos * inv_cruise_sv);
|
||||
if (ret)
|
||||
return ret;
|
||||
height += (sdir ? 1. : -1.);
|
||||
}
|
||||
} else if (!movexy_r) {
|
||||
// Optimized case for Z only moves
|
||||
double pos = ((sdir ? height-end_height : end_height-height)
|
||||
* inv_cruise_sv);
|
||||
while (count--) {
|
||||
int ret = queue_append(&qa, pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
pos += inv_cruise_sv;
|
||||
}
|
||||
} else {
|
||||
// General case (handles XY+Z moves)
|
||||
double start_pos = movexy_r*startxy_sd, zoffset = movez_r*startxy_sd;
|
||||
while (count--) {
|
||||
double relheight = movexy_r*height - zoffset;
|
||||
double v = safe_sqrt(arm_sd2 - relheight*relheight);
|
||||
double pos = start_pos + movez_r*height + (sdir ? -v : v);
|
||||
int ret = queue_append(&qa, pos * inv_cruise_sv);
|
||||
if (ret)
|
||||
return ret;
|
||||
height += (sdir ? 1. : -1.);
|
||||
}
|
||||
}
|
||||
queue_append_finish(qa);
|
||||
} else {
|
||||
// Move with constant acceleration
|
||||
double start_pos = movexy_r*startxy_sd, zoffset = movez_r*startxy_sd;
|
||||
double inv_accel = 1. / accel;
|
||||
start_pos += 0.5 * start_sv*start_sv * inv_accel;
|
||||
struct queue_append qa = queue_append_start(
|
||||
sc, print_time, 0.5 - start_sv * inv_accel * sc->mcu_freq);
|
||||
double accel_multiplier = 2. * inv_accel * sc->mcu_freq * sc->mcu_freq;
|
||||
while (count--) {
|
||||
double relheight = movexy_r*height - zoffset;
|
||||
double v = safe_sqrt(arm_sd2 - relheight*relheight);
|
||||
double pos = start_pos + movez_r*height + (sdir ? -v : v);
|
||||
v = safe_sqrt(pos * accel_multiplier);
|
||||
int ret = queue_append(&qa, accel_multiplier >= 0. ? v : -v);
|
||||
if (ret)
|
||||
return ret;
|
||||
height += (sdir ? 1. : -1.);
|
||||
}
|
||||
queue_append_finish(qa);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t
|
||||
stepcompress_push_delta(
|
||||
struct stepcompress *sc, double print_time, double move_sd
|
||||
, double start_sv, double accel
|
||||
, double height, double startxy_sd, double arm_sd, double movez_r)
|
||||
{
|
||||
double reversexy_sd = startxy_sd + arm_sd*movez_r;
|
||||
if (reversexy_sd <= 0.)
|
||||
// All steps are in down direction
|
||||
return _stepcompress_push_delta(
|
||||
sc, 0, print_time, move_sd, start_sv, accel
|
||||
, height, startxy_sd, arm_sd, movez_r);
|
||||
double movexy_r = movez_r ? sqrt(1. - movez_r*movez_r) : 1.;
|
||||
if (reversexy_sd >= move_sd * movexy_r)
|
||||
// All steps are in up direction
|
||||
return _stepcompress_push_delta(
|
||||
sc, 1, print_time, move_sd, start_sv, accel
|
||||
, height, startxy_sd, arm_sd, movez_r);
|
||||
// Steps in both up and down direction
|
||||
int res1 = _stepcompress_push_delta(
|
||||
sc, 1, print_time, reversexy_sd / movexy_r, start_sv, accel
|
||||
, height, startxy_sd, arm_sd, movez_r);
|
||||
if (res1 == ERROR_RET)
|
||||
return res1;
|
||||
int res2 = _stepcompress_push_delta(
|
||||
sc, 0, print_time, move_sd, start_sv, accel
|
||||
, height + res1, startxy_sd, arm_sd, movez_r);
|
||||
if (res2 == ERROR_RET)
|
||||
return res2;
|
||||
return res1 + res2;
|
||||
}
|
||||
|
||||
|
||||
@@ -629,6 +757,17 @@ steppersync_free(struct steppersync *ss)
|
||||
free(ss);
|
||||
}
|
||||
|
||||
// Set the conversion rate of 'print_time' to mcu clock
|
||||
void
|
||||
steppersync_set_time(struct steppersync *ss, double time_offset, double mcu_freq)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<ss->sc_num; i++) {
|
||||
struct stepcompress *sc = ss->sc_list[i];
|
||||
stepcompress_set_time(sc, time_offset, mcu_freq);
|
||||
}
|
||||
}
|
||||
|
||||
// Implement a binary heap algorithm to track when the next available
|
||||
// 'struct move' in the mcu will be available
|
||||
static void
|
||||
@@ -655,13 +794,16 @@ heap_replace(struct steppersync *ss, uint64_t req_clock)
|
||||
}
|
||||
|
||||
// Find and transmit any scheduled steps prior to the given 'move_clock'
|
||||
void
|
||||
int
|
||||
steppersync_flush(struct steppersync *ss, uint64_t move_clock)
|
||||
{
|
||||
// Flush each stepcompress to the specified move_clock
|
||||
int i;
|
||||
for (i=0; i<ss->sc_num; i++)
|
||||
stepcompress_flush(ss->sc_list[i], move_clock);
|
||||
for (i=0; i<ss->sc_num; i++) {
|
||||
int ret = stepcompress_flush(ss->sc_list[i], move_clock);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Order commands by the reqclock of each pending command
|
||||
struct list_head msgs;
|
||||
@@ -701,4 +843,5 @@ steppersync_flush(struct steppersync *ss, uint64_t move_clock)
|
||||
// Transmit commands
|
||||
if (!list_empty(&msgs))
|
||||
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,100 @@
|
||||
# Printer stepper support
|
||||
#
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import math, logging
|
||||
import homing
|
||||
import homing, pins
|
||||
|
||||
class PrinterStepper:
|
||||
def __init__(self, printer, config, name):
|
||||
self.printer = printer
|
||||
self.config = config
|
||||
self.name = name
|
||||
self.mcu_stepper = self.mcu_enable = self.mcu_endstop = None
|
||||
|
||||
self.step_dist = config.getfloat('step_distance')
|
||||
self.step_dist = config.getfloat('step_distance', above=0.)
|
||||
self.inv_step_dist = 1. / self.step_dist
|
||||
self.min_stop_interval = 0.
|
||||
self.mcu_stepper = pins.setup_pin(
|
||||
printer, 'stepper', config.get('step_pin'))
|
||||
dir_pin_params = pins.get_printer_pins(printer).parse_pin_desc(
|
||||
config.get('dir_pin'), can_invert=True)
|
||||
self.mcu_stepper.setup_dir_pin(dir_pin_params)
|
||||
self.mcu_stepper.setup_step_distance(self.step_dist)
|
||||
|
||||
self.homing_speed = config.getfloat('homing_speed', 5.0)
|
||||
self.homing_positive_dir = config.getboolean(
|
||||
'homing_positive_dir', False)
|
||||
self.homing_retract_dist = config.getfloat('homing_retract_dist', 5.)
|
||||
self.homing_stepper_phases = config.getint('homing_stepper_phases', None)
|
||||
self.homing_endstop_phase = config.getint('homing_endstop_phase', None)
|
||||
endstop_accuracy = config.getfloat('homing_endstop_accuracy', None)
|
||||
self.homing_endstop_accuracy = None
|
||||
enable_pin = config.get('enable_pin', None)
|
||||
self.mcu_enable = None
|
||||
if enable_pin is not None:
|
||||
self.mcu_enable = pins.setup_pin(printer, 'digital_out', enable_pin)
|
||||
self.mcu_enable.setup_max_duration(0.)
|
||||
self.need_motor_enable = True
|
||||
def _dist_to_time(self, dist, start_velocity, accel):
|
||||
# Calculate the time it takes to travel a distance with constant accel
|
||||
time_offset = start_velocity / accel
|
||||
return math.sqrt(2. * dist / accel + time_offset**2) - time_offset
|
||||
def set_max_jerk(self, max_halt_velocity, max_accel):
|
||||
# Calculate the firmware's maximum halt interval time
|
||||
last_step_time = self._dist_to_time(
|
||||
self.step_dist, max_halt_velocity, max_accel)
|
||||
second_last_step_time = self._dist_to_time(
|
||||
2. * self.step_dist, max_halt_velocity, max_accel)
|
||||
min_stop_interval = second_last_step_time - last_step_time
|
||||
self.mcu_stepper.setup_min_stop_interval(min_stop_interval)
|
||||
def motor_enable(self, print_time, enable=0):
|
||||
if enable and self.need_motor_enable:
|
||||
self.mcu_stepper.reset_step_clock(print_time)
|
||||
if (self.mcu_enable is not None
|
||||
and self.mcu_enable.get_last_setting() != enable):
|
||||
self.mcu_enable.set_digital(print_time, enable)
|
||||
self.need_motor_enable = not enable
|
||||
|
||||
class PrinterHomingStepper(PrinterStepper):
|
||||
def __init__(self, printer, config, name):
|
||||
PrinterStepper.__init__(self, printer, config, name)
|
||||
|
||||
self.mcu_endstop = pins.setup_pin(
|
||||
printer, 'endstop', config.get('endstop_pin'))
|
||||
self.mcu_endstop.add_stepper(self.mcu_stepper)
|
||||
self.position_min = config.getfloat('position_min', 0.)
|
||||
self.position_max = config.getfloat(
|
||||
'position_max', 0., above=self.position_min)
|
||||
self.position_endstop = config.getfloat('position_endstop')
|
||||
|
||||
self.homing_speed = config.getfloat('homing_speed', 5.0, above=0.)
|
||||
self.homing_positive_dir = config.getboolean('homing_positive_dir', None)
|
||||
if self.homing_positive_dir is None:
|
||||
axis_len = self.position_max - self.position_min
|
||||
if self.position_endstop <= self.position_min + axis_len / 4.:
|
||||
self.homing_positive_dir = False
|
||||
elif self.position_endstop >= self.position_max - axis_len / 4.:
|
||||
self.homing_positive_dir = True
|
||||
else:
|
||||
raise config.error(
|
||||
"Unable to infer homing_positive_dir in section '%s'" % (
|
||||
config.section,))
|
||||
self.homing_retract_dist = config.getfloat(
|
||||
'homing_retract_dist', 5., above=0.)
|
||||
self.homing_stepper_phases = config.getint(
|
||||
'homing_stepper_phases', None, minval=0)
|
||||
endstop_accuracy = config.getfloat(
|
||||
'homing_endstop_accuracy', None, above=0.)
|
||||
self.homing_endstop_accuracy = self.homing_endstop_phase = None
|
||||
if self.homing_stepper_phases:
|
||||
self.homing_endstop_phase = config.getint(
|
||||
'homing_endstop_phase', None, minval=0
|
||||
, maxval=self.homing_stepper_phases-1)
|
||||
if self.homing_endstop_phase is not None:
|
||||
# Adjust the endstop position so 0.0 is always at a full step
|
||||
micro_steps = self.homing_stepper_phases // 4
|
||||
phase_offset = (
|
||||
((self.homing_endstop_phase + micro_steps // 2) % micro_steps)
|
||||
- micro_steps // 2) * self.step_dist
|
||||
full_step = micro_steps * self.step_dist
|
||||
es_pos = (int(self.position_endstop / full_step + .5) * full_step
|
||||
+ phase_offset)
|
||||
if es_pos != self.position_endstop:
|
||||
logging.info("Changing %s endstop position to %.3f"
|
||||
" (from %.3f)", self.name, es_pos,
|
||||
self.position_endstop)
|
||||
self.position_endstop = es_pos
|
||||
if endstop_accuracy is None:
|
||||
self.homing_endstop_accuracy = self.homing_stepper_phases//2 - 1
|
||||
elif self.homing_endstop_phase is not None:
|
||||
@@ -34,61 +103,26 @@ class PrinterStepper:
|
||||
else:
|
||||
self.homing_endstop_accuracy = int(math.ceil(
|
||||
endstop_accuracy * self.inv_step_dist))
|
||||
if self.homing_endstop_accuracy >= self.homing_stepper_phases/2:
|
||||
if self.homing_endstop_accuracy >= self.homing_stepper_phases // 2:
|
||||
logging.info("Endstop for %s is not accurate enough for stepper"
|
||||
" phase adjustment" % (self.config.section,))
|
||||
" phase adjustment", name)
|
||||
self.homing_stepper_phases = None
|
||||
self.position_min = self.position_endstop = self.position_max = None
|
||||
if config.get('endstop_pin', None) is not None:
|
||||
self.position_min = config.getfloat('position_min', 0.)
|
||||
self.position_endstop = config.getfloat('position_endstop')
|
||||
self.position_max = config.getfloat('position_max', 0.)
|
||||
|
||||
self.need_motor_enable = True
|
||||
def set_max_jerk(self, max_halt_velocity, max_accel):
|
||||
jc = max_halt_velocity / max_accel
|
||||
inv_max_step_accel = self.step_dist / max_accel
|
||||
self.min_stop_interval = (math.sqrt(3.*inv_max_step_accel + jc**2)
|
||||
- math.sqrt(inv_max_step_accel + jc**2))
|
||||
def build_config(self):
|
||||
max_error = self.config.getfloat('max_error', 0.000050)
|
||||
step_pin = self.config.get('step_pin')
|
||||
dir_pin = self.config.get('dir_pin')
|
||||
min_stop_interval = max(0., self.min_stop_interval - max_error)
|
||||
mcu = self.printer.mcu
|
||||
self.mcu_stepper = mcu.create_stepper(
|
||||
step_pin, dir_pin, min_stop_interval, max_error)
|
||||
enable_pin = self.config.get('enable_pin', None)
|
||||
if enable_pin is not None:
|
||||
self.mcu_enable = mcu.create_digital_out(enable_pin, 0)
|
||||
endstop_pin = self.config.get('endstop_pin', None)
|
||||
if endstop_pin is not None:
|
||||
self.mcu_endstop = mcu.create_endstop(endstop_pin, self.mcu_stepper)
|
||||
def motor_enable(self, move_time, enable=0):
|
||||
if enable and self.need_motor_enable:
|
||||
mcu_time = self.mcu_stepper.print_to_mcu_time(move_time)
|
||||
self.mcu_stepper.reset_step_clock(mcu_time)
|
||||
if (self.mcu_enable is not None
|
||||
and self.mcu_enable.get_last_setting() != enable):
|
||||
mcu_time = self.mcu_enable.print_to_mcu_time(move_time)
|
||||
self.mcu_enable.set_digital(mcu_time, enable)
|
||||
self.need_motor_enable = not enable
|
||||
def enable_endstop_checking(self, move_time, step_time):
|
||||
mcu_time = self.mcu_endstop.print_to_mcu_time(move_time)
|
||||
self.mcu_endstop.home_start(mcu_time, step_time)
|
||||
return self.mcu_endstop
|
||||
def query_endstop(self, print_time):
|
||||
mcu_time = self.mcu_endstop.print_to_mcu_time(print_time)
|
||||
self.mcu_endstop.query_endstop(mcu_time)
|
||||
return self.mcu_endstop
|
||||
if self.mcu_endstop.get_mcu().is_fileoutput():
|
||||
self.homing_endstop_accuracy = self.homing_stepper_phases
|
||||
def get_homing_speed(self):
|
||||
# Round the configured homing speed so that it is an even
|
||||
# number of ticks per step.
|
||||
adjusted_freq = self.mcu_stepper.get_mcu().get_adjusted_freq()
|
||||
dist_ticks = adjusted_freq * self.step_dist
|
||||
ticks_per_step = round(dist_ticks / self.homing_speed)
|
||||
return dist_ticks / ticks_per_step
|
||||
def get_homed_offset(self):
|
||||
if not self.homing_stepper_phases or self.need_motor_enable:
|
||||
return 0
|
||||
pos = self.mcu_stepper.get_mcu_position()
|
||||
pos %= self.homing_stepper_phases
|
||||
if self.homing_endstop_phase is None:
|
||||
logging.info("Setting %s endstop phase to %d" % (
|
||||
self.config.section, pos))
|
||||
logging.info("Setting %s endstop phase to %d", self.name, pos)
|
||||
self.homing_endstop_phase = pos
|
||||
return 0
|
||||
delta = (pos - self.homing_endstop_phase) % self.homing_stepper_phases
|
||||
@@ -98,4 +132,4 @@ class PrinterStepper:
|
||||
raise homing.EndstopError(
|
||||
"Endstop %s incorrect phase (got %d vs %d)" % (
|
||||
self.name, pos, self.homing_endstop_phase))
|
||||
return delta
|
||||
return delta * self.step_dist
|
||||
|
||||
@@ -3,148 +3,174 @@
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import math, logging, time
|
||||
import cartesian, delta
|
||||
|
||||
EXTRUDE_DIFF_IGNORE = 1.02
|
||||
import math, logging
|
||||
import mcu, homing, cartesian, corexy, delta, extruder
|
||||
|
||||
# Common suffixes: _d is distance (in mm), _v is velocity (in
|
||||
# mm/second), _t is time (in seconds), _r is ratio (scalar between
|
||||
# 0.0 and 1.0)
|
||||
# mm/second), _v2 is velocity squared (mm^2/s^2), _t is time (in
|
||||
# seconds), _r is ratio (scalar between 0.0 and 1.0)
|
||||
|
||||
# Class to track each move request
|
||||
class Move:
|
||||
def __init__(self, toolhead, start_pos, end_pos, speed, accel):
|
||||
def __init__(self, toolhead, start_pos, end_pos, speed):
|
||||
self.toolhead = toolhead
|
||||
self.start_pos = tuple(start_pos)
|
||||
self.end_pos = tuple(end_pos)
|
||||
self.accel = accel
|
||||
self.do_calc_junction = True
|
||||
self.accel = toolhead.max_accel
|
||||
self.is_kinematic_move = True
|
||||
self.axes_d = axes_d = [end_pos[i] - start_pos[i] for i in (0, 1, 2, 3)]
|
||||
if axes_d[2]:
|
||||
# Move with Z
|
||||
move_d = math.sqrt(sum([d*d for d in axes_d[:3]]))
|
||||
self.do_calc_junction = False
|
||||
else:
|
||||
move_d = math.sqrt(axes_d[0]**2 + axes_d[1]**2)
|
||||
self.move_d = move_d = math.sqrt(sum([d*d for d in axes_d[:3]]))
|
||||
if not move_d:
|
||||
# Extrude only move
|
||||
move_d = abs(axes_d[3])
|
||||
if not move_d:
|
||||
# No move
|
||||
self.move_d = 0.
|
||||
return
|
||||
self.do_calc_junction = False
|
||||
self.move_d = move_d
|
||||
self.extrude_r = axes_d[3] / move_d
|
||||
# Junction speeds are velocities squared. The junction_delta
|
||||
# is the maximum amount of this squared-velocity that can
|
||||
# change in this move.
|
||||
self.junction_max = speed**2
|
||||
self.junction_delta = 2.0 * move_d * accel
|
||||
self.junction_start_max = 0.
|
||||
self.move_d = move_d = abs(axes_d[3])
|
||||
self.is_kinematic_move = False
|
||||
self.min_move_t = move_d / speed
|
||||
# Junction speeds are tracked in velocity squared. The
|
||||
# delta_v2 is the maximum amount of this squared-velocity that
|
||||
# can change in this move.
|
||||
self.max_start_v2 = 0.
|
||||
self.max_cruise_v2 = speed**2
|
||||
self.delta_v2 = 2.0 * move_d * self.accel
|
||||
self.max_smoothed_v2 = 0.
|
||||
self.smooth_delta_v2 = 2.0 * move_d * toolhead.max_accel_to_decel
|
||||
def limit_speed(self, speed, accel):
|
||||
self.junction_max = min(self.junction_max, speed**2)
|
||||
speed2 = speed**2
|
||||
if speed2 < self.max_cruise_v2:
|
||||
self.max_cruise_v2 = speed2
|
||||
self.min_move_t = self.move_d / speed
|
||||
self.accel = min(self.accel, accel)
|
||||
self.junction_delta = 2.0 * self.move_d * self.accel
|
||||
self.delta_v2 = 2.0 * self.move_d * self.accel
|
||||
self.smooth_delta_v2 = min(self.smooth_delta_v2, self.delta_v2)
|
||||
def calc_junction(self, prev_move):
|
||||
if not self.do_calc_junction or not prev_move.do_calc_junction:
|
||||
if not self.is_kinematic_move or not prev_move.is_kinematic_move:
|
||||
return
|
||||
# Find max junction_start_velocity between two moves
|
||||
if (self.extrude_r > prev_move.extrude_r * EXTRUDE_DIFF_IGNORE
|
||||
or prev_move.extrude_r > self.extrude_r * EXTRUDE_DIFF_IGNORE):
|
||||
# Extrude ratio between moves is too different
|
||||
return
|
||||
self.extrude_r = prev_move.extrude_r
|
||||
# Allow extruder to calculate its maximum junction
|
||||
extruder_v2 = self.toolhead.extruder.calc_junction(prev_move, self)
|
||||
# Find max velocity using approximated centripetal velocity as
|
||||
# described at:
|
||||
# https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/
|
||||
junction_cos_theta = -((self.axes_d[0] * prev_move.axes_d[0]
|
||||
+ self.axes_d[1] * prev_move.axes_d[1])
|
||||
axes_d = self.axes_d
|
||||
prev_axes_d = prev_move.axes_d
|
||||
junction_cos_theta = -((axes_d[0] * prev_axes_d[0]
|
||||
+ axes_d[1] * prev_axes_d[1]
|
||||
+ axes_d[2] * prev_axes_d[2])
|
||||
/ (self.move_d * prev_move.move_d))
|
||||
if junction_cos_theta > 0.999999:
|
||||
return
|
||||
junction_cos_theta = max(junction_cos_theta, -0.999999)
|
||||
sin_theta_d2 = math.sqrt(0.5*(1.0-junction_cos_theta))
|
||||
R = self.toolhead.junction_deviation * sin_theta_d2 / (1. - sin_theta_d2)
|
||||
self.junction_start_max = min(
|
||||
R * self.accel, self.junction_max, prev_move.junction_max
|
||||
, prev_move.junction_start_max + prev_move.junction_delta)
|
||||
def process(self, junction_start, junction_cruise, junction_end
|
||||
, cornering_min, cornering_max):
|
||||
self.max_start_v2 = min(
|
||||
R * self.accel, R * prev_move.accel, extruder_v2
|
||||
, self.max_cruise_v2, prev_move.max_cruise_v2
|
||||
, prev_move.max_start_v2 + prev_move.delta_v2)
|
||||
self.max_smoothed_v2 = min(
|
||||
self.max_start_v2
|
||||
, prev_move.max_smoothed_v2 + prev_move.smooth_delta_v2)
|
||||
def set_junction(self, start_v2, cruise_v2, end_v2):
|
||||
# Determine accel, cruise, and decel portions of the move distance
|
||||
inv_junction_delta = 1. / self.junction_delta
|
||||
accel_r = (junction_cruise-junction_start) * inv_junction_delta
|
||||
decel_r = (junction_cruise-junction_end) * inv_junction_delta
|
||||
cruise_r = 1. - accel_r - decel_r
|
||||
self.accel_r, self.cruise_r, self.decel_r = accel_r, cruise_r, decel_r
|
||||
inv_delta_v2 = 1. / self.delta_v2
|
||||
self.accel_r = accel_r = (cruise_v2 - start_v2) * inv_delta_v2
|
||||
self.decel_r = decel_r = (cruise_v2 - end_v2) * inv_delta_v2
|
||||
self.cruise_r = cruise_r = 1. - accel_r - decel_r
|
||||
# Determine move velocities
|
||||
start_v = math.sqrt(junction_start)
|
||||
cruise_v = math.sqrt(junction_cruise)
|
||||
end_v = math.sqrt(junction_end)
|
||||
self.start_v, self.cruise_v, self.end_v = start_v, cruise_v, end_v
|
||||
self.corner_min = math.sqrt(cornering_min)
|
||||
self.corner_max = math.sqrt(cornering_max)
|
||||
self.start_v = start_v = math.sqrt(start_v2)
|
||||
self.cruise_v = cruise_v = math.sqrt(cruise_v2)
|
||||
self.end_v = end_v = math.sqrt(end_v2)
|
||||
# Determine time spent in each portion of move (time is the
|
||||
# distance divided by average velocity)
|
||||
accel_t = accel_r * self.move_d / ((start_v + cruise_v) * 0.5)
|
||||
cruise_t = cruise_r * self.move_d / cruise_v
|
||||
decel_t = decel_r * self.move_d / ((end_v + cruise_v) * 0.5)
|
||||
self.accel_t, self.cruise_t, self.decel_t = accel_t, cruise_t, decel_t
|
||||
self.accel_t = accel_r * self.move_d / ((start_v + cruise_v) * 0.5)
|
||||
self.cruise_t = cruise_r * self.move_d / cruise_v
|
||||
self.decel_t = decel_r * self.move_d / ((end_v + cruise_v) * 0.5)
|
||||
def move(self):
|
||||
# Generate step times for the move
|
||||
next_move_time = self.toolhead.get_next_move_time()
|
||||
if self.is_kinematic_move:
|
||||
self.toolhead.kin.move(next_move_time, self)
|
||||
if self.axes_d[3]:
|
||||
self.toolhead.extruder.move(next_move_time, self)
|
||||
self.toolhead.update_move_time(accel_t + cruise_t + decel_t)
|
||||
self.toolhead.update_move_time(
|
||||
self.accel_t + self.cruise_t + self.decel_t)
|
||||
|
||||
LOOKAHEAD_FLUSH_TIME = 0.250
|
||||
|
||||
# Class to track a list of pending move requests and to facilitate
|
||||
# "look-ahead" across moves to reduce acceleration between moves.
|
||||
class MoveQueue:
|
||||
def __init__(self):
|
||||
self.extruder_lookahead = None
|
||||
self.queue = []
|
||||
self.junction_flush = 0.
|
||||
self.leftover = 0
|
||||
self.junction_flush = LOOKAHEAD_FLUSH_TIME
|
||||
def reset(self):
|
||||
del self.queue[:]
|
||||
self.leftover = 0
|
||||
self.junction_flush = LOOKAHEAD_FLUSH_TIME
|
||||
def set_flush_time(self, flush_time):
|
||||
self.junction_flush = flush_time
|
||||
def set_extruder(self, extruder):
|
||||
self.extruder_lookahead = extruder.lookahead
|
||||
def flush(self, lazy=False):
|
||||
flush_count = len(self.queue)
|
||||
move_info = [None] * flush_count
|
||||
self.junction_flush = LOOKAHEAD_FLUSH_TIME
|
||||
update_flush_count = lazy
|
||||
queue = self.queue
|
||||
flush_count = len(queue)
|
||||
# Traverse queue from last to first move and determine maximum
|
||||
# junction speed assuming the robot comes to a complete stop
|
||||
# after the last move.
|
||||
next_junction_end = cornering_min = cornering_max = 0.
|
||||
for i in range(flush_count-1, -1, -1):
|
||||
move = self.queue[i]
|
||||
reachable_start = next_junction_end + move.junction_delta
|
||||
junction_start = min(move.junction_start_max, reachable_start)
|
||||
junction_cruise = min((junction_start + reachable_start) * .5
|
||||
, move.junction_max)
|
||||
move_info[i] = (junction_start, junction_cruise, next_junction_end
|
||||
, cornering_min, cornering_max)
|
||||
if reachable_start > junction_start:
|
||||
cornering_min = junction_start
|
||||
if junction_start + move.junction_delta > next_junction_end:
|
||||
cornering_max = junction_cruise
|
||||
if lazy:
|
||||
delayed = []
|
||||
next_end_v2 = next_smoothed_v2 = peak_cruise_v2 = 0.
|
||||
for i in range(flush_count-1, self.leftover-1, -1):
|
||||
move = queue[i]
|
||||
reachable_start_v2 = next_end_v2 + move.delta_v2
|
||||
start_v2 = min(move.max_start_v2, reachable_start_v2)
|
||||
reachable_smoothed_v2 = next_smoothed_v2 + move.smooth_delta_v2
|
||||
smoothed_v2 = min(move.max_smoothed_v2, reachable_smoothed_v2)
|
||||
if smoothed_v2 < reachable_smoothed_v2:
|
||||
# It's possible for this move to accelerate
|
||||
if (smoothed_v2 + move.smooth_delta_v2 > next_smoothed_v2
|
||||
or delayed):
|
||||
# This move can decelerate or this is a full accel
|
||||
# move after a full decel move
|
||||
if update_flush_count and peak_cruise_v2:
|
||||
flush_count = i
|
||||
lazy = False
|
||||
next_junction_end = junction_start
|
||||
if lazy:
|
||||
flush_count = 0
|
||||
update_flush_count = False
|
||||
peak_cruise_v2 = min(move.max_cruise_v2, (
|
||||
smoothed_v2 + reachable_smoothed_v2) * .5)
|
||||
if delayed:
|
||||
# Propagate peak_cruise_v2 to any delayed moves
|
||||
if not update_flush_count and i < flush_count:
|
||||
for m, ms_v2, me_v2 in delayed:
|
||||
mc_v2 = min(peak_cruise_v2, ms_v2)
|
||||
m.set_junction(min(ms_v2, mc_v2), mc_v2
|
||||
, min(me_v2, mc_v2))
|
||||
del delayed[:]
|
||||
if not update_flush_count and i < flush_count:
|
||||
cruise_v2 = min((start_v2 + reachable_start_v2) * .5
|
||||
, move.max_cruise_v2, peak_cruise_v2)
|
||||
move.set_junction(min(start_v2, cruise_v2), cruise_v2
|
||||
, min(next_end_v2, cruise_v2))
|
||||
else:
|
||||
# Delay calculating this move until peak_cruise_v2 is known
|
||||
delayed.append((move, start_v2, next_end_v2))
|
||||
next_end_v2 = start_v2
|
||||
next_smoothed_v2 = smoothed_v2
|
||||
if update_flush_count:
|
||||
return
|
||||
# Allow extruder to do its lookahead
|
||||
move_count = self.extruder_lookahead(queue, flush_count, lazy)
|
||||
# Generate step times for all moves ready to be flushed
|
||||
for i in range(flush_count):
|
||||
self.queue[i].process(*move_info[i])
|
||||
for move in queue[:move_count]:
|
||||
move.move()
|
||||
# Remove processed moves from the queue
|
||||
del self.queue[:flush_count]
|
||||
if self.queue:
|
||||
self.junction_flush = 2. * self.queue[-1].junction_max
|
||||
self.leftover = flush_count - move_count
|
||||
del queue[:move_count]
|
||||
def add_move(self, move):
|
||||
self.queue.append(move)
|
||||
if len(self.queue) == 1:
|
||||
self.junction_flush = 2. * move.junction_max
|
||||
return
|
||||
move.calc_junction(self.queue[-2])
|
||||
self.junction_flush -= move.junction_delta
|
||||
self.junction_flush -= move.min_move_t
|
||||
if self.junction_flush <= 0.:
|
||||
# There are enough queued moves to return to zero velocity
|
||||
# from the first move's maximum possible velocity, so at
|
||||
@@ -158,113 +184,142 @@ class ToolHead:
|
||||
def __init__(self, printer, config):
|
||||
self.printer = printer
|
||||
self.reactor = printer.reactor
|
||||
self.extruder = printer.objects.get('extruder')
|
||||
kintypes = {'cartesian': cartesian.CartKinematics,
|
||||
'delta': delta.DeltaKinematics}
|
||||
self.kin = config.getchoice('kinematics', kintypes)(printer, config)
|
||||
self.max_speed = config.getfloat('max_velocity')
|
||||
self.max_accel = config.getfloat('max_accel')
|
||||
self.junction_deviation = config.getfloat('junction_deviation', 0.02)
|
||||
self.all_mcus = mcu.get_printer_mcus(printer)
|
||||
self.mcu = self.all_mcus[0]
|
||||
self.max_velocity = config.getfloat('max_velocity', above=0.)
|
||||
self.max_accel = config.getfloat('max_accel', above=0.)
|
||||
self.max_accel_to_decel = config.getfloat(
|
||||
'max_accel_to_decel', self.max_accel * 0.5
|
||||
, above=0., maxval=self.max_accel)
|
||||
self.junction_deviation = config.getfloat(
|
||||
'junction_deviation', 0.02, above=0.)
|
||||
self.move_queue = MoveQueue()
|
||||
self.commanded_pos = [0., 0., 0., 0.]
|
||||
# Print time tracking
|
||||
self.buffer_time_high = config.getfloat('buffer_time_high', 5.000)
|
||||
self.buffer_time_low = config.getfloat('buffer_time_low', 0.150)
|
||||
self.move_flush_time = config.getfloat('move_flush_time', 0.050)
|
||||
self.motor_off_delay = config.getfloat('motor_off_time', 60.000)
|
||||
self.buffer_time_low = config.getfloat(
|
||||
'buffer_time_low', 1.000, above=0.)
|
||||
self.buffer_time_high = config.getfloat(
|
||||
'buffer_time_high', 2.000, above=self.buffer_time_low)
|
||||
self.buffer_time_start = config.getfloat(
|
||||
'buffer_time_start', 0.250, above=0.)
|
||||
self.move_flush_time = config.getfloat(
|
||||
'move_flush_time', 0.050, above=0.)
|
||||
self.print_time = 0.
|
||||
self.need_check_stall = -1.
|
||||
self.print_time_stall = 0
|
||||
self.motor_off_time = self.reactor.NEVER
|
||||
self.print_stall = 0
|
||||
self.sync_print_time = True
|
||||
self.last_flush_from_idle = False
|
||||
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
||||
def build_config(self):
|
||||
self.kin.set_max_jerk(0.005 * self.max_accel, self.max_accel) # XXX
|
||||
self.kin.build_config()
|
||||
self.move_queue.set_flush_time(self.buffer_time_high)
|
||||
# Motor off tracking
|
||||
self.need_motor_off = False
|
||||
self.motor_off_time = config.getfloat('motor_off_time', 600., above=0.)
|
||||
self.motor_off_timer = self.reactor.register_timer(
|
||||
self._motor_off_handler, self.reactor.NOW)
|
||||
# Create kinematics class
|
||||
self.extruder = extruder.DummyExtruder()
|
||||
self.move_queue.set_extruder(self.extruder)
|
||||
kintypes = {'cartesian': cartesian.CartKinematics,
|
||||
'corexy': corexy.CoreXYKinematics,
|
||||
'delta': delta.DeltaKinematics}
|
||||
self.kin = config.getchoice('kinematics', kintypes)(
|
||||
self, printer, config)
|
||||
# Print time tracking
|
||||
def update_move_time(self, movetime):
|
||||
self.print_time += movetime
|
||||
flush_to_time = self.print_time - self.move_flush_time
|
||||
self.printer.mcu.flush_moves(flush_to_time)
|
||||
for m in self.all_mcus:
|
||||
m.flush_moves(flush_to_time)
|
||||
def get_next_move_time(self):
|
||||
if not self.print_time:
|
||||
self.print_time = self.buffer_time_low + STALL_TIME
|
||||
curtime = time.time()
|
||||
self.printer.mcu.set_print_start_time(curtime)
|
||||
if not self.sync_print_time:
|
||||
return self.print_time
|
||||
self.sync_print_time = False
|
||||
est_print_time = self.mcu.estimated_print_time(self.reactor.monotonic())
|
||||
if self.last_flush_from_idle and self.print_time > est_print_time:
|
||||
self.print_stall += 1
|
||||
self.last_flush_from_idle = False
|
||||
self.need_motor_off = True
|
||||
self.print_time = max(
|
||||
self.print_time, est_print_time + self.buffer_time_start)
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||
return self.print_time
|
||||
def get_last_move_time(self):
|
||||
def _flush_lookahead(self, must_sync=False):
|
||||
sync_print_time = self.sync_print_time
|
||||
self.move_queue.flush()
|
||||
return self.get_next_move_time()
|
||||
def reset_motor_off_time(self, eventtime):
|
||||
self.motor_off_time = eventtime + self.motor_off_delay
|
||||
def reset_print_time(self):
|
||||
self.move_queue.flush()
|
||||
self.printer.mcu.flush_moves(self.print_time)
|
||||
self.print_time = 0.
|
||||
self.last_flush_from_idle = False
|
||||
if sync_print_time or must_sync:
|
||||
self.sync_print_time = True
|
||||
self.move_queue.set_flush_time(self.buffer_time_high)
|
||||
self.need_check_stall = -1.
|
||||
self.reset_motor_off_time(time.time())
|
||||
self.reactor.update_timer(self.flush_timer, self.motor_off_time)
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
|
||||
for m in self.all_mcus:
|
||||
m.flush_moves(self.print_time)
|
||||
def get_last_move_time(self):
|
||||
self._flush_lookahead()
|
||||
return self.get_next_move_time()
|
||||
def reset_print_time(self, min_print_time=0.):
|
||||
self._flush_lookahead(must_sync=True)
|
||||
self.print_time = max(min_print_time, self.mcu.estimated_print_time(
|
||||
self.reactor.monotonic()))
|
||||
def _check_stall(self):
|
||||
if not self.print_time:
|
||||
# XXX - find better way to flush initial move_queue items
|
||||
if self.move_queue.queue:
|
||||
self.reactor.update_timer(self.flush_timer, time.time() + 0.100)
|
||||
eventtime = self.reactor.monotonic()
|
||||
if self.sync_print_time:
|
||||
# Building initial queue - make sure to flush on idle input
|
||||
self.reactor.update_timer(self.flush_timer, eventtime + 0.100)
|
||||
return
|
||||
eventtime = time.time()
|
||||
# Check if there are lots of queued moves and stall if so
|
||||
while 1:
|
||||
buffer_time = self.printer.mcu.get_print_buffer_time(
|
||||
eventtime, self.print_time)
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
buffer_time = self.print_time - est_print_time
|
||||
stall_time = buffer_time - self.buffer_time_high
|
||||
if stall_time <= 0.:
|
||||
break
|
||||
eventtime = self.reactor.pause(eventtime + stall_time)
|
||||
if not self.print_time:
|
||||
if self.mcu.is_fileoutput():
|
||||
self.need_check_stall = self.reactor.NEVER
|
||||
return
|
||||
self.need_check_stall = self.print_time - stall_time + 0.100
|
||||
eventtime = self.reactor.pause(eventtime + min(1., stall_time))
|
||||
self.need_check_stall = est_print_time + self.buffer_time_high + 0.100
|
||||
def _flush_handler(self, eventtime):
|
||||
try:
|
||||
if not self.print_time:
|
||||
self.move_queue.flush()
|
||||
if not self.print_time:
|
||||
if eventtime >= self.motor_off_time:
|
||||
self.motor_off()
|
||||
self.reset_print_time()
|
||||
self.motor_off_time = self.reactor.NEVER
|
||||
return self.motor_off_time
|
||||
print_time = self.print_time
|
||||
buffer_time = self.printer.mcu.get_print_buffer_time(
|
||||
eventtime, print_time)
|
||||
buffer_time = print_time - self.mcu.estimated_print_time(eventtime)
|
||||
if buffer_time > self.buffer_time_low:
|
||||
# Running normally - reschedule check
|
||||
return eventtime + buffer_time - self.buffer_time_low
|
||||
self.move_queue.flush()
|
||||
# Under ran low buffer mark - flush lookahead queue
|
||||
self._flush_lookahead(must_sync=True)
|
||||
if print_time != self.print_time:
|
||||
self.print_time_stall += 1
|
||||
self.dwell(self.buffer_time_low + STALL_TIME)
|
||||
return self.reactor.NOW
|
||||
self.reset_print_time()
|
||||
return self.motor_off_time
|
||||
self.last_flush_from_idle = True
|
||||
except:
|
||||
logging.exception("Exception in flush_handler")
|
||||
self.force_shutdown()
|
||||
def stats(self, eventtime):
|
||||
buffer_time = 0.
|
||||
if self.print_time:
|
||||
buffer_time = self.printer.mcu.get_print_buffer_time(
|
||||
eventtime, self.print_time)
|
||||
return "print_time=%.3f buffer_time=%.3f print_time_stall=%d" % (
|
||||
self.print_time, buffer_time, self.print_time_stall)
|
||||
self.printer.invoke_shutdown("Exception in flush_handler")
|
||||
return self.reactor.NEVER
|
||||
# Motor off timer
|
||||
def _motor_off_handler(self, eventtime):
|
||||
if not self.need_motor_off or not self.sync_print_time:
|
||||
return eventtime + self.motor_off_time
|
||||
elapsed_time = self.mcu.estimated_print_time(eventtime) - self.print_time
|
||||
if elapsed_time < self.motor_off_time:
|
||||
return eventtime + self.motor_off_time - elapsed_time
|
||||
try:
|
||||
self.motor_off()
|
||||
except:
|
||||
logging.exception("Exception in motor_off_handler")
|
||||
self.printer.invoke_shutdown("Exception in motor_off_handler")
|
||||
return eventtime + self.motor_off_time
|
||||
# Movement commands
|
||||
def get_position(self):
|
||||
return list(self.commanded_pos)
|
||||
def set_position(self, newpos):
|
||||
self.move_queue.flush()
|
||||
self._flush_lookahead()
|
||||
self.commanded_pos[:] = newpos
|
||||
self.kin.set_position(newpos)
|
||||
def move(self, newpos, speed):
|
||||
speed = min(speed, self.max_speed)
|
||||
move = Move(self, self.commanded_pos, newpos, speed, self.max_accel)
|
||||
speed = min(speed, self.max_velocity)
|
||||
move = Move(self, self.commanded_pos, newpos, speed)
|
||||
if not move.move_d:
|
||||
return
|
||||
if move.is_kinematic_move:
|
||||
self.kin.check_move(move)
|
||||
if move.axes_d[3]:
|
||||
self.extruder.check_move(move)
|
||||
@@ -284,10 +339,52 @@ class ToolHead:
|
||||
self.kin.motor_off(last_move_time)
|
||||
self.extruder.motor_off(last_move_time)
|
||||
self.dwell(STALL_TIME)
|
||||
logging.debug('; Max time of %f' % (last_move_time,))
|
||||
def query_endstops(self):
|
||||
self.need_motor_off = False
|
||||
logging.debug('; Max time of %f', last_move_time)
|
||||
def wait_moves(self):
|
||||
self._flush_lookahead()
|
||||
if self.mcu.is_fileoutput():
|
||||
return
|
||||
eventtime = self.reactor.monotonic()
|
||||
while (not self.sync_print_time
|
||||
or self.print_time >= self.mcu.estimated_print_time(eventtime)):
|
||||
eventtime = self.reactor.pause(eventtime + 0.100)
|
||||
def query_endstops(self, query_flags=""):
|
||||
last_move_time = self.get_last_move_time()
|
||||
return self.kin.query_endstops(last_move_time)
|
||||
def force_shutdown(self):
|
||||
self.printer.mcu.force_shutdown()
|
||||
return self.kin.query_endstops(last_move_time, query_flags)
|
||||
def set_extruder(self, extruder):
|
||||
last_move_time = self.get_last_move_time()
|
||||
self.extruder.set_active(last_move_time, False)
|
||||
extrude_pos = extruder.set_active(last_move_time, True)
|
||||
self.extruder = extruder
|
||||
self.move_queue.set_extruder(extruder)
|
||||
self.commanded_pos[3] = extrude_pos
|
||||
# Misc commands
|
||||
def check_active(self, eventtime):
|
||||
for m in self.all_mcus:
|
||||
m.check_active(self.print_time, eventtime)
|
||||
if not self.sync_print_time:
|
||||
return True
|
||||
return self.print_time + 60. > self.mcu.estimated_print_time(eventtime)
|
||||
def stats(self, eventtime):
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
buffer_time = max(0., self.print_time - est_print_time)
|
||||
return "print_time=%.3f buffer_time=%.3f print_stall=%d" % (
|
||||
self.print_time, buffer_time, self.print_stall)
|
||||
def do_shutdown(self):
|
||||
try:
|
||||
self.move_queue.reset()
|
||||
self.reset_print_time()
|
||||
except:
|
||||
logging.exception("Exception in do_shutdown")
|
||||
def get_max_velocity(self):
|
||||
return self.max_velocity, self.max_accel
|
||||
def get_max_axis_halt(self):
|
||||
# Determine the maximum velocity a cartesian axis could halt
|
||||
# at due to the junction_deviation setting. The 8.0 was
|
||||
# determined experimentally.
|
||||
return min(self.max_velocity,
|
||||
math.sqrt(8. * self.junction_deviation * self.max_accel))
|
||||
|
||||
def add_printer_objects(printer, config):
|
||||
printer.add_object('toolhead', ToolHead(printer, config))
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
import os, pty, fcntl, termios, signal
|
||||
import sys, os, pty, fcntl, termios, signal, logging
|
||||
import subprocess, traceback, shlex
|
||||
|
||||
# Return the SIGINT interrupt handler back to the OS default
|
||||
def fix_sigint():
|
||||
@@ -36,3 +36,34 @@ def create_pty(ptyname):
|
||||
old[3] = old[3] & ~termios.ECHO
|
||||
termios.tcsetattr(mfd, termios.TCSADRAIN, old)
|
||||
return mfd
|
||||
|
||||
def get_cpu_info():
|
||||
try:
|
||||
f = open('/proc/cpuinfo', 'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
except OSError:
|
||||
logging.debug("Exception on read /proc/cpuinfo: %s",
|
||||
traceback.format_exc())
|
||||
return "?"
|
||||
lines = [l.split(':', 1) for l in data.split('\n')]
|
||||
lines = [(l[0].strip(), l[1].strip()) for l in lines if len(l) == 2]
|
||||
core_count = [k for k, v in lines].count("processor")
|
||||
model_name = dict(lines).get("model name", "?")
|
||||
return "%d core %s" % (core_count, model_name)
|
||||
|
||||
def get_git_version():
|
||||
# Obtain version info from "git" program
|
||||
gitdir = os.path.join(sys.path[0], '..')
|
||||
if not os.path.exists(gitdir):
|
||||
logging.debug("No '.git' file/directory found")
|
||||
return "?"
|
||||
prog = "git -C %s describe --tags --long --dirty" % (gitdir,)
|
||||
try:
|
||||
process = subprocess.Popen(shlex.split(prog), stdout=subprocess.PIPE)
|
||||
output = process.communicate()[0]
|
||||
retcode = process.poll()
|
||||
except OSError:
|
||||
logging.debug("Exception on run: %s", traceback.format_exc())
|
||||
return "?"
|
||||
return output.strip()
|
||||
|
||||
20
lib/README
@@ -3,10 +3,22 @@ This directory contains external library code.
|
||||
The pjrc_usb_serial directory contains code from:
|
||||
http://www.pjrc.com/teensy/usb_serial.html
|
||||
version 1.7 (extracted on 20160605). It has been modified to compile
|
||||
on recent versions of gcc. See usb_serial.patch for the modifications.
|
||||
on recent versions of gcc, to support asynchronous notification of
|
||||
incoming data, and to not use SOF interrupts. See usb_serial.patch for
|
||||
the modifications.
|
||||
|
||||
The cmsis-sam3x8e directory contains code from the Arduino project:
|
||||
https://www.arduino.cc/
|
||||
version 1.5.1 (extracted on 20160608). It has been modified to
|
||||
compile with gcc's LTO feature. See cmsis-sam3x8e.patch for the
|
||||
modifications.
|
||||
version 1.5.1 (extracted on 20160608). It has been modified to compile
|
||||
with gcc's LTO feature. See cmsis-sam3x8e.patch for the modifications.
|
||||
|
||||
The hub-ctrl directory contains code from:
|
||||
https://github.com/codazoda/hub-ctrl.c/
|
||||
revision 42095e522859059e8a5f4ec05c1e3def01a870a9.
|
||||
|
||||
The pru_rpmsg directory contains code from:
|
||||
https://github.com/dinuxbg/pru-gcc-examples
|
||||
revision 425a42d82006cf0aa24be27b483d2f6a41607489. The code is taken
|
||||
from the repo's hc-sr04-range-sensor directory. It has been modified
|
||||
so that the IEP definitions compile correctly. See pru_rpmsg.patch for
|
||||
the modifications.
|
||||
|
||||
412
lib/hub-ctrl/hub-ctrl.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Free Software Initiative of Japan
|
||||
*
|
||||
* Author: NIIBE Yutaka <gniibe at fsij.org>
|
||||
*
|
||||
* This file can be distributed under the terms and conditions of the
|
||||
* GNU General Public License version 2 (or later).
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <usb.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
|
||||
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
|
||||
#define USB_PORT_FEAT_POWER 8
|
||||
#define USB_PORT_FEAT_INDICATOR 22
|
||||
#define USB_DIR_IN 0x80 /* to host */
|
||||
|
||||
#define COMMAND_SET_NONE 0
|
||||
#define COMMAND_SET_LED 1
|
||||
#define COMMAND_SET_POWER 2
|
||||
#define HUB_LED_GREEN 2
|
||||
|
||||
static void
|
||||
usage (const char *progname)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}] \\\n"
|
||||
" [-P PORT] [{-p [VALUE]|-l [VALUE]}]\n", progname);
|
||||
}
|
||||
|
||||
static void
|
||||
exit_with_usage (const char *progname)
|
||||
{
|
||||
usage (progname);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
#define HUB_CHAR_LPSM 0x0003
|
||||
#define HUB_CHAR_PORTIND 0x0080
|
||||
|
||||
struct usb_hub_descriptor {
|
||||
unsigned char bDescLength;
|
||||
unsigned char bDescriptorType;
|
||||
unsigned char bNbrPorts;
|
||||
unsigned char wHubCharacteristics[2];
|
||||
unsigned char bPwrOn2PwrGood;
|
||||
unsigned char bHubContrCurrent;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
#define CTRL_TIMEOUT 1000
|
||||
#define USB_STATUS_SIZE 4
|
||||
|
||||
#define MAX_HUBS 128
|
||||
struct hub_info {
|
||||
int busnum, devnum;
|
||||
struct usb_device *dev;
|
||||
int nport;
|
||||
int indicator_support;
|
||||
};
|
||||
|
||||
static struct hub_info hubs[MAX_HUBS];
|
||||
static int number_of_hubs_with_feature;
|
||||
|
||||
static void
|
||||
hub_port_status (usb_dev_handle *uh, int nport)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(" Hub Port Status:\n");
|
||||
for (i = 0; i < nport; i++)
|
||||
{
|
||||
char buf[USB_STATUS_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg (uh,
|
||||
USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_OTHER,
|
||||
USB_REQ_GET_STATUS,
|
||||
0, i + 1,
|
||||
buf, USB_STATUS_SIZE,
|
||||
CTRL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"cannot read port %d status, %s (%d)\n",
|
||||
i + 1, strerror(errno), errno);
|
||||
break;
|
||||
}
|
||||
|
||||
printf(" Port %d: %02x%02x.%02x%02x", i + 1,
|
||||
buf[3], buf [2],
|
||||
buf[1], buf [0]);
|
||||
|
||||
printf("%s%s%s%s%s",
|
||||
(buf[2] & 0x10) ? " C_RESET" : "",
|
||||
(buf[2] & 0x08) ? " C_OC" : "",
|
||||
(buf[2] & 0x04) ? " C_SUSPEND" : "",
|
||||
(buf[2] & 0x02) ? " C_ENABLE" : "",
|
||||
(buf[2] & 0x01) ? " C_CONNECT" : "");
|
||||
|
||||
printf("%s%s%s%s%s%s%s%s%s%s\n",
|
||||
(buf[1] & 0x10) ? " indicator" : "",
|
||||
(buf[1] & 0x08) ? " test" : "",
|
||||
(buf[1] & 0x04) ? " highspeed" : "",
|
||||
(buf[1] & 0x02) ? " lowspeed" : "",
|
||||
(buf[1] & 0x01) ? " power" : "",
|
||||
(buf[0] & 0x10) ? " RESET" : "",
|
||||
(buf[0] & 0x08) ? " oc" : "",
|
||||
(buf[0] & 0x04) ? " suspend" : "",
|
||||
(buf[0] & 0x02) ? " enable" : "",
|
||||
(buf[0] & 0x01) ? " connect" : "");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
usb_find_hubs (int listing, int verbose, int busnum, int devnum, int hub)
|
||||
{
|
||||
struct usb_bus *busses;
|
||||
struct usb_bus *bus;
|
||||
|
||||
number_of_hubs_with_feature = 0;
|
||||
busses = usb_get_busses();
|
||||
if (busses == NULL)
|
||||
{
|
||||
perror ("failed to access USB");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (bus = busses; bus; bus = bus->next)
|
||||
{
|
||||
struct usb_device *dev;
|
||||
|
||||
for (dev = bus->devices; dev; dev = dev->next)
|
||||
{
|
||||
usb_dev_handle *uh;
|
||||
int print = 0;
|
||||
|
||||
if (dev->descriptor.bDeviceClass != USB_CLASS_HUB)
|
||||
continue;
|
||||
|
||||
if (listing
|
||||
|| (verbose
|
||||
&& ((atoi (bus->dirname) == busnum && dev->devnum == devnum)
|
||||
|| hub == number_of_hubs_with_feature)))
|
||||
print = 1;
|
||||
|
||||
uh = usb_open (dev);
|
||||
|
||||
if (uh != NULL)
|
||||
{
|
||||
char buf[1024];
|
||||
int len;
|
||||
int nport;
|
||||
struct usb_hub_descriptor *uhd = (struct usb_hub_descriptor *)buf;
|
||||
if ((len = usb_control_msg (uh, USB_DIR_IN | USB_RT_HUB,
|
||||
USB_REQ_GET_DESCRIPTOR,
|
||||
USB_DT_HUB << 8, 0,
|
||||
buf, sizeof (buf), CTRL_TIMEOUT))
|
||||
> sizeof (struct usb_hub_descriptor))
|
||||
{
|
||||
if (!(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND)
|
||||
&& (uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM) >= 2)
|
||||
continue;
|
||||
|
||||
if (print)
|
||||
printf ("Hub #%d at %s:%03d\n",
|
||||
number_of_hubs_with_feature,
|
||||
bus->dirname, dev->devnum);
|
||||
|
||||
switch ((uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM))
|
||||
{
|
||||
case 0:
|
||||
if (print)
|
||||
fprintf (stderr, " INFO: ganged switching.\n");
|
||||
break;
|
||||
case 1:
|
||||
if (print)
|
||||
fprintf (stderr, " INFO: individual power switching.\n");
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (print)
|
||||
fprintf (stderr, " WARN: No power switching.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (print
|
||||
&& !(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND))
|
||||
fprintf (stderr, " WARN: Port indicators are NOT supported.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
perror ("Can't get hub descriptor");
|
||||
usb_close (uh);
|
||||
continue;
|
||||
}
|
||||
|
||||
nport = buf[2];
|
||||
hubs[number_of_hubs_with_feature].busnum = atoi (bus->dirname);
|
||||
hubs[number_of_hubs_with_feature].devnum = dev->devnum;
|
||||
hubs[number_of_hubs_with_feature].dev = dev;
|
||||
hubs[number_of_hubs_with_feature].indicator_support =
|
||||
(uhd->wHubCharacteristics[0] & HUB_CHAR_PORTIND)? 1 : 0;
|
||||
hubs[number_of_hubs_with_feature].nport = nport;
|
||||
|
||||
number_of_hubs_with_feature++;
|
||||
|
||||
if (verbose)
|
||||
hub_port_status (uh, nport);
|
||||
|
||||
usb_close (uh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return number_of_hubs_with_feature;
|
||||
}
|
||||
|
||||
int
|
||||
get_hub (int busnum, int devnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < number_of_hubs_with_feature; i++)
|
||||
if (hubs[i].busnum == busnum && hubs[i].devnum == devnum)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* HUB-CTRL - program to control port power/led of USB hub
|
||||
*
|
||||
* # hub-ctrl // List hubs available
|
||||
* # hub-ctrl -P 1 // Power off at port 1
|
||||
* # hub-ctrl -P 1 -p 1 // Power on at port 1
|
||||
* # hub-ctrl -P 2 -l // LED on at port 1
|
||||
*
|
||||
* Requirement: USB hub which implements port power control / indicator control
|
||||
*
|
||||
* Work fine:
|
||||
* Elecom's U2H-G4S: www.elecom.co.jp (indicator depends on power)
|
||||
* 04b4:6560
|
||||
*
|
||||
* Sanwa Supply's USB-HUB14GPH: www.sanwa.co.jp (indicators don't)
|
||||
*
|
||||
* Targus, Inc.'s PAUH212: www.targus.com (indicators don't)
|
||||
* 04cc:1521
|
||||
*
|
||||
* Hawking Technology's UH214: hawkingtech.com (indicators don't)
|
||||
*
|
||||
*/
|
||||
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
int busnum = 0, devnum = 0;
|
||||
int cmd = COMMAND_SET_NONE;
|
||||
int port = 1;
|
||||
int value = 0;
|
||||
int request, feature, index;
|
||||
int result = 0;
|
||||
int listing = 0;
|
||||
int verbose = 0;
|
||||
int hub = -1;
|
||||
usb_dev_handle *uh = NULL;
|
||||
int i;
|
||||
|
||||
if (argc == 1)
|
||||
listing = 1;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
if (argv[i][0] == '-')
|
||||
switch (argv[i][1])
|
||||
{
|
||||
case 'h':
|
||||
if (++i >= argc || busnum > 0 || devnum > 0)
|
||||
exit_with_usage (argv[0]);
|
||||
hub = atoi (argv[i]);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
if (++i >= argc || hub >= 0)
|
||||
exit_with_usage (argv[0]);
|
||||
busnum = atoi (argv[i]);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
if (++i >= argc || hub >= 0)
|
||||
exit_with_usage (argv[0]);
|
||||
devnum = atoi (argv[i]);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
if (++i >= argc)
|
||||
exit_with_usage (argv[0]);
|
||||
port = atoi (argv[i]);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (cmd != COMMAND_SET_NONE)
|
||||
exit_with_usage (argv[0]);
|
||||
if (++i < argc)
|
||||
value = atoi (argv[i]);
|
||||
else
|
||||
value = HUB_LED_GREEN;
|
||||
cmd = COMMAND_SET_LED;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (cmd != COMMAND_SET_NONE)
|
||||
exit_with_usage (argv[0]);
|
||||
if (++i < argc)
|
||||
value = atoi (argv[i]);
|
||||
else
|
||||
value= 0;
|
||||
cmd = COMMAND_SET_POWER;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
if (argc == 2)
|
||||
listing = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
exit_with_usage (argv[0]);
|
||||
}
|
||||
else
|
||||
exit_with_usage (argv[0]);
|
||||
|
||||
if ((busnum > 0 && devnum <= 0) || (busnum <= 0 && devnum > 0))
|
||||
/* BUS is specified, but DEV is'nt, or ... */
|
||||
exit_with_usage (argv[0]);
|
||||
|
||||
/* Default is the hub #0 */
|
||||
if (hub < 0 && busnum == 0)
|
||||
hub = 0;
|
||||
|
||||
/* Default is POWER */
|
||||
if (cmd == COMMAND_SET_NONE)
|
||||
cmd = COMMAND_SET_POWER;
|
||||
|
||||
usb_init ();
|
||||
usb_find_busses ();
|
||||
usb_find_devices ();
|
||||
|
||||
if (usb_find_hubs (listing, verbose, busnum, devnum, hub) <= 0)
|
||||
{
|
||||
fprintf (stderr, "No hubs found.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (listing)
|
||||
exit (0);
|
||||
|
||||
if (hub < 0)
|
||||
hub = get_hub (busnum, devnum);
|
||||
|
||||
if (hub >= 0 && hub < number_of_hubs_with_feature)
|
||||
uh = usb_open (hubs[hub].dev);
|
||||
|
||||
if (uh == NULL)
|
||||
{
|
||||
fprintf (stderr, "Device not found.\n");
|
||||
result = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmd == COMMAND_SET_POWER)
|
||||
if (value)
|
||||
{
|
||||
request = USB_REQ_SET_FEATURE;
|
||||
feature = USB_PORT_FEAT_POWER;
|
||||
index = port;
|
||||
}
|
||||
else
|
||||
{
|
||||
request = USB_REQ_CLEAR_FEATURE;
|
||||
feature = USB_PORT_FEAT_POWER;
|
||||
index = port;
|
||||
}
|
||||
else
|
||||
{
|
||||
request = USB_REQ_SET_FEATURE;
|
||||
feature = USB_PORT_FEAT_INDICATOR;
|
||||
index = (value << 8) | port;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printf ("Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d)\n",
|
||||
request, feature, index);
|
||||
|
||||
if (usb_control_msg (uh, USB_RT_PORT, request, feature, index,
|
||||
NULL, 0, CTRL_TIMEOUT) < 0)
|
||||
{
|
||||
perror ("failed to control.\n");
|
||||
result = 1;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
hub_port_status (uh, hubs[hub].nport);
|
||||
|
||||
usb_close (uh);
|
||||
}
|
||||
|
||||
exit (result);
|
||||
}
|
||||
@@ -325,7 +325,7 @@ void usb_init(void)
|
||||
UDCON = 0; // enable attach resistor
|
||||
usb_configuration = 0;
|
||||
cdc_line_rtsdtr = 0;
|
||||
UDIEN = (1<<EORSTE)|(1<<SOFE);
|
||||
UDIEN = (1<<EORSTE);
|
||||
sei();
|
||||
}
|
||||
|
||||
@@ -359,6 +359,7 @@ int16_t usb_serial_getchar(void)
|
||||
UEINTX = 0x6B;
|
||||
goto retry;
|
||||
}
|
||||
UEIENX = (1<<RXOUTE);
|
||||
SREG = intr_state;
|
||||
return -1;
|
||||
}
|
||||
@@ -775,7 +776,14 @@ static inline void usb_ack_out(void)
|
||||
//
|
||||
ISR(USB_COM_vect)
|
||||
{
|
||||
uint8_t intbits;
|
||||
uint8_t intbits = UEINT;
|
||||
if (intbits & (1<<CDC_RX_ENDPOINT)) {
|
||||
UENUM = CDC_RX_ENDPOINT;
|
||||
UEIENX = 0;
|
||||
extern void sched_wake_tasks(void);
|
||||
sched_wake_tasks();
|
||||
return;
|
||||
}
|
||||
const uint8_t *list;
|
||||
const uint8_t *cfg;
|
||||
uint8_t i, n, len, en;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--- usb_serial.c 2011-04-19 05:54:12.000000000 -0400
|
||||
+++ usb_serial.c 2016-06-04 23:48:52.590001697 -0400
|
||||
--- ../../../lib/pjrc/usb_serial/usb_serial.c 2011-04-19 05:54:12.000000000 -0400
|
||||
+++ usb_serial.c 2017-08-07 11:32:47.106357362 -0400
|
||||
@@ -30,6 +30,7 @@
|
||||
// Version 1.6: fix zero length packet bug
|
||||
// Version 1.7: fix usb_serial_set_control
|
||||
@@ -62,7 +62,24 @@
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
const uint8_t *addr;
|
||||
@@ -646,7 +647,9 @@
|
||||
@@ -324,7 +325,7 @@
|
||||
UDCON = 0; // enable attach resistor
|
||||
usb_configuration = 0;
|
||||
cdc_line_rtsdtr = 0;
|
||||
- UDIEN = (1<<EORSTE)|(1<<SOFE);
|
||||
+ UDIEN = (1<<EORSTE);
|
||||
sei();
|
||||
}
|
||||
|
||||
@@ -358,6 +359,7 @@
|
||||
UEINTX = 0x6B;
|
||||
goto retry;
|
||||
}
|
||||
+ UEIENX = (1<<RXOUTE);
|
||||
SREG = intr_state;
|
||||
return -1;
|
||||
}
|
||||
@@ -646,7 +648,9 @@
|
||||
// communication
|
||||
uint32_t usb_serial_get_baud(void)
|
||||
{
|
||||
@@ -73,3 +90,19 @@
|
||||
}
|
||||
uint8_t usb_serial_get_stopbits(void)
|
||||
{
|
||||
@@ -772,7 +776,14 @@
|
||||
//
|
||||
ISR(USB_COM_vect)
|
||||
{
|
||||
- uint8_t intbits;
|
||||
+ uint8_t intbits = UEINT;
|
||||
+ if (intbits & (1<<CDC_RX_ENDPOINT)) {
|
||||
+ UENUM = CDC_RX_ENDPOINT;
|
||||
+ UEIENX = 0;
|
||||
+ extern void sched_wake_tasks(void);
|
||||
+ sched_wake_tasks();
|
||||
+ return;
|
||||
+ }
|
||||
const uint8_t *list;
|
||||
const uint8_t *cfg;
|
||||
uint8_t i, n, len, en;
|
||||
|
||||
25
lib/pru_rpmsg/include/ReadMe.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
Programmable Real-time Unit (PRU) Software Support Package
|
||||
------------------------------------------------------------
|
||||
============================================================
|
||||
INCLUDE
|
||||
============================================================
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This directory provides header files for PRU firmware.
|
||||
|
||||
For more details about these header files, visit:
|
||||
|
||||
http://processors.wiki.ti.com/index.php/PRU-ICSS_Header_Files
|
||||
|
||||
|
||||
|
||||
ADDITIONAL RESOURCES
|
||||
|
||||
For more information about the PRU, visit:
|
||||
|
||||
PRU-ICSS Wiki - http://processors.wiki.ti.com/index.php/PRU-ICSS
|
||||
PRU Training Slides - http://www.ti.com/sitarabootcamp
|
||||
PRU Evaluation Hardware - http://www.ti.com/tool/PRUCAPE
|
||||
Support - http://e2e.ti.com
|
||||
|
||||
249
lib/pru_rpmsg/include/am335x/pru_cfg.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_CFG_H_
|
||||
#define _PRU_CFG_H_
|
||||
|
||||
/* PRU_CFG register set */
|
||||
typedef struct {
|
||||
|
||||
/* PRU_CFG_REVID register bit field */
|
||||
union {
|
||||
volatile uint32_t REVID;
|
||||
|
||||
volatile struct {
|
||||
unsigned REVID : 32;
|
||||
} REVID_bit;
|
||||
}; // 0x0
|
||||
|
||||
|
||||
/* PRU_CFG_SYSCFG register bit field */
|
||||
union {
|
||||
volatile uint32_t SYSCFG;
|
||||
|
||||
volatile struct {
|
||||
unsigned IDLE_MODE : 2;
|
||||
unsigned STANDBY_MODE : 2;
|
||||
unsigned STANDBY_INIT : 1;
|
||||
unsigned SUB_MWAIT : 1;
|
||||
unsigned rsvd6 : 26;
|
||||
} SYSCFG_bit;
|
||||
}; // 0x4
|
||||
|
||||
|
||||
/* PRU_CFG_GPCFG0 register bit field */
|
||||
union {
|
||||
volatile uint32_t GPCFG0;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_GPI_MODE : 2; // 1:0
|
||||
unsigned PRU0_GPI_CLK_MODE : 1; // 2
|
||||
unsigned PRU0_GPI_DIV0 : 5; // 7:3
|
||||
unsigned PRU0_GPI_DIV1 : 5; // 12:8
|
||||
unsigned PRU0_GPI_SB : 1; // 13
|
||||
unsigned PRU0_GPO_MODE : 1; // 14
|
||||
unsigned PRU0_GPO_DIV0 : 5; // 19:15
|
||||
unsigned PRU0_GPO_DIV1 : 5; // 24:20
|
||||
unsigned PRU0_GPO_SH_SEL : 1; // 25
|
||||
unsigned rsvd26 : 6; // 31:26
|
||||
} GPCFG0_bit;
|
||||
}; // 0x8
|
||||
|
||||
|
||||
/* PRU_CFG_GPCFG1 register bit field */
|
||||
union {
|
||||
volatile uint32_t GPCFG1;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU1_GPI_MODE : 2; // 1:0
|
||||
unsigned PRU1_GPI_CLK_MODE : 1; // 2
|
||||
unsigned PRU1_GPI_DIV0 : 5; // 7:3
|
||||
unsigned PRU1_GPI_DIV1 : 5; // 12:8
|
||||
unsigned PRU1_GPI_SB : 1; // 13
|
||||
unsigned PRU1_GPO_MODE : 1; // 14
|
||||
unsigned PRU1_GPO_DIV0 : 5; // 19:15
|
||||
unsigned PRU1_GPO_DIV1 : 5; // 24:20
|
||||
unsigned PRU1_GPO_SH_SEL : 1; // 25
|
||||
unsigned rsvd26 : 6; // 31:26
|
||||
} GPCFG1_bit;
|
||||
}; // 0xC
|
||||
|
||||
|
||||
/* PRU_CFG_CGR register bit field */
|
||||
union {
|
||||
volatile uint32_t CGR;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_CLK_STOP_REQ : 1; // 0
|
||||
unsigned PRU0_CLK_STOP_ACK : 1; // 1
|
||||
unsigned PRU0_CLK_EN : 1; // 2
|
||||
unsigned PRU1_CLK_STOP_REQ : 1; // 3
|
||||
unsigned PRU1_CLK_STOP_ACK : 1; // 4
|
||||
unsigned PRU1_CLK_EN : 1; // 5
|
||||
unsigned INTC_CLK_STOP_REQ : 1; // 6
|
||||
unsigned INTC_CLK_STOP_ACK : 1; // 7
|
||||
unsigned INTC_CLK_EN : 1; // 8
|
||||
unsigned UART_CLK_STOP_REQ : 1; // 9
|
||||
unsigned UART_CLK_STOP_ACK : 1; // 10
|
||||
unsigned UART_CLK_EN : 1; // 11
|
||||
unsigned ECAP_CLK_STOP_REQ : 1; // 12
|
||||
unsigned ECAP_CLK_STOP_ACK : 1; // 13
|
||||
unsigned ECAP_CLK_EN : 1; // 14
|
||||
unsigned IEP_CLK_STOP_REQ : 1; // 15
|
||||
unsigned IEP_CLK_STOP_ACK : 1; // 16
|
||||
unsigned IEP_CLK_EN : 1; // 17
|
||||
unsigned rsvd18 : 14; // 31:18
|
||||
} CGR_bit;
|
||||
}; // 0x10
|
||||
|
||||
|
||||
/* PRU_CFG_ISRP register bit field */
|
||||
union {
|
||||
volatile uint32_t ISRP;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_IMEM_PE_RAW : 4; // 3:0
|
||||
unsigned PRU0_DMEM_PE_RAW : 4; // 7:4
|
||||
unsigned PRU1_IMEM_PE_RAW : 4; // 11:8
|
||||
unsigned PRU1_DMEM_PE_RAW : 4; // 15:12
|
||||
unsigned RAM_PE_RAW : 4; // 19:16
|
||||
unsigned rsvd20 : 12; // 31:20
|
||||
} ISRP_bit;
|
||||
}; // 0x14
|
||||
|
||||
|
||||
/* PRU_CFG_ISP register bit field */
|
||||
union {
|
||||
volatile uint32_t ISP;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_IMEM_PE : 4; // 3:0
|
||||
unsigned PRU0_DMEM_PE : 4; // 7:4
|
||||
unsigned PRU1_IMEM_PE : 4; // 11:8
|
||||
unsigned PRU1_DMEM_PE : 4; // 15:12
|
||||
unsigned RAM_PE : 4; // 19:16
|
||||
unsigned rsvd20 : 12; // 31:20
|
||||
} ISP_bit;
|
||||
}; // 0x18
|
||||
|
||||
/* PRU_CFG_IESP register bit field */
|
||||
union {
|
||||
volatile uint32_t IESP;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_IMEM_PE_SET : 4; // 3:0
|
||||
unsigned PRU0_DMEM_PE_SET : 4; // 7:4
|
||||
unsigned PRU1_IMEM_PE_SET : 4; // 11:8
|
||||
unsigned PRU1_DMEM_PE_SET : 4; // 15:12
|
||||
unsigned RAM_PE_SET : 4; // 19:16
|
||||
unsigned rsvd20 : 12; // 31:20
|
||||
} IESP_bit;
|
||||
}; // 0x1C
|
||||
|
||||
|
||||
/* PRU_CFG_IECP register bit field */
|
||||
union {
|
||||
volatile uint32_t IECP;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU0_IMEM_PE_CLR : 4; // 3:0
|
||||
unsigned PRU0_DMEM_PE_CLR : 4; // 7:4
|
||||
unsigned PRU1_IMEM_PE_CLR : 4; // 11:8
|
||||
unsigned PRU1_DMEM_PE_CLR : 4; // 15:12
|
||||
unsigned rsvd16 : 16; // 31:16
|
||||
} IECP_bit;
|
||||
}; // 0x20
|
||||
|
||||
|
||||
uint32_t rsvd24; // 0x24
|
||||
|
||||
|
||||
/* PRU_CFG_PMAO register bit field */
|
||||
union {
|
||||
volatile uint32_t PMAO;
|
||||
|
||||
volatile struct {
|
||||
unsigned PMAO_PRU0 : 1; // 0
|
||||
unsigned PMAO_PRU1 : 1; // 1
|
||||
unsigned rsvd2 : 30; // 31:2
|
||||
} PMAO_bit;
|
||||
}; // 0x28
|
||||
|
||||
|
||||
uint32_t rsvd2c[1]; // 0x2C
|
||||
|
||||
|
||||
/* PRU_CFG_IEPCLK register bit field */
|
||||
union {
|
||||
volatile uint32_t IEPCLK;
|
||||
|
||||
volatile struct {
|
||||
unsigned OCP_EN : 1; // 0
|
||||
unsigned rsvd1 : 31; // 31:1
|
||||
} IEPCLK_bit;
|
||||
}; // 0x30
|
||||
|
||||
|
||||
/* PRU_CFG_SPP register bit field */
|
||||
union {
|
||||
volatile uint32_t SPP;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRU1_PAD_HP_EN : 1; // 0
|
||||
unsigned XFR_SHIFT_EN : 1; // 1
|
||||
unsigned rsvd2 : 30; // 31:2
|
||||
} SPP_bit;
|
||||
}; // 0x34
|
||||
|
||||
|
||||
uint32_t rsvd38[2]; // 0x38 - 0x3C
|
||||
|
||||
|
||||
union {
|
||||
volatile uint32_t PIN_MX;
|
||||
|
||||
volatile struct {
|
||||
unsigned PIN_MUX_SEL : 8; // 7:0
|
||||
unsigned rsvd2 : 24; // 31:8
|
||||
} PIN_MX_bit;
|
||||
}; //0x40
|
||||
} pruCfg;
|
||||
|
||||
#ifdef __GNUC__
|
||||
static volatile pruCfg *__CT_CFG = (void *)0x00026000;
|
||||
#define CT_CFG (*__CT_CFG)
|
||||
#else
|
||||
volatile __far pruCfg CT_CFG __attribute__((cregister("PRU_CFG", near), peripheral));
|
||||
#endif
|
||||
|
||||
#endif /* _PRU_CFG_H_ */
|
||||
155
lib/pru_rpmsg/include/am335x/pru_ctrl.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_CTRL_H_
|
||||
#define _PRU_CTRL_H_
|
||||
|
||||
/* PRU_CTRL register set */
|
||||
typedef struct {
|
||||
|
||||
/* PRU_CTRL_CTRL register bit field */
|
||||
union {
|
||||
volatile uint32_t CTRL;
|
||||
|
||||
volatile struct {
|
||||
unsigned SOFT_RST_N : 1;
|
||||
unsigned EN : 1;
|
||||
unsigned SLEEPING : 1;
|
||||
unsigned CTR_EN : 1;
|
||||
unsigned rsvd4 : 4;
|
||||
unsigned SINGLE_STEP : 1;
|
||||
unsigned rsvd9 : 6;
|
||||
unsigned RUNSTATE : 1;
|
||||
unsigned PCTR_RST_VAL : 16;
|
||||
} CTRL_bit;
|
||||
}; // 0x0
|
||||
|
||||
|
||||
/* PRU_CTRL_STS register bit field */
|
||||
union {
|
||||
volatile uint32_t STS;
|
||||
|
||||
volatile struct {
|
||||
unsigned PCTR : 16;
|
||||
unsigned rsvd16 : 16;
|
||||
} STS_bit;
|
||||
}; // 0x4
|
||||
|
||||
|
||||
/* PRU_CTRL_WAKEUP_EN register bit field */
|
||||
union {
|
||||
volatile uint32_t WAKEUP_EN;
|
||||
|
||||
volatile struct {
|
||||
unsigned BITWISE_ENS : 32;
|
||||
} WAKEUP_EN_bit;
|
||||
}; // 0x8
|
||||
|
||||
|
||||
/* PRU_CTRL_CYCLE register bit field */
|
||||
union {
|
||||
volatile uint32_t CYCLE;
|
||||
|
||||
volatile struct {
|
||||
unsigned CYCLECOUNT : 32;
|
||||
} CYCLE_bit;
|
||||
}; // 0xC
|
||||
|
||||
|
||||
/* PRU_CTRL_STALL register bit field */
|
||||
union {
|
||||
volatile uint32_t STALL;
|
||||
|
||||
volatile struct {
|
||||
unsigned STALLCOUNT : 32;
|
||||
} STALL_bit;
|
||||
}; // 0x10
|
||||
|
||||
|
||||
uint32_t rsvd14[3]; // 0x14 - 0x1C
|
||||
|
||||
|
||||
/* PRU_CTRL_CTBIR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t CTBIR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned C24_BLK_IDX : 8;
|
||||
unsigned rsvd8 : 8;
|
||||
unsigned C25_BLK_IDX : 8;
|
||||
unsigned rsvd24 : 8;
|
||||
} CTBIR0_bit;
|
||||
}; // 0x20
|
||||
|
||||
|
||||
/* PRU_CTRL_CTBIR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t CTBIR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned C26_BLK_IDX : 8;
|
||||
unsigned rsvd8 : 8;
|
||||
unsigned C27_BLK_IDX : 8;
|
||||
unsigned rsvd24 : 8;
|
||||
} CTBIR1_bit;
|
||||
}; // 0x24
|
||||
|
||||
|
||||
/* PRU_CTRL_CTPPR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t CTPPR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned C28_BLK_POINTER : 16;
|
||||
unsigned C29_BLK_POINTER : 16;
|
||||
} CTPPR0_bit;
|
||||
}; // 0x28
|
||||
|
||||
|
||||
/* PRU_CTRL_CTPPR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t CTPPR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned C30_BLK_POINTER : 16;
|
||||
unsigned C31_BLK_POINTER : 16;
|
||||
} CTPPR1_bit;
|
||||
}; // 0x2C
|
||||
|
||||
} pruCtrl;
|
||||
|
||||
/* Definition of control register structures. */
|
||||
#define PRU0_CTRL (*((volatile pruCtrl*)0x22000))
|
||||
#define PRU1_CTRL (*((volatile pruCtrl*)0x24000))
|
||||
|
||||
#endif /* _PRU_CTRL_H_ */
|
||||
128
lib/pru_rpmsg/include/am335x/pru_ecap.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_ECAP_H_
|
||||
#define _PRU_ECAP_H_
|
||||
|
||||
/* PRU_ECAP register set */
|
||||
typedef struct {
|
||||
|
||||
/* PRU_ECAP_TSCTR register bit field */
|
||||
union {
|
||||
volatile uint32_t TSCTR;
|
||||
|
||||
volatile struct {
|
||||
unsigned TSCTR : 32; //31:0
|
||||
} TSCTR_bit;
|
||||
}; // 0x0
|
||||
|
||||
/* PRU_ECAP_CTRPHS register bit field */
|
||||
union {
|
||||
volatile uint32_t CTRPHS;
|
||||
|
||||
volatile struct {
|
||||
unsigned CTRPHS : 32; //31:0
|
||||
} CTRPHS_bit;
|
||||
}; // 0x4
|
||||
|
||||
/* PRU_ECAP_CAP1 register bit field */
|
||||
union {
|
||||
volatile uint32_t CAP1;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP1 : 32; //31:0
|
||||
} CAP1_bit;
|
||||
}; // 0x8
|
||||
|
||||
/* PRU_ECAP_CAP2 register bit field */
|
||||
union {
|
||||
volatile uint32_t CAP2;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP2 : 32; //31:0
|
||||
} CAP2_bit;
|
||||
}; // 0xC
|
||||
|
||||
/* PRU_ECAP_CAP3 register bit field */
|
||||
union {
|
||||
volatile uint32_t CAP3;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP3 : 32; //31:0
|
||||
} CAP3_bit;
|
||||
}; // 0x10
|
||||
|
||||
/* PRU_ECAP_CAP4 register bit field */
|
||||
union {
|
||||
volatile uint32_t CAP4;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP4 : 32; //31:0
|
||||
} CAP4_bit;
|
||||
}; // 0x14
|
||||
|
||||
uint32_t rsvd118[4]; // 0x118 - 0x124
|
||||
|
||||
/* PRU_ECAP_ECCTL1 register bit field */
|
||||
volatile uint16_t ECCTL1; // 0x28
|
||||
|
||||
/* PRU_ECAP_ECCTL2 register bit field */
|
||||
volatile uint16_t ECCTL2; // 0x2A
|
||||
|
||||
/* PRU_ECAP_ECEINT register bit field */
|
||||
volatile uint16_t ECEINT; // 0x2C
|
||||
|
||||
/* PRU_ECAP_ECFLG register bit field */
|
||||
volatile uint16_t ECFLG; // 0x2E
|
||||
|
||||
/* PRU_ECAP_ECCLR register bit field */
|
||||
volatile uint16_t ECCLR; // 0x30
|
||||
|
||||
/* PRU_ECAP_ECFRC register bit field */
|
||||
volatile uint16_t ECFRC; // 0x32
|
||||
|
||||
uint32_t rsvd34[10]; // 0x34 - 0x58
|
||||
|
||||
/* PRU_ECAP_REVID register bit field */
|
||||
union {
|
||||
volatile uint32_t REVID;
|
||||
|
||||
volatile struct {
|
||||
unsigned REV : 32; //31:0
|
||||
} REVID_bit;
|
||||
}; // 0x5C
|
||||
} pruEcap;
|
||||
|
||||
volatile __far pruEcap CT_ECAP __attribute__((cregister("PRU_ECAP", near), peripheral));
|
||||
|
||||
#endif /* _PRU_ECAP_H_ */
|
||||
261
lib/pru_rpmsg/include/am335x/pru_iep.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_IEP_H_
|
||||
#define _PRU_IEP_H_
|
||||
|
||||
/* PRU IEP register set */
|
||||
typedef struct {
|
||||
|
||||
/* PRU_IEP_TMR_GLB_CFG register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_GLB_CFG;
|
||||
|
||||
volatile struct {
|
||||
unsigned CNT_EN : 1; // 0
|
||||
unsigned rsvd1 : 3; // 3:1
|
||||
unsigned DEFAULT_INC : 4; // 7:4
|
||||
unsigned CMP_INC : 12; // 19:8
|
||||
unsigned rsvd12 : 12; // 31:20
|
||||
} TMR_GLB_CFG_bit;
|
||||
}; // 0x0
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_GLB_STS register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_GLB_STS;
|
||||
|
||||
volatile struct {
|
||||
unsigned CNT_OVF : 1; // 0
|
||||
unsigned rsvd1 : 31; // 31:1
|
||||
} TMR_GLB_STS_bit;
|
||||
}; // 0x4
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_COMPEN register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_COMPEN;
|
||||
|
||||
volatile struct {
|
||||
unsigned COMPEN_CNT : 24; // 23:0
|
||||
unsigned rsvd24 : 8; // 31:24
|
||||
} TMR_COMPEN_bit;
|
||||
}; // 0x8
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CNT register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CNT;
|
||||
|
||||
volatile struct {
|
||||
unsigned COUNT : 32; // 31:0
|
||||
} TMR_CNT_bit;
|
||||
}; // 0xC
|
||||
|
||||
|
||||
uint32_t rsvd10[12]; // 0x10 - 0x3C
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP_CFG register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP_CFG;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP0_RST_CNT_EN : 1; // 0
|
||||
unsigned CMP_EN : 8; // 8:1
|
||||
unsigned rsvd9 : 23; // 31:9
|
||||
} TMR_CMP_CFG_bit;
|
||||
}; // 0x40
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP_STS register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP_STS;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP_HIT : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} TMR_CMP_STS_bit;
|
||||
}; // 0x44
|
||||
|
||||
/* PRU_IEP_TMR_CMP0 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP0;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP0 : 32; // 31:0
|
||||
} TMR_CMP0_bit;
|
||||
}; // 0x48
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP1 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP1;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP1 : 32; // 31:0
|
||||
} TMR_CMP1_bit;
|
||||
}; // 0x4C
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP2 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP2;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP2 : 32; // 31:0
|
||||
} TMR_CMP2_bit;
|
||||
}; // 0x50
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP3 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP3;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP3 : 32; // 31:0
|
||||
} TMR_CMP3_bit;
|
||||
}; // 0x54
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP4 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP4;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP4 : 32; // 31:0
|
||||
} TMR_CMP4_bit;
|
||||
}; // 0x58
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP5 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP5;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP5 : 32; // 31:0
|
||||
} TMR_CMP5_bit;
|
||||
}; // 0x5C
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP6 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP6;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP6 : 32; // 31:0
|
||||
} TMR_CMP6_bit;
|
||||
}; // 0x60
|
||||
|
||||
|
||||
/* PRU_IEP_TMR_CMP7 register bit field */
|
||||
union {
|
||||
volatile uint32_t TMR_CMP7;
|
||||
|
||||
volatile struct {
|
||||
unsigned CMP7 : 32; // 31:0
|
||||
} TMR_CMP7_bit;
|
||||
}; // 0x64
|
||||
|
||||
uint32_t rsvd68[166]; // 0x68 - 0x2FF
|
||||
|
||||
/* PRU_IEP_DIGIO_CTRL register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_CTRL;
|
||||
|
||||
volatile struct {
|
||||
unsigned RESERVED0 : 4; // 3:0
|
||||
unsigned IN_MODE : 1; // 4
|
||||
unsigned RESERVED5 : 27; // 31:5
|
||||
} DIGIO_CTRL_bit;
|
||||
}; // 0x300
|
||||
|
||||
uint32_t rsvd304[1]; // 0x304
|
||||
|
||||
/* PRU_IEP_DIGIO_DATA_IN register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_DATA_IN;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA_IN : 32; // 31:0
|
||||
} DIGIO_DATA_IN_bit;
|
||||
}; // 0x308
|
||||
|
||||
/* PRU_IEP_DIGIO_DATA_IN_RAW register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_DATA_IN_RAW;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA_IN_RAW : 32; // 31:0
|
||||
} DIGIO_DATA_IN_RAW_bit;
|
||||
}; // 0x30C
|
||||
|
||||
/* PRU_IEP_DIGIO_DATA_OUT register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_DATA_OUT;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA_OUT : 32; // 31:0
|
||||
} DIGIO_DATA_OUT_bit;
|
||||
}; // 0x310
|
||||
|
||||
/* PRU_IEP_DIGIO_DATA_OUT_EN register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_DATA_OUT_EN;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA_OUT_EN : 32; // 31:0
|
||||
} DIGIO_DATA_OUT_EN_bit;
|
||||
}; // 0x314
|
||||
|
||||
/* PRU_IEP_DIGIO_EXP register bit field */
|
||||
union {
|
||||
volatile uint32_t DIGIO_EXP;
|
||||
|
||||
volatile struct {
|
||||
unsigned SW_DATA_OUT_UPDATE : 1; // 0
|
||||
unsigned OUTVALID_OVR_EN : 1; // 1
|
||||
unsigned RESERVED2 : 30; // 31:2
|
||||
} DIGIO_EXP_bit;
|
||||
}; // 0x318
|
||||
|
||||
} pruIep;
|
||||
|
||||
#ifdef __GNUC__
|
||||
static volatile pruIep *__CT_IEP = (void *)0x0002e000;
|
||||
#define CT_IEP (*__CT_IEP)
|
||||
#else
|
||||
volatile __far pruIep CT_IEP __attribute__((cregister("PRU_IEP", far), peripheral));
|
||||
#endif
|
||||
|
||||
#endif /* _PRU_IEP_H_ */
|
||||
912
lib/pru_rpmsg/include/am335x/pru_intc.h
Normal file
@@ -0,0 +1,912 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_INTC_H_
|
||||
#define _PRU_INTC_H_
|
||||
|
||||
/* PRU INTC register set */
|
||||
typedef struct {
|
||||
|
||||
/* PRU_INTC_REVID register bit field */
|
||||
union {
|
||||
volatile uint32_t REVID;
|
||||
|
||||
volatile struct {
|
||||
unsigned REV_MINOR : 6; // 5:0
|
||||
unsigned REV_CUSTOM : 2; // 7:6
|
||||
unsigned REV_MAJOR : 3; // 10:8
|
||||
unsigned REV_RTL : 5; // 15:11
|
||||
unsigned REV_MODULE : 12; // 27:16
|
||||
unsigned rsvd28 : 2; // 29:28
|
||||
unsigned REV_SCHEME : 2; // 31:30
|
||||
} REVID_bit;
|
||||
}; // 0x0
|
||||
|
||||
|
||||
/* PRU_INTC_CR register bit field */
|
||||
union {
|
||||
volatile uint32_t CR;
|
||||
|
||||
volatile struct {
|
||||
unsigned rsvd0 : 2; // 1:0
|
||||
unsigned NEST_MODE : 2; // 3:2
|
||||
unsigned rsvd4 : 28; // 31:4
|
||||
} CR_bit;
|
||||
}; // 0x4
|
||||
|
||||
|
||||
uint32_t rsvd8[2]; // 0x8 - 0xC
|
||||
|
||||
|
||||
/* PRU_INTC_GER register bit field */
|
||||
union {
|
||||
volatile uint32_t GER;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_HINT_ANY : 1; // 0
|
||||
unsigned rsvd1 : 31; // 31:1
|
||||
} GER_bit;
|
||||
}; // 0x10
|
||||
|
||||
|
||||
uint32_t rsvd14[2]; // 0x14 - 0x18
|
||||
|
||||
|
||||
/* PRU_INTC_GNLR register bit field */
|
||||
union {
|
||||
volatile uint32_t GNLR;
|
||||
|
||||
volatile struct {
|
||||
unsigned GLB_NEST_LEVEL : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} GNLR_bit;
|
||||
}; // 0x1C
|
||||
|
||||
|
||||
/* PRU_INTC_SISR register bit field */
|
||||
union {
|
||||
volatile uint32_t SISR;
|
||||
|
||||
volatile struct {
|
||||
unsigned STS_SET_IDX : 10; // 9:0
|
||||
unsigned rsvd10 : 22; // 31:10
|
||||
} SISR_bit;
|
||||
}; // 0x20
|
||||
|
||||
|
||||
/* PRU_INTC_SICR register bit field */
|
||||
union {
|
||||
volatile uint32_t SICR;
|
||||
|
||||
volatile struct {
|
||||
unsigned STS_CLR_IDX : 10; // 9:0
|
||||
unsigned rsvd10 : 22; // 31:10
|
||||
} SICR_bit;
|
||||
}; // 0x24
|
||||
|
||||
|
||||
/* PRU_INTC_EISR register bit field */
|
||||
union {
|
||||
volatile uint32_t EISR;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_SET_IDX : 10; // 9:0
|
||||
unsigned rsvd10 : 22; // 31:10
|
||||
} EISR_bit;
|
||||
}; // 0x28
|
||||
|
||||
|
||||
/* PRU_INTC_EICR register bit field */
|
||||
union {
|
||||
volatile uint32_t EICR;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_CLR_IDX : 10; // 9:0
|
||||
unsigned rsvd10 : 22; // 31:10
|
||||
} EICR_bit;
|
||||
}; // 0x2C
|
||||
|
||||
|
||||
uint32_t rsvd30; // 0x30
|
||||
|
||||
|
||||
/* PRU_INTC_HIEISR register bit field */
|
||||
union {
|
||||
volatile uint32_t HIEISR;
|
||||
|
||||
volatile struct {
|
||||
unsigned HINT_EN_SET_IDX : 4; // 3:0
|
||||
unsigned rsvd4 : 28; // 31:4
|
||||
} HIEISR_bit;
|
||||
}; // 0x34
|
||||
|
||||
|
||||
/* PRU_INTC_HIDISR register bit field */
|
||||
union {
|
||||
volatile uint32_t HIDISR;
|
||||
|
||||
volatile struct {
|
||||
unsigned HINT_EN_CLR_IDX : 4; // 3:0
|
||||
unsigned rsvd4 : 28; // 31:4
|
||||
} HIDISR_bit;
|
||||
}; // 0x38
|
||||
|
||||
|
||||
uint32_t rsvd3C[17]; // 0x3C - 0x7C
|
||||
|
||||
|
||||
/* PRU_INTC_GPIR register bit field */
|
||||
union {
|
||||
volatile uint32_t GPIR;
|
||||
|
||||
volatile struct {
|
||||
unsigned GLB_PRI_INTR : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned GLB_NONE : 1; // 31
|
||||
} GPIR_bit;
|
||||
}; // 0x80
|
||||
|
||||
|
||||
uint32_t rsvd84[95]; // 0x84 - 0x1FC
|
||||
|
||||
|
||||
/* PRU_INTC_SRSR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t SRSR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned RAW_STS_31_0 : 32; // 31:0
|
||||
} SRSR0_bit;
|
||||
}; // 0x200
|
||||
|
||||
|
||||
/* PRU_INTC_SRSR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t SRSR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned RAW_STS_63_32 : 32; // 31:0
|
||||
} SRSR1_bit;
|
||||
}; // 0x204
|
||||
|
||||
|
||||
uint32_t rsvd208[30]; // 0x208 - 0x27C
|
||||
|
||||
|
||||
/* PRU_INTC_SECR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t SECR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned ENA_STS_31_0 : 32; // 31:0
|
||||
} SECR0_bit;
|
||||
}; // 0x280
|
||||
|
||||
|
||||
/* PRU_INTC_SECR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t SECR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned ENA_STS_63_32 : 32; // 31:0
|
||||
} SECR1_bit;
|
||||
}; // 0x284
|
||||
|
||||
|
||||
uint32_t rsvd288[30]; // 0x288 - 0x2FC
|
||||
|
||||
|
||||
/* PRU_INTC_ESR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t ESR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_SET_31_0 : 32; // 31:0
|
||||
} ESR0_bit;
|
||||
}; // 0x300
|
||||
|
||||
|
||||
/* PRU_INTC_ESR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t ESR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_SET_63_32 : 32; // 31:0
|
||||
} ESR1_bit;
|
||||
}; // 0x304
|
||||
|
||||
|
||||
uint32_t rsvd308[30]; // 0x308 - 0x37C
|
||||
|
||||
|
||||
/* PRU_INTC_ECR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_CLR_31_0 : 32; // 31:0
|
||||
} ECR0_bit;
|
||||
}; // 0x380
|
||||
|
||||
|
||||
/* PRU_INTC_ECR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_CLR_63_32 : 32; // 31:0
|
||||
} ECR1_bit;
|
||||
}; // 0x384
|
||||
|
||||
|
||||
uint32_t rsvd388[30]; // 0x388 - 0x3FC
|
||||
|
||||
|
||||
/* PRU_INTC_CMR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_0 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_1 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_2 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_3 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR0_bit;
|
||||
}; // 0x400
|
||||
|
||||
|
||||
/* PRU_INTC_CMR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_4 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_5 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_6 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_7 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR1_bit;
|
||||
}; // 0x404
|
||||
|
||||
|
||||
/* PRU_INTC_CMR2 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR2;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_8 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_9 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_10 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_11 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR2_bit;
|
||||
}; // 0x408
|
||||
|
||||
|
||||
/* PRU_INTC_CMR3 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR3;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_12 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_13 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_14 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_15 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR3_bit;
|
||||
}; // 0x40C
|
||||
|
||||
|
||||
/* PRU_INTC_CMR4 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR4;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_16 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_17 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_18 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_19 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR4_bit;
|
||||
}; // 0x410
|
||||
|
||||
|
||||
/* PRU_INTC_CMR5 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR5;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_20 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_21 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_22 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_23 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR5_bit;
|
||||
}; // 0x414
|
||||
|
||||
|
||||
/* PRU_INTC_CMR6 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR6;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_24 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_25 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_26 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_27 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR6_bit;
|
||||
}; // 0x418
|
||||
|
||||
|
||||
/* PRU_INTC_CMR7 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR7;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_28 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_29 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_30 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_31 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR7_bit;
|
||||
}; // 0x41C
|
||||
|
||||
|
||||
/* PRU_INTC_CMR8 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR8;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_32 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_33 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_34 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_35 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR8_bit;
|
||||
}; // 0x420
|
||||
|
||||
|
||||
/* PRU_INTC_CMR9 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR9;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_36 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_37 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_38 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_39 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR9_bit;
|
||||
}; // 0x424
|
||||
|
||||
|
||||
/* PRU_INTC_CMR10 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR10;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_40 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_41 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_42 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_43 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR10_bit;
|
||||
}; // 0x428
|
||||
|
||||
|
||||
/* PRU_INTC_CMR11 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR11;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_44 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_45 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_46 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_47 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR11_bit;
|
||||
}; // 0x42C
|
||||
|
||||
|
||||
/* PRU_INTC_CMR12 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR12;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_48 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_49 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_50 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_51 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR12_bit;
|
||||
}; // 0x430
|
||||
|
||||
|
||||
/* PRU_INTC_CMR13 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR13;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_52 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_53 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_54 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_55 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR13_bit;
|
||||
}; // 0x434
|
||||
|
||||
|
||||
/* PRU_INTC_CMR14 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR14;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_56 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_57 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_58 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_59 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR14_bit;
|
||||
}; // 0x438
|
||||
|
||||
|
||||
/* PRU_INTC_CMR15 register bit field */
|
||||
union {
|
||||
volatile uint32_t CMR15;
|
||||
|
||||
volatile struct {
|
||||
unsigned CH_MAP_60 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned CH_MAP_61 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned CH_MAP_62 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned CH_MAP_63 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} CMR15_bit;
|
||||
}; // 0x43C
|
||||
|
||||
|
||||
uint32_t rsvd440[240]; // 0x440 - 0x7FC
|
||||
|
||||
|
||||
/* PRU_INTC_HMR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t HMR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned HINT_MAP_0 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned HINT_MAP_1 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned HINT_MAP_2 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned HINT_MAP_3 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} HMR0_bit;
|
||||
}; // 0x800
|
||||
|
||||
|
||||
/* PRU_INTC_HMR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t HMR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned HINT_MAP_4 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned HINT_MAP_5 : 4; // 11:8
|
||||
unsigned rsvd12 : 4; // 15:12
|
||||
unsigned HINT_MAP_6 : 4; // 19:16
|
||||
unsigned rsvd20 : 4; // 23:20
|
||||
unsigned HINT_MAP_7 : 4; // 27:24
|
||||
unsigned rsvd28 : 4; // 31:28
|
||||
} HMR1_bit;
|
||||
}; // 0x804
|
||||
|
||||
|
||||
/* PRU_INTC_HMR2 register bit field */
|
||||
union {
|
||||
volatile uint32_t HMR2;
|
||||
|
||||
volatile struct {
|
||||
unsigned HINT_MAP_8 : 4; // 3:0
|
||||
unsigned rsvd4 : 4; // 7:4
|
||||
unsigned HINT_MAP_9 : 4; // 11:8
|
||||
unsigned rsvd12 : 20; // 31:12
|
||||
} HMR2_bit;
|
||||
}; // 0x808
|
||||
|
||||
|
||||
uint32_t rsvd80C[61]; // 0x80C - 0x8FC
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_0 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_0 : 1; // 31
|
||||
} HIPIR0_bit;
|
||||
}; // 0x900
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_1 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_1 : 1; // 31
|
||||
} HIPIR1_bit;
|
||||
}; // 0x904
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR2 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR2;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_2 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_2 : 1; // 31
|
||||
} HIPIR2_bit;
|
||||
}; // 0x908
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR3 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR3;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_3 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_3 : 1; // 31
|
||||
} HIPIR3_bit;
|
||||
}; // 0x90C
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR4 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR4;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_4 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_4 : 1; // 31
|
||||
} HIPIR4_bit;
|
||||
}; // 0x910
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR5 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR5;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_5 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_5 : 1; // 31
|
||||
} HIPIR5_bit;
|
||||
}; // 0x914
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR6 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR6;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_6 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_6 : 1; // 31
|
||||
} HIPIR6_bit;
|
||||
}; // 0x918
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR7 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR7;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_7 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_7 : 1; // 31
|
||||
} HIPIR7_bit;
|
||||
}; // 0x91C
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR8 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR8;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_8 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_8 : 1; // 31
|
||||
} HIPIR8_bit;
|
||||
}; // 0x920
|
||||
|
||||
|
||||
/* PRU_INTC_HIPIR9 register bit field */
|
||||
union {
|
||||
volatile uint32_t HIPIR9;
|
||||
|
||||
volatile struct {
|
||||
unsigned PRI_HINT_9 : 10; // 9:0
|
||||
unsigned rsvd10 : 21; // 30:10
|
||||
unsigned NONE_HINT_9 : 1; // 31
|
||||
} HIPIR9_bit;
|
||||
}; // 0x924
|
||||
|
||||
|
||||
uint32_t rsvd928[246]; // 0x928 - 0xCFC
|
||||
|
||||
|
||||
/* PRU_INTC_SIPR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t SIPR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned POLARITY_31_0 : 32; // 31:0
|
||||
} SIPR0_bit;
|
||||
}; // 0xD00
|
||||
|
||||
|
||||
/* PRU_INTC_SIPR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t SIPR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned POLARITY_63_32 : 32; // 31:0
|
||||
} SIPR1_bit;
|
||||
}; // 0xD04
|
||||
|
||||
|
||||
uint32_t rsvdD08[30]; // 0xD08 - 0xD7C
|
||||
|
||||
|
||||
/* PRU_INTC_SITR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t SITR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned TYPE_31_0 : 32; // 31:0
|
||||
} SITR0_bit;
|
||||
}; // 0xD80
|
||||
|
||||
|
||||
/* PRU_INTC_SITR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t SITR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned TYPE_63_32 : 32; // 31:0
|
||||
} SITR1_bit;
|
||||
}; // 0xD84
|
||||
|
||||
|
||||
uint32_t rsvdD84[222]; // 0xD88 - 0x10FC
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR0 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR0;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_0 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR0_bit;
|
||||
}; // 0x1100
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR1 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR1;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_1 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR1_bit;
|
||||
}; // 0x1104
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR2 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR2;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_2 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR2_bit;
|
||||
}; // 0x1108
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR3 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR3;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_3 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR3_bit;
|
||||
}; // 0x110C
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR4 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR4;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_4 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR4_bit;
|
||||
}; // 0x1110
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR5 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR5;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_5 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR5_bit;
|
||||
}; // 0x1114
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR6 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR6;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_6 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR6_bit;
|
||||
}; // 0x1118
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR7 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR7;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_7 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR7_bit;
|
||||
}; // 0x111C
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR8 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR8;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_8 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR8_bit;
|
||||
}; // 0x1120
|
||||
|
||||
|
||||
/* PRU_INTC_HINLR9 register bit field */
|
||||
union {
|
||||
volatile uint32_t HINLR9;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEST_HINT_9 : 9; // 8:0
|
||||
unsigned rsvd9 : 22; // 30:9
|
||||
unsigned AUTO_OVERRIDE : 1; // 31
|
||||
} HINLR9_bit;
|
||||
}; // 0x1124
|
||||
|
||||
|
||||
uint32_t rsvd1128[246]; // 0x1128 - 0x14FC
|
||||
|
||||
|
||||
/* PRU_INTC_HIER register bit field */
|
||||
union {
|
||||
volatile uint32_t HIER;
|
||||
|
||||
volatile struct {
|
||||
unsigned EN_HINT : 10; // 9:0
|
||||
unsigned rsvd9 : 22; // 31:10
|
||||
} HIER_bit;
|
||||
}; // 0x1500
|
||||
|
||||
} pruIntc;
|
||||
|
||||
#ifdef __GNUC__
|
||||
static volatile pruIntc *__CT_INTC = (void *)0x00020000;
|
||||
#define CT_INTC (*__CT_INTC)
|
||||
#else
|
||||
volatile __far pruIntc CT_INTC __attribute__((cregister("PRU_INTC", far), peripheral));
|
||||
#endif
|
||||
|
||||
#endif /* _PRU_INTC_H_ */
|
||||
285
lib/pru_rpmsg/include/am335x/pru_uart.h
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _PRU_UART_H_
|
||||
#define _PRU_UART_H_
|
||||
|
||||
/* UART Register set */
|
||||
typedef struct {
|
||||
|
||||
/*
|
||||
* RBR and THR register pair
|
||||
* This is a unique register pair in that RBR and THR
|
||||
* share the same address. RBR is read-only while THR is
|
||||
* write-only.
|
||||
*
|
||||
* Additionally, RBR and THR share an address with DLL. To
|
||||
* read/write RBR/THR write 0 to the DLAB bit in the LCR
|
||||
* register. To modify DLL write a 1.
|
||||
*
|
||||
* DLL also has a dedicated
|
||||
* address which does not require toggling the DLAB bit.
|
||||
*/
|
||||
union {
|
||||
/* PRU_UART_RBR register bit field */
|
||||
union {
|
||||
volatile uint32_t RBR;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} RBR_bit;
|
||||
};
|
||||
|
||||
/* PRU_UART_THR register bit field */
|
||||
union {
|
||||
volatile uint32_t THR;
|
||||
|
||||
volatile struct {
|
||||
unsigned DATA : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} THR_bit;
|
||||
};
|
||||
}; // 0x0
|
||||
|
||||
|
||||
/* PRU_UART_IER register bit field */
|
||||
/*
|
||||
* IER shares an address with DLH. To modify IER write 0
|
||||
* to the DLAB bit in the LCR register. To modify DLH write a 1.
|
||||
*
|
||||
* DLH also has a dedicated address which does not require
|
||||
* toggling the DLAB bit.
|
||||
*/
|
||||
union {
|
||||
volatile uint32_t IER;
|
||||
|
||||
volatile struct {
|
||||
unsigned ERBI : 1; // 0
|
||||
unsigned ETBEI : 1; // 1
|
||||
unsigned ELSI : 1; // 2
|
||||
unsigned EDSSI : 1; // 3
|
||||
unsigned rsvd4 : 28; // 31:4
|
||||
} IER_bit;
|
||||
}; // 0x4
|
||||
|
||||
|
||||
/*
|
||||
* IIR and FCR register pair
|
||||
* This is a unique register pair in that IIR and FCR
|
||||
* share the same address. IIR is read-only while FCR is
|
||||
* write-only.
|
||||
*/
|
||||
union {
|
||||
/* PRU_UART_IIR register bit field */
|
||||
union {
|
||||
volatile uint32_t IIR;
|
||||
|
||||
volatile struct {
|
||||
unsigned IPEND : 1; // 0
|
||||
unsigned INTID : 3; // 3:1
|
||||
unsigned rsvd4 : 2; // 5:4
|
||||
unsigned FIFOEN : 2; // 7:6
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} IIR_bit;
|
||||
};
|
||||
|
||||
/* PRU_UART_FCR register bit field */
|
||||
union {
|
||||
volatile uint32_t FCR;
|
||||
|
||||
volatile struct {
|
||||
unsigned FIFOEN : 1; // 0
|
||||
unsigned RXCLR : 1; // 1
|
||||
unsigned TXCLR : 1; // 2
|
||||
unsigned DMAMODE1 : 1; // 3
|
||||
unsigned rsvd4 : 2; // 5:4
|
||||
unsigned RXFIFTL : 2; // 7:6
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} FCR_bit;
|
||||
};
|
||||
}; // 0x8
|
||||
|
||||
|
||||
/* PRU_UART_LCR register bit field */
|
||||
union {
|
||||
volatile uint32_t LCR;
|
||||
|
||||
volatile struct {
|
||||
unsigned WLS : 2; // 1:0
|
||||
unsigned STB : 1; // 2
|
||||
unsigned PEN : 1; // 3
|
||||
unsigned EPS : 1; // 4
|
||||
unsigned SP : 1; // 5
|
||||
unsigned BC : 1; // 6
|
||||
unsigned DLAB : 1; // 7
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} LCR_bit;
|
||||
}; // 0xC
|
||||
|
||||
|
||||
/* PRU_UART_MCR register bit field */
|
||||
union {
|
||||
volatile uint32_t MCR;
|
||||
|
||||
volatile struct {
|
||||
unsigned rsvd0 : 1; // 0
|
||||
unsigned RTS : 1; // 1
|
||||
unsigned OUT1 : 1; // 2
|
||||
unsigned OUT2 : 1; // 3
|
||||
unsigned LOOP : 1; // 4
|
||||
unsigned AFE : 1; // 5
|
||||
unsigned rsvd8 : 26; // 31:6
|
||||
} MCR_bit;
|
||||
}; // 0x10
|
||||
|
||||
|
||||
/* PRU_UART_LSR register bit field */
|
||||
union {
|
||||
volatile uint32_t LSR;
|
||||
|
||||
volatile struct {
|
||||
unsigned DR : 1; // 0
|
||||
unsigned OE : 1; // 1
|
||||
unsigned PE : 1; // 2
|
||||
unsigned FE : 1; // 3
|
||||
unsigned BI : 1; // 4
|
||||
unsigned THRE : 1; // 5
|
||||
unsigned TEMT : 1; // 6
|
||||
unsigned RXFIFOE : 1; // 7
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} LSR_bit;
|
||||
}; // 0x14
|
||||
|
||||
|
||||
/* PRU_UART_MSR register bit field */
|
||||
union {
|
||||
volatile uint32_t MSR;
|
||||
|
||||
volatile struct {
|
||||
unsigned DCTS : 1; // 0
|
||||
unsigned DDSR : 1; // 1
|
||||
unsigned TERI : 1; // 2
|
||||
unsigned DCD : 1; // 3
|
||||
unsigned CTS : 1; // 4
|
||||
unsigned DSR : 1; // 5
|
||||
unsigned RI : 1; // 6
|
||||
unsigned CD : 1; // 7
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} MSR_bit;
|
||||
}; // 0x18
|
||||
|
||||
|
||||
/* PRU_UART_SCR register bit field */
|
||||
union {
|
||||
volatile uint32_t SCR;
|
||||
|
||||
volatile struct {
|
||||
unsigned SCR : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} SCR_bit;
|
||||
}; // 0x1C
|
||||
|
||||
|
||||
/* PRU_UART_DLL register bit field */
|
||||
union {
|
||||
volatile uint32_t DLL;
|
||||
|
||||
volatile struct {
|
||||
unsigned DLL : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} DLL_bit;
|
||||
}; // 0x20
|
||||
|
||||
|
||||
/* PRU_UART_DLH register bit field */
|
||||
union {
|
||||
volatile uint32_t DLH;
|
||||
|
||||
volatile struct {
|
||||
unsigned DLH : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} DLH_bit;
|
||||
}; // 0x24
|
||||
|
||||
|
||||
/* PRU_UART_REVID1 register bit field */
|
||||
union {
|
||||
volatile uint32_t REVID1;
|
||||
|
||||
volatile struct {
|
||||
unsigned REVID1 : 32; // 31:0
|
||||
} REVID1_bit;
|
||||
}; // 0x28
|
||||
|
||||
|
||||
/* PRU_UART_REVID2 register bit field */
|
||||
union {
|
||||
volatile uint32_t REVID2;
|
||||
|
||||
volatile struct {
|
||||
unsigned REVID2 : 8; // 7:0
|
||||
unsigned rsvd8 : 24; // 31:8
|
||||
} REVID2_bit;
|
||||
}; // 0x2C
|
||||
|
||||
|
||||
/* PRU_UART_PWREMU_MGMT register bit field */
|
||||
union {
|
||||
volatile uint32_t PWREMU_MGMT;
|
||||
|
||||
volatile struct {
|
||||
unsigned FREE : 1; // 0
|
||||
unsigned rsvd1 : 12; // 12:1
|
||||
unsigned URRST : 1; // 13
|
||||
unsigned UTRST : 1; // 14
|
||||
unsigned rsvd15 : 17; // 31:15
|
||||
} PWREMU_MGMT_bit;
|
||||
}; // 0x30
|
||||
|
||||
|
||||
/* PRU_UART_MDR register bit field */
|
||||
union {
|
||||
volatile uint32_t MDR;
|
||||
|
||||
volatile struct {
|
||||
unsigned OSM_SEL : 1; // 0
|
||||
unsigned rsvd1 : 31; // 31:1
|
||||
} MDR_bit;
|
||||
}; // 0x34
|
||||
|
||||
} pruUart;
|
||||
|
||||
volatile __far pruUart CT_UART __attribute__((cregister("PRU_UART", near), peripheral));
|
||||
|
||||
#endif /* _PRU_UART_H_ */
|
||||
210
lib/pru_rpmsg/include/am335x/sys_mailbox.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_MAILBOX_H_
|
||||
#define _SYS_MAILBOX_H_
|
||||
|
||||
/* SYS_MAILBOX register set */
|
||||
typedef struct {
|
||||
|
||||
/* SYS_MAILBOX_REVISION register bit field */
|
||||
union {
|
||||
volatile uint32_t REVISION;
|
||||
|
||||
volatile struct {
|
||||
unsigned MINOR : 6; //5:0
|
||||
unsigned CUSTOM : 2; //7:6
|
||||
unsigned MAJOR : 3; //10:8
|
||||
unsigned RTL : 5; //15:11
|
||||
unsigned FUNC : 12; //27:16
|
||||
unsigned rsvd28 : 2; //29:28
|
||||
unsigned SCHEME : 2; //31:30
|
||||
} REVISION_bit;
|
||||
}; // 0x0
|
||||
|
||||
uint32_t rsvd4[3]; // 0x4 - 0xC
|
||||
|
||||
/* SYS_MAILBOX_SYSCONFIG register bit field */
|
||||
union {
|
||||
volatile uint32_t SYSCONFIG;
|
||||
|
||||
volatile struct {
|
||||
unsigned SOFTRESET : 1; //0
|
||||
unsigned rsvd : 1; //1
|
||||
unsigned SLIDLEMODE : 2; //3:2
|
||||
unsigned rsvd1 : 28; //31:4
|
||||
} SYSCONFIG_bit;
|
||||
}; // 0x10
|
||||
|
||||
uint32_t rsvd14[11]; // 0x14 - 0x3C
|
||||
|
||||
/* SYS_MAILBOX_MESSAGE register bit fields */
|
||||
union {
|
||||
volatile uint32_t MESSAGE[8];
|
||||
|
||||
volatile struct {
|
||||
unsigned MESSAGE : 32; //31:0
|
||||
} MESSAGE_bit[8];
|
||||
}; // 0x40-0x5C
|
||||
|
||||
uint32_t rsvd60[8]; // 0x60 - 0x7C
|
||||
|
||||
/* SYS_MAILBOX_FIFOSTATUS register bit fields */
|
||||
union {
|
||||
volatile uint32_t FIFOSTATUS[8];
|
||||
|
||||
volatile struct {
|
||||
unsigned FIFOFULL : 1; //0
|
||||
unsigned rsvd : 31; //31:1
|
||||
} FIFOSTATUS_bit[8];
|
||||
}; // 0x80-0x9C
|
||||
|
||||
uint32_t rsvdA0[8]; // 0xA0 - 0xBC
|
||||
|
||||
/* SYS_MAILBOX_MSGSTATUS register bit fields */
|
||||
union {
|
||||
volatile uint32_t MSGSTATUS[8];
|
||||
|
||||
volatile struct {
|
||||
unsigned NBOFMSG : 3; //2:0
|
||||
unsigned rsvd : 29; //31:3
|
||||
} MSGSTATUS_bit[8];
|
||||
}; // 0xC0-DC
|
||||
|
||||
uint32_t rsvdE0[8]; // 0xE0 - 0xFC
|
||||
|
||||
volatile struct {
|
||||
union {
|
||||
volatile uint32_t STATUS_RAW;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEWMSGSTATUSMB0 : 1; //0
|
||||
unsigned NOTFULLSTATUSMB0 : 1; //1
|
||||
unsigned NEWMSGSTATUSMB1 : 1; //2
|
||||
unsigned NOTFULLSTATUSMB1 : 1; //3
|
||||
unsigned NEWMSGSTATUSMB2 : 1; //4
|
||||
unsigned NOTFULLSTATUSMB2 : 1; //5
|
||||
unsigned NEWMSGSTATUSMB3 : 1; //6
|
||||
unsigned NOTFULLSTATUSMB3 : 1; //7
|
||||
unsigned NEWMSGSTATUSMB4 : 1; //8
|
||||
unsigned NOTFULLSTATUSMB4 : 1; //9
|
||||
unsigned NEWMSGSTATUSMB5 : 1; //10
|
||||
unsigned NOTFULLSTATUSMB5 : 1; //11
|
||||
unsigned NEWMSGSTATUSMB6 : 1; //12
|
||||
unsigned NOTFULLSTATUSMB6 : 1; //13
|
||||
unsigned NEWMSGSTATUSMB7 : 1; //14
|
||||
unsigned NOTFULLSTATUSMB7 : 1; //15
|
||||
unsigned rsvd : 16; //31:16
|
||||
} STATUS_RAW_bit;
|
||||
};
|
||||
union {
|
||||
volatile uint32_t STATUS_CLR;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEWMSGSTATUSMB0 : 1; //0
|
||||
unsigned NOTFULLSTATUSMB0 : 1; //1
|
||||
unsigned NEWMSGSTATUSMB1 : 1; //2
|
||||
unsigned NOTFULLSTATUSMB1 : 1; //3
|
||||
unsigned NEWMSGSTATUSMB2 : 1; //4
|
||||
unsigned NOTFULLSTATUSMB2 : 1; //5
|
||||
unsigned NEWMSGSTATUSMB3 : 1; //6
|
||||
unsigned NOTFULLSTATUSMB3 : 1; //7
|
||||
unsigned NEWMSGSTATUSMB4 : 1; //8
|
||||
unsigned NOTFULLSTATUSMB4 : 1; //9
|
||||
unsigned NEWMSGSTATUSMB5 : 1; //10
|
||||
unsigned NOTFULLSTATUSMB5 : 1; //11
|
||||
unsigned NEWMSGSTATUSMB6 : 1; //12
|
||||
unsigned NOTFULLSTATUSMB6 : 1; //13
|
||||
unsigned NEWMSGSTATUSMB7 : 1; //14
|
||||
unsigned NOTFULLSTATUSMB7 : 1; //15
|
||||
unsigned rsvd : 16; //31:16
|
||||
} STATUS_CLR_bit;
|
||||
};
|
||||
union {
|
||||
volatile uint32_t ENABLE_SET;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEWMSGSTATUSMB0 : 1; //0
|
||||
unsigned NOTFULLSTATUSMB0 : 1; //1
|
||||
unsigned NEWMSGSTATUSMB1 : 1; //2
|
||||
unsigned NOTFULLSTATUSMB1 : 1; //3
|
||||
unsigned NEWMSGSTATUSMB2 : 1; //4
|
||||
unsigned NOTFULLSTATUSMB2 : 1; //5
|
||||
unsigned NEWMSGSTATUSMB3 : 1; //6
|
||||
unsigned NOTFULLSTATUSMB3 : 1; //7
|
||||
unsigned NEWMSGSTATUSMB4 : 1; //8
|
||||
unsigned NOTFULLSTATUSMB4 : 1; //9
|
||||
unsigned NEWMSGSTATUSMB5 : 1; //10
|
||||
unsigned NOTFULLSTATUSMB5 : 1; //11
|
||||
unsigned NEWMSGSTATUSMB6 : 1; //12
|
||||
unsigned NOTFULLSTATUSMB6 : 1; //13
|
||||
unsigned NEWMSGSTATUSMB7 : 1; //14
|
||||
unsigned NOTFULLSTATUSMB7 : 1; //15
|
||||
unsigned rsvd : 16; //31:16
|
||||
} ENABLE_SET_bit;
|
||||
};
|
||||
union {
|
||||
volatile uint32_t ENABLE_CLR;
|
||||
|
||||
volatile struct {
|
||||
unsigned NEWMSGSTATUSMB0 : 1; //0
|
||||
unsigned NOTFULLSTATUSMB0 : 1; //1
|
||||
unsigned NEWMSGSTATUSMB1 : 1; //2
|
||||
unsigned NOTFULLSTATUSMB1 : 1; //3
|
||||
unsigned NEWMSGSTATUSMB2 : 1; //4
|
||||
unsigned NOTFULLSTATUSMB2 : 1; //5
|
||||
unsigned NEWMSGSTATUSMB3 : 1; //6
|
||||
unsigned NOTFULLSTATUSMB3 : 1; //7
|
||||
unsigned NEWMSGSTATUSMB4 : 1; //8
|
||||
unsigned NOTFULLSTATUSMB4 : 1; //9
|
||||
unsigned NEWMSGSTATUSMB5 : 1; //10
|
||||
unsigned NOTFULLSTATUSMB5 : 1; //11
|
||||
unsigned NEWMSGSTATUSMB6 : 1; //12
|
||||
unsigned NOTFULLSTATUSMB6 : 1; //13
|
||||
unsigned NEWMSGSTATUSMB7 : 1; //14
|
||||
unsigned NOTFULLSTATUSMB7 : 1; //15
|
||||
unsigned rsvd : 16; //31:16
|
||||
} ENABLE_CLR_bit;
|
||||
};
|
||||
} IRQ[4];
|
||||
|
||||
} sysMailbox;
|
||||
|
||||
#ifdef __GNUC__
|
||||
static volatile sysMailbox *__CT_MBX = (void *)0x480C8000;
|
||||
#define CT_MBX (*__CT_MBX)
|
||||
#else
|
||||
volatile __far sysMailbox CT_MBX __attribute__((cregister("MBX0", far), peripheral));
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_MAILBOX_H_ */
|
||||
446
lib/pru_rpmsg/include/am335x/sys_pwmss.h
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_PWMSS_H_
|
||||
#define _SYS_PWMSS_H_
|
||||
|
||||
/* SYS_PWMSS register set */
|
||||
typedef struct {
|
||||
|
||||
/***************************/
|
||||
/* PWM Subsystem Registers */
|
||||
/***************************/
|
||||
/* SYS_PWMSS_IDVER register bit field */
|
||||
union {
|
||||
volatile uint32_t IDVER;
|
||||
|
||||
volatile struct {
|
||||
unsigned Y_MINOR : 6; //5:0
|
||||
unsigned CUSTOM : 2; //7:6
|
||||
unsigned X_MAJOR : 3; //10:8
|
||||
unsigned R_RTL : 5; //15:11
|
||||
unsigned FUNC : 12; //27:16
|
||||
unsigned rsvd28 : 2; //29:28
|
||||
unsigned SCHEME : 2; //31:30
|
||||
} IDVER_bit;
|
||||
}; // 0x0
|
||||
|
||||
/* SYS_PWMSS_SYSCONFIG register bit field */
|
||||
union {
|
||||
volatile uint32_t SYSCONFIG;
|
||||
|
||||
volatile struct {
|
||||
unsigned SOFTRESET : 1; //0
|
||||
unsigned FREEEMU : 1; //1
|
||||
unsigned IDLEMODE : 2; //3:2
|
||||
unsigned STANDBYMODE : 2; //5:4
|
||||
unsigned rsvd6 : 26; //31:6
|
||||
} SYSCONFIG_bit;
|
||||
}; // 0x4
|
||||
|
||||
/* SYS_PWMSS_CLKCONFIG register bit field */
|
||||
union {
|
||||
volatile uint32_t CLKCONFIG;
|
||||
|
||||
volatile struct {
|
||||
unsigned ECAPCLK_EN : 1; //0
|
||||
unsigned ECAPCLKSTOP_REQ : 1; //1
|
||||
unsigned rsvd2 : 2; //3:2
|
||||
unsigned EQEPCLK_EN : 1; //4
|
||||
unsigned EQEPCLKSTOP_REQ : 1; //5
|
||||
unsigned rsvd6 : 2; //7:6
|
||||
unsigned EPWMCLK_EN : 1; //8
|
||||
unsigned EPWMCLKSTOP_REQ : 1; //9
|
||||
unsigned rsvd10 : 22; //31:10
|
||||
} CLKCONFIG_bit;
|
||||
}; // 0x8
|
||||
|
||||
/* SYS_PWMSS_CLKSTATUS register bit field */
|
||||
union {
|
||||
volatile uint32_t CLKSTATUS;
|
||||
|
||||
volatile struct {
|
||||
unsigned ECAPCLK_EN_ACK : 1; //0
|
||||
unsigned ECAPCLKSTOP_ACK : 1; //1
|
||||
unsigned rsvd2 : 2; //3:2
|
||||
unsigned EQEPCLK_EN_ACK : 1; //4
|
||||
unsigned EQEPCLKSTOP_ACK : 1; //5
|
||||
unsigned rsvd6 : 2; //7:6
|
||||
unsigned EPWMCLK_EN_ACK : 1; //8
|
||||
unsigned EPWMCLKSTOP_ACK : 1; //9
|
||||
unsigned rsvd10 : 22; //31:10
|
||||
} CLKSTATUS_bit;
|
||||
}; // 0xC
|
||||
|
||||
uint32_t rsvd10[60]; // 0x10 - 0xFC
|
||||
|
||||
/*************************/
|
||||
/* eCAP Module Registers */
|
||||
/*************************/
|
||||
/* SYS_PWMSS_ECAP_TSCTR register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_TSCTR;
|
||||
|
||||
volatile struct {
|
||||
unsigned TSCTR : 32; //31:0
|
||||
} ECAP_TSCTR_bit;
|
||||
}; // 0x100
|
||||
|
||||
/* SYS_PWMSS_ECAP_CTRPHS register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_CTRPHS;
|
||||
|
||||
volatile struct {
|
||||
unsigned CTRPHS : 32; //31:0
|
||||
} ECAP_CTRPHS_bit;
|
||||
}; // 0x104
|
||||
|
||||
/* SYS_PWMSS_ECAP_CAP1 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_CAP1;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP1 : 32; //31:0
|
||||
} ECAP_CAP1_bit;
|
||||
}; // 0x108
|
||||
|
||||
/* SYS_PWMSS_ECAP_CAP2 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_CAP2;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP2 : 32; //31:0
|
||||
} ECAP_CAP2_bit;
|
||||
}; // 0x10C
|
||||
|
||||
/* SYS_PWMSS_ECAP_CAP3 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_CAP3;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP3 : 32; //31:0
|
||||
} ECAP_CAP3_bit;
|
||||
}; // 0x110
|
||||
|
||||
/* SYS_PWMSS_ECAP_CAP4 register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_CAP4;
|
||||
|
||||
volatile struct {
|
||||
unsigned CAP4 : 32; //31:0
|
||||
} ECAP_CAP4_bit;
|
||||
}; // 0x114
|
||||
|
||||
uint32_t rsvd118[4]; // 0x118 - 0x124
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECCTL1 register bit field */
|
||||
volatile uint16_t ECAP_ECCTL1; // 0x128
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECCTL2 register bit field */
|
||||
volatile uint16_t ECAP_ECCTL2; // 0x12A
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECEINT register bit field */
|
||||
volatile uint16_t ECAP_ECEINT; // 0x12C
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECFLG register bit field */
|
||||
volatile uint16_t ECAP_ECFLG; // 0x12E
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECCLR register bit field */
|
||||
volatile uint16_t ECAP_ECCLR; // 0x130
|
||||
|
||||
/* SYS_PWMSS_ECAP_ECFRC register bit field */
|
||||
volatile uint16_t ECAP_ECFRC; // 0x132
|
||||
|
||||
uint32_t rsvd134[10]; // 0x134 - 0x158
|
||||
|
||||
/* SYS_PWMSS_ECAP_REVID register bit field */
|
||||
union {
|
||||
volatile uint32_t ECAP_REVID;
|
||||
|
||||
volatile struct {
|
||||
unsigned REV : 32; //31:0
|
||||
} ECAP_REVID_bit;
|
||||
}; // 0x15C
|
||||
|
||||
uint32_t rsvd160[8]; // 0x160 - 0x17C
|
||||
|
||||
/*************************/
|
||||
/* eQEP Module Registers */
|
||||
/*************************/
|
||||
/* SYS_PWMSS_EQEP_QPOSCNT register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSCNT;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSCNT : 32; //31:0
|
||||
} EQEP_QPOSCNT_bit;
|
||||
}; // 0x180
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSINIT register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSINIT;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSINIT : 32; //31:0
|
||||
} EQEP_QPOSINIT_bit;
|
||||
}; // 0x184
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSMAX register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSMAX;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSMAX : 32; //31:0
|
||||
} EQEP_QPOSMAX_bit;
|
||||
}; // 0x188
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSCMP register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSCMP;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSCMP : 32; //31:0
|
||||
} EQEP_QPOSCMP_bit;
|
||||
}; // 0x18C
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSILAT register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSILAT;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSILAT : 32; //31:0
|
||||
} EQEP_QPOSILAT_bit;
|
||||
}; // 0x190
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSSLAT register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSSLAT;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSSLAT : 32; //31:0
|
||||
} EQEP_QPOSSLAT_bit;
|
||||
}; // 0x194
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSLAT register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QPOSLAT;
|
||||
|
||||
volatile struct {
|
||||
unsigned QPOSLAT : 32; //31:0
|
||||
} EQEP_QPOSLAT_bit;
|
||||
}; // 0x198
|
||||
|
||||
/* SYS_PWMSS_EQEP_QUTMR register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QUTMR;
|
||||
|
||||
volatile struct {
|
||||
unsigned QUTMR : 32; //31:0
|
||||
} EQEP_QUTMR_bit;
|
||||
}; // 0x19C
|
||||
|
||||
/* SYS_PWMSS_EQEP_QUPRD register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_QUPRD;
|
||||
|
||||
volatile struct {
|
||||
unsigned QUPRD : 32; //31:0
|
||||
} EQEP_QUPRD_bit;
|
||||
}; // 0x1A0
|
||||
|
||||
/* SYS_PWMSS_EQEP_QWDTMR register bit field */
|
||||
volatile uint16_t EQEP_QWDTMR; // 0x1A4
|
||||
|
||||
/* SYS_PWMSS_EQEP_QWDPRD register bit field */
|
||||
volatile uint16_t EQEP_QWDPRD; // 0x1A6
|
||||
|
||||
/* SYS_PWMSS_EQEP_QDECCTL register bit field */
|
||||
volatile uint16_t EQEP_QDECCTL; // 0x1A8
|
||||
|
||||
/* SYS_PWMSS_EQEP_QEPCTL register bit field */
|
||||
volatile uint16_t EQEP_QEPCTL; // 0x1AA
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCAPCTL register bit field */
|
||||
volatile uint16_t EQEP_QCAPCTL; // 0x1AC
|
||||
|
||||
/* SYS_PWMSS_EQEP_QPOSCTL register bit field */
|
||||
volatile uint16_t EQEP_QPOSCTL; // 0x1AE
|
||||
|
||||
/* SYS_PWMSS_EQEP_QEINT register bit field */
|
||||
volatile uint16_t EQEP_QEINT; // 0x1B0
|
||||
|
||||
/* SYS_PWMSS_EQEP_QFLG register bit field */
|
||||
volatile uint16_t EQEP_QFLG; // 0x1B2
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCLR register bit field */
|
||||
volatile uint16_t EQEP_QCLR; // 0x1B4
|
||||
|
||||
/* SYS_PWMSS_EQEP_QFRC register bit field */
|
||||
volatile uint16_t EQEP_QFRC; // 0x1B6
|
||||
|
||||
/* SYS_PWMSS_EQEP_QEPSTS register bit field */
|
||||
volatile uint16_t EQEP_QEPSTS; // 0x1B8
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCTMR register bit field */
|
||||
volatile uint16_t EQEP_QCTMR; // 0x1BA
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCPRD register bit field */
|
||||
volatile uint16_t EQEP_QCPRD; // 0x1BC
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCTMRLAT register bit field */
|
||||
volatile uint16_t EQEP_QCTMRLAT; // 0x1BE
|
||||
|
||||
/* SYS_PWMSS_EQEP_QCPRDLAT register bit field */
|
||||
volatile uint16_t EQEP_QCPRDLAT; // 0x1C0
|
||||
|
||||
uint16_t rsvd1C2[1]; // 0x1C2 - 0x1C3
|
||||
uint32_t rsvd1C4[6]; // 0x1C4 - 0x1D8
|
||||
|
||||
/* SYS_PWMSS_EQEP_REVID register bit field */
|
||||
union {
|
||||
volatile uint32_t EQEP_REVID;
|
||||
|
||||
volatile struct {
|
||||
unsigned REVID : 32; //31:0
|
||||
} EQEP_REVID_bit;
|
||||
}; // 0x1DC
|
||||
|
||||
uint32_t rsvd1E0[8]; // 0x1E0 - 0x1FC
|
||||
|
||||
/*************************/
|
||||
/* ePWM Module Registers */
|
||||
/*************************/
|
||||
/* SYS_PWMSS_EPWM_TBCTL register bit field */
|
||||
volatile uint16_t EPWM_TBCTL; // 0x200
|
||||
|
||||
/* SYS_PWMSS_EPWM_TBSTS register bit field */
|
||||
volatile uint16_t EPWM_TBSTS; // 0x202
|
||||
|
||||
/* SYS_PWMSS_EPWM_TBPHSHR register bit field */
|
||||
volatile uint16_t EPWM_TBPHSHR; // 0x204
|
||||
|
||||
/* SYS_PWMSS_EPWM_TBPHS register bit field */
|
||||
volatile uint16_t EPWM_TBPHS; // 0x206
|
||||
|
||||
/* SYS_PWMSS_EPWM_TBCNT register bit field */
|
||||
volatile uint16_t EPWM_TBCNT; // 0x208
|
||||
|
||||
/* SYS_PWMSS_EPWM_TBPRD register bit field */
|
||||
volatile uint16_t EPWM_TBPRD; // 0x20A
|
||||
|
||||
uint16_t rsvd20C[1]; // 0x20C - 0x20D
|
||||
|
||||
/* SYS_PWMSS_EPWM_CMPCTL register bit field */
|
||||
volatile uint16_t EPWM_CMPCTL; // 0x20E
|
||||
|
||||
/* SYS_PWMSS_EPWM_CMPAHR register bit field */
|
||||
volatile uint16_t EPWM_CMPAHR; // 0x210
|
||||
|
||||
/* SYS_PWMSS_EPWM_CMPA register bit field */
|
||||
volatile uint16_t EPWM_CMPA; // 0x212
|
||||
|
||||
/* SYS_PWMSS_EPWM_CMPB register bit field */
|
||||
volatile uint16_t EPWM_CMPB; // 0x214
|
||||
|
||||
/* SYS_PWMSS_EPWM_AQCTLA register bit field */
|
||||
volatile uint16_t EPWM_AQCTLA; // 0x216
|
||||
|
||||
/* SYS_PWMSS_EPWM_AQCTLB register bit field */
|
||||
volatile uint16_t EPWM_AQCTLB; // 0x218
|
||||
|
||||
/* SYS_PWMSS_EPWM_AQSFRC register bit field */
|
||||
volatile uint16_t EPWM_AQSFRC; // 0x21A
|
||||
|
||||
/* SYS_PWMSS_EPWM_AQCSFRC register bit field */
|
||||
volatile uint16_t EPWM_AQCSFRC; // 0x21C
|
||||
|
||||
/* SYS_PWMSS_EPWM_DBCTL register bit field */
|
||||
volatile uint16_t EPWM_DBCTL; // 0x21E
|
||||
|
||||
/* SYS_PWMSS_EPWM_DBRED register bit field */
|
||||
volatile uint16_t EPWM_DBRED; // 0x220
|
||||
|
||||
/* SYS_PWMSS_EPWM_DBFED register bit field */
|
||||
volatile uint16_t EPWM_DBFED; // 0x222
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZSEL register bit field */
|
||||
volatile uint16_t EPWM_TZSEL; // 0x224
|
||||
|
||||
uint16_t rsvd226[1]; // 0x226 - 0x227
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZCTL register bit field */
|
||||
volatile uint16_t EPWM_TZCTL; // 0x228
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZEINT register bit field */
|
||||
volatile uint16_t EPWM_TZEINT; // 0x22A
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZFLG register bit field */
|
||||
volatile uint16_t EPWM_TZFLG; // 0x22C
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZCLR register bit field */
|
||||
volatile uint16_t EPWM_TZCLR; // 0x22E
|
||||
|
||||
/* SYS_PWMSS_EPWM_TZFRC register bit field */
|
||||
volatile uint16_t EPWM_TZFRC; // 0x230
|
||||
|
||||
/* SYS_PWMSS_EPWM_ETSEL register bit field */
|
||||
volatile uint16_t EPWM_ETSEL; // 0x232
|
||||
|
||||
/* SYS_PWMSS_EPWM_ETPS register bit field */
|
||||
volatile uint16_t EPWM_ETPS; // 0x234
|
||||
|
||||
/* SYS_PWMSS_EPWM_ETFLG register bit field */
|
||||
volatile uint16_t EPWM_ETFLG; // 0x236
|
||||
|
||||
/* SYS_PWMSS_EPWM_ETCLR register bit field */
|
||||
volatile uint16_t EPWM_ETCLR; // 0x238
|
||||
|
||||
/* SYS_PWMSS_EPWM_ETFRC register bit field */
|
||||
volatile uint16_t EPWM_ETFRC; // 0x23A
|
||||
|
||||
/* SYS_PWMSS_EPWM_PCCTL register bit field */
|
||||
volatile uint16_t EPWM_PCCTL; // 0x23C
|
||||
|
||||
uint16_t rsvd23E[1]; // 0x23E - 0x23F
|
||||
uint32_t rsvd240[32]; // 0x240 - 0x2BC
|
||||
|
||||
/* SYS_PWMSS_EPWM_HRCNGF register bit field */
|
||||
volatile uint16_t EPWM_HRCNGF; // 0x2C0
|
||||
|
||||
} sysPwmss;
|
||||
|
||||
volatile __far sysPwmss PWMSS0 __attribute__((cregister("PWMSS0", far), peripheral));
|
||||
volatile __far sysPwmss PWMSS1 __attribute__((cregister("PWMSS1", far), peripheral));
|
||||
volatile __far sysPwmss PWMSS2 __attribute__((cregister("PWMSS2", far), peripheral));
|
||||
|
||||
#endif /* _SYS_PWMSS_H_ */
|
||||
268
lib/pru_rpmsg/include/pru_rpmsg.h
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Texas Instruments Incorporated nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File : pru_rpmsg.h
|
||||
*
|
||||
* Summary : An RPMsg interface for the PRU to use while communicating with
|
||||
* the ARM host.
|
||||
*
|
||||
* Notes :
|
||||
* - This file creates a structure (pru_rpmsg_transport) that contains
|
||||
* pointers to two pru_virtqueue structures. This structure is used as the
|
||||
* underlying transport layer of all RPMsg communication. Only one
|
||||
* pru_rpmsg_transport structure is needed because multiple logical channels
|
||||
* can use the same underlying transport.
|
||||
* - This pru_rpmsg interface is meant to sit on top of the pru_virtqueue
|
||||
* interface and abstract the communication even further away from the
|
||||
* underlying data structures. The goal is to make the communication as
|
||||
* simple as possible at the user application level.
|
||||
* - The logic for the PRU side is summarized below:
|
||||
*
|
||||
* PRU Slave:
|
||||
* - To receive buffer from the ARM host:
|
||||
* pru_rpmsg_receive(*transport, *src, *dst, *data, *len);
|
||||
* - To send buffer to the host:
|
||||
* pru_rpmsg_send(*transport, src, dst, *data, len);
|
||||
*/
|
||||
|
||||
#ifndef _PRU_RPMSG_H_
|
||||
#define _PRU_RPMSG_H_
|
||||
|
||||
#include <pru_virtqueue.h>
|
||||
#include <pru_virtio_ring.h>
|
||||
|
||||
/* Return value indicating no kick was sent */
|
||||
#define PRU_RPMSG_NO_KICK 1
|
||||
/* Return value indicating success */
|
||||
#define PRU_RPMSG_SUCCESS 0
|
||||
/* Return value indicating there were no available buffers */
|
||||
#define PRU_RPMSG_NO_BUF_AVAILABLE -1
|
||||
/* Return value indicating that the buffer from the virtqueue was too small */
|
||||
#define PRU_RPMSG_BUF_TOO_SMALL -2
|
||||
/* Return value indicating that an invalid head index was given */
|
||||
#define PRU_RPMSG_INVALID_HEAD -3
|
||||
/* Return value indication that an invalid event number was given */
|
||||
#define PRU_RPMSG_INVALID_EVENT -4
|
||||
|
||||
/* Max PRU-ICSS system event number for pru_mst_intr */
|
||||
#define MAX_VALID_EVENT 31
|
||||
/* Min PRU-ICSS system event number for pru_mst_intr */
|
||||
#define MIN_VALID_EVENT 16
|
||||
|
||||
/* The maximum size of the channel name and description */
|
||||
#define RPMSG_NAME_SIZE 32
|
||||
/* The maximum size of the buffer (including the header) */
|
||||
#define RPMSG_BUF_SIZE 512
|
||||
|
||||
enum pru_rpmsg_ns_flags {
|
||||
RPMSG_NS_CREATE = 0,
|
||||
RPMSG_NS_DESTROY = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Summary : pru_rpmsg_transport is a structure that groups together the
|
||||
* two pru_virtqueues that are needed for two-way communication
|
||||
* with the ARM. This structure provides a logical wrapper for
|
||||
* the transport layer of the application. NOTE: Multiple
|
||||
* (logical) channels can be implemented on top of the same
|
||||
* transport layer.
|
||||
*
|
||||
* Variables : virtqueue0: contains the pru_virtqueue that is used for the
|
||||
* PRU->ARM communication
|
||||
* virtqueue1: contains the pru_virtqueue that is used for
|
||||
* the ARM->PRU communication
|
||||
*/
|
||||
struct pru_rpmsg_transport {
|
||||
struct pru_virtqueue virtqueue0;
|
||||
struct pru_virtqueue virtqueue1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Summary : pru_rpmsg_init initializes the underlying transport layer
|
||||
* data structures.
|
||||
*
|
||||
* Parameters : transport: a pointer to the transport data structure that
|
||||
* contains the underlying data structures to be
|
||||
* initialized
|
||||
* vring0: a pointer to vring0 which is provided by the ARM
|
||||
* core through the resource table
|
||||
* vring1: a pointer to vring1 which is provided by the ARM
|
||||
* core through the resource table
|
||||
* to_arm_event: the number of the PRU-ICSS system event
|
||||
* that is specified in the device tree that
|
||||
* is used to 'kick' the ARM core
|
||||
* from_arm_event: the number of the PRU-ICSS system event
|
||||
* that is specified in the device tree
|
||||
* that is used to receive 'kicks' from the
|
||||
* ARM core
|
||||
*
|
||||
* Description : pru_rpmsg_init takes the vrings and the events provided
|
||||
* through the resource table and initializes the transport
|
||||
* layer. Once this function call is successful RPMsg
|
||||
* channels can be created and used.
|
||||
*
|
||||
* Return Value : Returns PRU_RPMSG_INVALID_EVENT if the values provided
|
||||
* in to_arm_event or from_arm_event are outside of the
|
||||
* allowable range of events. Returns PRU_RPMSG_SUCCESS
|
||||
* if the initialization is successful.
|
||||
*/
|
||||
int16_t pru_rpmsg_init(
|
||||
struct pru_rpmsg_transport *transport,
|
||||
struct fw_rsc_vdev_vring *vring0,
|
||||
struct fw_rsc_vdev_vring *vring1,
|
||||
uint32_t to_arm_event,
|
||||
uint32_t from_arm_event
|
||||
);
|
||||
|
||||
/**
|
||||
* Summary : pru_rpmsg_receive receives a message, if available, from
|
||||
* the ARM host.
|
||||
*
|
||||
* Parameters : transport: a pointer to the transport layer from which the
|
||||
* message should be received
|
||||
* src: a pointer that is populated with the source address
|
||||
* where the message originated
|
||||
* dst: a pointer that is populated with the destination
|
||||
* address where the message was sent (can help determine
|
||||
* for which channel the message is intended on the PRU)
|
||||
* data: a pointer that is populated with a local data buffer
|
||||
* containing the message payload
|
||||
* len: a pointer that is populated with the length of the
|
||||
* message payload
|
||||
*
|
||||
* Description : pru_rpmsg_receive uses the pru_virtqueue interface to get
|
||||
* an available buffer, copy the buffer into local memory,
|
||||
* add the buffer as a used buffer to the vring, and then kick
|
||||
* the remote processor if necessary. The src, dst, data, and
|
||||
* len pointers are populated with the information about the
|
||||
* message and local buffer data if the reception is
|
||||
* successful.
|
||||
*
|
||||
* Return Value : Returns PRU_RPMSG_NO_BUF_AVAILABLE if there is currently no
|
||||
* buffer available for receive. Returns PRU_RPMSG_INVALID_HEAD
|
||||
* if the head index returned for the available buffer is
|
||||
* invalid. Returns PRU_RPMSG_SUCCESS if the message is
|
||||
* successfully received.
|
||||
*/
|
||||
int16_t pru_rpmsg_receive(
|
||||
struct pru_rpmsg_transport *transport,
|
||||
uint16_t *src,
|
||||
uint16_t *dst,
|
||||
void *data,
|
||||
uint16_t *len
|
||||
);
|
||||
|
||||
/**
|
||||
* Summary : pru_rpmsg_send sends a message to the ARM host using the
|
||||
* virtqueues in the pru_rpmsg_transport structure. The
|
||||
* source and destination address of the message are passed
|
||||
* in as parameters to the function. The data to be sent and
|
||||
* its length are passed in the data and len parameters.
|
||||
*
|
||||
* Parameters : transport: a pointer to the transport layer from which the
|
||||
* message should be sent
|
||||
* src: the source address where this message will originate
|
||||
* dst: the destination address where the message will be sent
|
||||
* data: a pointer to a local data buffer containing the
|
||||
* message payload
|
||||
* len: the length of the message payload
|
||||
*
|
||||
* Description : pru_rpmsg_send sends a message to the src parameter and
|
||||
* from the dst parameter. The transport structure defines the
|
||||
* underlying transport mechanism that will be used. The
|
||||
* data parameter is a pointer to a local buffer that should
|
||||
* be sent to the destination address and the len parameter is
|
||||
* the length of that buffer.
|
||||
*
|
||||
* Return Value : Returns PRU_RPMSG_NO_BUF_AVAILABLE if there is currently no
|
||||
* buffer available for send. Returns PRU_RPMSG_BUF_TOO_SMALL
|
||||
* if the buffer from the vring is too small to hold the
|
||||
* message payload being sent. Returns PRU_RPMSG_INVALID_HEAD
|
||||
* if the head index returned for the send buffer is invalid.
|
||||
* Returns PRU_RPMSG_SUCCESS if the message is successfully
|
||||
* sent.
|
||||
*/
|
||||
int16_t pru_rpmsg_send(
|
||||
struct pru_rpmsg_transport *transport,
|
||||
uint32_t src,
|
||||
uint32_t dst,
|
||||
void *data,
|
||||
uint16_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Summary : pru_rpmsg_channel uses an RPMsg Name Service Announcment
|
||||
* to either create or destroy an RPMsg channel depending on
|
||||
* the pru_rpmsg_ns_flags parameter that is specified.
|
||||
*
|
||||
* Parameters : flags: an enum that is used to create (RPMSG_NS_CREATE) or
|
||||
* destroy (RPMSG_NS_DESTROY) an RPMsg channel
|
||||
* transport: a pointer to the transport layer on which this
|
||||
* Name Service Announcement will be sent
|
||||
* name: the name of the channel being created or destroyed
|
||||
* ******* The name of the channel is very important as
|
||||
* ******* it is the method that Linux on the ARM uses
|
||||
* ******* to connect a PRU firmware with a corresponding
|
||||
* ******* Linux driver
|
||||
* desc: the description of the RPMsg channel being created
|
||||
* or destroyed
|
||||
* port: the local source address of the RPMsg channel. This
|
||||
* is the address where PRU messages destined for the
|
||||
* ARM host will originate
|
||||
*
|
||||
* Description : pru_rpmsg_channel sends a message letting the ARM
|
||||
* host know that a channel is to be created or destroyed. If
|
||||
* a channel is to be created then this message will notify
|
||||
* the name server on the ARM host to create a new channel. If
|
||||
* a channel is to be destroyed this will tear down this
|
||||
* logical channel of communication between the PRU and the
|
||||
* ARM host.
|
||||
*
|
||||
* Return Value : Returns PRU_RPMSG_NO_BUF_AVAILABLE if there is currently no
|
||||
* buffer available for send. Returns PRU_RPMSG_BUF_TOO_SMALL
|
||||
* if the buffer from the vring is too small to hold the
|
||||
* message payload being sent. Returns PRU_RPMSG_INVALID_HEAD
|
||||
* if the head index returned for the send buffer is invalid.
|
||||
* Returns PRU_RPMSG_SUCCESS if the message is successfully
|
||||
* sent.
|
||||
*/
|
||||
int16_t pru_rpmsg_channel(
|
||||
enum pru_rpmsg_ns_flags flags,
|
||||
struct pru_rpmsg_transport *transport,
|
||||
char *name,
|
||||
char *desc,
|
||||
int32_t port
|
||||
);
|
||||
|
||||
#endif /* _PRU_RPMSG_H_ */
|
||||