Skip to content

Commit f7aab62

Browse files
2 parents 88ea083 + e1ea6a1 commit f7aab62

12 files changed

Lines changed: 5006 additions & 9 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
id: Showcasing Smartphone Microscope Images
3+
title: Showcasing Smartphone Microscope Images
4+
---
5+
6+
7+
![](../IMAGES/showcase/Sample_1.jpg)
8+
9+
10+
<video controls width="90%">
11+
<source src="../IMAGES/showcase/Sample_4x.mp4"/>
12+
</video>
13+
14+
15+
<video controls width="90%">
16+
<source src="../IMAGES/showcase/Sample_10x.mp4"/>
17+
</video>
18+
19+
20+
![](../IMAGES/showcase/Scale_4x_div0.1.jpg)
21+
22+
![](../IMAGES/showcase/Scale_10x_div0.1.jpg)
151 KB
Loading
Binary file not shown.
Binary file not shown.
4.75 MB
Loading
3.76 MB
Loading
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
mAIkroscope Galvo Interface Board – **Extended Hardware & Firmware Documentation**
2+
Rev A – 26 May 2025
3+
4+
5+
6+
### 1 Purpose & Key Features
7+
8+
* Dual-axis galvo driver interface for UC2 microscopes.
9+
* Seeed XIAO ESP32-S3 MCU handles scan synthesis and trigger generation.
10+
* On-board 12-bit SPI DAC (MCP4822) creates ±10 V differential XY signals for low-cost Chinese galvo drivers.
11+
* Three independent 50 Ω trigger outputs (pixel, line, frame).
12+
* CAN 2.0B bus for remote control via UC2-CAN protocol.
13+
* Integrated ±12 V/3.3 V/5 V analog rails, 2 A output budget for external driver modules.&#x20;
14+
15+
This is a scan using the Flimlabs.com software, their detector and their pulsed lasers. This is a 512x512 pixel^2 area
16+
17+
![](./IMAGES/scan.gif)
18+
19+
20+
21+
22+
### 2 System Architecture
23+
24+
```
25+
ESP32-S3 ↔ SPI ↔ MCP4822 → LM324 diff amps → SMA L± / R±
26+
│ │ ├─ pixel/line/frame triggers
27+
│ │ └─ U.FL laser blanking
28+
└─ TWAI CAN bus → SN65HVD230 → JST-XH backbone
29+
```
30+
31+
* **Two-phase DAC update:** write 12-bit sample into DAC register (CS low), then pulse **LDAC** to latch both channels simultaneously; saves one SPI transaction per point.&#x20;
32+
33+
![](./IMAGES/galvo.png)
34+
35+
36+
### 3 Electrical Specifications
37+
38+
| Parameter | Value | Comment | |
39+
| - | -- | | - |
40+
| Backbone input | 12 V ±5 % | via J1001-1 | |
41+
| Board self-consumption | ≈ 50 mA | excludes external galvo drivers | |
42+
| Galvo supply pass-thru | ±12 V, 2 A max | J1004/J1007/J1008 | |
43+
| DAC resolution / range | 12 bit, 0 – 3.3V at 4096 steps | converted to ±10 V diff | |
44+
| Output bandwidth | ≈ 25 kHz (-3 dB) | limited by LM324 stage | |
45+
| Trigger level | 3.3 V CMOS, 50 Ω back-terminated | SMA J1009-J1011 | |
46+
| CAN bus | ISO11898-2, 1 Mbit/s default | 120 Ω terminator selectable (JP2001) | |
47+
48+
![](./IMAGES/galvo_1.jpg)
49+
50+
51+
### 4 Connectors & Pinouts (summary)
52+
53+
* **Power + CAN (J1001, JST-XH-4):** GND, +12 V, CAN\_H - CAN\_L
54+
* **Galvo power out (J1004 big Molex 3.96 mm, J1007/J1008 KF2510):** ±12 V, GND – pin order matches driver boards.
55+
* **Differential XY (J1005/J1006, SMA-3):** L+, L-, GND and R+, R-, GND.
56+
* **Triggers (SMA-2):** Pixel (J1009), Line (J1010), Frame (J1011).
57+
* **Laser blanking (J10001, U.FL):** single-ended 50 Ω.
58+
* **XIAO headers (J1002/J1003):** break out all ESP32-S3 I/O; see GPIO map in §7.&#x20;
59+
60+
61+
62+
### 5 Analog Signal Chain
63+
64+
1. **MCP4822-E/SN** generates 0-3.3 V (12-bit).
65+
2. **Gain/offset network (LM324)** maps 0 V→0 V, ±2.048 V→±10 V.
66+
3. Second LM324 stage inverts each leg, giving true differential L± and R±.
67+
4. Outputs drive ≥ 20 mA into the 10 kΩ inputs of typical galvo amps.
68+
5. LC filters on ±12 VA rails set 50 Hz cutoff to suppress switching noise.&#x20;
69+
70+
71+
72+
### 6 Power Subsystem
73+
74+
* **TPS5430 buck:** 12 V → 3.3 V @ 0.8 A for MCU/CAN.
75+
* **TPS5430 inverting buck-boost:** 12 V → −12 V @ 1.5 A for op-amps.
76+
* **78L05 linear:** 5 V A for DAC/reference.
77+
* All rails filtered by π-LC networks; testpoints TP400x provided for bring-up.&#x20;
78+
79+
80+
81+
### 7 Firmware Overview
82+
83+
```cpp
84+
// Default pin mapping (Arduino style) on XIAO systems
85+
#define PIN_DAC_CS 8 // J1003-10 / GPIO8
86+
#define PIN_DAC_SCK 7 // J1003-9 / GPIO7
87+
#define PIN_DAC_SDI 9 // J1003-11 / GPIO9
88+
#define PIN_DAC_LDAC 6 // J1002-6 / GPIO6
89+
#define PIN_TRIG_PIXEL 2 // GPIO2
90+
#define PIN_TRIG_LINE 3 // GPIO3
91+
#define PIN_TRIG_FRAME 4 // GPIO4
92+
#define PIN_CAN_TX 5 // GPIO5
93+
#define PIN_CAN_RX 44 // GPIO44
94+
#define PIN_LASER 43 // GPIO43
95+
#define PIN_PUSHBUTTON 1 // GPIO1 (enable internal pull-up)
96+
```
97+
98+
* **Loop structure:**
99+
100+
```text
101+
for each Y line:
102+
for each X pixel:
103+
write X,Y to DAC; assert TRIG_PIXEL
104+
assert TRIG_LINE
105+
assert TRIG_FRAME
106+
```
107+
* **Coordinate depth:** 12 bit full-scale → map image size (e.g. 512 × 512) by `coord = (pixel * 4095) / (size-1)`.
108+
* **PlatformIO project:** clone `openUC2/galvo-interface-fw`, select `env:xiao_esp32s3`, build & upload via USB-C.
109+
* **UC2-CAN commands:**
110+
*Coming soon*
111+
112+
113+
114+
### 8 Typical Operating Procedure
115+
116+
1. Feed 12 V from microscope backbone into J1001-1 (CAN connecotr); connect CAN (optional), you can supply 12v/GND via the JST 4 Pin sconnector.
117+
2. Connect each galvo driver board with 3-pin power and SMA signal leads.
118+
3. Flash firmware; set parameters in firmware for min/max pixel and stepsize as well as pixel dwell time
119+
4. Monitor pixel trigger on oscilloscope
120+
5. Tune galvo PID on driver boards for minimal overshoot
121+
122+
### 9 iBOM
123+
124+
<iframe width="100%" style={{"aspect-ratio": "16 / 9"}} src="https://openuc2.github.io/kicad/ibom-galvo.html" title="iBOM" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
125+
126+
127+
128+
### 10 Firmware Architecture (openUC2-LaserScanner)
129+
130+
You can find the files here https://github.com/openUC2/openUC2-LaserScanner/blob/main/src/main.cpp
131+
132+
The UC2-ESP Firmware also has the CAN-enabled version of it in its code base here: https://github.com/youseetoo/uc2-esp32/blob/main/main/src/scanner/GalvoController.cpp
133+
134+
:::warn
135+
Sometimes the XIAO cannot be flashed immediately, for this you have to press boot and reset while firmware is flashed - or erase the firmware first using this tool: https://espressif.github.io/esptool-js/
136+
:::
137+
138+
139+
| Layer | Key files | Function |
140+
| ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
141+
| **Application** | `src/main.cpp` | Instantiates `SPIRenderer`, updates parameters in a `while(1)` loop, and calls `start()`; disables the watchdog and raises log level to warn for GPIO. ([GitHub][1]) |
142+
| **Render Engine** | `src/SPIRenderer.{h,cpp}` | Converts image geometry into DAC samples, handles SPI transfers, and asserts pixel/line/frame triggers with single–cycle GPIO writes. Implements `setParameters()`, `draw()`, `start()`. ([GitHub][2]) |
143+
| **Board-select macros** | same header | `IS_XIAO`, `IS_XIAO_UC2GALVOBOARD` choose the correct pin map at compile time. ([GitHub][3]) |
144+
| **Build system** | `platformio.ini` | Two build environments: **esp32dev** (generic) and **UC2\_3\_Xiao** (Seeed XIAO ESP32-S3). Both use ESP-IDF with Arduino component; UART at 921 kbit s⁻¹. ([GitHub][4]) |
145+
146+
#### 11.1 File structure
147+
148+
```
149+
openUC2-LaserScanner
150+
├── src/ # C++ sources
151+
│ ├── main.cpp # app_main() entry point
152+
│ ├── SPIRenderer.cpp # raster engine
153+
│ └── SPIRenderer.h
154+
├── include/ # (optional) extra headers
155+
├── lib/ # third-party libs, e.g. UC2-CAN
156+
├── python/ # helper scripts (ILDA, LUT generation)
157+
├── KICAD/ # electrical design
158+
├── platformio.ini # build targets
159+
└── sdkconfig.* # pre-tuned IDF configs
160+
```
161+
162+
#### 11.2 Build flow (PlatformIO)
163+
164+
1. Select **UC2\_3\_Xiao** in *platformio.ini*.
165+
2. `pio run` → ESP-IDF CMake → compile + link with Arduino component.
166+
3. `pio run -t upload` flashes over USB-CDC (921 kbit s⁻¹).
167+
4. Partition scheme `custom_partition_esp32s3.csv` reserves PSRAM cache and 1 MiB for OTA.
168+
169+
#### 11.3 Runtime tasks
170+
171+
| Task | Core affinity | Duty |
172+
| ----------------------------- | ------------- | ---------------------------------------------------------------------------- |
173+
| **mainTask (app\_main)** | core 0 | parameter sweep, calls renderer, yields 10 ms to scheduler. |
174+
| **IDF idle0/1** | core 0/1 | housekeeping (watchdog disabled for main). |
175+
| Additional tasks (*optional*) || CAN listener, WebSocket, or CLI can be added; use `xTaskCreatePinnedToCore`. |
176+
177+
The code presently runs everything in the foreground; heavy processing (e.g. CAN command parsing) should be off-loaded to a FreeRTOS task to keep the raster loop deterministic.
178+
179+
#### 11.4 SPIRenderer workflow
180+
181+
```text
182+
constructor()
183+
├─ configure GPIO for laser, LDAC, triggers
184+
├─ initialise SPI3 @20 MHz (XIAO) or HSPI (generic)
185+
└─ pre-compute nX, nY from (X_MIN…X_MAX)/STEP
186+
187+
start()
188+
└─ draw()
189+
190+
draw()
191+
for each frame
192+
assert all triggers high ← frame start
193+
for X = X_MIN…X_MAX
194+
for Y = Y_MIN…Y_MAX
195+
clear triggers
196+
hold LDAC low
197+
SPI out 16-bit word to DAC-A (X)
198+
SPI out 16-bit word to DAC-B (Y)
199+
release LDAC ← both axes latch simultaneously
200+
set pixel trigger, delay tPixelDwelltime µs
201+
issue line trigger each X-loop
202+
clear all triggers ← frame end
203+
```
204+
205+
* **Timing**`esp_rom_delay_us()` delivers sub-µs waits; replace with a hardware timer ISR for dwell times <5 µs.
206+
* **Throughput** – one SPI transaction per axis, LDAC latched once per pixel → 2 × 16 bits @ 20 MHz ≈ 1.6 µs transfer; 512×512 raster ≈ 0.42 s per frame exclusive of galvo settling.
207+
* **Triggers** – 50 Ω back-termination; rising edge marks *start* of integration for FLIM/PMT cards.
208+
209+
#### 11.5 Customisation hooks
210+
211+
| Use case | Modification |
212+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
213+
| **Different image size** | call `setParameters()` with new limits/step; renderer recomputes nX/nY without reallocating. |
214+
| **External control** | add a CAN listener task that writes into a shared parameters struct guarded by a mutex; call `renderer->start()` from that task. |
215+
| **Scan patterns** | replace the two nested `for` loops with a lookup table of `(x,y)` pairs (e.g. spiral, Lissajous). |
216+
| **Higher speed** | use queued (`spi_device_queue_trans`) DMA transfers and toggle LDAC from SPI post-trans callback to overlap SPI and galvo settling. |
217+
218+
#### 11.6 Pin map (compile-time)
219+
220+
| Signal | GPIO (UC2-Galvo) | GPIO (bare XIAO) |
221+
| --------------- | ---------------- | ---------------- |
222+
| **MOSI** | 9 | 7 |
223+
| **SCK** | 7 | 8 |
224+
| **CS** | 8 | 9 |
225+
| **LDAC** | 6 | 6 |
226+
| **TRIG PIXEL** | 2 | 2 |
227+
| **TRIG LINE** | 3 | 3 |
228+
| **TRIG FRAME** | 4 | 4 |
229+
| **LASER blank** | 43 | 43 |
230+
231+
Defined in `SPIRenderer.h`; switch boards via `-DIS_XIAO_UC2GALVOBOARD` or `-DIS_XIAO` build flag. ([GitHub][3])
232+
233+
#### 11.7 Extending the firmware
234+
235+
* **Finite-state machine** – wrap the renderer in a state class (IDLE, SCANNING, PAUSED).
236+
* **Streaming points** – replace the nested loops by a ring buffer filled by CAN/UART for arbitrary point lists.
237+
* **Persistent settings** – store last used scan parameters to NVS (`nvs_flash.h`) and recall on boot.
238+
* **Web OTA** – enable `esp_https_ota` component and reserve second app partition (already present in `custom_partition_esp32s3.csv`).
239+
240+
---
241+
242+
[1]: https://raw.githubusercontent.com/openUC2/openUC2-LaserScanner/main/src/main.cpp "raw.githubusercontent.com"
243+
[2]: https://raw.githubusercontent.com/openUC2/openUC2-LaserScanner/main/src/SPIRenderer.cpp "raw.githubusercontent.com"
244+
[3]: https://raw.githubusercontent.com/openUC2/openUC2-LaserScanner/main/src/SPIRenderer.h "raw.githubusercontent.com"
245+
[4]: https://raw.githubusercontent.com/openUC2/openUC2-LaserScanner/main/platformio.ini "raw.githubusercontent.com"
8.62 MB
Loading
2.9 MB
Loading
3.38 MB
Loading

0 commit comments

Comments
 (0)