Важная информация

User Tag List

Страница 3 из 17 ПерваяПервая 1234567 ... ПоследняяПоследняя
Показано с 21 по 30 из 163

Тема: ROM-плеер на ардуино

  1. #21
    Veteran Аватар для zebest
    Регистрация
    11.01.2008
    Адрес
    Ладошкино
    Сообщений
    1,668
    Записей в дневнике
    4
    Спасибо Благодарностей отдано 
    318
    Спасибо Благодарностей получено 
    221
    Поблагодарили
    174 сообщений
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    а елси весь файл в буфер зачитать а потом уже транслировать?? емкости ардуны не хватит?? а если Мегу взять ?
    Profi v3.2 -=- Speccy2010,r2

  2. #22
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    970
    Спасибо Благодарностей отдано 
    417
    Спасибо Благодарностей получено 
    392
    Поблагодарили
    217 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от svofski Посмотреть сообщение
    По-моему тут оптимизировать уже нечего, в рамках принятой концепции все на месте. Чтобы избавиться от задержек совсем, надо менять концепцию.

    Чтобы было максимально просто и не заморачиваться на специфическое железо, я бы сделал так:

    * Создаются 2 пинг-понг буфера. ...
    Интересная идея. А если сделать один буфер? Допустим, так:
    - Основная программа подготавливает данные и сливает их в массив, допустим, на 256 байт. И есть две переменных, первая -- индекс, откуда сейчас читаем, а вторая -- куда пишем.
    - Как буфер заполнится, запускаем таймер на время, равное периоду вывода 1 байта (8 бит). По прерыванию с него выводим байт с позиции чтения, делаем индекс чтения +1.
    - Далее основная программа заполняет с максимальной скоростью буфер до состояния "позиция записи" = ("позиция чтения" - 1), а по таймеру идёт непрерывный вывод данных из буфера. (Естественно, при достижении конца буфера запись циклически начнётся с его начала.)

    Нормально так будет?

    Цитата Сообщение от zebest Посмотреть сообщение
    а елси весь файл в буфер зачитать а потом уже транслировать?? емкости ардуны не хватит?? а если Мегу взять ?
    Не, там оперативки всего-то 2 Кб, на меге -- 8 Кб... Ну если только во флеш переносить.

  3. #23
    Veteran
    Регистрация
    22.02.2014
    Адрес
    г. Курган
    Сообщений
    1,653
    Спасибо Благодарностей отдано 
    214
    Спасибо Благодарностей получено 
    301
    Поблагодарили
    212 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Другими словами:
    - Буфер закольцован.
    - Программа вызываемая из прерывания, занимается отправкой с текущей позиции чтения из буфера.
    - Основная программа следит за разностью между индексами чтения и записи в буфер, и подчитывает, заполняя буфер.

  4. #24
    Guru Аватар для svofski
    Регистрация
    20.06.2007
    Адрес
    С.-Петербург
    Сообщений
    4,105
    Спасибо Благодарностей отдано 
    772
    Спасибо Благодарностей получено 
    643
    Поблагодарили
    398 сообщений
    Mentioned
    22 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Кольцо удобно когда поставщик и потребитель неизвестно в каком темпе работают: буфер последовательного канала итп. А тут все известно, чтение происходит порциями фиксированного размера в буфер, поэтому пинг-понг проще:

    * не надо считать никаких голов и хвостов
    * чтение всегда линейное, то есть можно пользоваться стандартными библиотеками для чтения прямо в буфер
    * просто гарантировать то, что запись не происходит туда, откуда еще не все прочитано

    Если очень хочется именно кольцо, то достаточно представить себе пинг-понг буфер кольцом длиной 2, где каждый элемент — это буфер

    - - - Добавлено - - -

    Цитата Сообщение от Improver Посмотреть сообщение
    Как буфер заполнится, запускаем таймер на время, равное периоду вывода 1 байта (8 бит). По прерыванию с него выводим байт с позиции чтения, делаем индекс чтения +1
    Так нельзя делать. Если обработчик все время будет выводить байт, то как только он выйдет, он тут же будет вызван снова, потому что пришел черед следующего байта. Получается, что наша программа просто переехала в прерывание, а времени на чтение как не было, так и нет.

    Смысл прерывания в том, что основное свободное время, которое у нас есть, находится между полубитами. Время вывода полубитов так же является критичным для задачи. Поэтому прерывание выводит только один полубит. Это происходит по таймеру, поэтому мы знаем, что вывод работает максимально точно. После вывода полубита обработчик обязательно должен вернуться, чтобы во-первых иметь возможность быть вызванным снова вовремя и, во-вторых, чтобы дать время основному циклу для поддержания заполненности буферов.
    Больше игр нет

  5. #25
    Veteran
    Регистрация
    22.02.2014
    Адрес
    г. Курган
    Сообщений
    1,653
    Спасибо Благодарностей отдано 
    214
    Спасибо Благодарностей получено 
    301
    Поблагодарили
    212 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Ну, если задержки вызывает именно чтение, то не обращать на них внимание. Читать стандартными функциями по 32 или 256 байт (как нравится) в буфер, до начала вывода преамбулы (строки 32 Байт) и не обращать внимание на паузы, т.к. Вектор их (в этом месте) спокойно пережуёт.

  6. #26
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    970
    Спасибо Благодарностей отдано 
    417
    Спасибо Благодарностей получено 
    392
    Поблагодарили
    217 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    svofski, пожалуй, Вы правы -- надо делать таймер на полупериод. Это получается, что чётные разы его срабатывания в программе будет просто инверсия состояния порта, а нечётные -- состояние будет меняться (или не меняться) в зависимости от выводимого бита... Ок, попробую это реализовать.

    - - - Добавлено - - -

    Цитата Сообщение от KTSerg Посмотреть сообщение
    Ну, если задержки вызывает именно чтение, то не обращать на них внимание. Читать стандартными функциями по 32 или 256 байт (как нравится) в буфер, до начала вывода преамбулы (строки 32 Байт) и не обращать внимание на паузы, т.к. Вектор их (в этом месте) спокойно пережуёт.
    В последнем варианте почти так и есть, но хотелось бы лучшего... :-) А буфер, как я догадываюсь, имеет размер одного кластера SD-карты.

  7. #27
    Guru Аватар для svofski
    Регистрация
    20.06.2007
    Адрес
    С.-Петербург
    Сообщений
    4,105
    Спасибо Благодарностей отдано 
    772
    Спасибо Благодарностей получено 
    643
    Поблагодарили
    398 сообщений
    Mentioned
    22 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Посмотрел бегло на SdFat в TZXDuino. Ключевой момент, где подкачивается буфер:
    https://github.com/sadken/TZXDuino/b...tFile.cpp#L760

    Кеш там - это буфер 512 байт, который используется для всего на свете. Для текущих данных, но так же и для таблиц FAT и каталогов. Это круто, но делает переделку этой библиотеки под пинг-понг буфер для данных задачей непропорционально премудрой.

    Скрытый текст

    Будь это мой проект, я бы эту библиотеку отправил туда, где ей место. А вместо нее взял бы адекватную, написанную без вселенских замашек. Но, пытаясь поставить себя на место Improver-a,
    [свернуть]


    я бы плюнул и оставил эту библиотеку как есть, раз она удобна в использовании и проверена. А данные читал бы в два буфера по 256 байт, только не read(void) а сразу read(uint8_t * dst, size_t count). Вот если памяти перестанет хватать, тогда уже можно подумать, как сделать оптимальней.
    Больше игр нет

  8. #28
    Veteran
    Регистрация
    22.02.2014
    Адрес
    г. Курган
    Сообщений
    1,653
    Спасибо Благодарностей отдано 
    214
    Спасибо Благодарностей получено 
    301
    Поблагодарили
    212 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Improver Посмотреть сообщение
    ...
    В последнем варианте почти так и есть, но хотелось бы лучшего... :-) А буфер, как я догадываюсь, имеет размер одного кластера SD-карты.
    Просто насколько я понимаю, стандартными средствами до того буфера (считанного кластера), нет доступа. Библиотека предоставляет лишь функцию, которая предоставляет пользователю данные из считанного кластера. Вот я и думаю, что вызывать стандартную функцию библиотеки для чтения одного байта, это избыточно, нужно сразу получить в свой буфер например 32 байта и спокойно их отправить, потом запросить следующие... правда это лишний расход памяти...

  9. #29
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    970
    Спасибо Благодарностей отдано 
    417
    Спасибо Благодарностей получено 
    392
    Поблагодарили
    217 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Очередная версия готова, вот:

    Версия на таймере1


    Для скетча требуется библиотека TimerOne и, также, как и для предыдущей версии, библиотека SdFat из проекта TZXDuino.
    Код:
    /*
     * SD-картридер подключяется к выводам ардуино:
     ** MOSI  - D11
     ** MISO  - D12
     ** CLK   - D13
     ** CS    - D10
     *
     *  Выход - D3
     */
    #include <SdFat.h>
    #include <TimerOne.h>
    
    SdFat sd;
    SdFile romFile;
    
    volatile byte BUFF[256];       // буфер данных
    volatile unsigned int CRB = 0; // индекс чтения из буфера
    volatile byte bBit = 15;       // индекс читаемого полубита
    unsigned int CWB = 0;          // индекс записи в буфер
    
    // Заголовок
    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; // номер пина, на который будет вывод сигнала
    
    const int Tpp = 256; // Длительность задержки сигнала в микросекундах (один полупериод)
    
    void setup() //процедура setup
    {
      pinMode(p, OUTPUT);  // объявляем пин как выход
      pinMode(10, OUTPUT); // CS для SD-картридера
    
      Timer1.initialize(Tpp);               // инициализировать timer1, и установить период Tpp мкс.
      Timer1.attachInterrupt(SendHalfBit);  // прикрепить SendHalfBit(), как обработчик прерывания по переполнению таймера
      Timer1.stop();
    
      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++){
            BUFF[CWB] = 0x00;
            CWB++;
          }
          for (j=0; j<=24; j++){
            BUFF[CWB] = 0x55;
            CWB++;
          }
        }  
    
        Timer1.start();    // Запускаем таймер............
    
        for (BLt=BLe; BLt>=1; BLt--){   // Вывод блоков данных в цикле
          CSz = BLs;
          CSz += BLe;
          CSz += BLt;
    
          for (j=0; j<=15; j++){        // 00h*16
            ToBUFF(0x00);
          }
          for (j=0; j<=3; j++){         // 55h*4
            ToBUFF(0x55);
          }
          ToBUFF(0xE6);                 // E6h*1
          for (j=0; j<=3; j++){         // 00h*4
            ToBUFF(0x00);
          }
    
          for (j=0; j<=26; j++){        // заголовок блока
            CSz += SB[j];
            ToBUFF(SB[j]);
          }
          ToBUFF(BLs);                  // начальный блок
          ToBUFF(BLe);                  // конечный блок
          ToBUFF(BLt);                  // осталось блоков
          ToBUFF(CSz);                  // контр.сумма заголовка
    
          for (Nst=0x80; Nst<=0x87; Nst++){   // вывод строк (8 шт.)
            for (j=0; j<=3; j++){       // 00h*4
              ToBUFF(0x00);
            }
            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;
            }
            ToBUFF(CSs);                // контр.сумма строки
          }  
        }
        // close the file:
        romFile.close();
    
        for (j=0; j<=15; j++){          // 00h*16 -- завершение вывода программы (?)
          ToBUFF(0x00);
        }
      }
      else {      
        tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс
      }
      while (CRB < CWB) {  // Ждём опустошения буфера
        delay(Tpp);
      }
      Timer1.stop();  // Останавливаем таймер............
      CRB = 0;        // Сбрасываем индексы.
      CWB = 0;
      bBit = 15;
      delay(15000);   // ждем 15 с
    }
    
    void ToBUFF(byte SBb){     // Подпрограмма записи байта в буфер
      while (CWB > CRB + 255) {// Если позиция записи больше, чем позиция чтения + размер буфера - 1
        delay(Tpp);            // Задержка (Tpp*1000 мкс = Tpp мс)
      }
      BUFF[lowByte(CWB)] = SBb;
      CWB++;
    }
    
    void SendHalfBit() {  // Подпрограмма вывода полубита по циклу таймера
      byte Pd=PORTD;
      if (bBit & 1){      // проверка индекса полубитов на чётность
    //    if ((bitRead(Pd, p))^(bitRead(BUFF[lowByte(CRB)], bBit/2))){ // Старый вариант...
        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--;
      }
      else{
        bBit = 15;
        CRB++;
      }
    }
    [свернуть]

    В итоге, решил всё-таки сделать на одном закольцованном буфере, а не на двух типа "пинг-понг" -- всё ради экономии памяти... Размер буфера 256 байт, что при таймере в 256 мкс даёт его ёмкость чуть больше секунды. В дебужной версии запись в буфер "догоняет" чтение и уходит в паузу примерно раз в секунду-полторы. На том же "Киберноиде" ни одного провала или ошибки в выводимом сигнале не обнаружено, даже при сокращении времени таймера до 160 мкс, что уже можно считать успехом.
    Кстати, по таймеру -- если я правильно посчитал, то при 256 мкс "несущая" частота должна быть примерно 2 кГц, а сколько максимум может вытянуть Вектор?

    Ещё такой ньюанс -- тут (в конце) пишут, что "при обращении к общим переменным прерывания, как правило, должны быть отключены", но я пока сделал без отключения -- посмотрим, если будет работать без глюков, то может и так оставим...

    Скетч со всеми нужными библиотеками в архиве: TestTP_fromSD_4.zip
    Пример выводимой wav-ки: TestWAV35.7z

    - - - Добавлено - - -

    Цитата Сообщение от KTSerg Посмотреть сообщение
    Вот я и думаю, что вызывать стандартную функцию библиотеки для чтения одного байта, это избыточно, нужно сразу получить в свой буфер например 32 байта и спокойно их отправить, потом запросить следующие... правда это лишний расход памяти...
    Пробовал я так сделать, и при этом задержки получаются даже немного больше... С буфером на SD не всё так просто: во-первых, в некоторых статьях упоминается о наличии в SD-картах аппаратного буфера чтения/записи, который может быть разного размера в зависимости от модели карты (пруфов не сохранил), а во-вторых, сами карты, в принципе, не умеют читать/писать информацию иначе, чем блоками размером в кластер. Поэтому, я думаю, чтение первого байта происходит с большей задержкой, чем остальных в пределах одного кластера. В общем, сейчас эта проблема решена использованием таймера и таким псевдо-распараллеливанием процессов чтения-передачи, что-то ещё делать с библиотекой я не вижу особого смысла.

    - - - Добавлено - - -

    Цитата Сообщение от svofski Посмотреть сообщение
    я бы плюнул и оставил эту библиотеку как есть, раз она удобна в использовании и проверена.
    Так и сделал. :-)

  10. #30
    Guru Аватар для svofski
    Регистрация
    20.06.2007
    Адрес
    С.-Петербург
    Сообщений
    4,105
    Спасибо Благодарностей отдано 
    772
    Спасибо Благодарностей получено 
    643
    Поблагодарили
    398 сообщений
    Mentioned
    22 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Поздравляю! Это уже красиво.

    Маленькое замечание — delay() для ожидания изменения бита не имеет смысла, потому что внутри его такой же цикл.

    ToBUFF() умеренно безопасен, пока получается поддерживать разрыв между указателями.
    Больше игр нет

Страница 3 из 17 ПерваяПервая 1234567 ... ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Похожие темы

  1. Портативный AY плеер.
    от Руслан в разделе Звук
    Ответов: 1
    Последнее: 16.04.2014, 08:46
  2. Service rom + 128 basic rom
    от VELESOFT в разделе Оси
    Ответов: 1
    Последнее: 24.03.2013, 04:48
  3. Плеер для pt 3
    от Руслан в разделе Музыка
    Ответов: 25
    Последнее: 14.08.2012, 19:25
  4. Advanced ROM Manager (ROM Switvcher + Prof. ROM)
    от Alex_NEMO в разделе Память
    Ответов: 4
    Последнее: 04.10.2010, 11:43
  5. AY плеер
    от newart в разделе Звук
    Ответов: 19
    Последнее: 20.07.2006, 00:03

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •