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

User Tag List

Страница 5 из 17 ПерваяПервая 123456789 ... ПоследняяПоследняя
Показано с 41 по 50 из 164

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

  1. #41
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    975
    Спасибо Благодарностей отдано 
    428
    Спасибо Благодарностей получено 
    396
    Поблагодарили
    221 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от svofski Посмотреть сообщение
    Вместо двух вложенных циклов будет просто цикл.
    Можно и так, это изменение имеет свои плюсы и минусы: цикл записи всегда будет "идти по пятам" чтения, т.е. если случится событие "в буфере нет места", то этот цикл начнёт с максимальной для ардуины скоростью проверять условие, опрашивать переменную CRB и запись продолжится, как только CRB увеличится на единицу. А если учесть, что, для безопасности, в основной программе все обращения к CRB должно быть внутри noInterrupts/interrupts, то получится совсем скверно для работы таймера и прерываний. Мне кажется, наличие второго вложенного цикла с ожиданием -- это тот костыль, который помогает. :-) Либо, опять же, можно вынести проверку условия "в буфере нет места" в подпрограмму обработки прерывания, но тогда окружать noInterrupts/interrupts прийдётся переменную CWB.

    Еще можно сразу после заполнения буфера загонять ардуину в сон (тут надо долго читать, какой именно из режимов сна правильно будет работать). Она будет просыпаться только на прерывание и забивание буфера, остальное время потреблять какой-то там наноампер.
    А это уже интересная идея.

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

    Цитата Сообщение от KTSerg Посмотреть сообщение
    Я вот чет притормозил... А зачем вообще необходима атомарность в BUFF[lowByte(CWB)] = SBb; ?
    В прерывании вообще нет обращения к CWB, его модификация не происходит (где-то в другом месте кода), ну разорвётся эта запись на прерывание, и чем это грозит? (если не считать, случай когда CRB догнал CWB...), Но опять-же, индекс CWB ведь не изменяется до окончания записи в буфер, значит и проблемы не должно быть...
    Да, именно так.
    Интересное наблюдение -- в программе сейчас нет проверки на то, что чтение из буфера обгонит запись, хотя теоретически это возможно, например, в случае, если SD-карта не читается. Надо будет сделать проверку и на это событие...

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

    По умолчанию

    В общем, не стоим на месте -- вот новая версия:

    Версия 0

    Кроме библиотеки TimerOne и SdFat, для работы теперь ещё требуется библиотека LiquidCrystal из стандартного комплекта IDE для ардуино.
    Тут постарался исправить все найденные ошибки, добавил экран и кнопки. Кроме того, теперь в wav-ке в заголовках передаётся имя воспроизводимого файла.
    Код:
    /*
      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;                    // признак того, что текущая позиция -- это директория
    
    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 };
    
    int Tpp = 256; // Начальная длительность задержки сигнала в микросекундах (один полупериод)
    
    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);  // объявляем пин как выход
      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(currentFile); // переходим к первому файлу в директории
    }
    
    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(currentFile);
        } 
        else {         // если не директория -- пробуем воспроизвести файл
          if(romFile.cwd()->exists(sfileName)) {
            printtext("Not ROM-file",1);
            for (int i=0;12;i++){
              if (sfileName[i]=='.'){                 // ищем точку в имени файла
                if ((sfileName[i+1]|0x20)=='r') {     // проверяем следующий символ (расширения) == 'r'|'R'
                  if ((sfileName[i+2]|0x20)=='o') {   // == 'o'|'O'
                    if ((sfileName[i+3]|0x20)=='m') { // == 'm'|'M'
                      printtext("Playing...",1);
                      if (PlayROM(sfileName, i)==0) { // Передаём короткое имя файла и позицию точки в качестве параметров
                        printtext("Done.",1); // Всё закончилось успешно.
                      }
                      else {
                        printtext("ERROR!",1); // Сообщение об ошибке
                      }
                    }
                  }
                }
                break;
              }
            }
          } 
          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(currentFile);
        break;
      case BT_up: // вверх по файлам в директории
        currentFile--;
        if(currentFile<1) {
          getMaxFile();
          currentFile = maxFile;
        }
        seekFile(currentFile);
        break;
      case BT_down: // вниз по файлам в директории
        currentFile++;
        if(currentFile>maxFile) { 
          currentFile=1; 
        }
        seekFile(currentFile);
        break;
      case BT_select: // пока что просто сообщение о нажатой кнопке, потом будет выход в настройки...
        printtext("<Select>",0);
        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.getName(fileName,filenameLength);
        romFile.close();
        maxFile++;
      }
      romFile.cwd()->rewind();
    }
    
    void seekFile(int pos) { // переход на позицию в директории, сохранение имени файла и показ его на экране
      romFile.cwd()->rewind();
      for(int i=1;i<=currentFile;i++) {
        romFile.openNext(romFile.cwd(),O_READ);
        romFile.getName(fileName,filenameLength);
        romFile.getSFN(sfileName);
        isDir = romFile.isDir();
        romFile.close();
      }
      printtext(sfileName,0);       // вывод имени текущего файла
      if (isDir==1) lcd.print('>'); // если это директория, добавляем в конце символ '>'
    }
    
    int PlayROM(char FName[], int pnt) // функция вывода файла
    {
      tone(p, 500, 100);     // включаем на 500 Гц на 100 мс
      delay(1000);           // ждем 1 с
    
      if (romFile.open(FName,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) BLe++; // корректировка количества блоков, если размер файла не кратен 256
    
        int StName = 0;
        for (i=14; i<=21; i++){              // заносим в SB имя файла
          if (StName < pnt) {                // имя ещё не закончилось?
            SB[i] = FName[StName];
            StName++;
          }
          else SB[i] = 0x20;                 // пробелы
        }
        StName = pnt;
        for (i=22; i<=24; i++){              // заносим в SB расширение файла
          StName++;
          SB[i] = FName[StName];
        }
    
        // Начинаем наполнять буфер
        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.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 (CRB_temp > CWB) {     // проверка -- не обогнало ли чтение запись?
                Timer1.stop();          // Останавливаем таймер
                CRB = 0;                // Сбрасываем индексы.
                CWB = 0;
                bBit = 15;
                return 1;               // выход из ПП с ошибкой 1.
              }
            }
            ToBUFF(CSs);                // контр.сумма строки
          }  
        }
        romFile.close(); // закрываем файл
    
        for (j=0; j<=31; j++) ToBUFF(0x00);// 00h*32 -- завершение вывода программы (?)
      }
      else tone(p, 200, 300); // нет файла -- включаем на 200 Гц на 300 мс
    
      noInterrupts();              // запрет прерываний
      CRB_temp = CRB;              // сохраняем CRB во временную переменную
      interrupts();                // разрешение прерываний
      while (CRB_temp < CWB) {     // Ждём опустошения буфера
        //delay(Tpp); // попробуем отключить этот delay... :-)
        noInterrupts();            // запрет прерываний
        CRB_temp = CRB;            // обновляем значение CRB
        interrupts();              // разрешение прерываний
      }
      Timer1.stop();  // Останавливаем таймер............
      CRB = 0;        // Сбрасываем индексы.
      CWB = 0;
      bBit = 15;
      return 0;       // выход из ПП с кодом 0.
    }
    
    void ToBUFF(byte SBb){     // Подпрограмма записи байта в буфер
      noInterrupts();               // запрет прерываний
      CRB_temp = CRB;               // сохраняем CRB во временную переменную
      interrupts();                 // разрешение прерываний
      while (CWB > CRB_temp + 255) {// Если позиция записи больше, чем позиция чтения + размер буфера - 1
        delay(Tpp);            // Задержка (Tpp*1000 мкс = Tpp мс)
        noInterrupts();
        CRB_temp = CRB;        // обновляем значение CRB
        interrupts();
      }
      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--;
      }
      else{
        bBit = 15;
        CRB++;
      }
    }
    [свернуть]

    Из трёх экранов для ардуин, завалявшихся у меня, решил использовать более-менее стандартный 1602, но вот только он у меня работает не по IIC-интерфейсу... Да и вообще это комплект из экрана и кнопок, именуемый "LCD Keypad Shield", поэтому пришлось разрисовать схему подключения (как смог).

    Схема ROM-player

    Нажмите на изображение для увеличения. 

Название:	ROM-player.jpg 
Просмотров:	301 
Размер:	47.7 Кб 
ID:	64310
    На этой схеме подключение SD-карты смог изобразить только условно, уточнить по подключению карты можно на рисунке zebest или в самом скетче. Кроме того, не расписаны номиналы сопротивлений (они стандартны для LCD Keypad Shield).
    [свернуть]

    Если надо будет переделать на другое подключение кнопок, то это легко будет сделать минимальной правкой подпрограммы getPressedButton, для другого экрана править скетч понадобится немного больше...

    Управление кнопками такое:
    - кнопки "вверх" и "вниз" -- выбор файла/директории на SD
    - кнопка "влево" -- перейти в выбранную директорию или запустить на воспроизведение файл
    - кнопка "вправо" -- перейти в корневую директорию
    - кнопка "select" пока не задействована, думаю потом добавить туда вызов настройки, например, изменение скорости воспроизведения.

    На экране в верхне строке пишется имя выбранного файла, а в нижней идёт отсчёт секунд от включения ардуинки (для теста делал, потом можно будет убрать). Во время воспроизведения в нижней строке экрана пишется "Playing...", а справа от надписи выводится количество оставшихся блоков.

    Скетч с библиотеками в одном архиве: ROM-player_0.zip
    Пример вывода: TestWAV38.7z

  3. #43
    Guru Аватар для svofski
    Регистрация
    20.06.2007
    Адрес
    С.-Петербург
    Сообщений
    4,115
    Спасибо Благодарностей отдано 
    792
    Спасибо Благодарностей получено 
    655
    Поблагодарили
    402 сообщений
    Mentioned
    22 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

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

    Но на всякий случай обращу внимание на то, что:
    * все циклы (пока не появится спящий режим) крутятся с максимальной для ардуины скоростью
    * задержка систематически будет образовываться из-за отвлечения основного цикла на чтение сектора и тогда игра в догоняйку будет иметь место с зазором больше 1
    * проверка булевого флажка никакого noInterrupts() не требует
    * если быть совсем дотошным, вычисление этого флажка изнутри прерывания гарантировано происходит сильно задолго до следующего и абсолютно никакой лепты в джиттер не вносит
    * атомарность не записи в буфер как таковой, а записи + инкремента указателя записи. А указатель фигурирует в проверке условия.

    P.S. кстати об экранчиках, у меня вот такой, я его нашел в пицце у tnt23 в пакетике "DO NOT EAT" и как заказать второй такой же не знаю:
    https://imgur.com/1Us47PA

    Завелся он u8glib-ом так:
    Код:
    // HW SPI:
    // 11: MOSI 13: SCK 
    U8G2_SH1106_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
    Целиком немудреный скетч: https://pastebin.com/nfps6aE3

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

  4. #44
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    975
    Спасибо Благодарностей отдано 
    428
    Спасибо Благодарностей получено 
    396
    Поблагодарили
    221 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от svofski Посмотреть сообщение
    кстати об экранчиках, у меня вот такой, я его нашел в пицце у tnt23 в пакетике "DO NOT EAT" и как заказать второй такой же не знаю:
    https://imgur.com/1Us47PA
    :-) У меня есть точно такой же, работает по 4SPI, но я почему-то решил, что это экзотика и использовал более распространённый 1602. И, кстати, да -- такие экранчики на том же али редко встречаются, в основном там с IIC-интерфейсом.

    Завелся он u8glib-ом
    Этой библиотекой не пробовал пользоваться, но через Adafruit_SSD1306 он работал неплохо. Попробую его поставить...

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

    По умолчанию

    А вот это разве не оно? Это первая ссылка, дальше не смотрел...
    https://ru.aliexpress.com/item/free-...595065663.html
    и гугля на "lcd 128x64 spi" много похожего даёт...

    сам увидел, это i2c а не spi...

    Не, это всё оно, интерфейс перемычками/резюками выбирается...
    https://ru.aliexpress.com/item/0-96-...671614837.html
    Последний раз редактировалось KTSerg; 21.02.2018 в 18:07.

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

    По умолчанию

    Цитата Сообщение от KTSerg Посмотреть сообщение
    А вот это разве не оно? Это первая ссылка, дальше не смотрел...
    https://ru.aliexpress.com/item/free-...595065663.html
    и гугля на "lcd 128x64 spi" много похожего даёт...

    сам увидел, это i2c а не spi...
    Не, это тоже он. Они визуально отличаются количеством контактов -- есть на 7, на 6 и на 4. С интерфейсом 4SPI должно быть 7. На I2C или 3SPI он переключается перепайкой сопротивлений.

  7. #46
    С любовью к вам, Yandex.Direct
    Размещение рекламы на форуме способствует его дальнейшему развитию

  8. #47
    Guru Аватар для svofski
    Регистрация
    20.06.2007
    Адрес
    С.-Петербург
    Сообщений
    4,115
    Спасибо Благодарностей отдано 
    792
    Спасибо Благодарностей получено 
    655
    Поблагодарили
    402 сообщений
    Mentioned
    22 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Ну вот, я думал, что это такой go to дисплей по умолчанию и даже развел свою плату расширения под него (хотя он там не нужен и я надеюсь, что плата будет полезной в основном без дисплея). А оказалось, что это какой-то редкоземельный уникальный экземпляр.

    Библиотека AdaFruit была первым, что я нашел и она мне совсем не понравилась (признаюсь, что я очень предвзято и совершенно иррационально обостренно негативно отношусь к этой лавочке). Окончательно меня добило ее бредовое лицензионное требование тащить с собой сплешскрин. Независимо от моей оценки, я требования авторов уважаю и отправляю такие библиотеки в помойку.
    Больше игр нет

  9. #48
    Guru Аватар для tnt23
    Регистрация
    28.03.2006
    Адрес
    Санкт-Петербург
    Сообщений
    2,634
    Спасибо Благодарностей отдано 
    391
    Спасибо Благодарностей получено 
    78
    Поблагодарили
    63 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Экранчик, который попался svofski, брался тут:

    https://www.aliexpress.com/item/OLED...603484783.html

    Если совсем пристально смотреть в корень, то от экранчика нужен собственно экранчик, подложка с джамперами не нужна. На плате разводится разъем для припайки шлейфа экранчика, нужные-ненужные ноги землятся или увешиваются конденсаторами, и все это подключается к ардуине по вкусу.

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

    По умолчанию

    В моем случае экранчик вставляется перпендикулярно, для чего удобней подложки с пинами ничего не придумаешь.
    Больше игр нет

  11. #50
    Master Аватар для Improver
    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    975
    Спасибо Благодарностей отдано 
    428
    Спасибо Благодарностей получено 
    396
    Поблагодарили
    221 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Экран пока не поменял, но выкладываю очередную версию плеера:

    ROM-player 1

    Для работы требуются те же библиотеки, что и ранее: 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;                    // признак того, что текущая позиция -- это директория
    
    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 };
    
    int Tpp = 256; // Начальная длительность задержки сигнала в микросекундах (один полупериод)
    
    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);  // объявляем пин как выход
      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(currentFile); // переходим к первому файлу в директории
    }
    
    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(currentFile);
        } 
        else {         // если не директория -- пробуем воспроизвести файл
          if(romFile.cwd()->exists(sfileName)) {
            printtext("Not ROM-file",1);
            for (int i=0;12;i++){
              if (sfileName[i]=='.'){                 // ищем точку в имени файла
                if ((sfileName[i+1]|0x20)=='r') {     // проверяем следующий символ (расширения) == 'r'|'R'
                  if ((sfileName[i+2]|0x20)=='o') {   // == 'o'|'O'
                    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);          // Сообщение об ошибке
                      }
                    }
                  }
                }
                break;
              }
            }
          } 
          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(currentFile);
        break;
      case BT_up: // вверх по файлам в директории
        currentFile--;
        if(currentFile<1) {
          getMaxFile();
          currentFile = maxFile;
        }
        seekFile(currentFile);
        break;
      case BT_down: // вниз по файлам в директории
        currentFile++;
        if(currentFile>maxFile) { 
          currentFile=1; 
        }
        seekFile(currentFile);
        break;
      case BT_select: // пока что просто сообщение о нажатой кнопке, потом будет выход в настройки...
        printtext("<Select>",0);
        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.getName(fileName,filenameLength);
        romFile.close();
        maxFile++;
      }
      romFile.cwd()->rewind();
    }
    
    void seekFile(int pos) { // переход на позицию в директории, сохранение имени файла и показ его на экране
      romFile.cwd()->rewind();
      for(int i=1;i<=currentFile;i++) {
        romFile.openNext(romFile.cwd(),O_READ);
        romFile.getName(fileName,filenameLength);
        romFile.getSFN(sfileName);
        isDir = romFile.isDir();
        romFile.close();
      }
      printtext(sfileName,0);       // вывод имени текущего файла
      if (isDir==1) lcd.print('>'); // если это директория, добавляем в конце символ '>'
    }
    
    int PlayROM(char FName[], int pnt) // функция вывода файла
    {
      tone(p, 500, 100);     // включаем на 500 Гц на 100 мс
      delay(1000);           // ждем 1 с
    
      if (romFile.open(FName,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) BLe++; // корректировка количества блоков, если размер файла не кратен 256
    
        int StName = 0;
        for (i=14; i<=21; i++){              // заносим в SB имя файла
          if (StName < pnt) {                // имя ещё не закончилось?
            SB[i] = FName[StName];
            StName++;
          }
          else SB[i] = 0x20;                 // пробелы
        }
        StName = pnt;
        for (i=22; i<=24; i++){              // заносим в SB расширение файла
          StName++;
          SB[i] = FName[StName];
        }
    
        // Начинаем наполнять буфер
        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.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--;
      }
      else{
        bBit = 15;
        CRB++;
      }
    }
    [свернуть]

    Изменил немногое, а именно:
    - сделал остановку воспроизведения при нажатии любой кнопки
    - немного изменил циклы задержки (которые тут много обсуждали), теперь там код выглядит так:
    Код:
    noInterrupts();
    CRB_temp = CRB;
    interrupts();
    if (CWB > (CRB_temp + 255)) {
      delay(Tpp);
    }
    т.е. теперь нет вложенных циклов, просто проверка условия и при его выполнении пустой цикл delay. Я просто вдруг заметил, что "while", стоявший там ранее, может быть исполнен максимум один раз, тогда какой в нём смысл?.. :-)

    Исходники с библиотеками в одном архиве: ROM-player_1.zip
    WAV-ку не делал, т.к. думаю, что там без изменений...

    И, напоследок, фотка, как сейчас выглядит плеер (для общего представления):

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

    Нажмите на изображение для увеличения. 

Название:	IMG_20180222_120451.jpg 
Просмотров:	242 
Размер:	99.5 Кб 
ID:	64321
    [свернуть]

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

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

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

Эту тему просматривают: 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

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

Ваши права

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