This commit is contained in:
Jeff Epler 2022-02-08 14:14:23 -06:00
parent fefdb6667e
commit 63a67ca062
No known key found for this signature in database
GPG key ID: D5BF15AB975AB4DE
4 changed files with 341 additions and 75 deletions

View file

@ -0,0 +1,129 @@
#include <Adafruit_Floppy.h>
// If using SAMD51, turn on TINYUSB USB stack
#if defined(ADAFRUIT_FEATHER_M4_EXPRESS)
#define DENSITY_PIN A0 // IDC 2
#define INDEX_PIN A1 // IDC 8
#define SELECT_PIN A2 // IDC 12
#define MOTOR_PIN A3 // IDC 16
#define DIR_PIN A4 // IDC 18
#define STEP_PIN A5 // IDC 20
#define WRDATA_PIN 13 // IDC 22 (not used during read)
#define WRGATE_PIN 12 // IDC 24 (not used during read)
#define TRK0_PIN 11 // IDC 26
#define PROT_PIN 10 // IDC 28
#define READ_PIN 9 // IDC 30
#define SIDE_PIN 6 // IDC 32
#define READY_PIN 5 // IDC 34
#elif defined (ARDUINO_ADAFRUIT_FEATHER_RP2040)
#define DENSITY_PIN A0 // IDC 2
#define INDEX_PIN A1 // IDC 8
#define SELECT_PIN A2 // IDC 12
#define MOTOR_PIN A3 // IDC 16
#define DIR_PIN 24 // IDC 18
#define STEP_PIN 25 // IDC 20
#define WRDATA_PIN 13 // IDC 22 (not used during read)
#define WRGATE_PIN 12 // IDC 24 (not used during read)
#define TRK0_PIN 11 // IDC 26
#define PROT_PIN 10 // IDC 28
#define READ_PIN 9 // IDC 30
#define SIDE_PIN 6 // IDC 32
#define READY_PIN 5 // IDC 34
#elif defined (ARDUINO_RASPBERRY_PI_PICO)
#define DENSITY_PIN 2 // IDC 2
#define INDEX_PIN 3 // IDC 8
#define SELECT_PIN 4 // IDC 12
#define MOTOR_PIN 5 // IDC 16
#define DIR_PIN 6 // IDC 18
#define STEP_PIN 7 // IDC 20
#define WRDATA_PIN 8 // IDC 22 (not used during read)
#define WRGATE_PIN 9 // IDC 24 (not used during read)
#define TRK0_PIN 10 // IDC 26
#define PROT_PIN 11 // IDC 28
#define READ_PIN 12 // IDC 30
#define SIDE_PIN 13 // IDC 32
#define READY_PIN 14 // IDC 34
#else
#error "Please set up pin definitions!"
#endif
Adafruit_Floppy floppy(DENSITY_PIN, INDEX_PIN, SELECT_PIN,
MOTOR_PIN, DIR_PIN, STEP_PIN,
WRDATA_PIN, WRGATE_PIN, TRK0_PIN,
PROT_PIN, READ_PIN, SIDE_PIN, READY_PIN);
// WARNING! there are 150K max flux pulses per track!
uint8_t flux_transitions[MAX_FLUX_PULSE_PER_TRACK];
uint32_t time_stamp = 0;
void setup() {
Serial.begin(115200);
while (!Serial) delay(100);
Serial.println("its time for a nice floppy transfer!");
floppy.debug_serial = &Serial;
if (!floppy.begin()) {
Serial.println("Failed to initialize floppy interface");
while (1) yield();
}
floppy.select(true);
if (! floppy.spin_motor(true)) {
Serial.println("Failed to spin up motor & find index pulse");
while (1) yield();
}
Serial.print("Seeking track...");
if (! floppy.goto_track(0)) {
Serial.println("Failed to seek to track");
while (1) yield();
}
Serial.println("done!");
}
void loop() {
uint32_t index_pulse_offset;
uint32_t captured_flux = floppy.capture_track(flux_transitions, sizeof(flux_transitions), &index_pulse_offset, true);
Serial.print("Captured ");
Serial.print(captured_flux);
Serial.println(" flux transitions");
//floppy.print_pulses(flux_transitions, captured_flux);
floppy.print_pulse_bins(flux_transitions, captured_flux, 255, true);
if ((millis() - time_stamp) > 1000) {
Serial.print("Ready? ");
Serial.println(digitalRead(READY_PIN) ? "No" : "Yes");
Serial.print("Write Protected? ");
Serial.println(digitalRead(PROT_PIN) ? "No" : "Yes");
Serial.print("Track 0? ");
Serial.println(digitalRead(TRK0_PIN) ? "No" : "Yes");
time_stamp = millis();
}
unsigned T_2 = floppy.getSampleFrequency() * 2 / 1000000;
unsigned T_3 = floppy.getSampleFrequency() * 3 / 1000000;
unsigned T_4 = floppy.getSampleFrequency() * 4 / 1000000;
for(size_t i=0; i<sizeof(flux_transitions); i+=3) {
flux_transitions[i] = T_2;
}
for(size_t i=1; i<sizeof(flux_transitions); i+=3) {
flux_transitions[i] = T_3;
}
for(size_t i=2; i<sizeof(flux_transitions); i+=3) {
flux_transitions[i] = T_4;
}
floppy.print_pulse_bins(flux_transitions, captured_flux, 255, true);
Serial.println("Writing track with T234234...");
Serial.printf("T2 = %d T3 = %d T4 = %d\n", T_2, T_3, T_4);
floppy.write_track(flux_transitions, sizeof(flux_transitions), true);
yield();
}

View file

@ -23,6 +23,7 @@ extern uint32_t rp2040_flux_capture(int indexpin, int rdpin,
volatile uint8_t *end,
uint32_t *falling_index_offset,
bool store_greaseweazle);
extern void rp2040_flux_write(int index_pin, int wrgate_pin, int wrdata_pin, uint8_t *pulses, uint8_t *pulse_end, bool store_greaseweazel);
#endif
#if !DEBUG_FLOPPY
@ -154,8 +155,7 @@ void Adafruit_Floppy::soft_reset(void) {
digitalWrite(_wrdatapin, HIGH);
}
if (_wrgatepin >= 0) {
pinMode(_wrgatepin, OUTPUT);
digitalWrite(_wrgatepin, HIGH);
pinMode(_wrgatepin, INPUT_PULLUP);
}
#ifdef BUSIO_USE_FAST_PINIO
@ -524,7 +524,9 @@ uint32_t Adafruit_Floppy::capture_track(volatile uint8_t *pulses,
void Adafruit_Floppy::write_track(uint8_t *pulses, uint32_t num_pulses,
bool store_greaseweazle) {
#if defined(__SAMD51__)
#if defined(ARDUINO_ARCH_RP2040)
rp2040_flux_write(_indexpin, _wrgatepin, _wrdatapin, pulses, pulses + num_pulses, store_greaseweazle);
#elif defined(__SAMD51__)
pinMode(_wrdatapin, OUTPUT);
digitalWrite(_wrdatapin, HIGH);

View file

@ -44,17 +44,18 @@ static const uint16_t fluxread[] = {
// vs wrapping.
0x0040, // jmp x--, wait_one
};
pio_program_t fluxread_struct = {.instructions = fluxread,
static const pio_program_t fluxread_struct = {.instructions = fluxread,
.length =
sizeof(fluxread) / sizeof(fluxread[0]),
.origin = -1};
static const int fluxwrite_sideset_pin_count = 0;
static const bool fluxwrite_sideset_enable = 0;
static const int fluxwrite_sideset_pin_count = 1;
static const bool fluxwrite_sideset_enable = 1;
static const uint16_t fluxwrite[] = {
// start:
// ;; ensure wgate is deasserted then wait for FIFO to be loaded
0xe001, // set pins, 1
// .side_set 1 opt
// start:
// ;; ensure wgate is deasserted then wait for FIFO to be loaded
0xb842, // nop side 1
0x80e0, // pull ifempty
// ; Wait for a falling edge on the index pin
// wait_index_high:
@ -63,32 +64,29 @@ static const uint16_t fluxwrite[] = {
// wait_index_low:
0x00c4, // jmp pin wait_index_low
// ; Enable gate 2us before writing
0xb042, // nop side 0
0xe033, // set x 19
// loop_gate:
0x0146, // jmp x--, loop_gate [1]
0x0147, // jmp x--, loop_gate [1]
// loop_flux:
0xe000, // set pins, 0 ; drive pin low
0x6030, // out x, 16 ; get the next timing pulse information
// ;; If x is zero here, either a 0 was explicitly put into the flux timing
// stream,
// ;; OR the data ended naturally OR there was an under-run. In any case,
// jump back
// ;; to the beginning to wait for available data and then an index pulse --
// we're done.
// ;; If x is zero here, either a 0 was explicitly put into the flux timing stream,
// ;; OR the data ended naturally OR there was an under-run. In any case, jump back
// ;; to the beginning to wait for available data and then an index pulse -- we're done.
0x0020, // jmp !x, start
// ;; output the fixed on time. 10 cycles (preload reg with 6) is about
// 0.5us.
// ;; note that wdc1772 has varying low times, from 570 to 1380us
// ;; output the fixed on time. 10 cycles (preload reg with 6) is about 0.5us.
// ;; note that wdc1772 has varying low times, from 570 to 1380us
0xe046, // set y, 6 ; fixed on time
// loop_low:
0x008b, // jmp y--, loop_low
0x008c, // jmp y--, loop_low
0xe001, // set pins, 1 ; drive pin high
0x80c0, // pull ifempty noblock
// loop_high:
0x004e, // jmp x--, loop_high
0x0007, // jmp loop_flux
0x004f, // jmp x--, loop_high
0x0008, // jmp loop_flux
};
pio_program_t fluxwrite_struct = {.instructions = fluxwrite,
static const pio_program_t fluxwrite_struct = {.instructions = fluxwrite,
.length =
sizeof(fluxwrite) / sizeof(fluxwrite[0]),
.origin = -1};
@ -100,19 +98,22 @@ typedef struct floppy_singleton {
uint16_t half;
} floppy_singleton_t;
static floppy_singleton_t g_floppy;
static floppy_singleton_t g_reader, g_writer;
const static PIO pio_instances[2] = {pio0, pio1};
static bool allocate_pio_set_program() {
static bool allocate_pio_set_program(floppy_singleton_t *info, const pio_program_t *program) {
memset(info, 0, sizeof(*info));
for (size_t i = 0; i < NUM_PIOS; i++) {
PIO pio = pio_instances[i];
if (!pio_can_add_program(pio, &fluxread_struct)) {
if (!pio_can_add_program(pio, program)) {
continue;
}
int sm = pio_claim_unused_sm(pio, false);
if (sm != -1) {
g_floppy.pio = pio;
g_floppy.sm = sm;
info->pio = pio;
info->sm = sm;
// cannot fail, we asked nicely already
info->offset = pio_add_program(pio, program);
return true;
}
}
@ -120,89 +121,72 @@ static bool allocate_pio_set_program() {
}
static bool init_capture(int index_pin, int read_pin) {
if (g_floppy.pio) {
if (g_reader.pio) {
return true;
}
Serial.println("init capture");
memset(&g_floppy, 0, sizeof(g_floppy));
if (!allocate_pio_set_program()) {
if (!allocate_pio_set_program(&g_reader, &fluxread_struct)) {
return false;
}
// cannot fail, we asked nicely already
g_floppy.offset = pio_add_program(g_floppy.pio, &fluxread_struct);
pio_gpio_init(g_floppy.pio, index_pin);
pio_gpio_init(g_floppy.pio, read_pin);
gpio_pull_up(index_pin);
pio_sm_config c = {0, 0, 0};
sm_config_set_wrap(&c, g_floppy.offset,
g_floppy.offset + fluxread_struct.length - 1);
sm_config_set_wrap(&c, g_reader.offset,
g_reader.offset + fluxread_struct.length - 1);
sm_config_set_jmp_pin(&c, read_pin);
sm_config_set_in_pins(&c, index_pin);
sm_config_set_in_shift(&c, true, true, 32);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
pio_sm_set_pins_with_mask(g_floppy.pio, g_floppy.sm, 1 << read_pin,
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, 1 << read_pin,
1 << read_pin);
float div = (float)clock_get_hz(clk_sys) / (3 * 24e6);
sm_config_set_clkdiv(&c, div); // 72MHz capture clock / 24MHz sample rate
pio_sm_init(g_floppy.pio, g_floppy.sm, g_floppy.offset, &c);
pio_sm_init(g_reader.pio, g_reader.sm, g_reader.offset, &c);
// g_floppy.dreq = pio_get_dreq(g_floppy.pio, g_floppy.sm, false);
// g_floppy.dma = dma_claim_unused_channel(false);
// rx_source = g_floppy.pio->rxf[g_floppy.sm];
// dma_channel_configure(g_floppy.dma, &c, tx_destination,
// g_reader.dreq = pio_get_dreq(g_reader.pio, g_reader.sm, false);
// g_reader.dma = dma_claim_unused_channel(false);
// rx_source = g_reader.pio->rxf[g_reader.sm];
// dma_channel_configure(g_reader.dma, &c, tx_destination,
return true;
}
static void start_common() {
pio_sm_exec(g_floppy.pio, g_floppy.sm, g_floppy.offset);
pio_sm_restart(g_floppy.pio, g_floppy.sm);
pio_sm_exec(g_reader.pio, g_reader.sm, g_reader.offset);
pio_sm_restart(g_reader.pio, g_reader.sm);
}
static uint16_t read_fifo() {
if (g_floppy.half) {
uint16_t result = g_floppy.half;
g_floppy.half = 0;
if (g_reader.half) {
uint16_t result = g_reader.half;
g_reader.half = 0;
return result;
}
uint32_t value = pio_sm_get_blocking(g_floppy.pio, g_floppy.sm);
g_floppy.half = value >> 16;
uint32_t value = pio_sm_get_blocking(g_reader.pio, g_reader.sm);
g_reader.half = value >> 16;
return value & 0xffff;
}
static void disable_capture(void) {
pio_sm_set_enabled(g_floppy.pio, g_floppy.sm, false);
pio_sm_set_enabled(g_reader.pio, g_reader.sm, false);
}
static void free_capture(void) {
if (!g_floppy.pio) {
if (!g_reader.pio) {
// already deinit
return;
}
disable_capture();
pio_sm_unclaim(g_floppy.pio, g_floppy.sm);
pio_remove_program(g_floppy.pio, &fluxwrite_struct, g_floppy.offset);
memset(&g_floppy, 0, sizeof(g_floppy));
pio_sm_unclaim(g_reader.pio, g_reader.sm);
pio_remove_program(g_reader.pio, &fluxwrite_struct, g_reader.offset);
memset(&g_reader, 0, sizeof(g_reader));
}
static void capture_foreground(int indexpin, uint8_t *start, uint8_t *end,
static void capture_foreground(int index_pin, uint8_t *start, uint8_t *end,
bool wait_index, bool stop_index,
uint32_t *falling_index_offset,
bool store_greaseweazle) {
// g_floppy.store_greaseweazle = store_greaseweazle;
// c = dma_channel_get_default_config(g_floppy.dma);
// channel_config_set_data_transfer_size(&c, DMA_SIZE_32);
// channel_config_set_dreq(&c, g_floppy.dreq);
// channel_config_set_read_increment(false);
// channel_config_set_write_increment(true);
// dma_channel_configure(g_floppy.dma, &c, start, (end-start) / 4, false);
// dma_start_channel_mask(1u << g_floppy.dma);
if (falling_index_offset) {
*falling_index_offset = ~0u;
}
@ -210,13 +194,13 @@ static void capture_foreground(int indexpin, uint8_t *start, uint8_t *end,
// wait for a falling edge of index pin, then enable the capture peripheral
if (wait_index) {
while (!gpio_get(indexpin)) { /* NOTHING */
while (!gpio_get(index_pin)) { /* NOTHING */
}
while (gpio_get(indexpin)) { /* NOTHING */
while (gpio_get(index_pin)) { /* NOTHING */
}
}
pio_sm_set_enabled(g_floppy.pio, g_floppy.sm, true);
pio_sm_set_enabled(g_reader.pio, g_reader.sm, true);
int last = read_fifo();
int i = 0;
while (start != end) {
@ -247,15 +231,109 @@ static void capture_foreground(int indexpin, uint8_t *start, uint8_t *end,
static void enable_capture_fifo() { start_common(); }
static bool init_write(int index_pin, int wrgate_pin, int wrdata_pin) {
if (g_reader.pio) {
return true;
}
if (!allocate_pio_set_program(&g_reader, &fluxread_struct)) {
return false;
}
uint32_t wrgate_bit = 1u << wrgate_pin;
uint32_t wrdata_bit = 1u << wrdata_pin;
uint32_t index_bit = 1u << index_pin;
pio_sm_set_pindirs_with_mask(g_reader.pio, g_reader.sm, wrgate_bit | wrdata_bit, wrgate_bit | wrdata_bit | index_bit);
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, wrgate_bit | wrdata_bit | index_bit, wrgate_bit | wrdata_bit | index_bit);
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, 0, wrgate_bit | wrdata_bit | index_bit);
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, wrgate_bit | wrdata_bit | index_bit, wrgate_bit | wrdata_bit | index_bit);
pio_gpio_init(g_reader.pio, wrgate_pin);
pio_gpio_init(g_reader.pio, wrdata_pin);
gpio_pull_up(index_pin);
pio_sm_config c = {0, 0, 0, 0};
sm_config_set_wrap(&c, g_writer.offset,
g_writer.offset + fluxread_struct.length - 1);
sm_config_set_jmp_pin(&c, index_pin);
sm_config_set_set_pins(&c, wrdata_pin, 1);
sm_config_set_sideset_pins(&c, wrgate_pin);
sm_config_set_sideset(&c, fluxwrite_sideset_pin_count, fluxwrite_sideset_enable, false);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
float div = (float)clock_get_hz(clk_sys) / (24e6);
sm_config_set_clkdiv(&c, div); // 24MHz output clock
pio_sm_init(g_writer.pio, g_writer.sm, g_writer.offset, &c);
return true;
}
#define OVERHEAD (15) // minimum pulse length due to PIO overhead, about 0.625us
static void write_fifo(unsigned value) {
if (value < OVERHEAD) { value = 1; }
else { value -= OVERHEAD; if(value > 0xffff) value = 0xffff; }
if (g_writer.half) {
unsigned write_val = (g_writer.half << 16) | value;
pio_sm_put_blocking(g_writer.pio, g_writer.sm, write_val);
g_writer.half = 0;
} else {
g_writer.half = value;
}
}
static void disable_write() {
pio_sm_exec(g_writer.pio, g_writer.sm, g_writer.offset);
pio_sm_restart(g_writer.pio, g_writer.sm);
}
static void write_foreground(int index_pin, uint8_t *pulses, uint8_t *pulse_end, bool store_greaseweazle) {
int old_index_state = gpio_get(index_pin);
int index_count = 0;
pio_sm_exec(g_writer.pio, g_writer.sm, g_writer.offset);
pio_sm_restart(g_writer.pio, g_writer.sm);
Serial.printf("write_foreground pulses=%p pulse_end=%p\n", pulses, pulse_end);
while(pulses != pulse_end) {
unsigned value = greaseunpack(&pulses, pulse_end, store_greaseweazle);
write_fifo(value);
do {
int index_state = gpio_get(index_pin);
if(old_index_state && !index_state) {
index_count ++;
// A full revolution has occurred, stop writing immediately
if(index_count == 2) {
disable_write();
Serial.printf("disable_write pulses=%p pulse_end=%p\n", pulses, pulse_end);
break;
}
}
} while(pio_sm_is_tx_fifo_full(g_writer.pio, g_writer.sm));
}
Serial.printf("end of write pulses=%p pulse_end=%p\n", pulses, pulse_end);
}
static void free_write() {
if (!g_writer.pio) {
// already deinit
return;
}
disable_write();
pio_sm_unclaim(g_writer.pio, g_writer.sm);
pio_remove_program(g_writer.pio, &fluxread_struct, g_writer.offset);
memset(&g_writer, 0, sizeof(g_writer));
}
#ifdef __cplusplus
#include <Adafruit_Floppy.h>
uint32_t rp2040_flux_capture(int indexpin, int rdpin, volatile uint8_t *pulses,
uint32_t rp2040_flux_capture(int index_pin, int rdpin, volatile uint8_t *pulses,
volatile uint8_t *pulse_end,
uint32_t *falling_index_offset,
bool store_greaseweazle) {
init_capture(indexpin, rdpin);
capture_foreground(indexpin, (uint8_t *)pulses, (uint8_t *)pulse_end, true,
init_capture(index_pin, rdpin);
capture_foreground(index_pin, (uint8_t *)pulses, (uint8_t *)pulse_end, true,
false, falling_index_offset, store_greaseweazle);
free_capture();
return pulse_end - pulses;
@ -271,7 +349,7 @@ bool Adafruit_Floppy::start_polled_capture(void) {
if (!init_capture())
return false;
start_common();
pio_sm_set_enabled(g_floppy.pio, g_floppy.sm, true);
pio_sm_set_enabled(g_reader.pio, g_reader.sm, true);
return true;
}
@ -290,5 +368,17 @@ uint16_t mfm_io_sample_flux(bool *index) {
return delta / 2;
}
void rp2040_flux_write(int index_pin, int wrgate_pin, int wrdata_pin, uint8_t *pulses, uint8_t *pulse_end, bool store_greaseweazle) {
if (!init_write(index_pin, wrgate_pin, wrdata_pin)) { return; }
write_foreground(index_pin, (uint8_t *)pulses, (uint8_t *)pulse_end, store_greaseweazle);
uint32_t wrgate_bit = 1u << wrgate_pin;
uint32_t wrdata_bit = 1u << wrdata_pin;
uint32_t index_bit = 1u << index_pin;
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, wrgate_bit | wrdata_bit | index_bit, wrgate_bit | wrdata_bit | index_bit);
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, 0, wrgate_bit | wrdata_bit | index_bit);
pio_sm_set_pins_with_mask(g_reader.pio, g_reader.sm, wrgate_bit | wrdata_bit | index_bit, wrgate_bit | wrdata_bit | index_bit);
free_write();
}
#endif
#endif

View file

@ -1,6 +1,7 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <Arduino.h>
// Encoding flux of duration T:
// 0: Impossible
@ -53,3 +54,47 @@ static inline uint8_t *greasepack(uint8_t *buf, uint8_t *end, unsigned value) {
return buf;
}
static inline unsigned greaseunpack(uint8_t **buf_, uint8_t *end, bool store_greaseweazel) {
#define BUF (*buf_)
if (!store_greaseweazel) {
if (!BUF || BUF == end) {
return 0xffff;
}
return *BUF++;
}
while(true) {
// already no data left
if (!BUF || BUF == end) {
return 0xffff;
}
size_t left = end-BUF;
uint8_t data = *BUF++;
size_t need = data == 255 ? 6 :
data >= cutoff_1byte ? 2 : 1;
if(left < need) {
BUF = end;
return 0xffff;
}
if (need == 1) {
return data;
}
if (need == 2) {
uint8_t data2 = *BUF++;
return (data - cutoff_1byte) * 250 + data2;
}
uint8_t data2 = *BUF++;
if (data2 != 2) { BUF += 4; continue; } // something other than FluxOp.Space
uint32_t value = (*BUF++ & 254) >> 1;
value += (*BUF++ & 254) << 6;
value += (*BUF++ & 254) << 13;
value += (*BUF++ & 254) << 20;
return value;
}
}
#undef BUF