mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-24 11:11:02 -07:00
Initial support for twin drives and 5.25" drives.
This commit is contained in:
@@ -2900,6 +2900,39 @@
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
|
||||
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="DRIVE_REG" persistent="">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
|
||||
<dependencies>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="DRIVE_REG.h" persistent="Generated_Source\PSoC5\DRIVE_REG.h">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="DRIVE_REG.c" persistent="Generated_Source\PSoC5\DRIVE_REG.c">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="DRIVE_REG_PM.c" persistent="Generated_Source\PSoC5\DRIVE_REG_PM.c">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
|
||||
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
#include "../protocol.h"
|
||||
|
||||
#define MOTOR_ON_TIME 5000 /* milliseconds */
|
||||
#define STEP_INTERVAL_TIME 3 /* ms */
|
||||
#define STEP_INTERVAL_TIME 6 /* ms */
|
||||
#define STEP_SETTLING_TIME 40 /* ms */
|
||||
|
||||
#define DISKSTATUS_WPT 1
|
||||
@@ -22,6 +22,7 @@ static bool motor_on = false;
|
||||
static uint32_t motor_on_time = 0;
|
||||
static bool homed = false;
|
||||
static int current_track = 0;
|
||||
static int current_drive = 0;
|
||||
|
||||
#define BUFFER_COUNT 16
|
||||
#define BUFFER_SIZE 64
|
||||
@@ -540,6 +541,19 @@ static void cmd_erase(struct erase_frame* f)
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
|
||||
static void cmd_set_drive(struct set_drive_frame* f)
|
||||
{
|
||||
if (current_drive != f->drive)
|
||||
{
|
||||
current_drive = f->drive;
|
||||
DRIVE_REG_Write(current_drive);
|
||||
homed = false;
|
||||
}
|
||||
|
||||
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SET_DRIVE_REPLY);
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
|
||||
static void handle_command(void)
|
||||
{
|
||||
static uint8_t input_buffer[FRAME_SIZE];
|
||||
@@ -580,6 +594,10 @@ static void handle_command(void)
|
||||
cmd_recalibrate();
|
||||
break;
|
||||
|
||||
case F_FRAME_SET_DRIVE_CMD:
|
||||
cmd_set_drive((struct set_drive_frame*) f);
|
||||
break;
|
||||
|
||||
default:
|
||||
send_error(F_ERROR_BAD_COMMAND);
|
||||
}
|
||||
@@ -594,6 +612,7 @@ int main(void)
|
||||
CAPTURE_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb);
|
||||
REPLAY_DMA_FINISHED_IRQ_StartEx(&replay_dma_finished_irq_cb);
|
||||
CAPTURE_COUNTER_Start();
|
||||
DRIVE_REG_Write(0);
|
||||
/* UART_Start(); */
|
||||
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
|
||||
|
||||
|
||||
17
README.md
17
README.md
@@ -40,6 +40,12 @@ in software. It doesn't rely on any floppy disk controller to interpret the
|
||||
pulsetrain, so we can be a lot cleverer. In fact, the disk doesn't even have
|
||||
to be spinning at the same speed.
|
||||
|
||||
**Q.** Does it work on 5.25" drives?
|
||||
|
||||
**A.** Yes! Although PC 5.25" drives spin at 360 RPM rather than 300 RPM,
|
||||
which means there's only 166ms of data on one per track rather than 200ms;
|
||||
if you try to write a 3.5" format disk onto one it probably won't work.
|
||||
|
||||
**Q.** That's awesome! What formats does it support?
|
||||
|
||||
**A.** I'm glad you asked the question. Not a lot, currently.
|
||||
@@ -91,9 +97,7 @@ Here's the physical stuff you need.
|
||||
|
||||
- one (1) standard PC floppy disk drive. You'll have to search around as
|
||||
they're increasingly hard to find. The FluxEngine should work with any
|
||||
standard 3.5" drive. It's theoretically capable of supporting 5.25"
|
||||
drives too but I'd need to modify the firmware timings. If you want this,
|
||||
[get in touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
standard 3.5" or 5.25" drive.
|
||||
|
||||
- some way of connecting the board to your drive. My prototype above uses a
|
||||
set of headers to let me attach the board directly on the back of the
|
||||
@@ -297,9 +301,10 @@ Commands which take `--source` or `--dest` take a parameter of the syntax
|
||||
disk; otherwise, you can specify a `.flux` file. The colon-separated
|
||||
modifiers limit which bits of the disk are accessed. To specify tracks, do
|
||||
`:t=0-9` (or just `:t=7`). For sides, do `:s=0-1` (or commonly just `:s=0`).
|
||||
If left unspecified, you get the default specified by the command, which will
|
||||
vary depending on which disk format you're using (and is usually the right
|
||||
one).
|
||||
If you have two drives, do `:d=1` (but bear in mind that you need to specify
|
||||
exactly one drive; ranges won't work). If left unspecified, you get the
|
||||
default specified by the command, which will vary depending on which disk
|
||||
format you're using (and is usually the right one).
|
||||
|
||||
### How it works
|
||||
|
||||
|
||||
@@ -79,17 +79,22 @@ void DataSpec::set(const std::string& spec)
|
||||
for (size_t i = 1; i < words.size(); i++)
|
||||
{
|
||||
auto mod = parseMod(words[i]);
|
||||
if ((mod.name != "t") && (mod.name != "s"))
|
||||
if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d"))
|
||||
Error() << fmt::format("unknown data modifier '{}'", mod.name);
|
||||
modifiers[mod.name] = mod;
|
||||
}
|
||||
|
||||
const auto& drives = modifiers["d"].data;
|
||||
if (drives.size() != 1)
|
||||
Error() << "you must specify exactly one drive";
|
||||
drive = *drives.begin();
|
||||
|
||||
const auto& tracks = modifiers["t"].data;
|
||||
const auto& sides = modifiers["s"].data;
|
||||
for (auto track : tracks)
|
||||
{
|
||||
for (auto side : sides)
|
||||
locations.push_back({ track, side });
|
||||
locations.push_back({ drive, track, side });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,15 @@ class DataSpec
|
||||
public:
|
||||
struct Location
|
||||
{
|
||||
unsigned drive;
|
||||
unsigned track;
|
||||
unsigned side;
|
||||
|
||||
bool operator == (const Location& other) const
|
||||
{ return (track == other.track) && (side == other.side); }
|
||||
{ return (drive == other.drive) && (track == other.track) && (side == other.side); }
|
||||
|
||||
bool operator != (const Location& other) const
|
||||
{ return (track != other.track) || (side != other.side); }
|
||||
{ return (drive != other.drive) || (track != other.track) || (side != other.side); }
|
||||
};
|
||||
|
||||
struct Modifier
|
||||
@@ -44,6 +45,7 @@ public:
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
std::vector<Location> locations;
|
||||
unsigned drive;
|
||||
};
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
"source for data",
|
||||
":t=0-79:s=0-1");
|
||||
":t=0-79:s=0-1:d=0");
|
||||
|
||||
static StringFlag destination(
|
||||
{ "--write-flux", "-f" },
|
||||
@@ -63,6 +63,7 @@ std::unique_ptr<Fluxmap> ReaderTrack::read()
|
||||
|
||||
std::unique_ptr<Fluxmap> CapturedReaderTrack::reallyRead()
|
||||
{
|
||||
usbSetDrive(drive);
|
||||
usbSeek(track);
|
||||
return usbRead(side, revolutions);
|
||||
}
|
||||
@@ -104,6 +105,7 @@ std::vector<std::unique_ptr<ReaderTrack>> readTracks()
|
||||
dataSpec.filename.empty()
|
||||
? (ReaderTrack*)new CapturedReaderTrack()
|
||||
: (ReaderTrack*)new FileReaderTrack());
|
||||
t->drive = location.drive;
|
||||
t->track = location.track;
|
||||
t->side = location.side;
|
||||
tracks.push_back(std::move(t));
|
||||
|
||||
@@ -10,6 +10,7 @@ class ReaderTrack
|
||||
public:
|
||||
virtual ~ReaderTrack() {}
|
||||
|
||||
int drive;
|
||||
int track;
|
||||
int side;
|
||||
|
||||
|
||||
12
lib/usb.cc
12
lib/usb.cc
@@ -244,3 +244,15 @@ void usbErase(int side)
|
||||
await_reply<struct any_frame>(F_FRAME_ERASE_REPLY);
|
||||
}
|
||||
|
||||
void usbSetDrive(int drive)
|
||||
{
|
||||
usb_init();
|
||||
|
||||
struct set_drive_frame f = {
|
||||
{ .type = F_FRAME_SET_DRIVE_CMD, .size = sizeof(f) },
|
||||
.drive = (uint8_t) drive
|
||||
};
|
||||
usb_cmd_send(&f, f.f.size);
|
||||
await_reply<struct any_frame>(F_FRAME_SET_DRIVE_REPLY);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,5 +11,6 @@ extern void usbTestBulkTransport();
|
||||
extern std::unique_ptr<Fluxmap> usbRead(int side, int revolutions);
|
||||
extern void usbWrite(int side, const Fluxmap& fluxmap);
|
||||
extern void usbErase(int side);
|
||||
extern void usbSetDrive(int drive);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,8 @@ enum
|
||||
F_FRAME_ERASE_REPLY, /* any_frame */
|
||||
F_FRAME_RECALIBRATE_CMD, /* any_frame */
|
||||
F_FRAME_RECALIBRATE_REPLY, /* any_frame */
|
||||
F_FRAME_SET_DRIVE_CMD, /* setdrive_frame */
|
||||
F_FRAME_SET_DRIVE_REPLY, /* any_frame */
|
||||
};
|
||||
|
||||
enum
|
||||
@@ -121,4 +123,10 @@ struct erase_frame
|
||||
uint8_t side;
|
||||
};
|
||||
|
||||
struct set_drive_frame
|
||||
{
|
||||
struct frame_header f;
|
||||
uint8_t drive;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "usb.h"
|
||||
#include "dataspec.h"
|
||||
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
"source for data",
|
||||
":d=0");
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
Flag::parseFlags(argc, argv);
|
||||
|
||||
usbSetDrive(source.value.drive);
|
||||
nanoseconds_t period = usbGetRotationalPeriod();
|
||||
std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl;
|
||||
|
||||
|
||||
@@ -35,40 +35,40 @@ static void test_parsemod(void)
|
||||
|
||||
static void test_dataspec(void)
|
||||
{
|
||||
DataSpec spec("foo:t=0-2:s=0-1");
|
||||
DataSpec spec("foo:t=0-2:s=0-1:d=0");
|
||||
assert(spec.filename == "foo");
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
|
||||
assert((std::string)spec == "foo:s=0-1:t=0-2");
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "foo:d=0:s=0-1:t=0-2");
|
||||
|
||||
spec.set("bar");
|
||||
assert(spec.filename == "bar");
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}}));
|
||||
assert((std::string)spec == "bar:s=0-1:t=0-2");
|
||||
{{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1}, {0, 2, 0}, {0, 2, 1}}));
|
||||
assert((std::string)spec == "bar:d=0:s=0-1:t=0-2");
|
||||
|
||||
spec.set(":t=0");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 0}, {0, 1}}));
|
||||
assert((std::string)spec == ":s=0-1:t=0");
|
||||
{{0, 0, 0}, {0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=0-1:t=0");
|
||||
|
||||
spec.set(":s=1");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{0, 1}}));
|
||||
assert((std::string)spec == ":s=1:t=0");
|
||||
{{0, 0, 1}}));
|
||||
assert((std::string)spec == ":d=0:s=1:t=0");
|
||||
|
||||
spec.set(":t=9");
|
||||
spec.set(":t=9:d=1");
|
||||
assert(spec.filename.empty());
|
||||
assert((spec.locations
|
||||
== std::vector<DataSpec::Location>
|
||||
{{9, 1}}));
|
||||
assert((std::string)spec == ":s=1:t=9");
|
||||
{{1, 9, 1}}));
|
||||
assert((std::string)spec == ":d=1:s=1:t=9");
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
|
||||
Reference in New Issue
Block a user