logic-analyzer
Loading...
Searching...
No Matches
capture_esp32_i2s.h
1#pragma once
2// Only the original ESP32 has the I2S0 "LCD/camera" registers this class
3// relies on (conf2.lcd_en/camera_en) - ESP32-S2/S3/C3 have a different I2S
4// peripheral and are not supported here.
5#if defined(ESP32) && defined(CONFIG_IDF_TARGET_ESP32)
6
7#include "Arduino.h"
8#include "driver/ledc.h"
9#include "driver/periph_ctrl.h"
10#include "esp_intr_alloc.h"
11#include "rom/lldesc.h"
12#include "esp32/rom/gpio.h"
13#include "soc/gpio_periph.h"
14#include "soc/i2s_reg.h"
15#include "soc/i2s_struct.h"
16#include "soc/io_mux_reg.h"
17
18// Some logic to analyse:
19#include "logic_analyzer.h"
20
21namespace logic_analyzer {
22
23// Global (single-instance) I2S DMA capture state - kept at namespace scope,
24// like the rest of this library's singletons (pin_reader_ptr, buffer_ptr),
25// so the interrupt handler below can be a plain function instead of a class
26// member (member functions placed in per-TU COMDAT sections have caused
27// "literal placed after use" link errors for IRAM_ATTR ISRs on some
28// esp32-arduino core versions).
29static volatile bool i2s_capture_dma_done = false;
30static intr_handle_t i2s_capture_intr_handle = nullptr;
31
32static void IRAM_ATTR i2s_capture_isr(void *arg) {
33 if (I2S0.int_raw.in_suc_eof) {
34 i2s_capture_dma_done = true;
35 esp_intr_disable(i2s_capture_intr_handle);
36 I2S0.conf.rx_start = 0;
37 }
38 I2S0.int_clr.val = I2S0.int_raw.val;
39}
40
70 public:
72 CaptureESP32I2S(int pinStart = 19, int clkOutPin = 32, int clkInPin = 34) {
73 clk_out_pin = clkOutPin;
74 clk_in_pin = clkInPin;
75 for (int j = 0; j < 8; j++) data_pins[j] = pinStart + j;
76 }
77
79 void setDataPins(const int pins[8]) {
80 for (int j = 0; j < 8; j++) data_pins[j] = pins[j];
81 }
82
84 void capture() override {
85 log("capture()");
86 start();
87 waitForResult();
88 dump();
89 setStatus(STOPPED);
90 log("Number of samples: %u", (unsigned) n_samples);
91 log("Time in us: %lu", run_time_us);
92 }
93
95 void captureAll() override {
96 log("captureAll()");
97 start();
98 waitForResult();
99 }
100
103 return run_time_us == 0 ? 0 : 1000000.0f * n_samples / run_time_us;
104 }
105
106 protected:
107 int data_pins[8];
108 int clk_out_pin;
109 int clk_in_pin;
110 uint32_t n_samples = 0;
111 unsigned long start_time = 0;
112 unsigned long run_time_us = 0;
113 bool is_setup = false;
114
115 static const size_t desc_size = 2000; // bytes per DMA descriptor (must be a multiple of 4)
116 lldesc_t *dma_desc = nullptr;
117 uint8_t **dma_buf = nullptr;
118 size_t dma_desc_count = 0;
119
121 void setupOnce() {
122 if (is_setup) return;
123 is_setup = true;
124
125 int sig_data_base = I2S0I_DATA_IN0_IDX;
126 for (int j = 0; j < 8; j++) gpioSetupIn(data_pins[j], sig_data_base + j);
127 gpioSetupIn(clk_in_pin, I2S0I_WS_IN_IDX);
128 // Signal 0x38 is the GPIO matrix's constant-1 input: tie HSYNC/VSYNC/HREF
129 // permanently active so the "camera" logic samples continuously instead
130 // of waiting for a real camera's frame/line sync signals.
131 gpio_matrix_in(0x38, I2S0I_V_SYNC_IDX, false);
132 gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false);
133 gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false);
134
135 periph_module_enable(PERIPH_I2S0_MODULE);
136 i2sConfReset();
137
138 I2S0.conf.rx_slave_mod = 1; // sampling clock is external (clk_in_pin)
139 I2S0.conf2.val = 0;
140 I2S0.conf2.lcd_en = 1; // parallel ("LCD") mode
141 I2S0.conf2.camera_en = 1; // use HSYNC/VSYNC/HREF (tied high above) to gate sampling
142
143 I2S0.clkm_conf.val = 0;
144 I2S0.clkm_conf.clka_en = 0;
145 I2S0.clkm_conf.clkm_div_a = 1;
146 I2S0.clkm_conf.clkm_div_b = 0;
147 I2S0.clkm_conf.clkm_div_num = 4;
148
149 I2S0.fifo_conf.dscr_en = 1;
150 I2S0.fifo_conf.rx_fifo_mod = 1;
151 I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
152 I2S0.conf_chan.rx_chan_mod = 1;
153
154 I2S0.conf.rx_right_first = 1;
155 I2S0.conf.rx_msb_right = 0;
156 I2S0.conf.rx_msb_shift = 0;
157 I2S0.conf.rx_mono = 1;
158 I2S0.conf.rx_short_sync = 1;
159 I2S0.timing.val = 0;
160
161 I2S0.sample_rate_conf.val = 0;
162 I2S0.sample_rate_conf.rx_bits_mod = 8; // 8 bit wide bus, matches PinBitArray
163 I2S0.sample_rate_conf.rx_bck_div_num = 1;
164
165 esp_intr_alloc(ETS_I2S0_INTR_SOURCE,
166 ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM,
167 &i2s_capture_isr, nullptr, &i2s_capture_intr_handle);
168 }
169
170 static void gpioSetupIn(int gpio, int sig) {
171 if (gpio < 0) return;
172 PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);
173 gpio_set_direction((gpio_num_t) gpio, GPIO_MODE_INPUT);
174 gpio_matrix_in(gpio, sig, false);
175 }
176
177 static void i2sConfReset() {
178 I2S0.lc_conf.in_rst = 1; I2S0.lc_conf.in_rst = 0;
179 I2S0.lc_conf.ahbm_rst = 1; I2S0.lc_conf.ahbm_rst = 0;
180 I2S0.lc_conf.ahbm_fifo_rst = 1; I2S0.lc_conf.ahbm_fifo_rst = 0;
181 I2S0.conf.rx_reset = 1; I2S0.conf.rx_reset = 0;
182 I2S0.conf.rx_fifo_reset = 1; I2S0.conf.rx_fifo_reset = 0;
183 while (I2S0.state.rx_fifo_reset_back) {}
184 }
185
187 void allocDma(uint32_t sampleCount) {
188 freeDma();
189 // Each 32 bit DMA word carries 2 samples (see dma_elem_t layout below),
190 // so we need sampleCount/2 32-bit words, rounded up to whole descriptors.
191 size_t total_bytes = ((sampleCount + 1) / 2) * 4;
192 dma_desc_count = (total_bytes + desc_size - 1) / desc_size;
193 if (dma_desc_count == 0) dma_desc_count = 1;
194
195 dma_buf = (uint8_t **) malloc(sizeof(uint8_t *) * dma_desc_count);
196 dma_desc = (lldesc_t *) malloc(sizeof(lldesc_t) * dma_desc_count);
197
198 for (size_t i = 0; i < dma_desc_count; i++) {
199 dma_buf[i] = (uint8_t *) malloc(desc_size);
200 lldesc_t *pd = &dma_desc[i];
201 pd->length = desc_size;
202 pd->size = desc_size;
203 pd->owner = 1;
204 pd->sosf = 1;
205 pd->buf = dma_buf[i];
206 pd->offset = 0;
207 pd->empty = 0;
208 bool last = (i + 1 == dma_desc_count);
209 pd->eof = last ? 1 : 0;
210 pd->qe.stqe_next = last ? nullptr : &dma_desc[i + 1];
211 }
212 }
213
214 void freeDma() {
215 if (dma_buf != nullptr) {
216 for (size_t i = 0; i < dma_desc_count; i++) free(dma_buf[i]);
217 free(dma_buf);
218 dma_buf = nullptr;
219 }
220 if (dma_desc != nullptr) {
221 free(dma_desc);
222 dma_desc = nullptr;
223 }
224 dma_desc_count = 0;
225 }
226
228 void enableSampleClock(uint32_t freqHz) {
229 ledcAttach(clk_out_pin, freqHz, 1);
230 ledcWrite(clk_out_pin, 1);
231 delay(1);
232 }
233
234 void start() {
235 setupOnce();
236 n_samples = logicAnalyzer().readCount();
237 allocDma(n_samples);
238 // clear stale heap contents so a missing/too-slow sample clock (see class
239 // comment) is reported as a flat 0 capture instead of leaking old heap data
240 for (size_t i = 0; i < dma_desc_count; i++) memset(dma_buf[i], 0, desc_size);
241
242 uint32_t freq = logicAnalyzer().captureFrequency();
243 enableSampleClock(freq);
244
245 i2s_capture_dma_done = false;
246 i2sConfReset();
247 I2S0.rx_eof_num = (n_samples + 1) / 2; // in 32 bit words (2 samples/word)
248 I2S0.in_link.addr = (uint32_t) &dma_desc[0];
249 I2S0.in_link.start = 1;
250 I2S0.int_clr.val = I2S0.int_raw.val;
251 I2S0.int_ena.val = 0;
252 I2S0.int_ena.in_suc_eof = 1;
253
254 run_time_us = 0;
255 start_time = micros();
256 esp_intr_enable(i2s_capture_intr_handle);
257 I2S0.conf.rx_start = 1;
258 }
259
260 void waitForResult() {
261 log("waitForResult()");
262 // the datasheet-documented rx_bck_div_num=1 corner case + slow clocks can
263 // take a while; bound the wait so a bad frequency can't hang forever.
264 unsigned long deadline = millis() + 5000;
265 while (!i2s_capture_dma_done && millis() < deadline) delay(1);
266 run_time_us = micros() - start_time;
267 ledcDetach(clk_out_pin);
268
269 RingBuffer &buf = logicAnalyzer().buffer();
270 buf.clear();
271 size_t written = 0;
272 for (size_t i = 0; i < dma_desc_count && written < n_samples; i++) {
273 uint8_t *b = dma_buf[i];
274 // 32 bit DMA words hold two samples as 00 s2 00 s1 (see dma_elem_t in
275 // the reference implementation) - byte 1 is the first sample, byte 3
276 // (== byte 1 of the next word) is the second.
277 for (size_t off = 0; off + 3 < desc_size && written < n_samples; off += 4) {
278 buf.write((PinBitArray) b[off + 1]);
279 written++;
280 if (written < n_samples) {
281 buf.write((PinBitArray) b[off + 3]);
282 written++;
283 }
284 }
285 }
286 log("waitForResult() -> %u records", (unsigned) buf.available());
287 }
288
289 void dump() {
290 size_t count = logicAnalyzer().available();
291 write(logicAnalyzer().buffer().data_ptr(), count);
292 }
293};
294
295} // namespace
296
297#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 the original ESP32, using the I2S0 peripheral's undocu...
Definition capture_esp32_i2s.h:69
void allocDma(uint32_t sampleCount)
(Re)allocates the DMA descriptor chain for the requested sample count
Definition capture_esp32_i2s.h:187
void setupOnce()
One-time GPIO/I2S peripheral setup.
Definition capture_esp32_i2s.h:121
void setDataPins(const int pins[8])
Defines the 8 data pins individually if they are not contiguous.
Definition capture_esp32_i2s.h:79
CaptureESP32I2S(int pinStart=19, int clkOutPin=32, int clkInPin=34)
Default Constructor - uses GPIO19..GPIO26 as the 8 bit data bus.
Definition capture_esp32_i2s.h:72
void captureAll() override
Used to measure the speed - capture into memory w/o dump.
Definition capture_esp32_i2s.h:95
void capture() override
starts the capturing of the data
Definition capture_esp32_i2s.h:84
void enableSampleClock(uint32_t freqHz)
Drives the sample clock via LEDC PWM on clk_out_pin, looped back into clk_in_pin.
Definition capture_esp32_i2s.h:228
float frequencyMeasured()
Provides the measured capturing frequency (based on the last capture)
Definition capture_esp32_i2s.h:102
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