From 72fa1ea8b4406d71f8fb8ea4f941755fc3526345 Mon Sep 17 00:00:00 2001 From: sanni Date: Thu, 15 Apr 2021 16:26:29 +0200 Subject: [PATCH] V5.7: Add Clockgen option to N64 Eeprom Uncomment #define clockgen_installed in options.h and switch CLK1 switch to ON position. With Adafruit clockgen the eeprom now runs at the correct 2Mhz. --- Cart_Reader/Cart_Reader.ino | 8 +- Cart_Reader/N64.ino | 314 ++++++++++++++++++++++++++++++++++-- Cart_Reader/options.h | 3 + Cart_Reader/snes_clk.cpp | 70 ++++---- 4 files changed, 340 insertions(+), 55 deletions(-) diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index 61274cc..c118cfc 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -1,8 +1,8 @@ /********************************************************************************** Cartridge Reader for Arduino Mega2560 - Date: 31.01.2021 - Version: 5.6 + Date: 15.04.2021 + Version: 5.7 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -43,7 +43,7 @@ **********************************************************************************/ #include -char ver[5] = "5.6"; +char ver[5] = "5.7"; #include "options.h" @@ -1201,10 +1201,12 @@ void wait_btn() { int b = checkButton(); #ifdef enable_N64 +#ifndef clockgen_installed // Send some clock pulses to the Eeprom in case it locked up if ((mode == mode_N64_Cart) && ((saveType == 5) || (saveType == 6))) { pulseClock_N64(1); } +#endif #endif // if the cart readers input button is pressed shortly diff --git a/Cart_Reader/N64.ino b/Cart_Reader/N64.ino index eaaa83d..9fbc712 100644 --- a/Cart_Reader/N64.ino +++ b/Cart_Reader/N64.ino @@ -4,6 +4,7 @@ #include "options.h" #ifdef enable_N64 +#include "snes_clk.h" /****************************************** Defines @@ -233,7 +234,11 @@ void n64CartMenu() { else if ((saveType == 5) || (saveType == 6)) { println_Msg(F("Reading Eep...")); display_Update(); +#ifdef clockgen_installed readEeprom(); +#else + readEeprom_CLK(); +#endif } else { print_Error(F("Savetype Error"), false); @@ -291,8 +296,14 @@ void n64CartMenu() { fileBrowser(F("Select eep file")); display_Clear(); +#ifdef clockgen_installed writeEeprom(); writeErrors = verifyEeprom(); +#else + writeEeprom_CLK(); + writeErrors = verifyEeprom_CLK(); +#endif + if (writeErrors == 0) { println_Msg(F("Eeprom verified OK")); display_Update(); @@ -388,10 +399,28 @@ void setup_N64_Cart() { PORTC &= ~(1 << 0); PORTC |= (1 << 1); +#ifdef clockgen_installed + // Adafruit Clock Generator + // last number is the clock correction factor which is custom for each clock generator + int32_t clock_offset = readClockOffset(); + if (clock_offset > INT32_MIN) { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, clock_offset); + } else { + clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); + } + + // Set Eeprom clock to 2Mhz + clockgen.set_freq(200000000ULL, SI5351_CLK1); + + // Start outputting Eeprom clock + clockgen.output_enable(SI5351_CLK1, 1); // Eeprom clock + +#else // Set Eeprom Clock Pin(PH1) to Output DDRH |= (1 << 1); // Output a high signal PORTH |= (1 << 1); +#endif // Set Eeprom Data Pin(PH4) to Input DDRH &= ~(1 << 4); @@ -401,6 +430,11 @@ void setup_N64_Cart() { // Set sram base address sramBase = 0x08000000; +#ifdef clockgen_installed + // Wait for clock generator + clockgen.update_status(); +#endif + // Wait until all is stable delay(300); @@ -1805,7 +1839,7 @@ void idCart() { } /****************************************** - Eeprom functions + Eeprom functions (without Adafruit clockgen) *****************************************/ // Send a clock pulse of 2us length, 50% duty, 500kHz void pulseClock_N64(unsigned int times) { @@ -1818,7 +1852,7 @@ void pulseClock_N64(unsigned int times) { } // Send one byte of data to eeprom -void sendData(byte data) { +void sendData_CLK(byte data) { for (byte i = 0; i < 8; i++) { // pull data line low N64_LOW; @@ -1842,7 +1876,7 @@ void sendData(byte data) { } // Send stop bit to eeprom -void sendStop() { +void sendStop_CLK() { N64_LOW; pulseClock_N64(2); N64_HIGH; @@ -1850,7 +1884,7 @@ void sendStop() { } // Capture 8 bytes in 64 bits into bit array tempBits -void readData() { +void readData_CLK() { for (byte i = 0; i < 64; i++) { // pulse clock until we get response from eeprom @@ -1871,6 +1905,253 @@ void readData() { } } +// Write Eeprom to cartridge +void writeEeprom_CLK() { + if ((saveType == 5) || (saveType == 6)) { + + // Create filepath + sprintf(filePath, "%s/%s", filePath, fileName); + println_Msg(F("Writing...")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + for (byte i = 0; i < (eepPages / 64); i++) { + myFile.read(sdBuffer, 512); + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Blink led + PORTB ^= (1 << 4); + + // Wait ~50ms between page writes or eeprom will have write errors + pulseClock_N64(26000); + + // Send write command + sendData_CLK(0x05); + // Send page number + sendData_CLK(pageNumber + (i * 64)); + // Send data to write + for (byte j = 0; j < 8; j++) { + sendData_CLK(sdBuffer[(pageNumber * 8) + j]); + } + sendStop_CLK(); + } + interrupts(); + } + + // Close the file: + myFile.close(); + println_Msg(F("Done")); + display_Update(); + delay(600); + } + else { + print_Error(F("SD Error"), true); + } + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Dump Eeprom to SD +void readEeprom_CLK() { + if ((saveType == 5) || (saveType == 6)) { + + // Wait 50ms or eeprom might lock up + pulseClock_N64(26000); + + // Get name, add extension and convert to char array for sd lib + strcpy(fileName, romName); + strcat(fileName, ".eep"); + + // create a new folder for the save file + EEPROM_readAnything(0, foldern); + sprintf(folder, "N64/SAVE/%s/%d", romName, foldern); + sd.mkdir(folder, true); + sd.chdir(folder); + + // write new folder number back to eeprom + foldern = foldern + 1; + EEPROM_writeAnything(0, foldern); + + // Open file on sd card + if (!myFile.open(fileName, O_RDWR | O_CREAT)) { + print_Error(F("Can't create file on SD"), true); + } + + for (byte i = 0; i < (eepPages / 64); i++) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Blink led + PORTB ^= (1 << 4); + + // Send read command + sendData_CLK(0x04); + // Send Page number + sendData_CLK(pageNumber + (i * 64)); + // Send stop bit + sendStop_CLK(); + + // read data + readData_CLK(); + sendStop_CLK(); + + // OR 8 bits into one byte for a total of 8 bytes + for (byte j = 0; j < 64; j += 8) { + sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; + } + // Wait 50ms between pages or eeprom might lock up + pulseClock_N64(26000); + } + interrupts(); + + // Write 64 pages at once to the SD card + myFile.write(sdBuffer, 512); + } + // Close the file: + myFile.close(); + //clear the screen + display_Clear(); + print_Msg(F("Saved to ")); + print_Msg(folder); + println_Msg(F("/")); + display_Update(); + } + else { + print_Error(F("Savetype Error"), true); + } +} + +// Check if a write succeeded, returns 0 if all is ok and number of errors if not +unsigned long verifyEeprom_CLK() { + if ((saveType == 5) || (saveType == 6)) { + writeErrors = 0; + + // Wait 50ms or eeprom might lock up + pulseClock_N64(26000); + + display_Clear(); + print_Msg(F("Verifying against ")); + println_Msg(filePath); + display_Update(); + + // Open file on sd card + if (myFile.open(filePath, O_READ)) { + + for (byte i = 0; i < (eepPages / 64); i++) { + // Disable interrupts for more uniform clock pulses + noInterrupts(); + + for (byte pageNumber = 0; pageNumber < 64; pageNumber++) { + // Blink led + PORTB ^= (1 << 4); + + // Send read command + sendData_CLK(0x04); + // Send Page number + sendData_CLK(pageNumber + (i * 64)); + // Send stop bit + sendStop_CLK(); + + // read data + readData_CLK(); + sendStop_CLK(); + + // OR 8 bits into one byte for a total of 8 bytes + for (byte j = 0; j < 64; j += 8) { + sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; + } + // Wait 50ms between pages or eeprom might lock up + pulseClock_N64(26000); + } + interrupts(); + + // Check sdBuffer content against file on sd card + for (int c = 0; c < 512; c++) { + if (myFile.read() != sdBuffer[c]) { + writeErrors++; + } + } + } + // Close the file: + myFile.close(); + } + else { + // SD Error + writeErrors = 999999; + print_Error(F("SD Error"), true); + } + // Return 0 if verified ok, or number of errors + return writeErrors; + } + else { + print_Error(F("Savetype Error"), true); + } +} + +/****************************************** + Eeprom functions (with Adafruit clockgen) +*****************************************/ +// Send one byte of data to eeprom +void sendData(byte data) { + for (byte i = 0; i < 8; i++) { + // pull data line low + N64_LOW; + + // if current bit is 1, pull high after ~1us + if (data >> 7) { + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + N64_HIGH; + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + } + // if current bit is 0 pull high after ~3us + else { + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + N64_HIGH; + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + } + + // rotate to the next bit + data <<= 1; + } +} + +// Send stop bit to eeprom +void sendStop() { + N64_LOW; + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + N64_HIGH; + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); +} + +// Capture 8 bytes in 64 bits into bit array tempBits +void readData() { + for (byte i = 0; i < 64; i++) { + + // wait until we get response from eeprom + while (N64_QUERY) { + } + + // Skip over the 1us low part of a high bit, Arduino running at 16Mhz -> one nop = 62.5ns + __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); + + // Read bit + tempBits[i] = N64_QUERY; + + // wait for line to go high again + while (!N64_QUERY) { + __asm__("nop\n\t"); + } + } +} + // Write Eeprom to cartridge void writeEeprom() { if ((saveType == 5) || (saveType == 6)) { @@ -1893,8 +2174,10 @@ void writeEeprom() { // Blink led PORTB ^= (1 << 4); - // Wait ~50ms between page writes or eeprom will have write errors - pulseClock_N64(26000); + // Wait ~50ms between page writes or eeprom will have write errors, Arduino running at 16Mhz -> one nop = 62.5ns + for (long i = 0; i < 115000; i++) { + __asm__("nop\n\t"); + } // Send write command sendData(0x05); @@ -1927,10 +2210,6 @@ void writeEeprom() { // Dump Eeprom to SD void readEeprom() { if ((saveType == 5) || (saveType == 6)) { - - // Wait 50ms or eeprom might lock up - pulseClock_N64(26000); - // Get name, add extension and convert to char array for sd lib strcpy(fileName, romName); strcat(fileName, ".eep"); @@ -1973,8 +2252,10 @@ void readEeprom() { for (byte j = 0; j < 64; j += 8) { sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; } - // Wait 50ms between pages or eeprom might lock up - pulseClock_N64(26000); + // Wait ~600us between pages + for (int i = 0; i < 2000; i++) { + __asm__("nop\n\t"); + } } interrupts(); @@ -2000,9 +2281,6 @@ unsigned long verifyEeprom() { if ((saveType == 5) || (saveType == 6)) { writeErrors = 0; - // Wait 50ms or eeprom might lock up - pulseClock_N64(26000); - display_Clear(); print_Msg(F("Verifying against ")); println_Msg(filePath); @@ -2034,8 +2312,10 @@ unsigned long verifyEeprom() { for (byte j = 0; j < 64; j += 8) { sdBuffer[(pageNumber * 8) + (j / 8)] = tempBits[0 + j] << 7 | tempBits[1 + j] << 6 | tempBits[2 + j] << 5 | tempBits[3 + j] << 4 | tempBits[4 + j] << 3 | tempBits[5 + j] << 2 | tempBits[6 + j] << 1 | tempBits[7 + j]; } - // Wait 50ms between pages or eeprom might lock up - pulseClock_N64(26000); + // Wait ~600us between pages + for (int i = 0; i < 2000; i++) { + __asm__("nop\n\t"); + } } interrupts(); diff --git a/Cart_Reader/options.h b/Cart_Reader/options.h index 31d8ca1..3d25f43 100644 --- a/Cart_Reader/options.h +++ b/Cart_Reader/options.h @@ -15,6 +15,9 @@ // Enable the second button #define enable_Button2 +// Read N64 Eeprom with Adadruit clockgen, CLK1 switch needs to be switch to ON +//#define clockgen_installed + // define enable_XXX to enable #define enable_FLASH #define enable_GBX diff --git a/Cart_Reader/snes_clk.cpp b/Cart_Reader/snes_clk.cpp index 40b009f..2d00f95 100644 --- a/Cart_Reader/snes_clk.cpp +++ b/Cart_Reader/snes_clk.cpp @@ -3,44 +3,44 @@ #include "atoi32.h" int32_t readClockOffset() { - File clock_file; - unsigned char* clock_buf; - int16_t i; - int32_t clock_offset; + File clock_file; + unsigned char* clock_buf; + int16_t i; + int32_t clock_offset; - if(!clock_file.open("/snes_clk.txt", FILE_READ)) { - return INT32_MIN; - } + if (!clock_file.open("/snes_clk.txt", FILE_READ)) { + return INT32_MIN; + } - clock_buf = malloc(12 * sizeof(char)); - i = clock_file.read(clock_buf, 11); - clock_file.close(); - if(i == -1) { - free(clock_buf); - return INT32_MIN; - } else if((i == 11) && (clock_buf[0] != '-')) { - free(clock_buf); - return INT32_MIN; - } else { - clock_buf[i] = 0; - } + clock_buf = malloc(12 * sizeof(char)); + i = clock_file.read(clock_buf, 11); + clock_file.close(); + if (i == -1) { + free(clock_buf); + return INT32_MIN; + } else if ((i == 11) && (clock_buf[0] != '-')) { + free(clock_buf); + return INT32_MIN; + } else { + clock_buf[i] = 0; + } - for(i = 0; i < 12; i++) { - if(clock_buf[i] != '-' && clock_buf[i] < '0' && clock_buf[i] > '9') { - if(i == 0) { - free(clock_buf); - return INT32_MIN; - } else if((i == 1) && (clock_buf[0] == '-')) { - free(clock_buf); - return INT32_MIN; - } else { - clock_buf[i] = 0; - } - } - } + for (i = 0; i < 12; i++) { + if (clock_buf[i] != '-' && clock_buf[i] < '0' && clock_buf[i] > '9') { + if (i == 0) { + free(clock_buf); + return INT32_MIN; + } else if ((i == 1) && (clock_buf[0] == '-')) { + free(clock_buf); + return INT32_MIN; + } else { + clock_buf[i] = 0; + } + } + } - clock_offset = atoi32_signed(clock_buf); - free(clock_buf); + clock_offset = atoi32_signed(clock_buf); + free(clock_buf); - return clock_offset; + return clock_offset; }