logic-analyzer
Loading...
Searching...
No Matches
capture_esp32s3_lcdcam.h
1#pragma once
2// Only chips with the LCD_CAM peripheral's DVP camera controller support this
3// (ESP32-S3, ESP32-P4). The original ESP32 does not have LCD_CAM at all - use
4// capture_esp32_i2s.h (I2S0 "camera mode") for that chip instead.
5#if defined(ESP32) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4))
6
7#include "Arduino.h"
8#include "esp_cam_ctlr_dvp.h"
9#include "esp_heap_caps.h"
10
11// Some logic to analyse:
12#include "logic_analyzer.h"
13
14namespace logic_analyzer {
15
16// Global (single-instance) capture state for the callback-based DVP driver -
17// see the equivalent note in capture_esp32_i2s.h for why these are plain
18// namespace-scope globals rather than class members.
19static volatile bool s3cam_trans_done = false;
20static uint8_t *s3cam_buffer = nullptr;
21static size_t s3cam_buflen = 0;
22
23static bool IRAM_ATTR s3cam_on_get_new_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) {
24 trans->buffer = s3cam_buffer;
25 trans->buflen = s3cam_buflen;
26 return false;
27}
28
29static bool IRAM_ATTR s3cam_on_trans_finished(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) {
30 s3cam_trans_done = true;
31 return false;
32}
33
65 public:
67 CaptureESP32S3LcdCam(int pinStart = 1, int xclkPin = 18, int pclkPin = 17,
68 int vsyncPin = 15, int dePin = 16) {
69 xclk_pin = xclkPin;
70 pclk_pin = pclkPin;
71 vsync_pin = vsyncPin;
72 de_pin = dePin;
73 for (int j = 0; j < 8; j++) data_pins[j] = pinStart + j;
74 }
75
77 void setDataPins(const int pins[8]) {
78 for (int j = 0; j < 8; j++) data_pins[j] = pins[j];
79 }
80
81 void capture() override {
82 log("capture()");
83 start();
84 waitForResult();
85 dump();
86 setStatus(STOPPED);
87 log("Number of samples: %u", (unsigned) n_samples);
88 log("Time in us: %lu", run_time_us);
89 }
90
91 void captureAll() override {
92 log("captureAll()");
93 start();
94 waitForResult();
95 }
96
97 float frequencyMeasured() {
98 return run_time_us == 0 ? 0 : 1000000.0f * n_samples / run_time_us;
99 }
100
101 protected:
102 int data_pins[8];
103 int xclk_pin, pclk_pin, vsync_pin, de_pin;
104 esp_cam_ctlr_handle_t handle = nullptr;
105 esp_cam_ctlr_dvp_pin_config_t pin_cfg{};
106 bool is_setup = false;
107 uint32_t configured_freq = 0;
108 uint32_t configured_samples = 0;
109 uint32_t n_samples = 0;
110 unsigned long start_time = 0;
111 unsigned long run_time_us = 0;
112
116 void setupFor(uint32_t freqHz) {
117 if (is_setup && configured_freq == freqHz && configured_samples == n_samples) return;
118 if (!psramFound()) {
119 // Avoid even attempting esp_cam_ctlr_alloc_buffer(): the driver
120 // logs its own ESP_LOGE line straight to the SUMP stream (same
121 // USB CDC port) before returning NULL, which we can't suppress.
122 log("No PSRAM found - CaptureESP32S3LcdCam requires a board with PSRAM enabled");
123 handle = nullptr;
124 is_setup = false;
125 return;
126 }
127 if (is_setup) {
128 esp_cam_ctlr_disable(handle);
129 esp_cam_ctlr_del(handle);
130 handle = nullptr;
131 is_setup = false;
132 }
133 configured_freq = freqHz;
134 configured_samples = n_samples;
135
136 // No real camera: hold DE/VSYNC statically active via pull-ups
137 // instead of wiring real sync signals.
138 pinMode(vsync_pin, INPUT_PULLUP);
139 pinMode(de_pin, INPUT_PULLUP);
140
141 pin_cfg = {};
142 pin_cfg.data_width = CAM_CTLR_DATA_WIDTH_8;
143 for (int j = 0; j < 8; j++) pin_cfg.data_io[j] = (gpio_num_t) data_pins[j];
144 for (int j = 8; j < CAM_DVP_DATA_SIG_NUM; j++) pin_cfg.data_io[j] = GPIO_NUM_NC;
145 pin_cfg.vsync_io = (gpio_num_t) vsync_pin;
146 pin_cfg.de_io = (gpio_num_t) de_pin;
147 pin_cfg.pclk_io = (gpio_num_t) pclk_pin;
148 pin_cfg.xclk_io = (gpio_num_t) xclk_pin;
149
150 esp_cam_ctlr_dvp_config_t cfg{};
151 cfg.ctlr_id = 0;
152 cfg.clk_src = CAM_CLK_SRC_DEFAULT;
153 cfg.h_res = n_samples > 0 ? n_samples : 1;
154 cfg.v_res = 1;
155 cfg.input_data_color_type = CAM_CTLR_COLOR_RAW8;
156 cfg.cam_data_width = 8;
157 cfg.bk_buffer_dis = 1; // we supply the buffer ourselves via the callback below
158 cfg.dma_burst_size = 0;
159 cfg.xclk_freq = freqHz;
160 cfg.pin = &pin_cfg;
161
162 esp_err_t err = esp_cam_new_dvp_ctlr(&cfg, &handle);
163 if (err != ESP_OK) {
164 log("esp_cam_new_dvp_ctlr failed: %d", (int) err);
165 handle = nullptr;
166 return;
167 }
168
169 esp_cam_ctlr_evt_cbs_t cbs{};
170 cbs.on_get_new_trans = s3cam_on_get_new_trans;
171 cbs.on_trans_finished = s3cam_on_trans_finished;
172 esp_cam_ctlr_register_event_callbacks(handle, &cbs, nullptr);
173 esp_cam_ctlr_enable(handle);
174
175 // Must use the controller's own allocator, not plain heap_caps_malloc,
176 // AND it must come from PSRAM (MALLOC_CAP_SPIRAM), not internal SRAM:
177 // this driver cache-syncs the buffer before/after DMA (esp_cache_msync),
178 // which only applies to cached external PSRAM - internal SRAM isn't
179 // cached at all, so esp_cache_msync() on it just fails, corrupting the
180 // SUMP byte stream (both go out over the same USB CDC port). This means
181 // a board WITH real, enabled PSRAM is a hard requirement for this class.
182 // esp_cache_msync() also requires the buffer *size* (not just its
183 // address) to be a multiple of the cache line size (32 bytes here) -
184 // round up, but only copy out the real n_samples bytes afterwards.
185 static const size_t cache_line = 32;
186 size_t alloc_len = ((n_samples + cache_line - 1) / cache_line) * cache_line;
187 if (s3cam_buffer != nullptr) free(s3cam_buffer);
188 s3cam_buffer = (uint8_t *) esp_cam_ctlr_alloc_buffer(handle, alloc_len, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
189 s3cam_buflen = alloc_len;
190 if (s3cam_buffer == nullptr) {
191 // Without a buffer, esp_cam_ctlr_start()/the driver's internal
192 // on_get_new_trans handling hits a hard assert() and reboots -
193 // bail out here instead so a missing/disabled PSRAM fails safely.
194 log("esp_cam_ctlr_alloc_buffer failed - is PSRAM present and enabled?");
195 esp_cam_ctlr_disable(handle);
196 esp_cam_ctlr_del(handle);
197 handle = nullptr;
198 return;
199 }
200
201 is_setup = true;
202 }
203
204 void start() {
205 n_samples = logicAnalyzer().readCount();
206 uint32_t freq = logicAnalyzer().captureFrequency();
207 if (freq == 0) freq = 1000000;
208 setupFor(freq);
209 if (handle == nullptr) return;
210
211 s3cam_trans_done = false;
212
213 run_time_us = 0;
214 start_time = micros();
215 esp_cam_ctlr_start(handle);
216 }
217
218 void waitForResult() {
219 log("waitForResult()");
220 if (handle == nullptr || s3cam_buffer == nullptr) return;
221 unsigned long deadline = millis() + 5000;
222 while (!s3cam_trans_done && millis() < deadline) delay(1);
223 run_time_us = micros() - start_time;
224 esp_cam_ctlr_stop(handle);
225
226 RingBuffer &buf = logicAnalyzer().buffer();
227 buf.clear();
228 size_t count = s3cam_trans_done ? n_samples : 0;
229 for (size_t i = 0; i < count; i++) buf.write((PinBitArray) s3cam_buffer[i]);
230 log("waitForResult() -> %u records", (unsigned) buf.available());
231 }
232
233 void dump() {
234 size_t count = logicAnalyzer().available();
235 write(logicAnalyzer().buffer().data_ptr(), count);
236 }
237};
238
239} // namespace
240
241#endif
Abstract Class for Capturing Logic. Create your own subclass if you want to implement your own optimi...
Definition logic_analyzer.h:316
LogicAnalyzer & logicAnalyzer()
Provides access to the LogicAnalyzer.
Definition logic_analyzer.h:329
virtual void setStatus(Status status)
Sets the status.
Definition logic_analyzer.h:339
Hardware-timed 8 bit parallel GPIO capture for ESP32-S3/P4, using the official esp_driver_cam "DVP" c...
Definition capture_esp32s3_lcdcam.h:64
void setDataPins(const int pins[8])
Defines the 8 data pins individually if they are not contiguous.
Definition capture_esp32s3_lcdcam.h:77
void setupFor(uint32_t freqHz)
Definition capture_esp32s3_lcdcam.h:116
CaptureESP32S3LcdCam(int pinStart=1, int xclkPin=18, int pclkPin=17, int vsyncPin=15, int dePin=16)
Default Constructor - data bus GPIO1..GPIO8, xclk=18, pclk=17, vsync=15, de=16.
Definition capture_esp32s3_lcdcam.h:67
void capture() override
Captures the data and dumps the result.
Definition capture_esp32s3_lcdcam.h:81
void captureAll() override
Used to masure the speed - capture into memory w/o dump!
Definition capture_esp32s3_lcdcam.h:91
RingBuffer & buffer()
Provides access to the buffer.
Definition logic_analyzer.h:626
size_t available()
returns the avialable buffer entries
Definition logic_analyzer.h:734
uint64_t captureFrequency()
provides the caputring frequency
Definition logic_analyzer.h:685
int readCount()
provides the read count
Definition logic_analyzer.h:664
void clear()
clears all entries
Definition logic_analyzer.h:214
Generic implementation of SUMP based logic analyzer.
void write(PinBitArray bits)
writes the status of all activated pins to the capturing device
Definition logic_analyzer.h:64
void log(const char *fmt,...)
Prints the content to the logger output stream.
Definition logic_analyzer.h:104