Move SSI timing-critical read to core 1 via FIFO
- Replace hardware/sync.h with pico/multicore.h - Implement ssi_read_core1(), setup1(), loop1() on core 1 - Core 1 owns the SSI bit-bang loop; no USB/timer IRQs interfere - Core 0 sends packed request word over FIFO, receives lo32/hi32/duration - Add requestSsiRead() helper on core 0 side - Remove noInterrupts / save_and_disable_interrupts (no longer needed) - Update startup banner to reflect dual-core architecture
This commit is contained in:
parent
d547ee0548
commit
58a2d37b38
1 changed files with 69 additions and 31 deletions
100
src/main.cpp
100
src/main.cpp
|
|
@ -1,7 +1,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
#include <hardware/sync.h>
|
|
||||||
#include <hardware/structs/sio.h>
|
#include <hardware/structs/sio.h>
|
||||||
|
#include <pico/multicore.h>
|
||||||
|
|
||||||
// CLOCK module (TX)
|
// CLOCK module (TX)
|
||||||
const uint8_t TX_DI = 0;
|
const uint8_t TX_DI = 0;
|
||||||
|
|
@ -23,47 +23,86 @@ Adafruit_NeoPixel led(1, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
const uint32_t TX_DI_MASK = 1u << TX_DI;
|
const uint32_t TX_DI_MASK = 1u << TX_DI;
|
||||||
const uint32_t RX_RO_MASK = 1u << RX_RO;
|
const uint32_t RX_RO_MASK = 1u << RX_RO;
|
||||||
|
|
||||||
|
// Inter-core protocol
|
||||||
|
// Request: [bits:8 | half_us:16 | reserved:8] -> core 1
|
||||||
|
// Response: [hi32][lo32][duration_us] -> core 0
|
||||||
|
struct SsiRequest {
|
||||||
|
uint8_t bits;
|
||||||
|
uint16_t half_us;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SsiResponse {
|
||||||
|
uint64_t value;
|
||||||
|
uint32_t duration_us;
|
||||||
|
};
|
||||||
|
|
||||||
void setStatus(uint8_t r, uint8_t g, uint8_t b) {
|
void setStatus(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
led.setPixelColor(0, led.Color(r, g, b));
|
led.setPixelColor(0, led.Color(r, g, b));
|
||||||
led.show();
|
led.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSI read with direct register I/O and interrupts disabled.
|
// =========================================================================
|
||||||
// Single-cycle GPIO ops (~8 ns each) instead of ~1 us digitalWrite().
|
// CORE 1: SSI worker
|
||||||
// Interrupts off so USB/timer IRQs can't insert delays mid-frame.
|
// =========================================================================
|
||||||
uint64_t ssi_read(uint8_t bits, uint16_t half_us) {
|
|
||||||
|
uint64_t ssi_read_core1(uint8_t bits, uint16_t half_us) {
|
||||||
uint64_t value = 0;
|
uint64_t value = 0;
|
||||||
|
|
||||||
// Save interrupt state and disable. save_and_disable_interrupts() returns
|
sio_hw->gpio_clr = TX_DI_MASK; // first falling edge: latch
|
||||||
// the previous state so we can restore it exactly (don't just blindly
|
|
||||||
// re-enable - a caller higher up the stack may have wanted them off).
|
|
||||||
uint32_t irq_state = save_and_disable_interrupts();
|
|
||||||
|
|
||||||
// Frame start: clock idles HIGH, drop to LOW = encoder latches position
|
|
||||||
sio_hw->gpio_clr = TX_DI_MASK;
|
|
||||||
busy_wait_us_32(half_us);
|
busy_wait_us_32(half_us);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < bits; i++) {
|
for (uint8_t i = 0; i < bits; i++) {
|
||||||
sio_hw->gpio_set = TX_DI_MASK; // rising edge: encoder shifts bit
|
sio_hw->gpio_set = TX_DI_MASK; // rising: encoder shifts
|
||||||
busy_wait_us_32(half_us);
|
busy_wait_us_32(half_us);
|
||||||
sio_hw->gpio_clr = TX_DI_MASK; // falling edge: sample now
|
sio_hw->gpio_clr = TX_DI_MASK; // falling: sample
|
||||||
uint32_t bit = (sio_hw->gpio_in & RX_RO_MASK) ? 1 : 0;
|
uint32_t bit = (sio_hw->gpio_in & RX_RO_MASK) ? 1 : 0;
|
||||||
value = (value << 1) | bit;
|
value = (value << 1) | bit;
|
||||||
busy_wait_us_32(half_us);
|
busy_wait_us_32(half_us);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return clock to idle HIGH
|
sio_hw->gpio_set = TX_DI_MASK; // back to idle
|
||||||
sio_hw->gpio_set = TX_DI_MASK;
|
busy_wait_us_32(30); // monoflop
|
||||||
|
|
||||||
// Re-enable interrupts before the (long) monoflop wait - no need to
|
|
||||||
// hold them off any longer, the timing-critical part is done.
|
|
||||||
restore_interrupts(irq_state);
|
|
||||||
|
|
||||||
busy_wait_us_32(30); // monoflop reset time
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup1() {
|
||||||
|
// Core 1 setup: nothing to do, GPIOs already configured by core 0.
|
||||||
|
// Importantly: no Serial, no USB, no millis IRQ active here by default
|
||||||
|
// when running in this dual-core mode.
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop1() {
|
||||||
|
// Block until core 0 sends a packed request word.
|
||||||
|
// Word layout: bits in upper 8, half_us in next 16, 8 unused
|
||||||
|
uint32_t req = rp2040.fifo.pop();
|
||||||
|
uint8_t bits = (req >> 24) & 0xFF;
|
||||||
|
uint16_t half_us = (req >> 8) & 0xFFFF;
|
||||||
|
|
||||||
|
uint32_t t0 = time_us_32();
|
||||||
|
uint64_t value = ssi_read_core1(bits, half_us);
|
||||||
|
uint32_t took = time_us_32() - t0;
|
||||||
|
|
||||||
|
// Push three words back: low32, high32, duration
|
||||||
|
rp2040.fifo.push((uint32_t)(value & 0xFFFFFFFF));
|
||||||
|
rp2040.fifo.push((uint32_t)(value >> 32));
|
||||||
|
rp2040.fifo.push(took);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// CORE 0: serial command handler
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
void requestSsiRead(uint8_t bits, uint16_t half_us, uint64_t& outValue, uint32_t& outTook) {
|
||||||
|
uint32_t req = ((uint32_t)bits << 24) | ((uint32_t)half_us << 8);
|
||||||
|
rp2040.fifo.push(req);
|
||||||
|
|
||||||
|
uint32_t lo = rp2040.fifo.pop();
|
||||||
|
uint32_t hi = rp2040.fifo.pop();
|
||||||
|
outTook = rp2040.fifo.pop();
|
||||||
|
outValue = ((uint64_t)hi << 32) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
void handleCommand(const String& cmd) {
|
void handleCommand(const String& cmd) {
|
||||||
if (!cmd.startsWith("READ ")) {
|
if (!cmd.startsWith("READ ")) {
|
||||||
Serial.println("ERR unknown command. Use: READ <bits> <half_us>");
|
Serial.println("ERR unknown command. Use: READ <bits> <half_us>");
|
||||||
|
|
@ -90,13 +129,13 @@ void handleCommand(const String& cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(0, 0, 16); // blue = reading
|
setStatus(0, 0, 16); // blue = reading
|
||||||
uint32_t t0 = micros();
|
uint64_t value;
|
||||||
uint64_t raw = ssi_read(bits, halfUs);
|
uint32_t took;
|
||||||
uint32_t elapsed = micros() - t0;
|
requestSsiRead((uint8_t)bits, (uint16_t)halfUs, value, took);
|
||||||
setStatus(0, 16, 0); // green = idle
|
setStatus(0, 16, 0); // green = idle
|
||||||
|
|
||||||
Serial.printf("OK bits=%d half_us=%d hex=0x%llX dec=%llu took=%luus\n",
|
Serial.printf("OK bits=%d half_us=%d hex=0x%llX dec=%llu took=%luus\n",
|
||||||
bits, halfUs, raw, raw, elapsed);
|
bits, halfUs, value, value, took);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
@ -110,18 +149,17 @@ void setup() {
|
||||||
pinMode(RX_DI, OUTPUT);
|
pinMode(RX_DI, OUTPUT);
|
||||||
pinMode(RX_RO, INPUT);
|
pinMode(RX_RO, INPUT);
|
||||||
|
|
||||||
digitalWrite(TX_DE, HIGH); // CLK module = transmit
|
digitalWrite(TX_DE, HIGH);
|
||||||
digitalWrite(TX_RE, HIGH);
|
digitalWrite(TX_RE, HIGH);
|
||||||
digitalWrite(RX_DE, LOW); // DATA module = receive
|
digitalWrite(RX_DE, LOW);
|
||||||
digitalWrite(RX_RE, LOW);
|
digitalWrite(RX_RE, LOW);
|
||||||
digitalWrite(RX_DI, LOW);
|
digitalWrite(RX_DI, LOW);
|
||||||
digitalWrite(TX_DI, HIGH); // SSI clock idles HIGH
|
digitalWrite(TX_DI, HIGH); // SSI idle HIGH
|
||||||
|
|
||||||
delay(200);
|
delay(200);
|
||||||
while (!Serial && millis() < 3000) { delay(10); }
|
while (!Serial && millis() < 3000) { delay(10); }
|
||||||
Serial.println("\nSSI bridge ready (B+C: direct registers, IRQs off during read)");
|
Serial.println("\nSSI bridge ready (dual-core: core 1 dedicated to SSI)");
|
||||||
Serial.println("Send: READ <bits> <half_us>");
|
Serial.println("Send: READ <bits> <half_us>");
|
||||||
Serial.println("Example: READ 25 5");
|
|
||||||
setStatus(0, 16, 0);
|
setStatus(0, 16, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue