User Tag List

Показано с 1 по 10 из 164

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

Комбинированный просмотр

Предыдущее сообщение Предыдущее сообщение   Следующее сообщение Следующее сообщение
  1. #1

    Регистрация
    06.02.2018
    Адрес
    г. Волгоград
    Сообщений
    1,065
    Спасибо Благодарностей отдано 
    582
    Спасибо Благодарностей получено 
    471
    Поблагодарили
    253 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    В прошедшие выходные получил посылку с известного китайского сайта, заказывал "Data Logger Shield" с ардуиной уно -- этот шилд как раз подходит для ROM-плеера, есть место, куда можно впаять схему выхода и разъём, плюс бонусом имеет на борту часы. Вот что сейчас имеем:

    Фотки

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

Название:	IMG_20180317_151835~.jpg 
Просмотров:	326 
Размер:	52.5 Кб 
ID:	64678

    И по отдельности:
    Нажмите на изображение для увеличения. 

Название:	IMG_20180317_151718~.jpg 
Просмотров:	342 
Размер:	47.3 Кб 
ID:	64679

    Бело-розовый проводок с разъёмом на фото -- это прямой выход с контакта D3, для опытов...
    [свернуть]

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

    И раз уж появился бонус в виде часов, решил его задействовать, вот новая версия скетча:

    ROM-player_4

    Для работы требуются библиотеки TimerOne, SdFat, а также стандартные LiquidCrystal, Wire и RTClib.
    Код:
    /*
     Вариант на "Data Logger Shield" и "LCD Keypad Shield"
     (на первом есть RTC -- используем для показа времени)
    
     Выход - D3
    
     "Data Logger Shield V1.0":
      SD-картридер подключен к выводам ардуино:
     * MOSI  - D11
     * MISO  - D12
     * CLK   - D13
     * CS    - D10
    
     "LCD Keypad Shield":
     Подключение экрана 1602А:
     * 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
     */
    
    // include the library code:
    #include <LiquidCrystal.h>
    #include <SdFat.h>
    #include <TimerOne.h>
    #include <Wire.h>
    #include "RTClib.h"
    
    RTC_DS1307 RTC;
    
    // 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 };
    
    char DT[15] = {
      '0', '0', ':', '0', '0', ':', '0', '0', ' ',
      '0', '0', '/', '0', '0', 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-картридера
      digitalWrite(10, HIGH);
      lcd.begin(16, 2);    // объявляем размер экрана 16 символов и 2 строки
    
      Timer1.initialize(Tpp);               // инициализировать timer1, и установить период Tpp мкс.
      Timer1.attachInterrupt(SendHalfBit);  // прикрепить SendHalfBit(), как обработчик прерывания по переполнению таймера
      Timer1.stop();
    
      Wire.begin();
      RTC.begin();
      if (! RTC.isrunning()) {
        printtext("RTC is NOT run!",0);
        // following line sets the RTC to the date & time this sketch was compiled
        RTC.adjust(DateTime(__DATE__, __TIME__));
        delay(1000);       // ждем 1 с
      }
      //RTC.adjust(DateTime(__DATE__, __TIME__));
    
      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() {
      DateTime now = RTC.now();
      DT[0] = now.hour()/10 + '0';              // перевод из целого в символ
      DT[1] = now.hour()%10 + '0';              // часы
      DT[3] = now.minute()/10 + '0';            // минуты
      DT[4] = now.minute()%10 + '0';            // минуты
      DT[6] = now.second()/10 + '0';            // секунды
      DT[7] = now.second()%10 + '0';            // секунды
      DT[9] = now.day()/10 + '0';               // день
      DT[10] = now.day()%10 + '0';              // день
      DT[12] = now.month()/10 + '0';            // месяц
      DT[13] = now.month()%10 + '0';            // месяц
      printtext(DT,1);                          // выводим время и дату
    
      int button = getPressedButton(); // какая кнопка нажата?
      while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка
        delay(50); 
      }
      switch (button)
      {
      case BT_right: // вход в директорию, или запуск файла на воспроизведение
        if(isDir==1) { //Если это директория, то переход в неё
          sd.chdir(fileName, true);
          getMaxFile();
          currentFile=1;
        } 
        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);
                      }
                    }
                  }
                  break;
                }
              }
            }
            else {
              printtext("File is too big",1);
            }
          } 
          else {
            printtext("No File Selected",1);
          }
          delay(1000);           // ждем 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;
        break;
      case BT_up: // вверх по файлам в директории
        currentFile--;
        if(currentFile<1) {
          getMaxFile();
          currentFile = maxFile;
        }
        break;
      case BT_down: // вниз по файлам в директории
        currentFile++;
        if(currentFile>maxFile) { 
          currentFile=1; 
        }
        break;
      case BT_select: // выход в настройки...
        printtext("Setup",0);
        printtext("Period(mks):",1);
        do {
          delay(300);
          button = getPressedButton(); // какая кнопка нажата?
          switch (button)
          {
          case BT_up:   
            if (Tpp<400) Tpp += 8;
            break;
          case BT_down:   
            if (Tpp>160) 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,1);
          lcd.print(Tpp);
        } 
        while(button!=BT_select); // Цикл пока не будет снова нажата кнопка select
        while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка
          delay(50); 
        }
        break;
      case BT_none: // ничего не нажато
        delay(100); 
        break;
      }
      if (button != BT_none) seekFile();  
    }
    
    //=================================================================
    int getPressedButton()  // функция проверки нажатой кнопки
    {
      int buttonValue = analogRead(0);
      if (buttonValue < 80) return BT_right;
      else if (buttonValue < 200) return BT_up;
      else if (buttonValue < 380) 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(text);
      lcd.print("                ");
    }
    
    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_4.7z

    Теперь, имея "генератор сигнала Вектора", можно двигаться дальше -- на второй ардуинке делать запись и сохранение данных... :-)

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

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

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

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

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

Ваши права

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