Занятно, кто б мог подумать. В Векторе и тут отличились
Таймеру1 можно период менять на ходу. Увеличивать его перед границей байта, потом обратно.
Занятно, кто б мог подумать. В Векторе и тут отличились
Таймеру1 можно период менять на ходу. Увеличивать его перед границей байта, потом обратно.
Больше игр нет
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Посмотрел логером соотношение импульсов на магнитофонном входе и чтение порта.
При 50мкс задержке между байтами конечно стало лучше. Но при увеличении интервала между байтами до 60мкс, программа стабильно грузится на стандартном заводском загрузчике в Вектор 06Ц.02, при полупериоде таймера... барабанная дробь...в 112мкс...!!!
Правда для вычисления загрузчиком задержек, я отключаю изменение значения таймера на время меандра. Но вроде и без этого отключения работало.
Программа в 21КБ грузится за 59сек, и это стандартный загрузчик...
Да, почти тридцать лет прошло, а секреты у Вектора ещё остались. :-)
Это хорошо, что так можно, иначе это было бы сложной проблемой... Так и сделал, и оно работает.Таймеру1 можно период менять на ходу. Увеличивать его перед границей байта, потом обратно.
Можно, при его наличии и если об этом знать заранее. А так, внимательного рассмотрения и измерения wav-ки оказалось достаточно.
После нескольких экспериментов установил, что для стабильного считывания нужно, чтобы задержка между байтами была в сумме равна около 256 мкс, т.е. при полупериоде 200 мкс в начале байта нужно добавить 56 мкс, 192 мкс -- +64 мкс и т.п.
По крайней мере, это на ардуино и моих конденсаторах, а сейчас у меня собрана не лучшая схема...
Фантастический результат! Мне такой на Векторе не ".02" не удалось достичь даже близко... Минимум у меня -- 176 мкс, и то в девяти из десяти случаях Вектор не успевает распознать начало записи (00h/55h...).программа стабильно грузится на стандартном заводском загрузчике в Вектор 06Ц.02, при полупериоде таймера... барабанная дробь...в 112мкс...!!!
У меня получилось загрузить тот же 40-ка килобайтный Киберноид за три минуты, таймер при этом был выставлен на 184 мкс. Тоже не плохо, я считаю.Программа в 21КБ грузится за 59сек, и это стандартный загрузчик...
- - - Добавлено - - -
Выкладываю улучшенную, по скорости вывода, версию:
ROM-player_3
Требования по библиотекам не изменились -- нужны TimerOne, SdFat и LiquidCrystal.Код:/* SD-картридер подключяется к выводам ардуино: * MOSI - D11 * MISO - D12 * CLK - D13 * CS - D10 Выход - D3 Подключение экрана 1602А (LCD Keypad Shield): * LCD RS pin - D8 * LCD Enable pin - D9 * LCD D4 pin - D4 * LCD D5 pin - D5 * LCD D6 pin - D6 * LCD D7 pin - D7 * LCD R/W pin - GND * LCD 5V pin - 5V Кнопки подключаются на вывод A0 (схема LCD Keypad Shield) */ // include the library code: #include <LiquidCrystal.h> #include <SdFat.h> #include <TimerOne.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(8, 9, 4, 5, 6, 7); SdFat sd; SdFile romFile; #define filenameLength 100 // максимальная длина имени файла char fileName[filenameLength + 1]; // имя текущего файла char sfileName[13]; // короткое имя текущего файла int currentFile = 1; // текущая позиция в директории int maxFile = 0; // всего позиций в лиректории (файлов и поддиректорий) byte isDir = 0; // признак того, что текущая позиция -- это директория byte BLs = 0x01; // начальный блок unsigned int Nbt; // размер файла, байт volatile byte BUFF[256]; // буфер данных volatile unsigned int CRB = 0; // индекс чтения из буфера volatile byte bBit = 15; // индекс читаемого полубита unsigned int CWB = 0; // индекс записи в буфер unsigned int CRB_temp = 0; // временная переменная для CRB // Заголовок byte SB[27] = { 0x4E, 0x4F, 0x44, 0x49, 0x53, 0x43, 0x30, 0x30, // NODISK00 0x31, 0x34, 0x30, 0x32, 0x31, 0x38, // дата: 140218 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // имя программы 0x20, 0x20, 0x20, 0x00, 0x00 }; volatile int Tpp = 200; // Начальная длительность задержки сигнала в микросекундах (один полупериод) volatile int Tb = 64; // Дополнительная задержка сигнала на начало байта в микросекундах const int p = 3; // номер пина, на который будет вывод сигнала const int BT_none = 0; // константы -- коды нажатой кнопки const int BT_right = 1; const int BT_up = 2; const int BT_down = 3; const int BT_left = 4; const int BT_select = 5; void setup() { pinMode(p, OUTPUT); // объявляем пин как выход digitalWrite(p, LOW); pinMode(10, OUTPUT); // CS для SD-картридера lcd.begin(16, 2); // объявляем размер экрана 16 символов и 2 строки Timer1.initialize(Tpp); // инициализировать timer1, и установить период Tpp мкс. Timer1.attachInterrupt(SendHalfBit); // прикрепить SendHalfBit(), как обработчик прерывания по переполнению таймера Timer1.stop(); while (!sd.begin(10,SPI_FULL_SPEED)){ // SD-карта готова? printtext("SD-card failed!",0); tone(p, 200, 100); // нет -- включаем на 200 Гц на 100 мс delay(3000); // ждем 3 с } printtext("SD-card is OK.",0); sd.chdir(); // устанавливаем корневую директорию SD getMaxFile(); // получаем количество файлов в директории seekFile(); // переходим к первому файлу в директории } void loop() { lcd.setCursor(0, 1); // устанавливаем курсор в позицию 0 в строке 1 lcd.print(millis()/1000); // выводим количество секунд с момента влючения ардуины -- это для теста... int button = getPressedButton(); // какая кнопка нажата? switch (button) { case BT_right: // вход в директорию, или запуск файла на воспроизведение if(isDir==1) { //Если это директория, то переход в неё sd.chdir(fileName, true); getMaxFile(); currentFile=1; seekFile(); } else { // если не директория -- пробуем воспроизвести файл if(romFile.cwd()->exists(sfileName)) { printtext("Not ROM-file",1); if (Nbt != 0xFFFF) { // проверяем размер файла for (int i=0;12;i++){ // цикл по имени файла if (sfileName[i]=='.'){ // ищем точку в имени файла if ((sfileName[i+1]|0x20)=='r') { // проверяем следующий символ (расширения) == 'r'|'R' if (((sfileName[i+2]|0x20)=='o')|((sfileName[i+2]|0x20)=='0')) { // == 'o'|'O'|'0' if ((sfileName[i+2]|0x20)!='0') { // проверка на вывод нулевого блока по расширению файла BLs = 0x01; // с первого блока } else { BLs = 0x00; // с нулевого блока } if ((sfileName[i+3]|0x20)=='m') { // == 'm'|'M' printtext("Playing...",1); int RCrom = PlayROM(sfileName, i);// Передаём короткое имя файла и позицию точки в качестве параметров switch (RCrom) // Проверяем код возврата { case 0: printtext("Done.",1); // Всё закончилось успешно. break; case 1: printtext("Stopped",1); // Сообщение об остановке while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } break; default: printtext("ERROR!",1); // Сообщение об ошибке } digitalWrite(p, LOW); seekFile(); } } } break; } } } else { printtext("File is too big",1); } } else { printtext("No File Selected",1); } delay(1000); // ждем 1 с printtext(" ",1); // очищаем строку 1 } break; case BT_left: // возврат в корневую директорию, ремарка ниже: //SDFat has no easy way to move up a directory, so returning to root is the easiest way. //each directory (except the root) must have a file called ROOT (no extension) sd.chdir(true); getMaxFile(); currentFile=1; seekFile(); break; case BT_up: // вверх по файлам в директории currentFile--; if(currentFile<1) { getMaxFile(); currentFile = maxFile; } seekFile(); break; case BT_down: // вниз по файлам в директории currentFile++; if(currentFile>maxFile) { currentFile=1; } seekFile(); break; case BT_select: // выход в настройки... printtext("Period(mks):",0); do { while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } delay(200); button = getPressedButton(); // какая кнопка нажата? switch (button) { case BT_up: if (Tpp<400) Tpp += 8; break; case BT_down: if (Tpp>176) Tpp = Tpp - 8; break; } if (Tpp <= 264) { // Выставляем значение задержки на начало байта Tb Tb = 16; // для полупериода от 248 до 264 if (Tpp <= 240) Tb = 264 - Tpp; // для полупериода меньше или равном 240 } else Tb = 0; // для полупериода больше 264 lcd.setCursor(12,0); lcd.print(Tpp); } while(button!=BT_select); // Цикл пока не будет снова нажата кнопка select seekFile(); break; case BT_none: // ничего не нажато break; } while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка delay(50); } } //================================================================= int getPressedButton() // функция проверки нажатой кнопки { int buttonValue = analogRead(0); if (buttonValue < 100) return BT_right; else if (buttonValue < 200) return BT_up; else if (buttonValue < 400) return BT_down; else if (buttonValue < 600) return BT_left; else if (buttonValue < 800) return BT_select; return BT_none; } void printtext(char* text, int l) { // Вывод текста на экран в строке l с очисткой строки lcd.setCursor(0,l); lcd.print(" "); lcd.setCursor(0,l); lcd.print(text); } void getMaxFile() { // считаем файлы в текущей директории и сохраняем в maxFile romFile.cwd()->rewind(); maxFile=0; while(romFile.openNext(romFile.cwd(),O_READ)) { romFile.close(); maxFile++; } } void seekFile() { // переход на позицию в директории, сохранение имени файла и показ его на экране romFile.cwd()->rewind(); for(int i=1;i<currentFile;i++) { // читаем первые файлы до currentFile romFile.openNext(romFile.cwd(),O_READ); romFile.close(); } romFile.openNext(romFile.cwd(),O_READ); // читаем данные текущего файла romFile.getName(fileName,filenameLength); romFile.getSFN(sfileName); isDir = romFile.isDir(); if (romFile.fileSize()<=49152) { // проверка размера файла Nbt = romFile.fileSize(); // размер файла ОК } else { Nbt = 0xFFFF; // слишком большой для загрузки } romFile.close(); printtext(sfileName,0); // вывод имени текущего файла if (isDir==1) lcd.print('>'); // если это директория, добавляем в конце символ '>' } int PlayROM(char FName[], int pnt) // функция вывода файла { delay(1000); // ждем 1 с if (romFile.open(FName,O_READ)) { // открываем файл. Открылся? byte BLe = Nbt/256; // всего блоков byte BLt; // осталось блоков byte Nst; // номер строки byte St; // выводимый байт byte CSz = 0x00; // контрольная сумма заголовка byte CSs = 0x00; // контрольная сумма строки byte i; byte j; if (Nbt%256 != 0) BLe++; // корректировка количества блоков, если размер файла не кратен 256 for (i=0; i<=7; i++){ // заносим в SB имя файла if (i < pnt) { // имя ещё не закончилось? if ((FName[i]>=0x61) && (FName[i]<=0x7A)) { SB[i+14] = FName[i] - 0x20; // на заглавные буквы } else if (FName[i]!=0x7E) { // не тильда SB[i+14] = FName[i]; } else { SB[i+14] = '_'; // меняем тильду на подчёркивание, иначе это будет русская "Ч" } } else SB[i+14] = 0x20; // пробелы } for (i=1; i<=3; i++){ // заносим в SB расширение файла if ((FName[pnt+i]>=0x61) && (FName[pnt+i]<=0x7A)) { SB[i+21] = FName[pnt+i] - 0x20; // на заглавные буквы } else { SB[i+21] = FName[pnt+i]; } } dir_t d; romFile.dirEntry(&d); // Считываем дату файла uint16_t AAA = FAT_DAY(d.lastWriteDate); // Сохраняем дату файла в заголовке -- день SB[8] = (AAA%100)/10 + '0'; // перевод из целого в символ SB[9] = AAA%10 + '0'; AAA = FAT_MONTH(d.lastWriteDate); // месяц SB[10] = (AAA%100)/10 + '0'; SB[11] = AAA%10 + '0'; AAA = FAT_YEAR(d.lastWriteDate); // последние две цифры года SB[12] = (AAA%100)/10 + '0'; SB[13] = AAA%10 + '0'; // Начинаем наполнять буфер for (i=0; i<=3; i++){ // преамбула (4*(00H*25+55H*25)) for (j=0; j<=24; j++){ BUFF[lowByte(CWB)] = 0x00; CWB++; } for (j=0; j<=24; j++){ BUFF[lowByte(CWB)] = 0x55; CWB++; } } Timer1.setPeriod(Tpp); // Выставляем период таймера Timer1.start(); // Запускаем таймер............ for (BLt=BLe; BLt>=1; BLt--){ // Вывод блоков данных в цикле CSz = BLs; CSz += BLe; CSz += BLt; for (j=0; j<=15; j++) ToBUFF(0x00);// 00h*16 for (j=0; j<=3; j++) ToBUFF(0x55);// 55h*4 ToBUFF(0xE6); // E6h*1 for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4 for (j=0; j<=26; j++){ // заголовок блока CSz += SB[j]; ToBUFF(SB[j]); } ToBUFF(BLs); // начальный блок ToBUFF(BLe); // конечный блок ToBUFF(BLt); // осталось блоков lcd.setCursor(12, 1); // выводим на экран кол-во оставшихся блоков lcd.print(BLt); lcd.print(" "); ToBUFF(CSz); // контр.сумма заголовка for (Nst=0x80; Nst<=0x87; Nst++){ // вывод строк (8 шт.) for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4 ToBUFF(0xE6); // E6h*1 CSs = Nst; ToBUFF(Nst); // номер строки CSs += CSz; ToBUFF(CSz); // контр.сумма заголовка // начинаем вывод строки данных for (j=0; j<=31; j++){ // цикл на 32 байта if (Nbt > 0){ // ещё есть данные? St = romFile.read(); // читаем очередной байт из файла Nbt--; } else { // нет -- дополняем нулями St = 0x00; } ToBUFF(St); // передаём считанный байт CSs += St; if (getPressedButton()!=BT_none) { // кнопка нажата? Timer1.stop(); // Останавливаем таймер CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; romFile.close(); // закрываем файл return 1; // выход из ПП с ошибкой 1. } if (CRB_temp > CWB) { // проверка -- не обогнало ли чтение запись? Timer1.stop(); // Останавливаем таймер CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; romFile.close(); // закрываем файл return 2; // выход из ПП с ошибкой 2. } } ToBUFF(CSs); // контр.сумма строки } } romFile.close(); // закрываем файл for (j=0; j<=31; j++) ToBUFF(0x00);// 00h*32 -- завершение вывода программы (?) } else tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс do{ // Ждём опустошения буфера delay(Tpp/64); // задержка на вывод 1 байта (~Tpp*16/1000) noInterrupts(); // запрет прерываний CRB_temp = CRB; // сохраняем CRB во временную переменную interrupts(); // разрешение прерываний } while (CRB_temp < CWB); Timer1.stop(); // Останавливаем таймер............ CRB = 0; // Сбрасываем индексы. CWB = 0; bBit = 15; return 0; // выход из ПП с кодом 0. } void ToBUFF(byte SBb){ // Подпрограмма записи байта в буфер noInterrupts(); // запрет прерываний CRB_temp = CRB; // сохраняем CRB во временную переменную interrupts(); // разрешение прерываний if (CWB > (CRB_temp + 255)) { // Если позиция записи больше, чем позиция чтения + размер буфера - 1 delay(Tpp); // Задержка (Tpp*1000 мкс = Tpp мс = 125 байт) } BUFF[lowByte(CWB)] = SBb; CWB++; } void SendHalfBit() { // Подпрограмма вывода полубита по циклу таймера byte Pd=PORTD; if (bBit & 1){ // проверка индекса полубитов на чётность if (( (Pd >> p)^( BUFF[lowByte(CRB)] >> (bBit >> 1) ))&1){ // Если состояние порта и выводимый бит разные Pd ^= (1 << p); // инвертируем бит в позиции p } } else{ // чётный -- просто инвертируем порт Pd ^= (1 << p); // инвертируем бит в позиции p(=3) } PORTD = Pd; // вывод в порт p if (bBit > 0) { // правим счётчики полубитов и байтов bBit--; if (bBit == 14) Timer1.setPeriod(Tpp); // Выставляем период таймера (биты) } else{ bBit = 15; CRB++; if (CRB > 200) Timer1.setPeriod(Tpp+Tb); // Выставляем увеличенный период таймера (начало байта) } }[свернуть]
Изменения:
- сделана дополнительная задержка между байтами при передаче данных, что позволило существенно повысить скорость чтения Вектором.
Примечание: для увеличения стабильности определения Вектором начала передачи и расчёта скорости, добавление задержек в начальный заголовок передаваемых данных не делается.
Исходники с библиотеками в одном архиве: ROM-player_3.7z
До такого я не допетрил, но заметил, что длительность полупериода должна быть кратной 16. Т.е. на 112, 128, 156, 172 ... таймингах нормально грузит, а на значениях между ними, даже с разницей 1-2 единицы от указанных величин, либо вообще не подхватывает, либо быстро ошибку ловит...... После нескольких экспериментов установил, что для стабильного считывания нужно, чтобы задержка между байтами была в сумме равна около 256 мкс, т.е. при полупериоде 200 мкс в начале байта нужно добавить 56 мкс, 192 мкс -- +64 мкс и т.п. ...
Наконец получилось с помощью объединённого ROM-плеера/SD-ROM картриджа считать заводской загрузчик, на SD-карту, не прибегая ни к каким припаиваниям, замыканиям, и прочим аппаратным ухищрениям. Просто загрузил программку, которая слила загрузчик на SD-карту.
Сравню с имеющимися загрузчиками, может есть отличия...
От загрузчика kish2 отличается.
Последний раз редактировалось KTSerg; 11.03.2018 в 22:21.
Не, нужно было не заставить ПЗУ включиться, а обмануть загрузчик и перехватить управление ДО выключения ПЗУ (до сброса).
Я тупанул, пошел по прямой дорожке, и потратил почти целый день на войну с загрузчиком, пытаясь перехватить у него управление.
Не мог понять почему читаю сплошные "00", даже схему изучал, думал, что прочитать инфу, из программы за пределами области памяти загрузчика, нельзя (ошибочное предположение, но другое в голову не приходило). Оказалось, что перед передачей управления моей (загруженной) программе, загрузчик успевал щелкнуть рэлюхой (её управление заведено на схему "сброса"), и ПЗУ отключалось.
Мог за пол часа, в эмуляторе сделать скриншот загрузочной сетки Вектора, вырезать из него экранную область, дорисовать квадратики в загрузочной сетке, поправить стек в адресах 0xDExxh, прикрепить полученное к своей программе ... и получить результат.
С кнопкой это ROM-DUMPER
А чисто программный - ROM-REAPER
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)