/ Adafruit_Floppy.cpp
Adafruit_Floppy.cpp
1 #include "Adafruit_Floppy.h" 2 3 #define DEBUG_FLOPPY (0) 4 5 // We need to read and write some pins at optimized speeds - use raw registers 6 // or native SDK API! 7 #ifdef BUSIO_USE_FAST_PINIO 8 #define read_index() (*indexPort & indexMask) 9 #define read_data() (*dataPort & dataMask) 10 #define set_debug_led() (*ledPort |= ledMask) 11 #define clr_debug_led() (*ledPort &= ~ledMask) 12 #elif defined(ARDUINO_ARCH_RP2040) 13 #define read_index() gpio_get(_indexpin) 14 #define read_data() gpio_get(_rddatapin) 15 #define set_debug_led() gpio_put(led_pin, 1) 16 #define clr_debug_led() gpio_put(led_pin, 0) 17 #endif 18 19 #if !DEBUG_FLOPPY 20 #undef set_debug_led 21 #undef clr_debug_led 22 #define set_debug_led() ((void)0) 23 #define clr_debug_led() ((void)0) 24 #endif 25 26 /**************************************************************************/ 27 /*! 28 @brief Create a hardware interface to a floppy drive 29 @param densitypin A pin connected to the floppy Density Select input 30 @param indexpin A pin connected to the floppy Index Sensor output 31 @param selectpin A pin connected to the floppy Drive Select input 32 @param motorpin A pin connected to the floppy Motor Enable input 33 @param directionpin A pin connected to the floppy Stepper Direction input 34 @param steppin A pin connected to the floppy Stepper input 35 @param wrdatapin A pin connected to the floppy Write Data input 36 @param wrgatepin A pin connected to the floppy Write Gate input 37 @param track0pin A pin connected to the floppy Track 00 Sensor output 38 @param protectpin A pin connected to the floppy Write Protect Sensor output 39 @param rddatapin A pin connected to the floppy Read Data output 40 @param sidepin A pin connected to the floppy Side Select input 41 @param readypin A pin connected to the floppy Ready/Disk Change output 42 43 */ 44 /**************************************************************************/ 45 46 Adafruit_Floppy::Adafruit_Floppy(int8_t densitypin, int8_t indexpin, 47 int8_t selectpin, int8_t motorpin, 48 int8_t directionpin, int8_t steppin, 49 int8_t wrdatapin, int8_t wrgatepin, 50 int8_t track0pin, int8_t protectpin, 51 int8_t rddatapin, int8_t sidepin, 52 int8_t readypin) { 53 _densitypin = densitypin; 54 _indexpin = indexpin; 55 _selectpin = selectpin; 56 _motorpin = motorpin; 57 _directionpin = directionpin; 58 _steppin = steppin; 59 _wrdatapin = wrdatapin; 60 _wrgatepin = wrgatepin; 61 _track0pin = track0pin; 62 _protectpin = protectpin; 63 _rddatapin = rddatapin; 64 _sidepin = sidepin; 65 _readypin = readypin; 66 } 67 68 /**************************************************************************/ 69 /*! 70 @brief Initializes the GPIO pins but do not start the motor or anything 71 */ 72 /**************************************************************************/ 73 void Adafruit_Floppy::begin(void) { soft_reset(); } 74 75 /**************************************************************************/ 76 /*! 77 @brief Set back the object and pins to initial state 78 */ 79 /**************************************************************************/ 80 void Adafruit_Floppy::soft_reset(void) { 81 // deselect drive 82 pinMode(_selectpin, OUTPUT); 83 digitalWrite(_selectpin, HIGH); 84 85 // motor enable pin, drive low to turn on motor 86 pinMode(_motorpin, OUTPUT); 87 digitalWrite(_motorpin, HIGH); 88 89 // set motor direction (low is in, high is out) 90 pinMode(_directionpin, OUTPUT); 91 digitalWrite(_directionpin, LOW); // move inwards to start 92 93 // step track pin, pulse low for 3us min, 3ms max per pulse 94 pinMode(_steppin, OUTPUT); 95 digitalWrite(_steppin, HIGH); 96 97 // side selector 98 pinMode(_sidepin, OUTPUT); 99 digitalWrite(_sidepin, HIGH); // side 0 to start 100 101 pinMode(_indexpin, INPUT_PULLUP); 102 pinMode(_track0pin, INPUT_PULLUP); 103 pinMode(_protectpin, INPUT_PULLUP); 104 pinMode(_readypin, INPUT_PULLUP); 105 pinMode(_rddatapin, INPUT_PULLUP); 106 107 #ifdef BUSIO_USE_FAST_PINIO 108 indexPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(_indexpin)); 109 indexMask = digitalPinToBitMask(_indexpin); 110 #endif 111 112 select_delay_us = 10; 113 step_delay_us = 10000; 114 settle_delay_ms = 15; 115 motor_delay_ms = 1000; 116 watchdog_delay_ms = 1000; 117 bus_type = BUSTYPE_IBMPC; 118 119 if (led_pin >= 0) { 120 pinMode(led_pin, OUTPUT); 121 digitalWrite(led_pin, LOW); 122 } 123 } 124 125 /**************************************************************************/ 126 /*! 127 @brief Whether to select this drive 128 @param selected True to select/enable 129 */ 130 /**************************************************************************/ 131 void Adafruit_Floppy::select(bool selected) { 132 digitalWrite(_selectpin, !selected); // Selected logic level 0! 133 // Select drive 134 delayMicroseconds(select_delay_us); 135 } 136 137 /**************************************************************************/ 138 /*! 139 @brief Which head/side to read from 140 @param head Head 0 or 1 141 */ 142 /**************************************************************************/ 143 void Adafruit_Floppy::side(uint8_t head) { 144 digitalWrite(_sidepin, !head); // Head 0 is logic level 1, head 1 is logic 0! 145 } 146 147 /**************************************************************************/ 148 /*! 149 @brief Turn on or off the floppy motor, if on we wait till we get an index 150 pulse! 151 @param motor_on True to turn on motor, False to turn it off 152 @returns False if turning motor on and no index pulse found, true otherwise 153 */ 154 /**************************************************************************/ 155 bool Adafruit_Floppy::spin_motor(bool motor_on) { 156 digitalWrite(_motorpin, !motor_on); // Motor on is logic level 0! 157 if (!motor_on) 158 return true; // we're done, easy! 159 160 delay(motor_delay_ms); // Main motor turn on 161 162 uint32_t index_stamp = millis(); 163 bool timedout = false; 164 165 if (debug_serial) 166 debug_serial->print("Waiting for index pulse..."); 167 168 while (digitalRead(_indexpin)) { 169 if ((millis() - index_stamp) > 10000) { 170 timedout = true; // its been 10 seconds? 171 break; 172 } 173 } 174 175 if (timedout) { 176 if (debug_serial) 177 debug_serial->println("Didn't find an index pulse!"); 178 return false; 179 } 180 if (debug_serial) 181 debug_serial->println("Found!"); 182 return true; 183 } 184 185 /**************************************************************************/ 186 /*! 187 @brief Seek to the desired track, requires the motor to be spun up! 188 @param track_num The track to step to 189 @return True If we were able to get to the track location 190 */ 191 /**************************************************************************/ 192 bool Adafruit_Floppy::goto_track(uint8_t track_num) { 193 // track 0 is a very special case because its the only one we actually know we 194 // got to. if we dont know where we are, or we're going to track zero, step 195 // back till we get there. 196 if ((_track < 0) || track_num == 0) { 197 if (debug_serial) 198 debug_serial->println("Going to track 0"); 199 200 // step back a lil more than expected just in case we really seeked out 201 uint8_t max_steps = 250; 202 while (max_steps--) { 203 if (!digitalRead(_track0pin)) { 204 _track = 0; 205 break; 206 } 207 step(STEP_OUT, 1); 208 } 209 210 if (digitalRead(_track0pin)) { 211 // we never got a track 0 indicator :( 212 if (debug_serial) 213 debug_serial->println("Could not find track 0"); 214 return false; // we 'timed' out, were not able to locate track 0 215 } 216 } 217 delay(settle_delay_ms); 218 219 // ok its a non-track 0 step, first, we cant go past 79 ok? 220 track_num = min(track_num, MAX_TRACKS - 1); 221 if (debug_serial) 222 debug_serial->printf("Going to track %d\n\r", track_num); 223 224 if (_track == track_num) { // we are there already 225 return true; 226 } 227 228 int8_t steps = (int8_t)track_num - (int8_t)_track; 229 if (steps > 0) { 230 if (debug_serial) 231 debug_serial->printf("Step in %d times\n\r", steps); 232 step(STEP_IN, steps); 233 } else { 234 steps = abs(steps); 235 if (debug_serial) 236 debug_serial->printf("Step out %d times\n\r", steps); 237 step(STEP_OUT, steps); 238 } 239 delay(settle_delay_ms); 240 _track = track_num; 241 242 return true; 243 } 244 245 /**************************************************************************/ 246 /*! 247 @brief Step the track motor 248 @param dir STEP_OUT or STEP_IN depending on desired direction 249 @param times How many steps to take 250 */ 251 /**************************************************************************/ 252 void Adafruit_Floppy::step(bool dir, uint8_t times) { 253 digitalWrite(_directionpin, dir); 254 delayMicroseconds(10); // 1 microsecond, but we're generous 255 256 while (times--) { 257 digitalWrite(_steppin, HIGH); 258 delayMicroseconds(step_delay_us); 259 digitalWrite(_steppin, LOW); 260 delayMicroseconds(step_delay_us); 261 digitalWrite(_steppin, HIGH); // end high 262 yield(); 263 } 264 } 265 266 /**************************************************************************/ 267 /*! 268 @brief The current track location, based on internal caching 269 @return The cached track location 270 */ 271 /**************************************************************************/ 272 int8_t Adafruit_Floppy::track(void) { return _track; } 273 274 /**************************************************************************/ 275 /*! 276 @brief Capture one track's worth of flux transitions, between two falling 277 index pulses 278 @param pulses A pointer to an array of memory we can use to store into 279 @param max_pulses The size of the allocated pulses array 280 @return Number of pulses we actually captured 281 */ 282 /**************************************************************************/ 283 uint32_t Adafruit_Floppy::capture_track(uint8_t *pulses, uint32_t max_pulses) { 284 unsigned pulse_count; 285 uint8_t *pulses_ptr = pulses; 286 uint8_t *pulses_end = pulses + max_pulses; 287 288 #ifdef BUSIO_USE_FAST_PINIO 289 BusIO_PortReg *dataPort, *ledPort; 290 BusIO_PortMask dataMask, ledMask; 291 dataPort = (BusIO_PortReg *)portInputRegister(digitalPinToPort(_rddatapin)); 292 dataMask = digitalPinToBitMask(_rddatapin); 293 ledPort = (BusIO_PortReg *)portOutputRegister(digitalPinToPort(led_pin)); 294 ledMask = digitalPinToBitMask(led_pin); 295 #endif 296 297 memset(pulses, 0, max_pulses); // zero zem out 298 299 noInterrupts(); 300 wait_for_index_pulse_low(); 301 302 // wait for one clean flux pulse so we dont get cut off. 303 // don't worry about losing this pulse, we'll get it on our 304 // overlap run! 305 306 // ok we have a h-to-l transition so... 307 bool last_index_state = read_index(); 308 uint8_t index_transitions = 0; 309 310 // if data line is low, wait till it rises 311 if (!read_data()) { 312 while (!read_data()) 313 ; 314 } 315 // if data line is high, wait till it drops down 316 if (read_data()) { 317 while (read_data()) 318 ; 319 } 320 321 while (true) { 322 bool index_state = read_index(); 323 // ahh a L to H transition 324 if (!last_index_state && index_state) { 325 index_transitions++; 326 if (index_transitions == 327 2) // and its the second one, so we're done with this track! 328 break; 329 } 330 last_index_state = index_state; 331 332 // muahaha, now we can read track data! 333 // Don't start counting at zero because we lost some time checking for 334 // index. Empirically, at 180MHz and -O3 on M4, this gives the most 'even' 335 // timings, moving the bins from 41/63/83 to 44/66/89 336 pulse_count = 3; 337 338 // while pulse is in the low pulse, count up 339 while (!read_data()) { 340 pulse_count++; 341 } 342 set_debug_led(); 343 344 // while pulse is high, keep counting up 345 while (read_data()) 346 pulse_count++; 347 clr_debug_led(); 348 349 pulses_ptr[0] = min(255, pulse_count); 350 pulses_ptr++; 351 if (pulses_ptr == pulses_end) { 352 break; 353 } 354 } 355 // whew done 356 interrupts(); 357 return pulses_ptr - pulses; 358 } 359 360 /**************************************************************************/ 361 /*! 362 @brief Busy wait until the index line goes from high to low 363 */ 364 /**************************************************************************/ 365 void Adafruit_Floppy::wait_for_index_pulse_low(void) { 366 // initial state 367 bool index_state = read_index(); 368 bool last_index_state = index_state; 369 370 // wait until last index state is H and current state is L 371 while (true) { 372 index_state = read_index(); 373 if (last_index_state && !index_state) { 374 return; 375 } 376 last_index_state = index_state; 377 } 378 } 379 380 /**************************************************************************/ 381 /*! 382 @brief Pretty print the counts in a list of flux transitions 383 @param pulses A pointer to an array of memory containing pulse counts 384 @param num_pulses The size of the pulses in the array 385 */ 386 /**************************************************************************/ 387 void Adafruit_Floppy::print_pulses(uint8_t *pulses, uint32_t num_pulses) { 388 if (!debug_serial) 389 return; 390 391 for (uint32_t i = 0; i < num_pulses; i++) { 392 debug_serial->print(pulses[i]); 393 debug_serial->print(", "); 394 } 395 debug_serial->println(); 396 } 397 /**************************************************************************/ 398 /*! 399 @brief Pretty print a simple histogram of flux transitions 400 @param pulses A pointer to an array of memory containing pulse counts 401 @param num_pulses The size of the pulses in the array 402 @param max_bins The maximum number of histogram bins to use (default 64) 403 */ 404 /**************************************************************************/ 405 void Adafruit_Floppy::print_pulse_bins(uint8_t *pulses, uint32_t num_pulses, 406 uint8_t max_bins) { 407 if (!debug_serial) 408 return; 409 410 // lets bin em! 411 uint32_t bins[max_bins][2]; 412 memset(bins, 0, max_bins * 2 * sizeof(uint32_t)); 413 // we'll add each pulse to a bin so we can figure out the 3 buckets 414 for (uint32_t i = 0; i < num_pulses; i++) { 415 uint8_t p = pulses[i]; 416 // find a bin for this pulse 417 uint8_t bin = 0; 418 for (bin = 0; bin < max_bins; bin++) { 419 // bin already exists? increment the count! 420 if (bins[bin][0] == p) { 421 bins[bin][1]++; 422 break; 423 } 424 if (bins[bin][0] == 0) { 425 // ok we never found the bin, so lets make it this one! 426 bins[bin][0] = p; 427 bins[bin][1] = 1; 428 break; 429 } 430 } 431 if (bin == max_bins) 432 debug_serial->println("oof we ran out of bins but we'll keep going"); 433 } 434 // this is a very lazy way to print the bins sorted 435 for (uint8_t pulse_w = 1; pulse_w < 255; pulse_w++) { 436 for (uint8_t b = 0; b < max_bins; b++) { 437 if (bins[b][0] == pulse_w) { 438 debug_serial->print(bins[b][0]); 439 debug_serial->print(": "); 440 debug_serial->println(bins[b][1]); 441 } 442 } 443 } 444 }