Browse Source

pmw frame and data capture mode and visualization scripts.

Thomas Buck 1 year ago
parent
commit
d4ecc9ce11

+ 1
- 1
firmware/include/config.h View File

14
 
14
 
15
 #define INVERT_MOUSE_X_AXIS false
15
 #define INVERT_MOUSE_X_AXIS false
16
 #define INVERT_MOUSE_Y_AXIS true
16
 #define INVERT_MOUSE_Y_AXIS true
17
-#define DEFAULT_MOUSE_SENSITIVITY PMW_CPI_TO_SENSE(800)
17
+#define DEFAULT_MOUSE_SENSITIVITY PMW_CPI_TO_SENSE(500)
18
 
18
 
19
 #endif // __CONFIG_H__
19
 #endif // __CONFIG_H__

+ 2
- 1
firmware/include/log.h View File

14
         ##__VA_ARGS__)
14
         ##__VA_ARGS__)
15
 
15
 
16
 // for interactive output. is not stored or re-played.
16
 // for interactive output. is not stored or re-played.
17
-#define print(fmt, ...) debug_log(false, fmt "\r\n", ##__VA_ARGS__)
17
+#define print(fmt, ...) debug_log(false, fmt, ##__VA_ARGS__)
18
+#define println(fmt, ...) debug_log(false, fmt "\r\n", ##__VA_ARGS__)
18
 
19
 
19
 void debug_log(bool log, const char *format, ...) __attribute__((format(printf, 2, 3)));
20
 void debug_log(bool log, const char *format, ...) __attribute__((format(printf, 2, 3)));
20
 void log_dump_to_usb(void);
21
 void log_dump_to_usb(void);

+ 7
- 0
firmware/include/pmw3360.h View File

5
 #ifndef __PMW3360_H__
5
 #ifndef __PMW3360_H__
6
 #define __PMW3360_H__
6
 #define __PMW3360_H__
7
 
7
 
8
+#include <sys/types.h>
9
+
8
 struct pmw_motion {
10
 struct pmw_motion {
9
     bool motion;
11
     bool motion;
10
     int32_t delta_x;
12
     int32_t delta_x;
12
 };
14
 };
13
 
15
 
14
 int pmw_init(void);
16
 int pmw_init(void);
17
+void pmw_run(void);
15
 bool pmw_is_alive(void);
18
 bool pmw_is_alive(void);
16
 
19
 
17
 struct pmw_motion pmw_get(void);
20
 struct pmw_motion pmw_get(void);
31
 #define PMW_CPI_TO_SENSE(cpi) ((cpi / 100) - 1)
34
 #define PMW_CPI_TO_SENSE(cpi) ((cpi / 100) - 1)
32
 
35
 
33
 void pmw_print_status(void);
36
 void pmw_print_status(void);
37
+void pmw_dump_data(void);
38
+
39
+ssize_t pmw_frame_capture(uint8_t *buff, size_t buffsize);
40
+#define PMW_FRAME_CAPTURE_LEN 1296
34
 
41
 
35
 #endif // __PMW3360_H__
42
 #endif // __PMW3360_H__

+ 2
- 0
firmware/include/util.h View File

15
 void reset_to_bootloader(void);
15
 void reset_to_bootloader(void);
16
 void reset_to_main(void);
16
 void reset_to_main(void);
17
 
17
 
18
+void hexdump(uint8_t *buff, size_t len);
19
+
18
 #endif // __UTIL_H__
20
 #endif // __UTIL_H__

+ 41
- 25
firmware/src/console.c View File

30
     if (strlen(line) == 0) {
30
     if (strlen(line) == 0) {
31
         if ((strlen(cnsl_last_command) > 0) && (strcmp(cnsl_last_command, "repeat") != 0)) {
31
         if ((strlen(cnsl_last_command) > 0) && (strcmp(cnsl_last_command, "repeat") != 0)) {
32
             // repeat last command once
32
             // repeat last command once
33
-            print("repeating command \"%s\"", cnsl_last_command);
33
+            println("repeating command \"%s\"", cnsl_last_command);
34
             cnsl_interpret(cnsl_last_command);
34
             cnsl_interpret(cnsl_last_command);
35
-            print();
35
+            println();
36
         }
36
         }
37
         return;
37
         return;
38
     } else if (strcmp(line, "repeat") == 0) {
38
     } else if (strcmp(line, "repeat") == 0) {
48
     } else if ((strcmp(line, "help") == 0)
48
     } else if ((strcmp(line, "help") == 0)
49
             || (strcmp(line, "h") == 0)
49
             || (strcmp(line, "h") == 0)
50
             || (strcmp(line, "?") == 0)) {
50
             || (strcmp(line, "?") == 0)) {
51
-        print("Trackball Firmware Usage:");
52
-        print("    cpi - print current sensitivity");
53
-        print("  cpi N - set sensitivity");
54
-        print("   pmws - print PMW3360 status");
55
-        print("   pmwr - reset PMW3360");
56
-        print("  reset - reset back into this firmware");
57
-        print("   \\x18 - reset to bootloader");
58
-        print(" repeat - repeat last command every %d milliseconds", CNSL_REPEAT_MS);
59
-        print("   help - print this message");
60
-        print("Press Enter with no input to repeat last command.");
61
-        print("Use repeat to continuously execute last command.");
62
-        print("Stop this by calling repeat again.");
51
+        println("Trackball Firmware Usage:");
52
+        println("    cpi - print current sensitivity");
53
+        println("  cpi N - set sensitivity");
54
+        println("   pmws - print PMW3360 status");
55
+        println("   pmwf - print PMW3360 frame capture");
56
+        println("   pmwd - print PMW3360 data dump");
57
+        println("   pmwr - reset PMW3360");
58
+        println("  reset - reset back into this firmware");
59
+        println("   \\x18 - reset to bootloader");
60
+        println(" repeat - repeat last command every %d milliseconds", CNSL_REPEAT_MS);
61
+        println("   help - print this message");
62
+        println("Press Enter with no input to repeat last command.");
63
+        println("Use repeat to continuously execute last command.");
64
+        println("Stop this by calling repeat again.");
63
     } else if (strcmp(line, "pmws") == 0) {
65
     } else if (strcmp(line, "pmws") == 0) {
64
         pmw_print_status();
66
         pmw_print_status();
67
+    } else if (strcmp(line, "pmwd") == 0) {
68
+        pmw_dump_data();
69
+    } else if (strcmp(line, "pmwf") == 0) {
70
+        uint8_t frame[PMW_FRAME_CAPTURE_LEN];
71
+        ssize_t r = pmw_frame_capture(frame, PMW_FRAME_CAPTURE_LEN);
72
+        if (r == PMW_FRAME_CAPTURE_LEN) {
73
+            println("PMW3360 frame capture:");
74
+            hexdump(frame, PMW_FRAME_CAPTURE_LEN);
75
+
76
+            println("Re-Initializing PMW3360");
77
+            pmw_init();
78
+        } else {
79
+            println("error capturing frame (%d)", r);
80
+        }
65
     } else if (strcmp(line, "pmwr") == 0) {
81
     } else if (strcmp(line, "pmwr") == 0) {
66
-        print("user requests re-initializing of PMW3360");
82
+        println("user requests re-initializing of PMW3360");
67
         int r = pmw_init();
83
         int r = pmw_init();
68
         if (r < 0) {
84
         if (r < 0) {
69
-            print("error initializing PMW3360");
85
+            println("error initializing PMW3360");
70
         } else {
86
         } else {
71
-            print("PMW3360 re-initialized successfully");
87
+            println("PMW3360 re-initialized successfully");
72
         }
88
         }
73
     } else if (strcmp(line, "cpi") == 0) {
89
     } else if (strcmp(line, "cpi") == 0) {
74
         uint8_t sense = pmw_get_sensitivity();
90
         uint8_t sense = pmw_get_sensitivity();
75
         uint16_t cpi = PMW_SENSE_TO_CPI(sense);
91
         uint16_t cpi = PMW_SENSE_TO_CPI(sense);
76
-        print("current cpi: %u (0x%02X)", cpi, sense);
92
+        println("current cpi: %u (0x%02X)", cpi, sense);
77
     } else if (str_startswith(line, "cpi ")) {
93
     } else if (str_startswith(line, "cpi ")) {
78
         const char *num_str = line + 4;
94
         const char *num_str = line + 4;
79
         uintmax_t num = strtoumax(num_str, NULL, 10);
95
         uintmax_t num = strtoumax(num_str, NULL, 10);
80
         if ((num < 100) || (num > 12000)) {
96
         if ((num < 100) || (num > 12000)) {
81
-            print("invalid cpi %llu, needs to be %u <= cpi <= %u", num, 100, 12000);
97
+            println("invalid cpi %llu, needs to be %u <= cpi <= %u", num, 100, 12000);
82
         } else {
98
         } else {
83
             num = PMW_CPI_TO_SENSE(num);
99
             num = PMW_CPI_TO_SENSE(num);
84
-            print("setting cpi to 0x%02llX", num);
100
+            println("setting cpi to 0x%02llX", num);
85
             pmw_set_sensitivity(num);
101
             pmw_set_sensitivity(num);
86
         }
102
         }
87
     } else if (strcmp(line, "reset") == 0) {
103
     } else if (strcmp(line, "reset") == 0) {
88
         reset_to_main();
104
         reset_to_main();
89
     } else {
105
     } else {
90
-        print("unknown command \"%s\"", line);
106
+        println("unknown command \"%s\"", line);
91
         return;
107
         return;
92
     }
108
     }
93
 
109
 
94
-    print();
110
+    println();
95
 }
111
 }
96
 
112
 
97
 void cnsl_init(void) {
113
 void cnsl_init(void) {
122
             && (strcmp(cnsl_repeated_command, "repeat") != 0)) {
138
             && (strcmp(cnsl_repeated_command, "repeat") != 0)) {
123
         uint32_t now = to_ms_since_boot(get_absolute_time());
139
         uint32_t now = to_ms_since_boot(get_absolute_time());
124
         if (now >= (last_repeat_time + CNSL_REPEAT_MS)) {
140
         if (now >= (last_repeat_time + CNSL_REPEAT_MS)) {
125
-            print("repeating command \"%s\"", cnsl_repeated_command);
141
+            println("repeating command \"%s\"", cnsl_repeated_command);
126
             cnsl_interpret(cnsl_repeated_command);
142
             cnsl_interpret(cnsl_repeated_command);
127
-            print();
143
+            println();
128
 
144
 
129
             last_repeat_time = now;
145
             last_repeat_time = now;
130
         }
146
         }
131
     } else {
147
     } else {
132
         if (repeat_command) {
148
         if (repeat_command) {
133
-            print("nothing to repeat");
149
+            println("nothing to repeat");
134
         }
150
         }
135
         repeat_command = false;
151
         repeat_command = false;
136
     }
152
     }

+ 1
- 12
firmware/src/main.c View File

12
 #include "usb.h"
12
 #include "usb.h"
13
 #include "pmw3360.h"
13
 #include "pmw3360.h"
14
 
14
 
15
-#define HEALTH_CHECK_INTERVAL_MS 500
16
-static uint32_t last_health_check = 0;
17
-
18
 int main(void) {
15
 int main(void) {
19
     heartbeat_init();
16
     heartbeat_init();
20
 
17
 
41
         heartbeat_run();
38
         heartbeat_run();
42
         usb_run();
39
         usb_run();
43
         cnsl_run();
40
         cnsl_run();
44
-
45
-        uint32_t now = to_ms_since_boot(get_absolute_time());
46
-        if (now >= (last_health_check + HEALTH_CHECK_INTERVAL_MS)) {
47
-            last_health_check = now;
48
-            if (!pmw_is_alive()) {
49
-                debug("PMW3360 is dead. resetting!");
50
-                while (1) { }
51
-            }
52
-        }
41
+        pmw_run();
53
     }
42
     }
54
 
43
 
55
     return 0;
44
     return 0;

+ 123
- 22
firmware/src/pmw3360.c View File

20
 #include "pico/stdlib.h"
20
 #include "pico/stdlib.h"
21
 #include "pico/binary_info.h"
21
 #include "pico/binary_info.h"
22
 #include "hardware/spi.h"
22
 #include "hardware/spi.h"
23
+#include "hardware/watchdog.h"
23
 
24
 
24
 #include "config.h"
25
 #include "config.h"
25
 #include "log.h"
26
 #include "log.h"
28
 #include "pmw3360_srom.h"
29
 #include "pmw3360_srom.h"
29
 #include "pmw3360.h"
30
 #include "pmw3360.h"
30
 
31
 
32
+#define HEALTH_CHECK_INTERVAL_MS 1000
33
+
31
 #if !defined(spi_default) || !defined(PICO_DEFAULT_SPI_SCK_PIN) || !defined(PICO_DEFAULT_SPI_TX_PIN) || !defined(PICO_DEFAULT_SPI_RX_PIN) || !defined(PICO_DEFAULT_SPI_CSN_PIN)
34
 #if !defined(spi_default) || !defined(PICO_DEFAULT_SPI_SCK_PIN) || !defined(PICO_DEFAULT_SPI_TX_PIN) || !defined(PICO_DEFAULT_SPI_RX_PIN) || !defined(PICO_DEFAULT_SPI_CSN_PIN)
32
 #error PMW3360 API requires a board with SPI pins
35
 #error PMW3360 API requires a board with SPI pins
33
 #endif
36
 #endif
34
 
37
 
35
 static volatile int32_t delta_x = 0, delta_y = 0;
38
 static volatile int32_t delta_x = 0, delta_y = 0;
36
 static volatile bool mouse_motion = false;
39
 static volatile bool mouse_motion = false;
40
+static uint32_t last_health_check = 0;
37
 
41
 
38
 #ifdef PMW_IRQ_COUNTERS
42
 #ifdef PMW_IRQ_COUNTERS
39
 static uint64_t pmw_irq_count_all = 0;
43
 static uint64_t pmw_irq_count_all = 0;
48
 #endif // PMW_IRQ_COUNTERS
52
 #endif // PMW_IRQ_COUNTERS
49
 
53
 
50
 void pmw_print_status(void) {
54
 void pmw_print_status(void) {
55
+    bool com = pmw_is_alive();
56
+    if (com) {
57
+        println("Communication to PMW3360 is working");
58
+    } else {
59
+        println("ERROR: can not communicate to PMW3360");
60
+    }
61
+
51
 #ifdef PMW_IRQ_COUNTERS
62
 #ifdef PMW_IRQ_COUNTERS
52
-    print("    pmw_irq_cnt_all = %llu", pmw_irq_count_all);
53
-    print(" pmw_irq_cnt_motion = %llu", pmw_irq_count_motion);
54
-    print("pmw_irq_cnt_no_move = %llu", pmw_irq_count_no_motion);
55
-    print("pmw_irq_cnt_surface = %llu", pmw_irq_count_on_surface);
56
-    print(" pmw_irq_cnt_lifted = %llu", pmw_irq_count_lifted);
57
-    print("    pmw_irq_cnt_run = %llu", pmw_irq_count_run);
58
-    print("  pmw_irq_cnt_rest1 = %llu", pmw_irq_count_rest1);
59
-    print("  pmw_irq_cnt_rest2 = %llu", pmw_irq_count_rest2);
60
-    print("  pmw_irq_cnt_rest3 = %llu", pmw_irq_count_rest3);
63
+    println("Interrupt statistics:");
64
+    println("    pmw_irq_cnt_all = %llu", pmw_irq_count_all);
65
+    println(" pmw_irq_cnt_motion = %llu", pmw_irq_count_motion);
66
+    println("pmw_irq_cnt_no_move = %llu", pmw_irq_count_no_motion);
67
+    println("pmw_irq_cnt_surface = %llu", pmw_irq_count_on_surface);
68
+    println(" pmw_irq_cnt_lifted = %llu", pmw_irq_count_lifted);
69
+    println("    pmw_irq_cnt_run = %llu", pmw_irq_count_run);
70
+    println("  pmw_irq_cnt_rest1 = %llu", pmw_irq_count_rest1);
71
+    println("  pmw_irq_cnt_rest2 = %llu", pmw_irq_count_rest2);
72
+    println("  pmw_irq_cnt_rest3 = %llu", pmw_irq_count_rest3);
61
 #endif // PMW_IRQ_COUNTERS
73
 #endif // PMW_IRQ_COUNTERS
62
 }
74
 }
63
 
75
 
314
     return sense_y;
326
     return sense_y;
315
 }
327
 }
316
 
328
 
329
+static void pmw_irq_start(void) {
330
+    gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, true);
331
+}
332
+
333
+static void pmw_irq_stop(void) {
334
+    gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, false);
335
+}
336
+
317
 static void pmw_irq_init(void) {
337
 static void pmw_irq_init(void) {
318
     // setup MOTION pin interrupt to handle reading data
338
     // setup MOTION pin interrupt to handle reading data
319
     gpio_add_raw_irq_handler(PMW_MOTION_PIN, pmw_motion_irq);
339
     gpio_add_raw_irq_handler(PMW_MOTION_PIN, pmw_motion_irq);
320
-    gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, true);
340
+    pmw_irq_start();
321
     irq_set_enabled(IO_IRQ_BANK0, true);
341
     irq_set_enabled(IO_IRQ_BANK0, true);
322
 
342
 
323
     // make MOTION pin available to picotool
343
     // make MOTION pin available to picotool
324
     bi_decl(bi_1pin_with_name(PMW_MOTION_PIN, "PMW3360 MOTION"));
344
     bi_decl(bi_1pin_with_name(PMW_MOTION_PIN, "PMW3360 MOTION"));
325
 }
345
 }
326
 
346
 
327
-static void pmw_irq_stop(void) {
328
-    gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, false);
329
-    irq_set_enabled(IO_IRQ_BANK0, false);
330
-}
331
-
332
 bool pmw_is_alive(void) {
347
 bool pmw_is_alive(void) {
333
     bool r = true;
348
     bool r = true;
334
     gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, false);
349
     gpio_set_irq_enabled(PMW_MOTION_PIN, GPIO_IRQ_LEVEL_LOW, false);
343
     return r;
358
     return r;
344
 }
359
 }
345
 
360
 
361
+ssize_t pmw_frame_capture(uint8_t *buff, size_t buffsize) {
362
+    if ((buffsize < PMW_FRAME_CAPTURE_LEN) || (buff == NULL)) {
363
+        debug("invalid or too small buffer (%u < %u)", buffsize, PMW_FRAME_CAPTURE_LEN);
364
+        return -1;
365
+    }
366
+
367
+    pmw_irq_stop();
368
+
369
+    // write 0 to Rest_En bit of Config2 register to disable Rest mode
370
+    pmw_write_register(REG_CONFIG2, 0x00);
371
+
372
+    // write 0x83 to Frame_Capture register
373
+    pmw_write_register(REG_FRAME_CAPTURE, 0x83);
374
+
375
+    // write 0xC5 to Frame_Capture register
376
+    pmw_write_register(REG_FRAME_CAPTURE, 0xC5);
377
+
378
+    // wait for 20ms
379
+    busy_wait_ms(20);
380
+
381
+    // continue burst read from Raw_data_Burst register until all 1296 raw data are transferred
382
+    pmw_read_register_burst(REG_RAW_DATA_BURST, buff, PMW_FRAME_CAPTURE_LEN);
383
+
384
+    return PMW_FRAME_CAPTURE_LEN;
385
+}
386
+
387
+#define PMW_DATA_DUMP_SAMPLES 5000
388
+
389
+void pmw_dump_data(void) {
390
+    struct pmw_motion_report buff[PMW_DATA_DUMP_SAMPLES];
391
+
392
+    println("Will now capture %u data samples from PMW3360", PMW_DATA_DUMP_SAMPLES);
393
+    println("Move trackball to generate some data!");
394
+
395
+    pmw_irq_stop();
396
+
397
+    for (size_t i = 0; i < PMW_DATA_DUMP_SAMPLES; i++) {
398
+        // wait until MOTION pin is set
399
+        while (gpio_get(PMW_MOTION_PIN)) {
400
+            watchdog_update();
401
+        }
402
+
403
+        buff[i] = pmw_motion_read();
404
+    }
405
+
406
+    println();
407
+    println("time,motion,observation,delta_x,delta_y,squal,raw_sum,raw_max,raw_min,shutter");
408
+    for (size_t i = 0; i < PMW_DATA_DUMP_SAMPLES; i++) {
409
+        watchdog_update();
410
+
411
+        uint16_t delta_x_raw = buff[i].delta_x_l | (buff[i].delta_x_h << 8);
412
+        uint16_t delta_y_raw = buff[i].delta_y_l | (buff[i].delta_y_h << 8);
413
+        uint16_t shutter_raw = buff[i].shutter_lower | (buff[i].shutter_upper << 8);
414
+
415
+        print("%llu,", to_us_since_boot(get_absolute_time()));
416
+        print("%u,", buff[i].motion);
417
+        print("%u,", buff[i].observation);
418
+        print("%ld,", convert_two_complement(delta_x_raw));
419
+        print("%ld,", convert_two_complement(delta_y_raw));
420
+        print("%u,", buff[i].squal);
421
+        print("%u,", buff[i].raw_data_sum);
422
+        print("%u,", buff[i].maximum_raw_data);
423
+        print("%u,", buff[i].minimum_raw_data);
424
+        println("%u", shutter_raw);
425
+    }
426
+    println();
427
+
428
+    pmw_irq_start();
429
+}
430
+
346
 int pmw_init(void) {
431
 int pmw_init(void) {
432
+    // initializing takes a while (~160ms)
433
+    watchdog_update();
434
+
347
     pmw_irq_stop();
435
     pmw_irq_stop();
348
     pmw_spi_init();
436
     pmw_spi_init();
349
 
437
 
350
     uint8_t srom_id = pmw_power_up();
438
     uint8_t srom_id = pmw_power_up();
351
-
352
     uint8_t prod_id = pmw_read_register(REG_PRODUCT_ID);
439
     uint8_t prod_id = pmw_read_register(REG_PRODUCT_ID);
353
     uint8_t inv_prod_id = pmw_read_register(REG_INVERSE_PRODUCT_ID);
440
     uint8_t inv_prod_id = pmw_read_register(REG_INVERSE_PRODUCT_ID);
354
     uint16_t srom_checksum = pmw_srom_checksum();
441
     uint16_t srom_checksum = pmw_srom_checksum();
368
         return -1;
455
         return -1;
369
     }
456
     }
370
 
457
 
371
-    if (srom_id != pmw_fw_id) {
372
-        debug("PMW3360 error: invalid SROM ID (0x%02X != 0x%02X)", srom_id, pmw_fw_id);
373
-        return -1;
374
-    }
458
+    if ((srom_id != pmw_fw_id) || (srom_checksum != pmw_fw_crc)) {
459
+        if (srom_id != pmw_fw_id) {
460
+            debug("PMW3360 error: invalid SROM ID (0x%02X != 0x%02X)", srom_id, pmw_fw_id);
461
+        }
462
+
463
+        if (srom_checksum != pmw_fw_crc) {
464
+            debug("PMW3360 error: invalid SROM CRC (0x%04X != 0x%04X)", srom_checksum, pmw_fw_crc);
465
+        }
375
 
466
 
376
-    if (srom_checksum != pmw_fw_crc) {
377
-        debug("PMW3360 error: invalid SROM CRC (0x%04X != 0x%04X)", srom_checksum, pmw_fw_crc);
467
+        debug("this may require a power-cycle to fix!");
378
         return -1;
468
         return -1;
379
     }
469
     }
380
 
470
 
396
 
486
 
397
     return 0;
487
     return 0;
398
 }
488
 }
489
+
490
+void pmw_run(void) {
491
+    uint32_t now = to_ms_since_boot(get_absolute_time());
492
+    if (now >= (last_health_check + HEALTH_CHECK_INTERVAL_MS)) {
493
+        last_health_check = now;
494
+        if (!pmw_is_alive()) {
495
+            debug("PMW3360 is dead. resetting!");
496
+            reset_to_main();
497
+        }
498
+    }
499
+}

+ 13
- 0
firmware/src/util.c View File

8
 #include "hardware/watchdog.h"
8
 #include "hardware/watchdog.h"
9
 
9
 
10
 #include "config.h"
10
 #include "config.h"
11
+#include "log.h"
11
 #include "util.h"
12
 #include "util.h"
12
 
13
 
13
 #define HEARTBEAT_INTERVAL_MS 500
14
 #define HEARTBEAT_INTERVAL_MS 500
64
         asm volatile("nop");
65
         asm volatile("nop");
65
     }
66
     }
66
 }
67
 }
68
+
69
+void hexdump(uint8_t *buff, size_t len) {
70
+    for (size_t i = 0; i < len; i += 16) {
71
+        for (size_t j = 0; (j < 16) && ((i + j) < len); j++) {
72
+            print("0x%02X", buff[i + j]);
73
+            if ((j < 15) && ((i + j) < (len - 1))) {
74
+                print(" ");
75
+            }
76
+        }
77
+        println();
78
+    }
79
+}

+ 14
- 0
firmware/util/README.md View File

1
+# RP2040 Trackball Firmware Utilities
2
+
3
+Some utilities to help with this project.
4
+
5
+## Frame Capture and Visualization
6
+
7
+The PMW3360 mouse sensor can do full captures of the raw 2D image seen by the chip.
8
+These can be grabbed from the firmware by opening the debug USB serial port and running the `pmwf` command.
9
+The output should be pasted into a textfile and can then be visualized using the included `visualize_frame.py` script.
10
+
11
+## Data Capture and Visualization
12
+
13
+The firmware can also grab a number of data samples from the sensor, by running the `pmwd` command.
14
+The output can then be visualized with `visualize_data.py`.

+ 43
- 0
firmware/util/visualize_data.py View File

1
+#!/usr/bin/env python
2
+
3
+import matplotlib.pyplot as plt
4
+import numpy as np
5
+import sys
6
+import math
7
+
8
+if len(sys.argv) < 2:
9
+    print("Usage:")
10
+    print("    " + sys.argv[0] + " filename")
11
+    sys.exit(0)
12
+
13
+fig, axes = plt.subplots(6, 1, figsize=(5, 15))
14
+
15
+data = []
16
+lines_heading = []
17
+with open(sys.argv[1]) as f:
18
+    lines = f.readlines()
19
+    lines_heading = lines[0].split(',')
20
+    lines_data = lines[1:]
21
+    for line in lines_data:
22
+        nums = line.split(',')
23
+        line_data = []
24
+        for r in nums:
25
+            line_data.append(int(r))
26
+        data.append(line_data)
27
+
28
+start_time = data[0][0]
29
+for i in range(0, len(data)):
30
+    data[i][0] = (data[i][0] - start_time) / 1000.0 / 1000.0
31
+
32
+print("samples: " + str(len(data)))
33
+
34
+x = [ r[0] for r in data ]
35
+
36
+for n in range(3, 9):
37
+    y = [ r[n] for r in data ]
38
+    im = axes[n - 3].plot(x, y)
39
+    axes[n - 3].set_title(lines_heading[n])
40
+
41
+plt.suptitle(sys.argv[1], fontsize=18)
42
+plt.tight_layout()
43
+plt.show()

+ 47
- 0
firmware/util/visualize_frame.py View File

1
+#!/usr/bin/env python
2
+
3
+import matplotlib.pyplot as plt
4
+import numpy as np
5
+import sys
6
+import math
7
+
8
+if len(sys.argv) < 2:
9
+    print("Usage:")
10
+    print("    " + sys.argv[0] + " filename [...]")
11
+    sys.exit(0)
12
+
13
+fig, axes = plt.subplots(1, len(sys.argv) - 1, figsize=(15, 5))
14
+
15
+# support one or multiple files
16
+if not isinstance(axes, np.ndarray):
17
+    axes = [axes]
18
+
19
+for n in range(0, len(sys.argv) - 1):
20
+    print("reading " + sys.argv[n + 1])
21
+
22
+    frame = []
23
+    with open(sys.argv[n + 1]) as f:
24
+        lines = f.readlines()
25
+        for line in lines:
26
+            nums = line.split()
27
+            for r in nums:
28
+                frame.append(int(r, 16))
29
+
30
+    print("frame length: " + str(len(frame)))
31
+    row_len = math.sqrt(len(frame))
32
+    print("row length: " + str(row_len))
33
+    row_len = int(row_len)
34
+
35
+    frame2d = []
36
+    for i in range(0, row_len):
37
+        row = []
38
+        for j in range(0, row_len):
39
+            row.append(frame[i * row_len + j])
40
+        frame2d.append(row)
41
+
42
+    im = axes[n].imshow(frame2d, vmin=0, vmax=0xFF, cmap='plasma')
43
+    axes[n].set_title(sys.argv[n + 1], fontsize=18)
44
+
45
+fig.colorbar(im, ax=axes, label='Value Range')
46
+plt.suptitle('Pan and Zoom on colorbar to adjust', fontsize=10)
47
+plt.show()

Loading…
Cancel
Save