92d67b6ca8cce3f823ffb57b2aa1dcca0ccf76c0
[simavr] / examples / board_reprap / src / reprap.c
1 /*
2         simduino.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <libgen.h>
29 #include <pthread.h>
30
31 #include "sim_avr.h"
32 #include "avr_ioport.h"
33 #include "sim_elf.h"
34 #include "sim_hex.h"
35 #include "sim_gdb.h"
36
37 #include "reprap_gl.h"
38
39 #include "button.h"
40 #include "reprap.h"
41
42 #define __AVR_ATmega644__
43 #include "marlin/pins.h"
44
45 #include <stdbool.h>
46 #define PROGMEM
47 #include "marlin/Configuration.h"
48
49 /*
50  * these are the sources of heat and cold to register to the heatpots
51  */
52 enum {
53         TALLY_AMBIANT = 1,
54         TALLY_HOTEND_PWM,
55         TALLY_HOTBED,
56         TALLY_HOTEND_FAN,
57 };
58
59 reprap_t reprap;
60
61 avr_t * avr = NULL;
62
63 typedef struct ardupin_t {
64         uint32_t port : 7, pin : 3, analog : 1, adc : 3, pwm : 1, ardupin;
65 } ardupin_t, *ardupin_p;
66
67 ardupin_t arduidiot_644[32] = {
68         [ 0] = { .ardupin =  0, .port = 'B', .pin =  0 },
69         [ 1] = { .ardupin =  1, .port = 'B', .pin =  1 },
70         [ 2] = { .ardupin =  2, .port = 'B', .pin =  2 },
71         [ 3] = { .ardupin =  3, .port = 'B', .pin =  3 },
72         [ 4] = { .ardupin =  4, .port = 'B', .pin =  4 },
73         [ 5] = { .ardupin =  5, .port = 'B', .pin =  5 },
74         [ 6] = { .ardupin =  6, .port = 'B', .pin =  6 },
75         [ 7] = { .ardupin =  7, .port = 'B', .pin =  7 },
76
77         [ 8] = { .ardupin =  8, .port = 'D', .pin =  0 },
78         [ 9] = { .ardupin =  9, .port = 'D', .pin =  1 },
79         [10] = { .ardupin = 10, .port = 'D', .pin =  2 },
80         [11] = { .ardupin = 11, .port = 'D', .pin =  3 },
81         [12] = { .ardupin = 12, .port = 'D', .pin =  4 },
82         [13] = { .ardupin = 13, .port = 'D', .pin =  5 },
83         [14] = { .ardupin = 14, .port = 'D', .pin =  6 },
84         [15] = { .ardupin = 15, .port = 'D', .pin =  7 },
85
86         [16] = { .ardupin = 16, .port = 'C', .pin =  0 },
87         [17] = { .ardupin = 17, .port = 'C', .pin =  1 },
88         [18] = { .ardupin = 18, .port = 'C', .pin =  2 },
89         [19] = { .ardupin = 19, .port = 'C', .pin =  3 },
90         [20] = { .ardupin = 20, .port = 'C', .pin =  4 },
91         [21] = { .ardupin = 21, .port = 'C', .pin =  5 },
92         [22] = { .ardupin = 22, .port = 'C', .pin =  6 },
93         [23] = { .ardupin = 23, .port = 'C', .pin =  7 },
94
95         [24] = { .ardupin = 24, .port = 'A', .pin =  7, .analog = 1, .adc = 7 },
96         [25] = { .ardupin = 25, .port = 'A', .pin =  6, .analog = 1, .adc = 6 },
97         [26] = { .ardupin = 26, .port = 'A', .pin =  5, .analog = 1, .adc = 5 },
98         [27] = { .ardupin = 27, .port = 'A', .pin =  4, .analog = 1, .adc = 4 },
99         [28] = { .ardupin = 28, .port = 'A', .pin =  3, .analog = 1, .adc = 3 },
100         [29] = { .ardupin = 29, .port = 'A', .pin =  2, .analog = 1, .adc = 2 },
101         [30] = { .ardupin = 30, .port = 'A', .pin =  1, .analog = 1, .adc = 1 },
102         [31] = { .ardupin = 31, .port = 'A', .pin =  0, .analog = 1, .adc = 0 },
103 };
104
105 // gnu hackery to make sure the parameter is expanded
106 #define _TERMISTOR_TABLE(num) \
107                 temptable_##num
108 #define TERMISTOR_TABLE(num) \
109                 _TERMISTOR_TABLE(num)
110
111 struct avr_irq_t *
112 get_ardu_irq(
113                 struct avr_t * avr,
114                 int ardupin,
115                 ardupin_t pins[])
116 {
117         if (pins[ardupin].ardupin != ardupin) {
118                 printf("%s pin %d isn't correct in table\n", __func__, ardupin);
119                 return NULL;
120         }
121         struct avr_irq_t * irq = avr_io_getirq(avr,
122                         AVR_IOCTL_IOPORT_GETIRQ(pins[ardupin].port), pins[ardupin].pin);
123         if (!irq) {
124                 printf("%s pin %d PORT%C%d not found\n", __func__, ardupin, pins[ardupin].port, pins[ardupin].pin);
125                 return NULL;
126         }
127         return irq;
128 }
129
130 /*
131  * called when the AVR change any of the pins on port B
132  * so lets update our buffer
133  */
134 static void
135 hotbed_change_hook(
136                 struct avr_irq_t * irq,
137                 uint32_t value,
138                 void * param)
139 {
140 //      printf("%s %d\n", __func__, value);
141 //      pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq);
142         heatpot_tally(
143                         &reprap.hotbed,
144                         TALLY_HOTEND_PWM,
145                         value ? 1.0f : 0 );
146 }
147 static void
148 hotend_change_hook(
149                 struct avr_irq_t * irq,
150                 uint32_t value,
151                 void * param)
152 {
153 //      printf("%s %d\n", __func__, value);
154 //      pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq);
155         heatpot_tally(
156                         &reprap.hotend,
157                         TALLY_HOTBED,
158                         value ? 1.0f : 0 );
159 }
160 static void
161 hotend_fan_change_hook(
162                 struct avr_irq_t * irq,
163                 uint32_t value,
164                 void * param)
165 {
166         printf("%s %d\n", __func__, value);
167 //      pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq);
168         heatpot_tally(
169                         &reprap.hotend,
170                         TALLY_HOTEND_FAN,
171                         value ? -0.05 : 0 );
172 }
173
174
175
176 char avr_flash_path[1024];
177 int avr_flash_fd = 0;
178
179 // avr special flash initalization
180 // here: open and map a file to enable a persistent storage for the flash memory
181 void avr_special_init( avr_t * avr)
182 {
183         // open the file
184         avr_flash_fd = open(avr_flash_path, O_RDWR|O_CREAT, 0644);
185         if (avr_flash_fd < 0) {
186                 perror(avr_flash_path);
187                 exit(1);
188         }
189         // resize and map the file the file
190         (void)ftruncate(avr_flash_fd, avr->flashend + 1);
191         ssize_t r = read(avr_flash_fd, avr->flash, avr->flashend + 1);
192         if (r != avr->flashend + 1) {
193                 fprintf(stderr, "unable to load flash memory\n");
194                 perror(avr_flash_path);
195                 exit(1);
196         }
197 }
198
199 // avr special flash deinitalization
200 // here: cleanup the persistent storage
201 void avr_special_deinit( avr_t* avr)
202 {
203         puts(__func__);
204         lseek(avr_flash_fd, SEEK_SET, 0);
205         ssize_t r = write(avr_flash_fd, avr->flash, avr->flashend + 1);
206         if (r != avr->flashend + 1) {
207                 fprintf(stderr, "unable to load flash memory\n");
208                 perror(avr_flash_path);
209         }
210         close(avr_flash_fd);
211         uart_pty_stop(&reprap.uart_pty);
212 }
213
214 #define MEGA644_GPIOR0 0x3e
215
216 static void
217 reprap_relief_callback(
218                 struct avr_t * avr,
219                 avr_io_addr_t addr,
220                 uint8_t v,
221                 void * param)
222 {
223 //      printf("%s write %x\n", __func__, addr);
224         static uint16_t tick = 0;
225         if (!(tick++ & 0xf))
226                 usleep(100);
227 }
228
229 static void *
230 avr_run_thread(
231                 void * ignore)
232 {
233         while (1) {
234                 avr_run(avr);
235         }
236         return NULL;
237 }
238
239 void
240 reprap_init(
241                 avr_t * avr,
242                 reprap_p r)
243 {
244         r->avr = avr;
245         uart_pty_init(avr, &r->uart_pty);
246         uart_pty_connect(&r->uart_pty, '0');
247
248         thermistor_init(avr, &r->therm_hotend, 0,
249                         (short*)TERMISTOR_TABLE(TEMP_SENSOR_0),
250                         sizeof(TERMISTOR_TABLE(TEMP_SENSOR_0)) / sizeof(short) / 2,
251                         OVERSAMPLENR, 25.0f);
252         thermistor_init(avr, &r->therm_hotbed, 2,
253                         (short*)TERMISTOR_TABLE(TEMP_SENSOR_BED),
254                         sizeof(TERMISTOR_TABLE(TEMP_SENSOR_BED)) / sizeof(short) / 2,
255                         OVERSAMPLENR, 30.0f);
256         thermistor_init(avr, &r->therm_spare, 1,
257                         (short*)temptable_5, sizeof(temptable_5) / sizeof(short) / 2,
258                         OVERSAMPLENR, 10.0f);
259
260         heatpot_init(avr, &r->hotend, "hotend", 28.0f);
261         heatpot_init(avr, &r->hotbed, "hotbed", 25.0f);
262
263         heatpot_tally(&r->hotend, TALLY_AMBIANT, -0.5f);
264         heatpot_tally(&r->hotbed, TALLY_AMBIANT, -0.3f);
265
266         /* connect heatpot temp output to thermistors */
267         avr_connect_irq(r->hotend.irq + IRQ_HEATPOT_TEMP_OUT,
268                         r->therm_hotend.irq + IRQ_TERM_TEMP_VALUE_IN);
269         avr_connect_irq(r->hotbed.irq + IRQ_HEATPOT_TEMP_OUT,
270                         r->therm_hotbed.irq + IRQ_TERM_TEMP_VALUE_IN);
271
272         avr_irq_register_notify(
273                         get_ardu_irq(avr, HEATER_0_PIN, arduidiot_644),
274                         hotend_change_hook, NULL);
275         avr_irq_register_notify(
276                         get_ardu_irq(avr, FAN_PIN, arduidiot_644),
277                         hotend_fan_change_hook, NULL);
278         avr_irq_register_notify(
279                         get_ardu_irq(avr, HEATER_BED_PIN, arduidiot_644),
280                         hotbed_change_hook, NULL);
281
282         //avr_irq_register_notify()
283         float axis_pp_per_mm[4] = DEFAULT_AXIS_STEPS_PER_UNIT;  // from Marlin!
284         {
285                 avr_irq_t * e = get_ardu_irq(avr, X_ENABLE_PIN, arduidiot_644);
286                 avr_irq_t * s = get_ardu_irq(avr, X_STEP_PIN, arduidiot_644);
287                 avr_irq_t * d = get_ardu_irq(avr, X_DIR_PIN, arduidiot_644);
288                 avr_irq_t * m = get_ardu_irq(avr, X_MIN_PIN, arduidiot_644);
289
290                 stepper_init(avr, &r->step_x, "X", axis_pp_per_mm[0], 100, 220, 0);
291                 stepper_connect(&r->step_x, s, d, e, m, stepper_endstop_inverted);
292         }
293         {
294                 avr_irq_t * e = get_ardu_irq(avr, Y_ENABLE_PIN, arduidiot_644);
295                 avr_irq_t * s = get_ardu_irq(avr, Y_STEP_PIN, arduidiot_644);
296                 avr_irq_t * d = get_ardu_irq(avr, Y_DIR_PIN, arduidiot_644);
297                 avr_irq_t * m = get_ardu_irq(avr, Y_MIN_PIN, arduidiot_644);
298
299                 stepper_init(avr, &r->step_y, "Y", axis_pp_per_mm[1], 100, 220, 0);
300                 stepper_connect(&r->step_y, s, d, e, m, stepper_endstop_inverted);
301         }
302         {
303                 avr_irq_t * e = get_ardu_irq(avr, Z_ENABLE_PIN, arduidiot_644);
304                 avr_irq_t * s = get_ardu_irq(avr, Z_STEP_PIN, arduidiot_644);
305                 avr_irq_t * d = get_ardu_irq(avr, Z_DIR_PIN, arduidiot_644);
306                 avr_irq_t * m = get_ardu_irq(avr, Z_MIN_PIN, arduidiot_644);
307
308                 stepper_init(avr, &r->step_z, "Z", axis_pp_per_mm[2], 20, 110, 0);
309                 stepper_connect(&r->step_z, s, d, e, m, stepper_endstop_inverted);
310         }
311         {
312                 avr_irq_t * e = get_ardu_irq(avr, E0_ENABLE_PIN, arduidiot_644);
313                 avr_irq_t * s = get_ardu_irq(avr, E0_STEP_PIN, arduidiot_644);
314                 avr_irq_t * d = get_ardu_irq(avr, E0_DIR_PIN, arduidiot_644);
315
316                 stepper_init(avr, &r->step_e, "E", axis_pp_per_mm[3], 0, 0, 0);
317                 stepper_connect(&r->step_e, s, d, e, NULL, 0);
318         }
319
320 }
321
322 int main(int argc, char *argv[])
323 {
324         char path[256];
325         strcpy(path, argv[0]);
326         strcpy(path, dirname(path));
327         strcpy(path, dirname(path));
328         printf("Stripped base directory to '%s'\n", path);
329         chdir(path);
330
331         int debug = 0;
332
333         for (int i = 1; i < argc; i++)
334                 if (!strcmp(argv[i], "-d"))
335                         debug++;
336         avr = avr_make_mcu_by_name("atmega644");
337         if (!avr) {
338                 fprintf(stderr, "%s: Error creating the AVR core\n", argv[0]);
339                 exit(1);
340         }
341 //      snprintf(avr_flash_path, sizeof(avr_flash_path), "%s/%s", pwd, "simduino_flash.bin");
342         strcpy(avr_flash_path,  "reprap_flash.bin");
343         // register our own functions
344         avr->special_init = avr_special_init;
345         avr->special_deinit = avr_special_deinit;
346         avr_init(avr);
347         avr->frequency = 20000000;
348         avr->aref = avr->avcc = avr->vcc = 5 * 1000;    // needed for ADC
349
350         elf_firmware_t f;
351         const char * fname = "/opt/reprap/tvrrug/Marlin/Marlin/applet/Marlin.elf";
352         // try to load an ELF file, before trying the .hex
353         if (elf_read_firmware(fname, &f) == 0) {
354                 printf("firmware %s f=%d mmcu=%s\n", fname, (int)f.frequency, f.mmcu);
355                 avr_load_firmware(avr, &f);
356         } else {
357                 char path[1024];
358                 uint32_t base, size;
359 //              snprintf(path, sizeof(path), "%s/%s", pwd, "ATmegaBOOT_168_atmega328.ihex");
360                 strcpy(path, "marlin/Marlin.hex");
361 //              strcpy(path, "marlin/bootloader-644-20MHz.hex");
362                 uint8_t * boot = read_ihex_file(path, &size, &base);
363                 if (!boot) {
364                         fprintf(stderr, "%s: Unable to load %s\n", argv[0], path);
365                         exit(1);
366                 }
367                 printf("Firmware %04x(%04x in AVR talk): %d bytes (%d words)\n", base, base/2, size, size/2);
368                 memcpy(avr->flash + base, boot, size);
369                 free(boot);
370                 avr->pc = base;
371                 avr->codeend = avr->flashend;
372         }
373         //avr->trace = 1;
374
375         // even if not setup at startup, activate gdb if crashing
376         avr->gdb_port = 1234;
377         if (debug) {
378                 printf("AVR is stopped, waiting on gdb on port %d. Use 'target remote :%d' in avr-gdb\n",
379                                 avr->gdb_port, avr->gdb_port);
380                 avr->state = cpu_Stopped;
381                 avr_gdb_init(avr);
382         }
383
384         // Marlin doesn't loop, sleep, so we don't know when it's idle
385         // I changed Marlin to do a spurious write to the GPIOR0 register so we can trap it
386         avr_register_io_write(avr, MEGA644_GPIOR0, reprap_relief_callback, NULL);
387
388         reprap_init(avr, &reprap);
389
390         gl_init(argc, argv);
391         pthread_t run;
392         pthread_create(&run, NULL, avr_run_thread, NULL);
393
394         gl_runloop();
395
396 }