IPATH += ${simavr}/simavr/sim
VPATH = src
+VPATH += src/c3
VPATH += ../parts
VPATH += ../shared
# for the Open Motion Controller board
CFLAGS += -DMOTHERBOARD=91
+CFLAGS += ${shell pkg-config --cflags pangocairo}
+ifneq (${shell uname}, Darwin)
+LDFLAGS += -lGL -lglut -lGLU
+else
+LDFLAGS += -framework GLUT -framework OpenGL
+endif
+LDFLAGS += ${shell pkg-config --libs pangocairo}
LDFLAGS += -lpthread -lutil -ldl
+LDFLAGS += -lm
+
+C3SRC = ${wildcard src/c3/*.c}
+C3OBJ = ${patsubst src/c3%,${OBJ}%,${C3SRC:.c=.o}}
+
+CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}}
+
all: obj ${firmware} ${target}
board = ${OBJ}/${target}.elf
+${board} : ${C3OBJ}
${board} : ${OBJ}/mongoose.o
${board} : ${OBJ}/button.o
${board} : ${OBJ}/uart_pty.o
${board} : ${OBJ}/thermistor.o
${board} : ${OBJ}/heatpot.o
${board} : ${OBJ}/stepper.o
+${board} : ${OBJ}/c_utils.o
${board} : ${OBJ}/${target}.o
+${board} : ${OBJ}/${target}_gl.o
${target}: ${board}
@echo $@ done
clean: clean-${OBJ}
rm -rf *.a *.axf ${target} *.vcd
+
#include "sim_elf.h"
#include "sim_hex.h"
#include "sim_gdb.h"
-#include "sim_vcd_file.h"
#include "mongoose.h"
+#include "reprap_gl.h"
+
#include "button.h"
-#include "uart_pty.h"
-#include "thermistor.h"
-#include "thermistor.h"
-#include "heatpot.h"
-#include "stepper.h"
+#include "reprap.h"
#define __AVR_ATmega644__
#include "marlin/pins.h"
TALLY_HOTEND_FAN,
};
-thermistor_t therm_hotend;
-thermistor_t therm_hotbed;
-thermistor_t therm_spare;
-heatpot_t hotend;
-heatpot_t hotbed;
-
-stepper_t step_x, step_y, step_z, step_e;
+reprap_t reprap;
-uart_pty_t uart_pty;
avr_t * avr = NULL;
-avr_vcd_t vcd_file;
typedef struct ardupin_t {
uint32_t port : 7, pin : 3, analog : 1, adc : 3, pwm : 1, ardupin;
* called when the AVR change any of the pins on port B
* so lets update our buffer
*/
-void hotbed_change_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+void
+hotbed_change_hook(
+ struct avr_irq_t * irq,
+ uint32_t value,
+ void * param)
{
printf("%s %d\n", __func__, value);
// pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq);
}
+
char avr_flash_path[1024];
int avr_flash_fd = 0;
perror(avr_flash_path);
}
close(avr_flash_fd);
- uart_pty_stop(&uart_pty);
+ uart_pty_stop(&reprap.uart_pty);
}
static void *
void * param)
{
// printf("%s write %x\n", __func__, addr);
- usleep(1000);
+ static uint16_t tick = 0;
+ if (!(tick++ & 0xf))
+ usleep(100);
}
+static void *
+avr_run_thread(
+ void * ignore)
+{
+ while (1) {
+ avr_run(avr);
+ }
+ return NULL;
+}
+
+void
+reprap_init(
+ avr_t * avr,
+ reprap_p r)
+{
+ r->avr = avr;
+ uart_pty_init(avr, &r->uart_pty);
+ uart_pty_connect(&r->uart_pty, '0');
+
+ thermistor_init(avr, &r->therm_hotend, 0,
+ (short*)TERMISTOR_TABLE(TEMP_SENSOR_0),
+ sizeof(TERMISTOR_TABLE(TEMP_SENSOR_0)) / sizeof(short) / 2,
+ OVERSAMPLENR, 25.0f);
+ thermistor_init(avr, &r->therm_hotbed, 2,
+ (short*)TERMISTOR_TABLE(TEMP_SENSOR_BED),
+ sizeof(TERMISTOR_TABLE(TEMP_SENSOR_BED)) / sizeof(short) / 2,
+ OVERSAMPLENR, 30.0f);
+ thermistor_init(avr, &r->therm_spare, 1,
+ (short*)temptable_5, sizeof(temptable_5) / sizeof(short) / 2,
+ OVERSAMPLENR, 10.0f);
+
+ heatpot_init(avr, &r->hotend, "hotend", 28.0f);
+ heatpot_init(avr, &r->hotbed, "hotbed", 25.0f);
+
+ /* connect heatpot temp output to thermistors */
+ avr_connect_irq(r->hotend.irq + IRQ_HEATPOT_TEMP_OUT,
+ r->therm_hotend.irq + IRQ_TERM_TEMP_VALUE_IN);
+ avr_connect_irq(r->hotbed.irq + IRQ_HEATPOT_TEMP_OUT,
+ r->therm_hotbed.irq + IRQ_TERM_TEMP_VALUE_IN);
+
+ float axis_pp_per_mm[4] = DEFAULT_AXIS_STEPS_PER_UNIT; // from Marlin!
+ {
+ avr_irq_t * e = get_ardu_irq(avr, X_ENABLE_PIN, arduidiot_644);
+ avr_irq_t * s = get_ardu_irq(avr, X_STEP_PIN, arduidiot_644);
+ avr_irq_t * d = get_ardu_irq(avr, X_DIR_PIN, arduidiot_644);
+ avr_irq_t * m = get_ardu_irq(avr, X_MIN_PIN, arduidiot_644);
+
+ stepper_init(avr, &r->step_x, "X", axis_pp_per_mm[0], 100, 220, 0);
+ stepper_connect(&r->step_x, s, d, e, m, stepper_endstop_inverted);
+ }
+ {
+ avr_irq_t * e = get_ardu_irq(avr, Y_ENABLE_PIN, arduidiot_644);
+ avr_irq_t * s = get_ardu_irq(avr, Y_STEP_PIN, arduidiot_644);
+ avr_irq_t * d = get_ardu_irq(avr, Y_DIR_PIN, arduidiot_644);
+ avr_irq_t * m = get_ardu_irq(avr, Y_MIN_PIN, arduidiot_644);
+
+ stepper_init(avr, &r->step_y, "Y", axis_pp_per_mm[1], 100, 220, 0);
+ stepper_connect(&r->step_y, s, d, e, m, stepper_endstop_inverted);
+ }
+ {
+ avr_irq_t * e = get_ardu_irq(avr, Z_ENABLE_PIN, arduidiot_644);
+ avr_irq_t * s = get_ardu_irq(avr, Z_STEP_PIN, arduidiot_644);
+ avr_irq_t * d = get_ardu_irq(avr, Z_DIR_PIN, arduidiot_644);
+ avr_irq_t * m = get_ardu_irq(avr, Z_MIN_PIN, arduidiot_644);
+
+ stepper_init(avr, &r->step_z, "Z", axis_pp_per_mm[2], 20, 110, 0);
+ stepper_connect(&r->step_z, s, d, e, m, stepper_endstop_inverted);
+ }
+ {
+ avr_irq_t * e = get_ardu_irq(avr, E0_ENABLE_PIN, arduidiot_644);
+ avr_irq_t * s = get_ardu_irq(avr, E0_STEP_PIN, arduidiot_644);
+ avr_irq_t * d = get_ardu_irq(avr, E0_DIR_PIN, arduidiot_644);
+
+ stepper_init(avr, &r->step_e, "E", axis_pp_per_mm[3], 0, 0, 0);
+ stepper_connect(&r->step_e, s, d, e, NULL, 0);
+ }
+
+}
int main(int argc, char *argv[])
{
// I changed Marlin to do a spurious write to the GPIOR0 register so we can trap it
avr_register_io_write(avr, MEGA644_GPIOR0, reprap_relief_callback, NULL);
- uart_pty_init(avr, &uart_pty);
- uart_pty_connect(&uart_pty, '0');
-
- thermistor_init(avr, &therm_hotend, 0,
- (short*)TERMISTOR_TABLE(TEMP_SENSOR_0),
- sizeof(TERMISTOR_TABLE(TEMP_SENSOR_0)) / sizeof(short) / 2,
- OVERSAMPLENR, 25.0f);
- thermistor_init(avr, &therm_hotbed, 2,
- (short*)TERMISTOR_TABLE(TEMP_SENSOR_BED),
- sizeof(TERMISTOR_TABLE(TEMP_SENSOR_BED)) / sizeof(short) / 2,
- OVERSAMPLENR, 30.0f);
- thermistor_init(avr, &therm_spare, 1,
- (short*)temptable_5, sizeof(temptable_5) / sizeof(short) / 2,
- OVERSAMPLENR, 10.0f);
-
- heatpot_init(avr, &hotend, "hotend", 28.0f);
- heatpot_init(avr, &hotbed, "hotbed", 25.0f);
-
- /* connect heatpot temp output to thermistors */
- avr_connect_irq(hotend.irq + IRQ_HEATPOT_TEMP_OUT, therm_hotend.irq + IRQ_TERM_TEMP_VALUE_IN);
- avr_connect_irq(hotbed.irq + IRQ_HEATPOT_TEMP_OUT, therm_hotbed.irq + IRQ_TERM_TEMP_VALUE_IN);
-
- float axis_pp_per_mm[4] = DEFAULT_AXIS_STEPS_PER_UNIT; // from Marlin!
- {
- avr_irq_t * e = get_ardu_irq(avr, X_ENABLE_PIN, arduidiot_644);
- avr_irq_t * s = get_ardu_irq(avr, X_STEP_PIN, arduidiot_644);
- avr_irq_t * d = get_ardu_irq(avr, X_DIR_PIN, arduidiot_644);
- avr_irq_t * m = get_ardu_irq(avr, X_MIN_PIN, arduidiot_644);
-
- stepper_init(avr, &step_x, "X", axis_pp_per_mm[0], 100, 220, 0);
- stepper_connect(&step_x, s, d, e, m, stepper_endstop_inverted);
- }
- {
- avr_irq_t * e = get_ardu_irq(avr, Y_ENABLE_PIN, arduidiot_644);
- avr_irq_t * s = get_ardu_irq(avr, Y_STEP_PIN, arduidiot_644);
- avr_irq_t * d = get_ardu_irq(avr, Y_DIR_PIN, arduidiot_644);
- avr_irq_t * m = get_ardu_irq(avr, Y_MIN_PIN, arduidiot_644);
-
- stepper_init(avr, &step_y, "Y", axis_pp_per_mm[1], 100, 220, 0);
- stepper_connect(&step_y, s, d, e, m, stepper_endstop_inverted);
- }
- {
- avr_irq_t * e = get_ardu_irq(avr, Z_ENABLE_PIN, arduidiot_644);
- avr_irq_t * s = get_ardu_irq(avr, Z_STEP_PIN, arduidiot_644);
- avr_irq_t * d = get_ardu_irq(avr, Z_DIR_PIN, arduidiot_644);
- avr_irq_t * m = get_ardu_irq(avr, Z_MIN_PIN, arduidiot_644);
-
- stepper_init(avr, &step_z, "Z", axis_pp_per_mm[2], 20, 110, 0);
- stepper_connect(&step_z, s, d, e, m, stepper_endstop_inverted);
- }
- {
- avr_irq_t * e = get_ardu_irq(avr, E0_ENABLE_PIN, arduidiot_644);
- avr_irq_t * s = get_ardu_irq(avr, E0_STEP_PIN, arduidiot_644);
- avr_irq_t * d = get_ardu_irq(avr, E0_DIR_PIN, arduidiot_644);
-
- stepper_init(avr, &step_e, "E", axis_pp_per_mm[3], 0, 0, 0);
- stepper_connect(&step_e, s, d, e, NULL, 0);
- }
+ reprap_init(avr, &reprap);
const char *options[] = {"listening_ports", "9090", NULL};
struct mg_context *ctx = mg_start(&mongoose_callback, NULL, options);
printf("mongoose %p\n", ctx);
- while (1) {
- int state = avr_run(avr);
- if ( state == cpu_Done || state == cpu_Crashed)
- break;
- }
+ gl_init(argc, argv);
+ pthread_t run;
+ pthread_create(&run, NULL, avr_run_thread, NULL);
+
+ gl_runloop();
+
mg_stop(ctx);
}
--- /dev/null
+/*
+ reprap.h
+
+ Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __REPRAP_H___
+#define __REPRAP_H___
+
+#include "sim_avr.h"
+#include "thermistor.h"
+#include "heatpot.h"
+#include "stepper.h"
+#include "uart_pty.h"
+#include "sim_vcd_file.h"
+
+typedef struct reprap_t {
+ struct avr_t * avr;
+ thermistor_t therm_hotend;
+ thermistor_t therm_hotbed;
+ thermistor_t therm_spare;
+ heatpot_t hotend;
+ heatpot_t hotbed;
+
+ stepper_t step_x, step_y, step_z, step_e;
+
+ uart_pty_t uart_pty;
+ avr_vcd_t vcd_file;
+} reprap_t, *reprap_p;
+
+#endif /* __REPRAP_H___ */
--- /dev/null
+/*
+ reprap_gl.c
+
+ Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if __APPLE__
+#include <GLUT/glut.h>
+#else
+#include <GL/glut.h>
+#endif
+
+#include <stdio.h>
+
+#include "reprap.h"
+#include "reprap_gl.h"
+
+#include "c3/c3.h"
+#include "c3/c3camera.h"
+#include "c3/c3arcball.h"
+
+int _w = 800, _h = 600;
+c3cam cam;
+c3arcball arcball;
+c3object_p root;
+c3object_p head;
+c3geometry_array_t geo_sorted = C_ARRAY_NULL;
+
+extern reprap_t reprap;
+
+static void
+_gl_key_cb(
+ unsigned char key,
+ int x,
+ int y) /* called on key press */
+{
+ switch (key) {
+ case 'q':
+ // avr_vcd_stop(&vcd_file);
+ exit(0);
+ break;
+ case 'r':
+ printf("Starting VCD trace; press 's' to stop\n");
+ // avr_vcd_start(&vcd_file);
+ break;
+ case 's':
+ printf("Stopping VCD trace\n");
+ // avr_vcd_stop(&vcd_file);
+ break;
+ }
+}
+
+
+static void
+_gl_display_cb(void) /* function called whenever redisplay needed */
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Set up projection matrix
+ glMatrixMode(GL_PROJECTION); // Select projection matrix
+ glLoadIdentity(); // Start with an identity matrix
+
+ gluPerspective(45, _w / _h, 0, 10000);
+
+// glEnable(GL_TEXTURE_2D);
+ glShadeModel(GL_SMOOTH);
+
+ glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND); // Enable Blending
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Type Of Blending To Use
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ // glMultMatrixf(arcball.rot.n);
+ glMultMatrixf(cam.mtx.n);
+ glTranslatef( -cam.eye.n[VX], -cam.eye.n[VY], -cam.eye.n[VZ] );
+ // glMultMatrixf(arcball.rot.n);
+
+ c3vec3 headp = c3vec3f(
+ stepper_get_position_mm(&reprap.step_x),
+ stepper_get_position_mm(&reprap.step_y),
+ stepper_get_position_mm(&reprap.step_z));
+ c3mat4 headmove = translation3D(headp);
+ c3transform_set(head->transform.e[0], &headmove);
+
+ if (root->dirty) {
+ printf("reproject\n");
+ c3mat4 m = identity3D();
+ c3object_project(root, &m);
+ c3geometry_array_clear(&geo_sorted);
+ c3object_get_geometry(root, &geo_sorted);
+ }
+
+ for (int gi = 0; gi < geo_sorted.count; gi++) {
+ c3geometry_p g = geo_sorted.e[gi];
+ glColor4fv(g->mat.color.n);
+ glVertexPointer(3, GL_FLOAT, 0, g->projected.count ? g->projected.e : g->vertice.e);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glDrawArrays(g->type, 0, g->vertice.count);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ }
+
+ glMatrixMode(GL_PROJECTION); // Select projection matrix
+ glLoadIdentity(); // Start with an identity matrix
+ glOrtho(0, _w, 0, _h, 0, 10);
+ glScalef(1,-1,1);
+ glTranslatef(0, -1 * _h, 0);
+
+ glMatrixMode(GL_MODELVIEW); // Select modelview matrix
+
+ #if 0
+ glPushMatrix();
+ glLoadIdentity(); // Start with an identity matrix
+ glScalef(3, 3, 1);
+ hd44780_gl_draw(
+ &hd44780,
+ colors[color][0], /* background */
+ colors[color][1], /* character background */
+ colors[color][2], /* text */
+ colors[color][3] /* shadow */ );
+ glPopMatrix();
+#endif
+ glutSwapBuffers();
+}
+
+int button;
+c3vec2 move;
+c3cam startcam;
+
+static
+void _gl_button_cb(
+ int b,
+ int s,
+ int x,
+ int y)
+{
+ button = s == GLUT_DOWN ? b : 0;
+ startcam = cam;
+ move = c3vec2f(x, y);
+ if (s == GLUT_DOWN)
+ c3arcball_mouse_down(&arcball, x, y);
+ else
+ c3arcball_mouse_up(&arcball);
+}
+
+void
+_gl_motion_cb(
+ int x,
+ int y)
+{
+ c3vec2 m = c3vec2f(x, y);
+ c3vec2 delta = c3vec2_sub(move, m);
+
+// printf("%s b%d click %.1f,%.1f now %d,%d\n",
+// __func__, button, move.n[0], move.n[1], x, y);
+
+ switch (button) {
+ case GLUT_LEFT_BUTTON: {
+
+ // c3cam_eye_yaw(&cam, delta.n[0] / 4);
+ // c3cam_eye_pitch(&cam, delta.n[1] / 4);
+
+ c3mat4 rotx = rotation3D(c3vec3f(1.0, 0, 0), delta.n[1] / 4);
+ c3mat4 roty = rotation3D(c3vec3f(0.0, 0.0, 1.0), delta.n[0] / 4);
+ rotx = c3mat4_mul(&rotx, &roty);
+ c3cam_rot_about_lookat(&cam, &rotx);
+
+ c3cam_update_matrix(&cam);
+// c3arcball_mouse_motion(&arcball, x, y, 0,0,0);
+ } break;
+ case GLUT_RIGHT_BUTTON: {
+
+ } break;
+ }
+ move = m;
+}
+
+// gl timer. if the lcd is dirty, refresh display
+static void
+_gl_timer_cb(
+ int i)
+{
+ //static int oldstate = -1;
+ // restart timer
+ c3arcball_idle(&arcball);
+ glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
+ glutPostRedisplay();
+}
+
+int
+gl_init(
+ int argc,
+ char *argv[] )
+{
+ glutInit(&argc, argv); /* initialize GLUT system */
+
+ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
+ glutInitWindowSize(_w, _h); /* width=400pixels height=500pixels */
+ /*window =*/ glutCreateWindow("Press 'q' to quit"); /* create window */
+
+ glutDisplayFunc(_gl_display_cb); /* set window's display callback */
+ glutKeyboardFunc(_gl_key_cb); /* set window's key callback */
+ glutTimerFunc(1000 / 24, _gl_timer_cb, 0);
+
+ glutMouseFunc(_gl_button_cb);
+ glutMotionFunc(_gl_motion_cb);
+
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glEnable(GL_LINE_SMOOTH);
+ cam = c3cam_new();
+ cam.lookat = c3vec3f(100.0, 100.0, 0.0);
+ cam.eye = c3vec3f(100.0, -100.0, 100.0);
+ c3cam_update_matrix(&cam);
+
+ c3arcball_init_center(&arcball, c3vec2f(_w/2, _h/2), 100);
+// hd44780_gl_init();
+
+ root = c3object_new(NULL);
+
+ c3object_p grid = c3object_new(root);
+ {
+ c3geometry_p g = c3geometry_new(GL_LINES, grid);
+ g->mat.color = c3vec4f(1.0, 1.0, 1.0, 1.0);
+ for (int x = 0; x < 20; x++) {
+ for (int y = 0; y < 20; y++) {
+ c3vec3 p[4] = {
+ c3vec3f(-1+x*10,y*10,0), c3vec3f(1+x*10,y*10,0),
+ c3vec3f(x*10,-1+y*10,0), c3vec3f(x*10,1+y*10,0),
+ };
+ c3vertex_array_insert(&g->vertice,
+ g->vertice.count, p, 4);
+ }
+ }
+ }
+ head = c3object_new(root);
+ c3transform_new(head);
+ {
+ c3geometry_p g = c3geometry_new(GL_LINES, head);
+ g->mat.color = c3vec4f(1.0, 0.0, 0.0, 1.0);
+ c3vec3 p[4] = {
+ c3vec3f(-1, 0, 0), c3vec3f(1, 0, 0),
+ c3vec3f(0, -1, 0), c3vec3f(0, 1, 0),
+ };
+ c3vertex_array_insert(&g->vertice,
+ g->vertice.count, p, 4);
+ }
+ return 1;
+}
+
+int
+gl_runloop()
+{
+ glutMainLoop();
+ return 0;
+}
--- /dev/null
+/*
+ reprap_gl.h
+
+ Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
+
+ This file is part of simavr.
+
+ simavr is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ simavr is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with simavr. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __REPRAP_GL_H___
+#define __REPRAP_GL_H___
+
+int
+gl_init(
+ int argc,
+ char *argv[] );
+
+int
+gl_runloop();
+
+#endif /* __REPRAP_GL_H___ */
return;
if (value)
return;
- p->position += p->dir ? -1 : 1;
+ p->position += p->dir ? 1 : -1;
if (p->position < 0)
p->position = 0;
if (p->endstop && p->position < p->endstop)
p->timer_period = avr_usec_to_cycles(p->avr, 100000 / 1000); // 1ms
avr_cycle_timer_register(p->avr, p->timer_period, stepper_update_timer, p);
}
+
+float
+stepper_get_position_mm(
+ stepper_p p)
+{
+ return p->position / p->steps_per_mm;
+}
+
avr_irq_t * endstop,
uint16_t flags);
+float
+stepper_get_position_mm(
+ stepper_p p);
+
#endif /* __STEPPER_H___ */