Improver, ты генерируешь сигнал таймером-компарером?
Improver, ты генерируешь сигнал таймером-компарером?
Больше игр нет
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Test_17~.wav спокойно читается в эмуляторах штатными загрузчиками, длительности выровнялись.
Итак, продолжаем развивать проект...
Подключил SD-картридер, схема подключениея такая, как в сообщении #19 zebest, только пока без кнопок и экрана. Да, и вывод сигнала остался на D3.
Скетч
В корень SD-карты положить файл "cybernoi.rom". Если карта не обнаружена, то подаётся сигнал 200Гц/100мс, если не обнаружен файл, то сигнал 200Гц/300мс.
Код:/* * SD-картридер подключяется к выводам ардуино: ** MOSI - D11 ** MISO - D12 ** CLK - D13 ** CS - D10 * * Выход - D3 */ #include <SD.h> File romFile; // Заголовок byte SB[27] = { 0x4E, 0x4F, 0x44, 0x49, 0x53, 0x43, 0x30, 0x30, // NODISK00 0x31, 0x39, 0x30, 0x31, 0x31, 0x38, // дата: 190118 0x74, 0x65, 0x73, 0x74, 0x74, 0x70, 0x20, 0x20, // testtp..... 0x20, 0x20, 0x20, 0x00, 0x00 }; int p = 3; // номер пина, на который будет вывод сигнала void setup() //процедура setup { pinMode(p, OUTPUT); //объявляем пин как выход pinMode(10, OUTPUT); while (!SD.begin(10)){ // SD-карта готова? tone(p, 200, 100); // нет -- включаем на 200 Гц на 100 мс delay(3000); // ждем 3 с } } void loop() //процедура loop { tone(p, 500, 100); //включаем на 500 Гц на 100 мс delay(1000); //ждем 1 с romFile = SD.open("cybernoi.rom"); // открываем файл if (romFile) { // открылся? byte BLs = 0x01; // начальный блок unsigned int Nbt = romFile.size(); // всего байтов byte BLe = Nbt/256; // всего блоков byte BLt; // осталось блоков byte Nst; // номер строки byte St; // выводимый байт byte CSz = 0x00; // контрольная сумма заголовка byte CSs = 0x00; // контрольная сумма строки byte i; byte j; if (Nbt%256 != 0){ // корректировка количества блоков, если размер файла не кратен 256 BLe++; } // Начинаем вывод for (i=0; i<=3; i++){ // преамбула (4*(00H*25+55H*25)) for (j=0; j<=24; j++){ SendByte(0x00); } for (j=0; j<=24; j++){ SendByte(0x55); } } for (BLt=BLe; BLt>=1; BLt--){ // Вывод блоков данных CSz = BLs; CSz += BLe; CSz += BLt; for (j=0; j<=15; j++){ // 00h*16 SendByte(0x00); } for (j=0; j<=3; j++){ // 55h*4 SendByte(0x55); } SendByte(0xE6); // E6h*1 for (j=0; j<=3; j++){ // 00h*4 SendByte(0x00); } for (j=0; j<=26; j++){ // заголовок блока CSz += SB[j]; SendByte(SB[j]); } SendByte(BLs); // начальный блок SendByte(BLe); // конечный блок SendByte(BLt); // осталось блоков SendByte(CSz); // контр.сумма заголовка for (Nst=0x80; Nst<=0x87; Nst++){ // вывод строк (8 шт.) for (j=0; j<=3; j++){ // 00h*4 SendByte(0x00); } SendByte(0xE6); // E6h*1 CSs = Nst; SendByte(Nst); // номер строки CSs += CSz; SendByte(CSz); // контр.сумма заголовка for (j=0; j<=31; j++){ // собственно, строка данных if (Nbt > 0){ // ещё есть данные? St = romFile.read(); // читаем очередной байт из файла Nbt--; } else { // нет -- дополняем нулями St = 0x00; } CSs += St; SendByte(St); } SendByte(CSs); // контр.сумма строки } } for (j=0; j<=15; j++){ // 00h*16 -- завершение вывода программы (?) SendByte(0x00); } // close the file: romFile.close(); } else { tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс } delay(15000); // ждем 15 с } void SendByte(byte SBt){ // Подпрограмма вывода байта byte Pd=PORTD; byte i=8; do{ // Выводим биты начиная со старшего i--; if ((bitRead(Pd, p))^(bitRead(SBt, i))){ // Если состояние порта и выводимый бит разные Pd ^= (1 << p); // инвертируем бит в позиции p=3 PORTD = Pd; // вывод в порт p } else{ delayMicroseconds(16); // Задержка для выравнивания длительности сигнала } delayMicroseconds(256); // Задержка первого полупериода сигнала Pd ^= (1 << p); // инвертируем бит в позиции p PORTD = Pd; // вывод в порт p delayMicroseconds(256); // Задержка второго полупериода сигнала } while (i>0); }[свернуть]
Для проверки взял один из самых больших ROM-файлов, игру Cybernoid, на выгрузку ушло примерно 4 минуты. Всё, вроде, отработало здорово, но при распознавании в wav2rom вылезло 45 ошибок. :-( Причём все ошибки в первом байте блока (не каждого -- в 45 из 159 блоков):
Просмотр сигнала в той же программе выявил ошибку:
Где-то возникают тормоза -- или прерывание срабатывает, или чтение с карты тормозит... Причём эти ошибки есть даже если выгружаемая программа короткая, например тот же тест техпрогона. В общем, надо ещё подумать...
Вот скетч в архиве: Test_TP_fromSD.ino.zip, WAV-файл не выкладываю -- нет смысла...
- - - Добавлено - - -
Да, оставим на потом. А с другой стороны, с выводом без всяких таймеров легко справлялся проц КР580ВМ80А -- и ардуина тоже должна справится. :-)
Предполагаю, что эта процедура берет очередной байт из буфера, а когда буфер заканчивается, читает целиком следующий сектор, или несколько, если надо свериться с FAT, что и вызывает задержку.Код:St = romFile.read(); // читаем очередной байт из файла
Не знаком с драйвером SD от ардуины. Думаю, что вряд ли там можно висеть колбеком на чтение байта. Формат Вектора предполагает ресинхронизацию между блоками. Значит, если рассчитать размер буфера так, чтобы буфер всегда подсасывался на границе блока, можно эту проблему замести под ковер.
Больше игр нет
svofski, я сейчас попробовал сделать предварительную считку байта до того, как блок начал выводиться, при этом задержка вылезла в заголовок. Если там допустима некоторая рассинхронизация, то это решение... Как я понимаю, задержка может происходить до начала заголовка блока (00h*16, 55h*4...)?
Это мое предположение, да. По идее после последнего байта любого блока можно сделать безболезненную паузу. Не знаю, как на самом деле на это реагирует загрузчик.
Параллельно я бы все-таки поинтересовался недрами драйвера, чтобы научиться читать поток без задержек. Это пригодится для форматов без блоков.
Больше игр нет
На сколько я помню, загрузчик начинает принимать новый блок только когда сможет принять байт Е6h, либо его инверсию 19h. Так что паузы между блоками действительно должны проходить безболезненно.
Но покопать дровишки лишним не будет...
У меня сейчас место расчищено для других дел, так что реал ждет своей очереди на полке. В эмулятор грузится хорошо.
- - - Добавлено - - -
В Тапире видно пропуски:
Для стандартного загрузчика безболезненно можно сделать полупериод 5. Это будет не огромное ускорение, то все же.
Больше игр нет
Использованием библиотеки SdFat из проекта TZXDuino и переделкой подпрограммы вывода байта SendByte удалось сократить провалы в передаче сигнала раза в полтора, но всё равно она сейчас составляет примерно шесть полупериодов сигнала. Пробовал предварительно вычитывать 256 байт (1 блок) в буфер, но толку от этого нет -- всё равно задержки...
Вот что имеем в последней версии программы:
Скетч
Код:/* * SD-картридер подключяется к выводам ардуино: ** MOSI - D11 ** MISO - D12 ** CLK - D13 ** CS - D10 * * Выход - D3 */ #include <SdFat.h> SdFat sd; SdFile romFile; // Заголовок byte SB[27] = { 0x4E, 0x4F, 0x44, 0x49, 0x53, 0x43, 0x30, 0x30, // NODISK00 0x31, 0x34, 0x30, 0x32, 0x31, 0x38, // дата: 140218 0x74, 0x65, 0x73, 0x74, 0x74, 0x70, 0x20, 0x20, // testtp..... 0x20, 0x20, 0x20, 0x00, 0x00 }; int p = 3; // номер пина, на который будет вывод сигнала unsigned long Tzd = 0; // переменная для расчёта задержки const int Tpp = 256; // Длительность задержки сигнала в микросекундах (один полупериод) void setup() //процедура setup { pinMode(p, OUTPUT); //объявляем пин как выход pinMode(10, OUTPUT); while (!sd.begin(10,SPI_FULL_SPEED)){ // SD-карта готова? tone(p, 200, 100); // нет -- включаем на 200 Гц на 100 мс delay(3000); // ждем 3 с } sd.chdir(); // устанавливаем корневую директорию SD } void loop() //процедура loop { tone(p, 500, 100); //включаем на 500 Гц на 100 мс delay(1000); //ждем 1 с if (romFile.open("cybernoi.rom",O_READ)) { // открываем файл. Открылся? byte BLs = 0x01; // начальный блок unsigned int Nbt = romFile.fileSize(); // всего байт byte BLe = Nbt/256; // всего блоков byte BLt; // осталось блоков byte Nst; // номер строки byte St; // выводимый байт byte CSz = 0x00; // контрольная сумма заголовка byte CSs = 0x00; // контрольная сумма строки byte i; byte j; if (Nbt%256 != 0){ // корректировка количества блоков, если размер файла не кратен 256 BLe++; } // Начинаем вывод for (i=0; i<=3; i++){ // преамбула (4*(00H*25+55H*25)) for (j=0; j<=24; j++){ SendByte(0x00); } for (j=0; j<=24; j++){ SendByte(0x55); } } for (BLt=BLe; BLt>=1; BLt--){ // Вывод блоков данных CSz = BLs; CSz += BLe; CSz += BLt; // упреждающее чтение первого байта блока данных с карты (он всегда есть) St = romFile.read(); // читаем байт из файла Nbt--; for (j=0; j<=15; j++){ // 00h*16 SendByte(0x00); } for (j=0; j<=3; j++){ // 55h*4 SendByte(0x55); } SendByte(0xE6); // E6h*1 for (j=0; j<=3; j++){ // 00h*4 SendByte(0x00); } for (j=0; j<=26; j++){ // заголовок блока CSz += SB[j]; SendByte(SB[j]); } SendByte(BLs); // начальный блок SendByte(BLe); // конечный блок SendByte(BLt); // осталось блоков SendByte(CSz); // контр.сумма заголовка for (Nst=0x80; Nst<=0x87; Nst++){ // вывод строк (8 шт.) for (j=0; j<=3; j++){ // 00h*4 SendByte(0x00); } SendByte(0xE6); // E6h*1 CSs = Nst; SendByte(Nst); // номер строки CSs += CSz; SendByte(CSz); // контр.сумма заголовка CSs += St; // начинаем вывод строки данных SendByte(St); // предварительно считанный байт for (j=0; j<=30; j++){ // остальные 31 байт if (Nbt > 0){ // ещё есть данные? St = romFile.read(); // читаем очередной байт из файла Nbt--; } else { // нет -- дополняем нулями St = 0x00; } SendByte(St); // передаём считанный байт CSs += St; } SendByte(CSs); // контр.сумма строки if (Nst !=0x87) { if (Nbt > 0){ // ещё есть данные? St = romFile.read(); // читаем очередной байт из файла Nbt--; } else { // нет -- дополняем нулями St = 0x00; } } } } for (j=0; j<=15; j++){ // 00h*16 -- завершение вывода программы (?) SendByte(0x00); } // close the file: romFile.close(); } else { tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс } delay(15000); // ждем 15 с } void SendByte(byte SBt){ // Подпрограмма вывода байта byte Pd=PORTD; byte i=8; do{ // Выводим биты начиная со старшего i--; Tzd = micros() - Tzd; // Вычисляем, какая была задержка if (Tzd < Tpp) { // Если прошло времени меньше, чем установленная задержка delayMicroseconds(Tpp - Tzd); // Задержка второго полупериода сигнала (предыдущего) } Pd ^= (1 << p); // инвертируем бит в позиции p(=3) PORTD = Pd; // вывод в порт p delayMicroseconds(Tpp); // Задержка первого полупериода сигнала Tzd = micros(); // начало отсчёта длительности задержки if ((bitRead(Pd, p))^(bitRead(SBt, i))){ // Если состояние порта и выводимый бит разные Pd ^= (1 << p); // инвертируем бит в позиции p PORTD = Pd; // вывод в порт p } } while (i>0); }[свернуть]
И он же вместе с библиотекой в архиве: TestTP_fromSD_3.zip
WAV-ка с примером вывода: TestWAV29.7z
Будем оптимизировать дальше... :-)
По-моему тут оптимизировать уже нечего, в рамках принятой концепции все на месте. Чтобы избавиться от задержек совсем, надо менять концепцию.
Чтобы было максимально просто и не заморачиваться на специфическое железо, я бы сделал так:
* Создаются 2 пинг-понг буфера. Один на запись, второй на чтение. В идеале конечно надо библиотеке SD объяснить, что читать надо прямо в них, а не копировать по байту. Иначе очень жирно будет буферов, что для AVR особенно критично.
* Прерывание от таймера настраивается на полупериод кассетного бита. Оно выводит один полубит и сдвигает указатель на следующий полубит. Когда кончается бит, на следующий бит, когда кончается байт, на следующий байт. Когда закончится буфер А, переключается на чтение буфера Б и ставит флажок "нужен буфер".
* Основной цикл слушает флажок "нужен буфер" и запускает чтение буфера А, пока выводится Б и наоборот.
Больше игр нет
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)