I have started a project to control a 3.5inch FDD using a Teensy3.0. The idea is to be able to read Atari ST floppy disks. There is code for similar projects on the web, which I downloaded and used as a starting point, but I have observed the waveforms and created my own code (Teensy specific). The hardware interface is quite simple, just a pair of 74LS05 hex open collector inverter ICs. Signals at the Teensy end look nice and clean.
I can step in and out and detect track 0. I am now working on the code to decode the MFM stream. The read data stream consists of a set of pulses of about 0.5us width, the distance between can be 2,4 or 6us. I am attaching an interrupt to the rising edge of the read data which calls an ISR to read a FTM timer, use or store the value, then reset the timer ready for the next pulse.
Here is the code so far...
While I was looking at the datastream on the scope I noted an anomoly. The yellow trace is the read data from the floppy (actually inverted by the 74LS05), and the blue trace is a Teensy debug output pin which goes high on entry to the ISR and low on exit. I wanted to check that the ISR completed fast enough, under 2us. It does!.
So there is a short interrupt latency(is that the word?) of about 1us. The ISR takes about 1us. BUT every now and again (the pulse underlined in blue) the ISR response is delayed. This messes up the time recorded between pulses and causes the decoded data stream to be corrupted.
My question is .. what is causing this?
My guess is that there is another interrupt which occurs at that time, but if so, what? The only other thing I can think of is "Serial", but the link is inactive during data read time (or could this be something to do with USB ???).
So I did some searching on this forum. I wanted to make the edge detect interrupt the highest priority. I have the following, which appeared to work, but I am not 100% certain it is correct.
I have developed the code to decode the actual data in the ISR and I am able to read the raw data and I can see sector headers and data, BUT it is corrupted. I see a stream of zeroes become a stream of ones. This could be explained by having a single pulse read longer than 2us. NOTE: a stream of 2us pulses means a stream of all ones of all zeroes (you need the sync byte to determine which), but if there is a rogue 3us pulse then the stream will be inverted when decoded.
Could my ISR be interrupted by something else causing a delay and bad timer reading?
During user ISR are lower priority interrupts disabled?
Any help or pointers would be appreciated.
--badsector
I can step in and out and detect track 0. I am now working on the code to decode the MFM stream. The read data stream consists of a set of pulses of about 0.5us width, the distance between can be 2,4 or 6us. I am attaching an interrupt to the rising edge of the read data which calls an ISR to read a FTM timer, use or store the value, then reset the timer ready for the next pulse.
Here is the code so far...
Code:
//
// modified by badsector
// 31-Dec-2019 : Port to Teensy 3.0
// : Using 74LS05 which is inverting.
// 01-Jan-2020 : Add timer code to measure time between pulses from floppy read pin.
//
#include <stdio.h> // for function sprintf
const byte STEP_TIME = 10;
const byte STEP_PULSE = 2;
const byte ON = 1;
const byte OFF = 0;
const byte IN = 1;
const byte OUT = 0; // towards outer track (track 0)
const byte toFDDdirSelect = 11;
const byte toFDDmotorOn = 12;
const byte toFDDselect = 14;
const byte toFDDstep = 15;
const byte toFDDside = 16;
const byte fromFDDtrack00 = 23;
const byte fromFDDindexPin = 22;
const byte fromFDDreadPin = 21;
const byte debugpin = 20;
const byte userButton = 0;
const word len = 1400;
byte data[len];
int track = 1;
byte stepdir=OUT;
int motoron=ON;
int drivesel=ON;
char sbuf[99];
void setup() {
Serial.begin(9600);
pinMode(fromFDDindexPin, INPUT_PULLUP);
pinMode(fromFDDreadPin, INPUT_PULLUP);
pinMode(fromFDDtrack00, INPUT_PULLUP);
pinMode(toFDDstep, OUTPUT);
pinMode(toFDDdirSelect, OUTPUT);
pinMode(toFDDmotorOn, OUTPUT);
pinMode(toFDDselect, OUTPUT);
pinMode(toFDDside, OUTPUT);
pinMode(userButton, INPUT);
pinMode(debugpin, OUTPUT);
digitalWrite(debugpin,LOW);
digitalWrite(toFDDstep, LOW); // sets it high
digitalWrite(toFDDdirSelect, (stepdir==OUT)?LOW:HIGH);
digitalWrite(toFDDside, HIGH);
digitalWrite(toFDDmotorOn, (motoron==ON)?HIGH:LOW);
digitalWrite(toFDDselect, (drivesel==ON)?HIGH:LOW);
}
bool isTrack00(void) {
return (digitalRead(fromFDDtrack00)==HIGH);
}
void stepOut() {
stepdir=OUT;
digitalWrite(toFDDdirSelect, (stepdir==OUT)?LOW:HIGH);
}
void stepIn() {
stepdir=IN;
digitalWrite(toFDDdirSelect, (stepdir==OUT)?LOW:HIGH);
}
void stepPulse() {
digitalWrite(toFDDstep, HIGH);
delay(STEP_PULSE); // 800us minimum
digitalWrite(toFDDstep, LOW);
delay(STEP_TIME);
if (isTrack00()) {
track=0;
} else {
if (stepdir==IN) { track++; } else { track--; }
}
}
//--------------------------------------------------------------------------------------------
volatile int events;
volatile uint16_t eventdata[1024];
//--------------------------------------------------------------------------------------------
void FDdecode(void) {
uint16_t timer;
digitalWrite(debugpin,HIGH);
FTM0_SC = FTM_SC_CLKS(0) | FTM_SC_PS(2); // STOP CLOCK
timer = FTM0_CNT;
if (events<1024) {
eventdata[events++] = timer; // read count
}
FTM0_CNT = 0; // reset count
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(2); // RESTART CLOCK
digitalWrite(debugpin,LOW);
}
//--------------------------------------------------------------------------------------------
void readTrack() {
// wait for index pulse to go (low from floppy) (high seen by Teensy)
while (digitalRead(fromFDDindexPin)==HIGH); // in case the index pulse is already active
while (digitalRead(fromFDDindexPin)==LOW);
FTM0_MODE = FTM_MODE_FTMEN | FTM_MODE_WPDIS;
FTM0_SC = FTM_SC_CLKS(0) | FTM_SC_PS(2); // disable clock + no prescale
FTM0_CNT = 0;
FTM0_MOD = 0xFFFF;
events=0;
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(2); // system (48MHz) clock + no prescale
attachInterrupt(fromFDDreadPin,FDdecode,RISING);
delay(10); // 10ms - enough to capture some sample data
detachInterrupt(fromFDDreadPin);
FTM0_SC = FTM_SC_CLKS(0) | FTM_SC_PS(2); // disable clock + no prescale
sprintf(sbuf,"No of events = %d\n",events);
Serial.println(sbuf);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool active = true;
byte k = 25;
unsigned char cmd;
void loop() {
if (Serial.available()) {
cmd = Serial.read();
if (cmd=='s') {
stepPulse();
//Serial.println("step...");
sprintf(sbuf, "Track0 = %s track = %d Step dir = %s",isTrack00()?"TRUE":"false",track,stepdir==OUT?"OUT":"IN");
Serial.println(sbuf);
}
if (cmd==' ') {
sprintf(sbuf, "Track0 = %s track = %d Step dir = %s",isTrack00()?"TRUE":"false",track,stepdir==OUT?"OUT":"IN");
Serial.println(sbuf);
}
if (cmd=='i') {
stepIn();
Serial.println("step dir in...");
}
if (cmd=='o') {
stepOut();
Serial.println("step dir out...");
}
if (cmd=='m') {
if (motoron==ON) { motoron=OFF; } else { motoron=ON; }
digitalWrite(toFDDmotorOn, (motoron==ON)?HIGH:LOW);
Serial.println("motor ...");
}
if (cmd=='d') {
if (drivesel==ON) { drivesel=OFF; } else { drivesel=ON; }
digitalWrite(toFDDselect, (drivesel==ON)?HIGH:LOW);
Serial.println("drive ...");
}
if (cmd=='r') {
drivesel=ON; digitalWrite(toFDDselect, (drivesel==ON)?HIGH:LOW);
motoron =ON; digitalWrite(toFDDmotorOn, (motoron ==ON)?HIGH:LOW);
delay(100); // wait for drive to spin up
Serial.println("reading track ...");
readTrack();
int i;
for (i=0;i<1024;i++) {
uint16_t t;
t=eventdata[i];
byte d;
if (t < 30) { d=22; }
else if (t < 50) { d=0; }
else if (t < 75) { d=1; }
else { d=99; }
sprintf(sbuf, "%2d ", d);
Serial.print(sbuf);
}
Serial.println("");
}
}
}
While I was looking at the datastream on the scope I noted an anomoly. The yellow trace is the read data from the floppy (actually inverted by the 74LS05), and the blue trace is a Teensy debug output pin which goes high on entry to the ISR and low on exit. I wanted to check that the ISR completed fast enough, under 2us. It does!.
So there is a short interrupt latency(is that the word?) of about 1us. The ISR takes about 1us. BUT every now and again (the pulse underlined in blue) the ISR response is delayed. This messes up the time recorded between pulses and causes the decoded data stream to be corrupted.
My question is .. what is causing this?
My guess is that there is another interrupt which occurs at that time, but if so, what? The only other thing I can think of is "Serial", but the link is inactive during data read time (or could this be something to do with USB ???).
So I did some searching on this forum. I wanted to make the edge detect interrupt the highest priority. I have the following, which appeared to work, but I am not 100% certain it is correct.
Code:
const byte fromFDDreadPin = 21; // PORT-D IRQ #43
const byte fromFDDreadPinIRQ = 43;
NVIC_SET_PRIORITY(fromFDDreadPinIRQ, 0); // HIGHEST priority
I have developed the code to decode the actual data in the ISR and I am able to read the raw data and I can see sector headers and data, BUT it is corrupted. I see a stream of zeroes become a stream of ones. This could be explained by having a single pulse read longer than 2us. NOTE: a stream of 2us pulses means a stream of all ones of all zeroes (you need the sync byte to determine which), but if there is a rogue 3us pulse then the stream will be inverted when decoded.
Could my ISR be interrupted by something else causing a delay and bad timer reading?
During user ISR are lower priority interrupts disabled?
Any help or pointers would be appreciated.
--badsector