Initial commit.
This commit is contained in:
commit
adafcac042
16 changed files with 21645 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
*.gcode
|
||||||
|
*.elf
|
||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
*.s
|
||||||
|
*.hex
|
||||||
|
*.swp
|
||||||
|
__pycache__
|
91
3d/lock/lock_0.1.scad
Normal file
91
3d/lock/lock_0.1.scad
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
|
||||||
|
opn_h = 48;
|
||||||
|
opn_w = 11.2;
|
||||||
|
|
||||||
|
d = 3;
|
||||||
|
|
||||||
|
btn_s = 6;
|
||||||
|
btn_dist = 34;
|
||||||
|
btn_d = 3.2;
|
||||||
|
|
||||||
|
h = btn_dist+btn_s+1.4+2;
|
||||||
|
|
||||||
|
hub = 1.5;
|
||||||
|
|
||||||
|
l_d = 24;
|
||||||
|
|
||||||
|
module ring(dia, thickness, w) {
|
||||||
|
translate([0, w, 0]) rotate ([90, 0, 0])
|
||||||
|
difference() {
|
||||||
|
cylinder(w, dia, dia, $fn=128);
|
||||||
|
translate([0, 0, -0.1])
|
||||||
|
cylinder(w+0.2, dia-thickness, dia-thickness, $fn=128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate([90, 0, 0]) {
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
cube([d, btn_s, h]);
|
||||||
|
/* Top clamp */
|
||||||
|
translate([d, 0, h-1.4])
|
||||||
|
cube([btn_d, btn_s, 1.4]);
|
||||||
|
/* Bottom clamp */
|
||||||
|
translate([d, 0, h-btn_s-(1.4*2)-0.4])
|
||||||
|
cube([btn_d, btn_s, 1.4]);
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
translate([d, -0.2, btn_dist-0.2])
|
||||||
|
cube([btn_s, btn_s+0.4, btn_s+0.4]);
|
||||||
|
translate([d, -0.1, 0])
|
||||||
|
cube([btn_d+0.1, btn_s+0.2, (btn_dist-(8+hub/2)+btn_s/2)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/* Foot */
|
||||||
|
difference() {
|
||||||
|
rotate([0, 30, 0])
|
||||||
|
cube([20, btn_s, d]);
|
||||||
|
translate([16, -0.1, -10])
|
||||||
|
cube([btn_s, btn_s+0.2, btn_s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Side hold */
|
||||||
|
/*
|
||||||
|
translate([0, 0, btn_dist+((btn_s-3.7)/2)])
|
||||||
|
cube([d, btn_s+2, 3.7]);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Ledge */
|
||||||
|
/*
|
||||||
|
translate([0, 0, h-1.4])
|
||||||
|
cube([d+btn_d+hub+1.4, btn_s, 1.4]);
|
||||||
|
*/
|
||||||
|
translate([d+btn_d+hub, 0, h-24])
|
||||||
|
intersection() {
|
||||||
|
translate([24, 0, 24])
|
||||||
|
ring(24, 1.4, btn_s);
|
||||||
|
cube([12, 24, 24]);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate([(d+btn_d+hub+1.4)/2, 0, h])
|
||||||
|
intersection() {
|
||||||
|
ring((d+btn_d+hub+1.4)/2, 1.4, btn_s);
|
||||||
|
translate([-10, 0, 0])
|
||||||
|
cube([20, 10, 10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
translate([4+d+btn_d+hub+0.2, 0, btn_dist+btn_s/2])
|
||||||
|
ring(4, 0.6, btn_s);
|
||||||
|
|
||||||
|
translate([d+btn_d, 0, btn_dist-(8+hub/2)+btn_s/2])
|
||||||
|
intersection() {
|
||||||
|
translate([0, 0, 4+hub])
|
||||||
|
ring(4+hub, 0.6, btn_s);
|
||||||
|
cube([6, 6, 4+hub]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
9466
3d/lock/lock_0.1.stl
Normal file
9466
3d/lock/lock_0.1.stl
Normal file
File diff suppressed because it is too large
Load diff
156
3d/lock/lock_0.3.scad
Normal file
156
3d/lock/lock_0.3.scad
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
|
||||||
|
render_lock = true;
|
||||||
|
render_wings = false;
|
||||||
|
|
||||||
|
d = 4; // Thickness (button/wall distance)
|
||||||
|
|
||||||
|
btn_s = 6; // Button (edge) size
|
||||||
|
btn_dist = 34; // Button distance from floor
|
||||||
|
btn_d = 3.2; // Button depth
|
||||||
|
hub = 1.5; // Button hub
|
||||||
|
|
||||||
|
h = btn_dist+btn_s+1.4+2;
|
||||||
|
|
||||||
|
fil = 0.32; // Filament thickness
|
||||||
|
l_d = 24; // Ledge size
|
||||||
|
l_thick = fil*4; // Ledge thickness
|
||||||
|
foot_s = 34; // Foot size
|
||||||
|
|
||||||
|
w_t = fil*4; // Wing thickness
|
||||||
|
w_h = 8; // Wing height
|
||||||
|
w_l = 4; // Wing length (per wing)
|
||||||
|
w_notch = 1; // Notch depth
|
||||||
|
|
||||||
|
module ring(dia, thickness, w) {
|
||||||
|
translate([0, w, 0]) rotate ([90, 0, 0])
|
||||||
|
difference() {
|
||||||
|
cylinder(w, dia, dia, $fn=128);
|
||||||
|
translate([0, 0, -0.1])
|
||||||
|
cylinder(w+0.2, dia-thickness, dia-thickness, $fn=128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (render_lock) {
|
||||||
|
rotate([90, 0, 0]) {
|
||||||
|
|
||||||
|
/* Base */
|
||||||
|
//difference() {
|
||||||
|
translate([0, 0, foot_s/2])
|
||||||
|
cube([d, btn_s, h-foot_s/2]);
|
||||||
|
// Wing notch cut-out
|
||||||
|
//translate([-0.4, btn_s-w_notch-0.4, btn_dist-w_h*2+w_t-0.2])
|
||||||
|
// cube([w_t+0.8, w_notch+0.5, w_h/2+0.8]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* Top clamp */
|
||||||
|
translate([d, 0, h-fil*4])
|
||||||
|
cube([btn_d, btn_s, fil*4]);
|
||||||
|
/* Bottom clamp */
|
||||||
|
translate([d, 0, h-btn_s-(fil*4*2)-0.4])
|
||||||
|
cube([btn_d, btn_s, fil*4]);
|
||||||
|
/* Notch */
|
||||||
|
notch = fil*4;
|
||||||
|
translate([0, 0, h-btn_s+btn_s/4-fil*4-0.2])
|
||||||
|
difference() {
|
||||||
|
cube([d, btn_s+notch, btn_s/2]);
|
||||||
|
translate([-0.1, 0.1, 4*fil])
|
||||||
|
cube([d-4*fil+0.1, btn_s+notch+0.2, btn_s/2-8*fil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Angel wing clamp */
|
||||||
|
/*
|
||||||
|
translate([d, 0, btn_dist-w_h*2]) {
|
||||||
|
cube([w_t+0.6, btn_s, fil*4]);
|
||||||
|
translate([w_t+0.6, 0, 0])
|
||||||
|
cube([fil*4, btn_s, w_h]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Wire clamp */
|
||||||
|
/*
|
||||||
|
w_s = 1.2;
|
||||||
|
translate([d, 0, btn_dist-10-w_s]) {
|
||||||
|
cube([w_s, btn_s/2, fil*4]);
|
||||||
|
translate([w_s, 0, 0])
|
||||||
|
cube([fil*4, btn_s/2, 10]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Ledge */
|
||||||
|
translate([d+btn_d+hub+0.2, 0, h-42+l_thick])
|
||||||
|
intersection() {
|
||||||
|
translate([42, 0, 42])
|
||||||
|
ring(42, l_thick, btn_s);
|
||||||
|
cube([8, 42, 42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension
|
||||||
|
ext = 2;
|
||||||
|
translate([0, 0, h+l_thick])
|
||||||
|
cube([l_thick, btn_s, ext]);
|
||||||
|
translate([d+btn_d+hub+0.2, 0, h+l_thick])
|
||||||
|
cube([l_thick, btn_s, ext]);
|
||||||
|
|
||||||
|
translate([0, 0, h])
|
||||||
|
difference() {
|
||||||
|
cube([l_thick*2, btn_s, l_thick]);
|
||||||
|
translate([l_thick*2, -0.1, l_thick]) rotate([-90, 0, 0])
|
||||||
|
cylinder(btn_s+0.2, l_thick, l_thick, $fn=64);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate([(d+btn_d+hub+l_thick+0.2)/2, 0, h+l_thick+ext])
|
||||||
|
intersection() {
|
||||||
|
ring((d+btn_d+hub+l_thick+0.2)/2, l_thick, btn_s);
|
||||||
|
translate([-10, 0, 0])
|
||||||
|
cube([20, 10, 10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Foot */
|
||||||
|
translate([foot_s, 0, foot_s/2])
|
||||||
|
intersection() {
|
||||||
|
ring(foot_s, d, btn_s);
|
||||||
|
translate([-foot_s, -0.1, -foot_s])
|
||||||
|
cube([foot_s-12, btn_s+0.2, foot_s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} // endif (render_lock)
|
||||||
|
|
||||||
|
/* Angel wings */
|
||||||
|
if (render_wings) {
|
||||||
|
translate([-20, 0, 0]) {
|
||||||
|
|
||||||
|
// Middle piece
|
||||||
|
translate([0, -0.2, 0])
|
||||||
|
cube([w_t, btn_s+0.4, w_h]);
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
translate([0, -w_t-0.2, 0])
|
||||||
|
cube([d+w_t, w_t, w_h]);
|
||||||
|
translate([0, btn_s+0.2, 0])
|
||||||
|
cube([d+w_t, w_t, w_h]);
|
||||||
|
|
||||||
|
// Notch
|
||||||
|
translate([d, -0.2, 0])
|
||||||
|
cube([w_t, w_notch, w_h/2]);
|
||||||
|
|
||||||
|
// Wings
|
||||||
|
translate([d, -w_l-w_t-0.2, 0])
|
||||||
|
cube([w_t, w_l, w_h]);
|
||||||
|
translate([d, btn_s+w_t+0.2, 0])
|
||||||
|
cube([w_t, w_l, w_h]);
|
||||||
|
|
||||||
|
// Wire clamps
|
||||||
|
wire = 1.2;
|
||||||
|
translate([d-w_t-wire, -w_l-w_t-0.2, 0])
|
||||||
|
cube([w_t, w_l-wire, w_h/2]);
|
||||||
|
translate([d-wire, -w_l-w_t-0.2, 0])
|
||||||
|
cube([wire, w_t, w_h/2]);
|
||||||
|
|
||||||
|
translate([d-w_t-wire, btn_s+w_t+0.2+wire, 0])
|
||||||
|
cube([w_t, w_l-wire, w_h/2]);
|
||||||
|
translate([d-wire, btn_s+w_t+w_l+0.2-w_t])
|
||||||
|
cube([wire, w_t, w_h/2]);
|
||||||
|
|
||||||
|
}
|
||||||
|
} // endif (render_wings)
|
6792
3d/lock/lock_0.3.stl
Normal file
6792
3d/lock/lock_0.3.stl
Normal file
File diff suppressed because it is too large
Load diff
BIN
3d/mount/mount magnet.stl
Normal file
BIN
3d/mount/mount magnet.stl
Normal file
Binary file not shown.
59
3d/mount/mount.scad
Normal file
59
3d/mount/mount.scad
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
fil = 0.25;
|
||||||
|
w = 31+fil*6;
|
||||||
|
l = 12;
|
||||||
|
cls = 6.4;
|
||||||
|
base_width = 30;
|
||||||
|
base_height = 15;
|
||||||
|
|
||||||
|
// Size of magnets used (plus clearance).
|
||||||
|
magnet = [20.1+1, 6.1+0.4, 1.6+0.8];
|
||||||
|
|
||||||
|
// Glue mount or magnet mount?
|
||||||
|
magnet_mount = true;
|
||||||
|
|
||||||
|
// Clamp.
|
||||||
|
module clamp (){
|
||||||
|
translate([0, fil*6, 0])
|
||||||
|
cube([fil*6, fil*6, l]);
|
||||||
|
translate([fil*6+2.5, fil*6, 0])
|
||||||
|
cube([cls-2.5, fil*8, l]);
|
||||||
|
cube([fil*3+cls, fil*6, l]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower clamp.
|
||||||
|
translate ([0, -fil*4, 0])
|
||||||
|
clamp();
|
||||||
|
|
||||||
|
// Upper clamp.
|
||||||
|
translate ([0, w+fil*4, l]) rotate([180, 0, 0])
|
||||||
|
clamp();
|
||||||
|
|
||||||
|
difference () {
|
||||||
|
minkowski () {
|
||||||
|
union () {
|
||||||
|
// Clamp connection.
|
||||||
|
translate([fil*3+cls, -4, 0])
|
||||||
|
cube([0.001, w+4+fil, l]);
|
||||||
|
// Clamp to base.
|
||||||
|
translate([fil*3+cls, -4, 0]) rotate([0, 0, 270])
|
||||||
|
cube([0.001, 4, l]);
|
||||||
|
|
||||||
|
// Base.
|
||||||
|
translate([fil*3+cls+4, -4, 0]) rotate([0, 0, 345])
|
||||||
|
if (magnet_mount) {
|
||||||
|
cube([magnet[2], magnet[1]*2, magnet[0]]);
|
||||||
|
} else {
|
||||||
|
cube([0.001, base_height, base_width]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cylinder(0.001, fil*3, fil*3, $fn=64);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate([fil*3+cls+4, -4, 0]) rotate([0, 0, 345])
|
||||||
|
if (magnet_mount) {
|
||||||
|
translate([magnet[2]/2, magnet[1]/2, -0.001])
|
||||||
|
cube([magnet[2], magnet[1], magnet[0]+0.002]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4524
3d/mount/mount.stl
Normal file
4524
3d/mount/mount.stl
Normal file
File diff suppressed because it is too large
Load diff
121
LICENSE
Normal file
121
LICENSE
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
57
Makefile
Normal file
57
Makefile
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
TARGET=wc-licht
|
||||||
|
MCU=atmega8
|
||||||
|
FREQUENCY=1000000
|
||||||
|
SOURCES=wc-licht.c
|
||||||
|
ASM_SOURCES=interrupts.S utils.S
|
||||||
|
|
||||||
|
PROGRAMMER=usbtiny
|
||||||
|
PORT=usb
|
||||||
|
BAUD=19200
|
||||||
|
#PROGRAMMER=arduino
|
||||||
|
#PORT=/dev/ttyUSB0
|
||||||
|
#BAUD=19200
|
||||||
|
|
||||||
|
OBJECTS=$(SOURCES:.c=.o)
|
||||||
|
OBJECTS+=$(ASM_SOURCES:.S=.o)
|
||||||
|
CFLAGS=-Os -Wall -std=gnu11 "-DF_CPU=$(FREQUENCY)"
|
||||||
|
LDFLAGS=
|
||||||
|
|
||||||
|
all: hex #eeprom
|
||||||
|
|
||||||
|
hex: $(TARGET).hex
|
||||||
|
|
||||||
|
eeprom: $(TARGET)_eeprom.hex
|
||||||
|
|
||||||
|
$(TARGET).hex: $(TARGET).elf
|
||||||
|
avr-objcopy -O ihex -j .data -j .text $(TARGET).elf $(TARGET).hex
|
||||||
|
|
||||||
|
$(TARGET)_eeprom.hex: $(TARGET).elf
|
||||||
|
avr-objcopy -O ihex -j .eeprom --change-section-lma .eeprom=1 $(TARGET).elf $(TARGET)_eeprom.hex
|
||||||
|
|
||||||
|
$(TARGET).elf: $(OBJECTS)
|
||||||
|
avr-gcc $(LDFLAGS) -mmcu=$(MCU) $(OBJECTS) -o $(TARGET).elf
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
avr-gcc -c $(CFLAGS) -mmcu=$(MCU) $< -o $@
|
||||||
|
|
||||||
|
.S.o:
|
||||||
|
avr-gcc -c $(CFLAGS) -mmcu=$(MCU) $< -o $@
|
||||||
|
|
||||||
|
size:
|
||||||
|
avr-size --mcu=$(MCU) -C $(TARGET).elf
|
||||||
|
|
||||||
|
asm:
|
||||||
|
avr-gcc -S $(CFLAGS) -fverbose-asm -mmcu=$(MCU) $(TARGET).c -o $(TARGET).s
|
||||||
|
|
||||||
|
program: hex
|
||||||
|
avrdude -p$(MCU) -P$(PORT) -b$(BAUD) -c$(PROGRAMMER) -Uflash:w:$(TARGET).hex:a
|
||||||
|
|
||||||
|
clean_tmp:
|
||||||
|
rm -rf *.o
|
||||||
|
rm -rf *.elf
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.o
|
||||||
|
rm -rf *.elf
|
||||||
|
rm -rf *.hex
|
||||||
|
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# WC-Licht
|
||||||
|
|
||||||
|
Besetzt-Anzeige über den WCs.
|
||||||
|
|
129
animations.h
Normal file
129
animations.h
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
const Animation intro = {
|
||||||
|
17, // Number of frames.
|
||||||
|
{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0},
|
||||||
|
{0, 0, 1, 2, 2, 1, 0, 0, 0, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{0, 1, 2, 4, 4, 2, 1, 0, 24, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
{24, 1, 2, 4, 4, 2, 1, 0, 36, 25, 2, 4, 4, 2, 1, 0},
|
||||||
|
{36, 25, 2, 4, 4, 2, 1, 0, 66, 37, 26, 4, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{66, 37, 26, 4, 4, 2, 1, 0, 66, 37, 26, 132, 132, 2, 1, 0},
|
||||||
|
{66, 37, 26, 132, 132, 2, 1, 0, 66, 37, 154, 68, 68, 130, 1, 0},
|
||||||
|
{66, 37, 154, 68, 68, 130, 1, 0, 66, 165, 90, 36, 36, 66, 129, 0},
|
||||||
|
|
||||||
|
{66, 165, 90, 36, 36, 66, 129, 0, 66, 165, 90, 36, 36, 66, 129, 24},
|
||||||
|
{66, 165, 90, 36, 36, 66, 129, 24, 66, 165, 90, 36, 36, 66, 153, 36},
|
||||||
|
{66, 165, 90, 36, 36, 66, 153, 36, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
|
||||||
|
{66, 165, 90, 60, 60, 90, 165, 66, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
{66, 165, 126, 60, 60, 126, 165, 66, 66, 165, 90, 60, 60, 90, 165, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 165, 126, 60, 60, 126, 165, 66},
|
||||||
|
{195, 231, 126, 60, 60, 126, 231, 195, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animation intro = {
|
||||||
|
19, // Number of frames.
|
||||||
|
{
|
||||||
|
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 1, 3, 3, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
|
||||||
|
{0, 1, 3, 6, 6, 3, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0},
|
||||||
|
{0, 3, 6, 12, 12, 6, 3, 0, 0, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
{0, 3, 6, 12, 12, 6, 3, 0, 0, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{24, 3, 6, 12, 12, 6, 3, 0, 0, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
{60, 27, 6, 12, 12, 6, 3, 0, 24, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
{102, 63, 30, 12, 12, 6, 3, 0, 36, 25, 2, 4, 4, 2, 1, 0},
|
||||||
|
{66, 103, 62, 28, 12, 6, 3, 0, 66, 39, 30, 12, 4, 2, 1, 0},
|
||||||
|
{66, 103, 62, 28, 12, 6, 3, 0, 66, 39, 30, 12, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{66, 103, 62, 156, 140, 6, 3, 0, 66, 39, 30, 12, 4, 2, 1, 0},
|
||||||
|
{66, 103, 190, 220, 204, 134, 3, 0, 66, 39, 30, 140, 132, 2, 1, 0},
|
||||||
|
{66, 231, 254, 124, 108, 198, 131, 0, 66, 39, 158, 76, 68, 130, 1, 0},
|
||||||
|
{66, 231, 126, 60, 60, 102, 195, 0, 66, 231, 126, 60, 36, 66, 129, 0},
|
||||||
|
{66, 231, 126, 60, 60, 102, 195, 0, 66, 231, 126, 60, 36, 66, 129, 0},
|
||||||
|
|
||||||
|
{66, 231, 126, 60, 60, 102, 195, 24, 66, 231, 126, 60, 36, 66, 129, 0},
|
||||||
|
{66, 231, 126, 60, 60, 102, 219, 60, 66, 231, 126, 60, 36, 66, 129, 24},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 126, 66, 231, 126, 60, 36, 66, 153, 36},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
const Animation interlude = {
|
||||||
|
10, // Number of frames.
|
||||||
|
{
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 36, 36, 126, 231, 66},
|
||||||
|
{66, 231, 126, 36, 36, 126, 231, 66, 66, 231, 90, 36, 36, 90, 231, 66},
|
||||||
|
{66, 231, 90, 36, 36, 90, 231, 66, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
|
||||||
|
{66, 165, 90, 36, 36, 90, 165, 66, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
{66, 165, 90, 36, 36, 90, 165, 66, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
|
||||||
|
{66, 165, 90, 60, 60, 90, 165, 66, 66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
{66, 165, 126, 60, 60, 126, 165, 66, 66, 165, 90, 60, 60, 90, 165, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 165, 126, 60, 60, 126, 165, 66},
|
||||||
|
{195, 231, 126, 60, 60, 126, 231, 195, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animation interlude = {
|
||||||
|
9, // Number of frames.
|
||||||
|
{
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 0, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 0, 126, 60, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 0, 60, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 0, 60, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 0, 126, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 0, 231, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 0, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 231, 0},
|
||||||
|
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66, 66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
const Animation intro1 = {
|
||||||
|
23, // Numer of frames.
|
||||||
|
{
|
||||||
|
{0, 0, 0, 1, 1, 0, 0, 0},
|
||||||
|
{0, 0, 1, 2, 2, 1, 0, 0},
|
||||||
|
{0, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{24, 1, 2, 4, 4, 2, 1, 0},
|
||||||
|
{36, 25, 2, 4, 4, 2, 1, 0},
|
||||||
|
{66, 37, 26, 4, 4, 2, 1, 0},
|
||||||
|
|
||||||
|
{66, 37, 26, 132, 132, 2, 1, 0},
|
||||||
|
{66, 37, 154, 68, 68, 130, 1, 0},
|
||||||
|
{66, 165, 90, 36, 36, 66, 129, 0},
|
||||||
|
|
||||||
|
{66, 165, 90, 36, 36, 66, 129, 24},
|
||||||
|
{66, 165, 90, 36, 36, 66, 153, 36},
|
||||||
|
{66, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
|
||||||
|
{67, 165, 90, 36, 36, 90, 165, 66},
|
||||||
|
{66, 167, 90, 36, 36, 90, 165, 66},
|
||||||
|
{66, 167, 94, 36, 36, 90, 165, 66},
|
||||||
|
{66, 167, 94, 44, 36, 90, 165, 66},
|
||||||
|
{194, 167, 94, 44, 52, 90, 165, 66},
|
||||||
|
{66, 231, 94, 44, 52, 122, 165, 66},
|
||||||
|
{66, 231, 126, 44, 52, 122, 229, 66},
|
||||||
|
{66, 231, 126, 60, 52, 122, 229, 66},
|
||||||
|
{66, 231, 126, 60, 60, 122, 229, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 229, 66},
|
||||||
|
{66, 231, 126, 60, 60, 126, 231, 66},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
76
interrupts.S
Normal file
76
interrupts.S
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
#define VERT_PORT PORTB
|
||||||
|
#define HORIZ_DDR DDRD
|
||||||
|
#define SHADE_CYCLES 16
|
||||||
|
|
||||||
|
.section .data
|
||||||
|
iteration:
|
||||||
|
.byte 0
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.extern cmd
|
||||||
|
.extern screen
|
||||||
|
.global TWI_vect
|
||||||
|
.global TIMER2_COMP_vect
|
||||||
|
|
||||||
|
; TIMER2 interrupt routine.
|
||||||
|
; Registers:
|
||||||
|
; r0 SREG
|
||||||
|
; r18 Iteration number
|
||||||
|
; Vertical position (VERT_PORT)
|
||||||
|
; Busy loop
|
||||||
|
; Image data (HORIZ_DDR)
|
||||||
|
; X *screen
|
||||||
|
|
||||||
|
TIMER2_COMP_vect:
|
||||||
|
push r0
|
||||||
|
push r18
|
||||||
|
push XL
|
||||||
|
push XH
|
||||||
|
in r0, _SFR_IO_ADDR(SREG) ; Save SREG.
|
||||||
|
|
||||||
|
|
||||||
|
ldi XL, lo8(screen) ; Load start address of the screen into X.
|
||||||
|
ldi XH, hi8(screen)
|
||||||
|
|
||||||
|
lds r18, iteration
|
||||||
|
add XL, r18 ; add as offset to X.
|
||||||
|
adc XH, r1
|
||||||
|
|
||||||
|
inc r18 ; Increase iteration.
|
||||||
|
andi r18, 0x0f ; Clip iteration into range 0:7.
|
||||||
|
sts iteration, r18
|
||||||
|
|
||||||
|
in r18, _SFR_IO_ADDR(VERT_PORT) ; Load vertical position into r18.
|
||||||
|
lsl r18 ; Switch to the next vertical position.
|
||||||
|
brne set_row
|
||||||
|
ldi r18, 1 ; Reset to 1 if 0.
|
||||||
|
|
||||||
|
clt ; Clear T flag in SREG.
|
||||||
|
|
||||||
|
set_row:
|
||||||
|
out _SFR_IO_ADDR(HORIZ_DDR), r1 ; Zero out row.
|
||||||
|
out _SFR_IO_ADDR(VERT_PORT), r18 ; Push next position into io register.
|
||||||
|
load_img:
|
||||||
|
ld r18, X+ ; Load image data into r18
|
||||||
|
out _SFR_IO_ADDR(HORIZ_DDR), r18 ; Push image data into row.
|
||||||
|
|
||||||
|
brts exit
|
||||||
|
set ; Set T flag in SREG to exit after the second iteration.
|
||||||
|
adiw X, 7
|
||||||
|
ldi r18, SHADE_CYCLES
|
||||||
|
wait: ; Busy loop.
|
||||||
|
dec r18
|
||||||
|
brne wait
|
||||||
|
rjmp load_img
|
||||||
|
|
||||||
|
exit:
|
||||||
|
out _SFR_IO_ADDR(SREG), r0 ; Restore SREG.
|
||||||
|
pop XH
|
||||||
|
pop XL
|
||||||
|
pop r18
|
||||||
|
pop r0
|
||||||
|
reti
|
||||||
|
|
39
utils.S
Normal file
39
utils.S
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#define FRAME_SIZE 8
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
;
|
||||||
|
.global copy_layer
|
||||||
|
.type copy_layer, @function
|
||||||
|
copy_layer:
|
||||||
|
movw X, r24 ; Source frame.
|
||||||
|
movw Z, r22 ; Target frame.
|
||||||
|
ldi r24, FRAME_SIZE*2 ; Number of iterations needed.
|
||||||
|
|
||||||
|
copy:
|
||||||
|
ld r0, X+
|
||||||
|
st Z+, r0
|
||||||
|
dec r24
|
||||||
|
brne copy
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
.global push_layer
|
||||||
|
.type push_layer, @function
|
||||||
|
push_layer:
|
||||||
|
movw X, r24 ; Source frame.
|
||||||
|
movw Z, r22 ; Target frame.
|
||||||
|
ldi r24, FRAME_SIZE ; Number of iterations needed.
|
||||||
|
|
||||||
|
push:
|
||||||
|
ldd r0, Z+1
|
||||||
|
st Z+, r0
|
||||||
|
ld r0, X+
|
||||||
|
st Z+, r0
|
||||||
|
dec r24
|
||||||
|
brne push
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
88
wc-licht.c
Normal file
88
wc-licht.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* WC-Licht.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/sleep.h>
|
||||||
|
#include "wc-licht.h"
|
||||||
|
#include "animations.h"
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t fb[SIZE_X*2]; // Framebuffer.
|
||||||
|
uint8_t screen[SIZE_X*2]; // Screen buffer.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Woot, main!
|
||||||
|
*/
|
||||||
|
int main (void) {
|
||||||
|
/*
|
||||||
|
* Init display output ports.
|
||||||
|
*/
|
||||||
|
// Horizontal pins sink current.
|
||||||
|
HORIZ_PORT = 0x00;
|
||||||
|
HORIZ_DDR = 0x00;
|
||||||
|
// Vertical pins deliver current.
|
||||||
|
VERT_PORT = 0x00;
|
||||||
|
VERT_DDR = 0xff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init timer for display refresh interrupts.
|
||||||
|
*/
|
||||||
|
TCCR2 |= 1 << WGM21; // CTC mode.
|
||||||
|
TCCR2 |= (1 << CS21) | (1 << CS20); // Prescaling: CLK/32.
|
||||||
|
OCR2 = 32; // Set output compare register.
|
||||||
|
TIMSK |= 1 << OCIE2; // Enable interrupt.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init timer for animation delays.
|
||||||
|
*/
|
||||||
|
TCCR1B |= 1 << WGM12; // CTC Mode.
|
||||||
|
TCCR1B |= (1 << CS12) | (1 << CS10); // clk/1024.
|
||||||
|
//TIMSK |= 1 << OCIE1A; // Enable interrupt.
|
||||||
|
|
||||||
|
ACSR |= 1 << ACD; // Disable analog comparator.
|
||||||
|
|
||||||
|
sleep_enable();
|
||||||
|
sei(); // Starts display.
|
||||||
|
|
||||||
|
// Play intro.
|
||||||
|
play_animation(&intro);
|
||||||
|
DELAY = INTERLUDE_DELAY;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main loop.
|
||||||
|
*/
|
||||||
|
while (1) {
|
||||||
|
if (TIFR & (1 << OCF1A)) {
|
||||||
|
// Time for a new animation.
|
||||||
|
TIFR |= 1 << OCF1A;
|
||||||
|
|
||||||
|
play_animation(&interlude);
|
||||||
|
DELAY = INTERLUDE_DELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep_cpu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void play_animation (const Animation* anim) {
|
||||||
|
DELAY = ANIMATION_DELAY;
|
||||||
|
TCNT1 = 0x0000; // Re-start delay timer.
|
||||||
|
TIFR |= 1 << OCF1A; // Unset delay passed flag.
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < anim->length; i++) {
|
||||||
|
copy_layer(anim->frames[i], screen);
|
||||||
|
|
||||||
|
while (!(TIFR & (1 << OCF1A))) {
|
||||||
|
// Delay.
|
||||||
|
sleep_cpu();
|
||||||
|
};
|
||||||
|
|
||||||
|
TCNT1 = 0x0000; // Re-start delay timer.
|
||||||
|
TIFR |= 1 << OCF1A; // Unset delay passed flag.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
35
wc-licht.h
Normal file
35
wc-licht.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* WC-Licht header file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define ANIMATION_DELAY 195 // ~ 200ms @ 1MHz/1024
|
||||||
|
#define ANIMATION_DELAY 49 // ~ 50ms @ 1MHz/1024
|
||||||
|
//#define INTERLUDE_DELAY 19531 // ~ 20s @ 1MHz/1024
|
||||||
|
#define INTERLUDE_DELAY 3906 // ~ 4s @ 1MHz/1024
|
||||||
|
#define SIZE_X 8
|
||||||
|
#define SIZE_Y 8
|
||||||
|
|
||||||
|
#define HORIZ_PORT PORTD
|
||||||
|
#define HORIZ_DDR DDRD
|
||||||
|
#define HORIZ_PIN PIND
|
||||||
|
#define HORIZ HORIZ_DDR
|
||||||
|
|
||||||
|
#define VERT_PORT PORTB
|
||||||
|
#define VERT_DDR DDRB
|
||||||
|
#define VERT_PIN PINB
|
||||||
|
#define VERT VERT_PORT
|
||||||
|
|
||||||
|
#define DELAY OCR1A
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t length;
|
||||||
|
const uint8_t frames[][SIZE_X*2];
|
||||||
|
} Animation;
|
||||||
|
|
||||||
|
|
||||||
|
void play_animation (const Animation*);
|
||||||
|
void copy_layer (const uint8_t*, uint8_t*);
|
||||||
|
void push_layer (const uint8_t*, uint8_t*);
|
||||||
|
int main (void);
|
||||||
|
|
Loading…
Reference in a new issue