Arduino ZX Spectrum AY Player

    Автономный проигрыватель мелодий с компьютера ZX Spectrum на Arduino с минимальным количеством деталей.




    Похоже на то, что спектрумовские мелодии навсегда останутся в моём сердце, так как я регулярно слушаю любимые композиции, используя замечательный бульбовский проигрыватель.



    Но не очень удобно быть привязанным к компьютеру. Эту проблему я временно решал, используя не менее замечательный EEE PC. Но хотелось ещё большей миниатюрности.



    Поиски в интернете привели на следующих красавцев:




    Они восхитительны своей элементной базой, которая вызывает ностальгические воспоминания, но я понимал, что моя лень не позволит мне довести такой проект до конца.

    Мне нужно было что-то небольшое. И вот — практически идеальный кандидат:

    AVR AY-player
    — играет файлы *.PSG
    — поддерживаемая файловая система FAT16
    — количество каталогов в корне диска 32
    — количество файлов в каталоге 42 (итого 32*42=1344 файлов)
    — сортировка каталогов и файлов в каталогах по первым двум буквам имени



    Схема выглядит весьма приемлемой по размеру:


    Конечно же нашёлся фатальный недостаток, который портил идиллию: нет режима случайного выбора композиции. (возможно стоило просто попросить автора добавить эту функцию в прошивку?).

    Джва года я искал подходящий вариант, моё терпение кончилось и я решил действовать.

    Исходя из моей фантастической лени, я выбрал минимальные телодвижения:
    1. Берём Arduino Mini Pro, чтобы не возится с обвязкой.
    2. Нужна SD-карта, чтобы где-то хранить музыку. Значит берём SD-shield.
    3. Нужен музыкальный сопроцессор. Самый маленький — AY-3-8912.

    Был ещё вариант сэмулировать сопроцессор программным путём, но хотелось «тёплого лампового звука», евпочя.

    Для воспроизведения будем использовать PSG-формат.

    Структура PSG-формата
    Offset Number of byte Description
    +0 3 Identifier 'PSG'
    +3 1 Marker “End of Text” (1Ah)
    +4 1 Version number
    +5 1 Player frequency (for versions 10+)
    +6 10 Data

    Data — последовательности пар байтов записи в регистр.
    Первый байт — номер регистра (от 0 до 0x0F), второй — значение.
    Вместо номера регистра могут быть специальные маркеры: 0xFF, 0xFE или 0xFD
    0xFD — конец композиции.
    0xFF — ожидание 20 мс.
    0xFE — следующий байт показывает сколько раз выждать по 80 мс.

    Как конвертировать в PSG
    1. Устанавливаем бульбовский проигрыватель.
    2. Открываем плейлист кнопкой [PL].
    3. Добавляем мелодии в плейлист.
    4. Выбираем мелодию в списке, правой кнопкой вызываем меню, в нём Convert to PSG...
    5. Сохраняем желательно под именем не длиннее 8 символов, иначе оно будет отображено не полнос~1.тью.

    Начнём с подключения SD-карты. Лень подсказала взять стандартное подключение SD-shield и использовать стандартную библиотеку для работы с картой.

    Единственное отличие — для удобства использовал 10 вывод в качестве сигнала выбора карты:


    Для проверки берём стандартный скетч:

    скетч списка файлов на карте
    #include <SPI.h>
    #include <SD.h>
    
    void setup() {
      Serial.begin(9600);
      Serial.print("Initializing SD card...");
    
      if (!SD.begin(10)) {
        Serial.println("initialization failed!");
        return;
      }
      Serial.println("initialization done.");
    
      File root = SD.open("/");
      printDirectory(root);
    
      Serial.println("done!");
    }
    
    void loop() {
    }
    
    void printDirectory(File dir) {
      while (true) {
        File entry =  dir.openNextFile();
        if (!entry)  break;
        Serial.print(entry.name());
        if (!entry.isDirectory()) {
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
        }
        entry.close();
      }
    }
    

    Форматируем карту, пишем туда несколько файлов, запускам… не работает!
    Вот у меня так всегда — наистандартнейшая задача — и сразу косяки.

    Берём другую флешку — (была старенькая на 32Mb, берём новенькую на 2Gb) — ага, заработало, но через раз. Полчаса чесания лба, перестановка соединений поближе к карте (чтобы проводники были короче), развязочный конденсатор по питанию — и работать стало в 100% случаев. Ладно, едем дальше…

    Теперь надо завести сопроцессор — ему нужна тактовая частота 1.75 МГц. Вместо того, чтобы спаять генератор на 14 МГц кварце и поставить делитель, тратим полдня на чтение доков по микроконтроллеру и узнаём, что можно сделать хардовые 1.77(7) МГц, используя быстрый ШИМ:

      pinMode(3, OUTPUT);
      TCCR2A = 0x23;
      TCCR2B = 0x09;
      OCR2A = 8;
      OCR2B = 3; 
    


    Далее, заводим сброс музсопроцессора на пин 2, нижний ниббл шины данных на A0-A3, верхний на 4,5,6,7, BC1 на пин 8, BDIR на пин 9. Аудио выходы для простоты подключим в моно режиме:



    На макетке:


    Заливаем пробный скетч (откуда утащил массив не помню)
    void resetAY(){
      pinMode(A0, OUTPUT); // D0
      pinMode(A1, OUTPUT);
      pinMode(A2, OUTPUT);
      pinMode(A3, OUTPUT); // D3
      
      pinMode(4, OUTPUT); // D4
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT); // D7
      
      pinMode(8, OUTPUT);  // BC1
      pinMode(9, OUTPUT);  // BDIR
      
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
      
      pinMode(2, OUTPUT);
      digitalWrite(2, LOW);
      delay(100);
      digitalWrite(2, HIGH);
      delay(100);
      
      for (int i=0;i<16;i++) ay_out(i,0);
    }
    
    void setupAYclock(){
      pinMode(3, OUTPUT);
      TCCR2A = 0x23;
      TCCR2B = 0x09;
      OCR2A = 8;
      OCR2B = 3; 
    }
    
    void setup() {
      setupAYclock();
      resetAY();
    }
    
    void ay_out(unsigned char port, unsigned char data){
      PORTB = PORTB & B11111100;
    
      PORTC = port & B00001111;
      PORTD = PORTD & B00001111;
    
      PORTB = PORTB | B00000011;
      delayMicroseconds(1);
      PORTB = PORTB & B11111100;
    
      PORTC = data & B00001111;
      PORTD = (PORTD & B00001111) | (data & B11110000);
    
      PORTB = PORTB | B00000010;
      delayMicroseconds(1);
      PORTB = PORTB & B11111100;
    }
    
    unsigned int cb = 0;
    
    byte rawData[] = {
        0xFF, 0x00, 0x8E, 0x02, 0x38, 0x03, 0x02, 0x04, 0x0E, 0x05, 0x02, 0x07, 
        0x1A, 0x08, 0x0F, 0x09, 0x10, 0x0A, 0x0E, 0x0B, 0x47, 0x0D, 0x0E, 0xFF, 
        0x00, 0x77, 0x04, 0x8E, 0x05, 0x03, 0x07, 0x3A, 0x08, 0x0E, 0x0A, 0x0D, 
        0xFF, 0x00, 0x5E, 0x04, 0x0E, 0x05, 0x05, 0x0A, 0x0C, 0xFF, 0x04, 0x8E, 
        0x05, 0x06, 0x07, 0x32, 0x08, 0x00, 0x0A, 0x0A, 0xFF, 0x05, 0x08, 0x0A, 
        0x07, 0xFF, 0x04, 0x0E, 0x05, 0x0A, 0x0A, 0x04, 0xFF, 0x00, 0x8E, 0x04, 
        0x8E, 0x05, 0x00, 0x07, 0x1E, 0x08, 0x0F, 0x0A, 0x0B, 0x0D, 0x0E, 0xFF, 
        0x00, 0x77, 0x08, 0x0E, 0x0A, 0x06, 0xFF, 0x00, 0x5E, 0x07, 0x3E, 0x0A, 
        0x00, 0xFF, 0x07, 0x36, 0x08, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x8E, 0x07, 
        0x33, 0x08, 0x0B, 0x0A, 0x0F, 0x0D, 0x0E, 0xFF, 0x04, 0x77, 0x08, 0x06, 
        0x0A, 0x0E, 0xFF, 0x04, 0x5E, 0x07, 0x3B, 0x08, 0x00, 0xFF, 0x07, 0x1B, 
        0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0x1C, 0x03, 0x01, 0x04, 0x8E, 0x07, 
        0x33, 0x08, 0x0B, 0x0A, 0x0B, 0x0B, 0x23, 0x0D, 0x0E, 0xFF, 0x04, 0x77, 
        0x08, 0x06, 0x0A, 0x0A, 0xFF, 0x04, 0x5E, 0x07, 0x3B, 0x08, 0x00, 0x0A, 
        0x09, 0xFF, 0x07, 0x1B, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0x8E, 0x03, 
        0x00, 0x04, 0x0E, 0x05, 0x01, 0x07, 0x18, 0x08, 0x0F, 0x09, 0x0B, 0x0A, 
        0x0E, 0xFF, 0x00, 0x77, 0x02, 0x77, 0x04, 0x8E, 0x06, 0x01, 0x08, 0x0E, 
        0x09, 0x0A, 0x0A, 0x0D, 0xFF, 0x00, 0x5E, 0x02, 0x5E, 0x04, 0x0E, 0x05, 
        0x02, 0x06, 0x02, 0x09, 0x09, 0x0A, 0x0C, 0xFF, 0x02, 0x8E, 0x04, 0x8E, 
        0x07, 0x30, 0x08, 0x00, 0x09, 0x08, 0x0A, 0x0A, 0xFF, 0x02, 0x77, 0xFF,
        0xFF
    }
    
    void pseudoInterrupt(){
      while (rawData[cb]<0xFF) {
       ay_out(rawData[cb],rawData[cb+1]);
       cb++;
       cb++;
     }
     if (rawData[cb]==0xff) cb++;
     
     if (cb>20*12)  cb=0;
    }
    
    void loop() {
      delay(20);
      pseudoInterrupt();
    }
    

    И слышим полсекунды какой-то прекрасной мелодии! (на самом деле я тут ещё два часа ищу как я забыл отпустить ресет после инициализации).

    На этом железная часть закончена, а в программной добавляем прерывания 50 Гц, считывание файла и запись в регистры сопроцессора.

    Окончательный вариант программы
    #include <SPI.h>
    #include <SD.h>
    
    void resetAY(){
      pinMode(A0, OUTPUT); // D0
      pinMode(A1, OUTPUT);
      pinMode(A2, OUTPUT);
      pinMode(A3, OUTPUT); // D3
      
      pinMode(4, OUTPUT); // D4
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT); // D7
      
      pinMode(8, OUTPUT);  // BC1
      pinMode(9, OUTPUT);  // BDIR
      
      digitalWrite(8,LOW);
      digitalWrite(9,LOW);
      
      pinMode(2, OUTPUT);
      digitalWrite(2, LOW);
      delay(100);
      digitalWrite(2, HIGH);
      delay(100);
      
      for (int i=0;i<16;i++) ay_out(i,0);
    }
    
    
    void setupAYclock(){
      pinMode(3, OUTPUT);
      TCCR2A = 0x23;
      TCCR2B = 0x09;
      OCR2A = 8;
      OCR2B = 3; 
    }
    
    void setup() {
      Serial.begin(9600);
      randomSeed(analogRead(4)+analogRead(5));
    
      initFile();
    
      setupAYclock();
      resetAY();
      setupTimer();
    }
    
    void setupTimer(){
      cli();
      
      TCCR1A = 0;
      TCCR1B = 0;
      TCNT1  = 0;
      OCR1A = 1250;
    
      TCCR1B |= (1 << WGM12);
      TCCR1B |= (1 << CS12);
      TIMSK1 |= (1 << OCIE1A);
    
      sei();
    }
    
    void ay_out(unsigned char port, unsigned char data){
      PORTB = PORTB & B11111100;
    
      PORTC = port & B00001111;
      PORTD = PORTD & B00001111;
    
      PORTB = PORTB | B00000011;
      delayMicroseconds(1);
      PORTB = PORTB & B11111100;
    
      PORTC = data & B00001111;
      PORTD = (PORTD & B00001111) | (data & B11110000);
    
      PORTB = PORTB | B00000010;
      delayMicroseconds(1);
      PORTB = PORTB & B11111100;
    }
    
    unsigned int playPos = 0;
    unsigned int fillPos = 0;
    const int bufSize = 200;
    byte playBuf[bufSize]; // 31 bytes per frame max, 50*31 = 1550 per sec, 155 per 0.1 sec
    
    File fp;
    boolean playFinished = false;
    
    void loop() {
      fillBuffer();
      if (playFinished){
        fp.close();
        openRandomFile();
        playFinished = false;
      }
    }
    
    void fillBuffer(){
      int fillSz = 0;
      int freeSz = bufSize;
      if (fillPos>playPos) {
        fillSz = fillPos-playPos;
        freeSz = bufSize - fillSz;
      }
      if (playPos>fillPos) {
        freeSz = playPos - fillPos;
        fillSz = bufSize - freeSz;
      }
      
      freeSz--; // do not reach playPos
      while (freeSz>0){
        byte b = 0xFD;
        if (fp.available()){
          b = fp.read();
        }
        playBuf[fillPos] = b;
        fillPos++;
        if (fillPos==bufSize) fillPos=0;
        freeSz--;
      }
    }
    
    void prepareFile(char *fname){
      Serial.print("prepare [");
      Serial.print(fname);
      Serial.println("]...");
      
      fp = SD.open(fname);
      
      if (!fp){
        Serial.println("error opening music file");
        return;
      }  
        
      while (fp.available()) {
        byte b = fp.read();
        if (b==0xFF) break;
      }
     
      fillPos = 0;
      playPos = 0;
      cli();  
      fillBuffer();
      resetAY();
      sei();
    }
    
    File root;
    int fileCnt = 0;
    
    void openRandomFile(){
      int sel = random(0,fileCnt-1);
    
      Serial.print("File selection = ");
      Serial.print(sel, DEC);
      Serial.println();
      
      root.rewindDirectory();
      
      int i = 0;
      while (true) {
        File entry =  root.openNextFile();
        if (!entry)  break;
    
        Serial.print(entry.name());
        if (!entry.isDirectory()) {
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
          
          if (i==sel) prepareFile(entry.name());
          i++;
        }
        entry.close();
      }
    }
    
    void initFile(){
      Serial.print("Initializing SD card...");
      pinMode(10, OUTPUT);
      digitalWrite(10, HIGH);
    
      if (!SD.begin(10)) {
        Serial.println("initialization failed!");
        return;
      }
      Serial.println("initialization done.");
    
      root = SD.open("/");
    
      // reset AY
    
      fileCnt = countDirectory(root);
      Serial.print("Files cnt = ");
      Serial.print(fileCnt, DEC);
      Serial.println();
      openRandomFile();
    
      Serial.print("Buffer size = ");
      Serial.print(bufSize, DEC);
      Serial.println();
      
      Serial.print("fillPos = ");
      Serial.print(fillPos, DEC);
      Serial.println();
    
      Serial.print("playPos = ");
      Serial.print(playPos, DEC);
      Serial.println();
    
      for (int i=0; i<bufSize;i++){
        Serial.print(playBuf[i],HEX);
        Serial.print("-");
        if (i%16==15) Serial.println();
      }
    
      Serial.println("done!");
    }
    
    int countDirectory(File dir) {
      int res = 0;
      root.rewindDirectory();
      while (true) {
    
        File entry =  dir.openNextFile();
        if (!entry)  break;
    
        Serial.print(entry.name());
        if (!entry.isDirectory()) {
          Serial.print("\t\t");
          Serial.println(entry.size(), DEC);
          res++;
        }
        entry.close();
      }
      
      return res;
    }
    
    int skipCnt = 0;
    
    ISR(TIMER1_COMPA_vect){
      if (skipCnt>0){
        skipCnt--;
      } else {
        int fillSz = 0;
        int freeSz = bufSize;
        if (fillPos>playPos) {
          fillSz = fillPos-playPos;
          freeSz = bufSize - fillSz;
        }
        if (playPos>fillPos) {
          freeSz = playPos - fillPos;
          fillSz = bufSize - freeSz;
        }
    
        boolean ok = false;
        int p = playPos;
        while (fillSz>0){
          byte b = playBuf[p];
          p++; if (p==bufSize) p=0;
          fillSz--;
          
          if (b==0xFF){ ok = true; break; }
          if (b==0xFD){ 
            ok = true; 
            playFinished = true;
            for (int i=0;i<16;i++) ay_out(i,0);
            break; 
          }
          if (b==0xFE){ 
            if (fillSz>0){
              skipCnt = playBuf[p];
              p++; if (p==bufSize) p=0;
              fillSz--;
              
              skipCnt = 4*skipCnt;
              ok = true; 
              break; 
            } 
          }
          if (b<=252){
            if (fillSz>0){
              byte v = playBuf[p];
              p++; if (p==bufSize) p=0;
              fillSz--;
              
              if (b<16) ay_out(b,v);
            } 
          }
        } // while (fillSz>0)
      
        if (ok){
          playPos = p;
        }
      } // else skipCnt 
    }
    

    Для полной автономности я ещё добавил усилитель на TDA2822M, сам проигрыватель потребляет 90 мА, вместе с усилителем — около 200 мА, при желании можно питать от аккумуляторов.



    Обе макетки вместе:


    Вот на этом этапе я пока остановился, музыку слушаю с макетки, раздумываю в каком корпусе я бы хотел это собрать. Думал подключить индикатор, но необходимости как-то не испытываю.

    Реализация пока сыровата, т.к. устройство в состоянии разработки, но т.к. я могу его забросить на пару лет в таком состоянии, решил написать статью по горячим следам. Вопросы, предложения, замечания, исправления — приветствую в комментариях.

    Использованная литература:

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 53

      +1
      Взрывозащищенное исполнение. Отлично!
        0
        Вот что-то подобное я очень хотел.

        Однако, опять же что-то сырое. Хочу автономный плеер. Тёплый и ламповый :)
          +1
          Сырое, но работает. Автономный, тёплый и ламповый, что не так-то?
            0
            Пока устройство не имеет корпуса и не используется, значит устройства нет. Я уже писал об этом.

            geektimes.ru/post/257212
              +1
              Спасибо, теперь понятно. Но я не разделяю вашу точку зрения.
              Я делаю корпус из того, что под руку придётся, это сильно экономит время.

              Вот например моё готовое устройство RFID-сканер:

                0
                Крутое устройство, респект. Что внутрях? Я тоже делал такую читалку.

                Не важно из чего и как делается. Важно, чтобы устройство было не на соплях на столе, а законченным.
                  0
                  Arduino+RDM6300. Планировал копировщик, но случайно собрал на другой базе (в другом корпусе собран), поэтому пока остался ридером. Как скучно будет — допилю копирование.

                    0
                    У меня приятель сделал пассивный эмулятор таких меток на AVR.

                    Можно было не вешать ещё одну платку, а сделать всё на АВР. Я делал.
                      0
                      Поделишься исходниками твоего проекта на AVR? Я бы много времени сэкономил.
                        0
                        Которого, эмуля или ридера? Ридер постараюсь найти, но с кучей переездом и прочим могли пролюбится. А вот эмуль найду.
                          0
                          Давай в личку, а то какой-то оффтоп пошёл?
                            0
                            Может в этом офтопе кто-то найдёт для себя что-то полезное. После обеда всё скину сюда.
                              0
                              Хорошо. Софтовый ридер интереснее, чем эмуль, но кидай всё, что не жалко.
                                +1
                                Вот сам эмулятор (то с чего мы начинали).

                                Сишный код
                                #include <avr/io.h>
                                #include <avr/sfr_defs.h>
                                #include <compat/ina90.h>
                                #include <avr/interrupt.h>
                                #include <stdlib.h>
                                #include <avr/sleep.h>
                                
                                #ifndef cbi
                                #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
                                #endif
                                #ifndef sbi
                                #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
                                #endif
                                
                                volatile int bit_counter=0;
                                volatile int byte_counter=0;
                                volatile int half=0;
                                
                                unsigned char data_card[5][8] = {{0xFF,0xC5,0x38,0x2D,0x19,0xD1,0xC9,0x5A},    //test
                                                                {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},     // other cards
                                				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},    
                                				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},    
                                				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};    
                                
                                unsigned char data[8];
                                
                                void delay_ms(int ms) {              // 1.000 MHz
                                  int i;
                                  for (i=0; i < ms; i++) {
                                	TCNT0=0;
                                	while (TCNT0 <  125);
                                  }
                                }
                                
                                ISR(SIG_OUTPUT_COMPARE1A){
                                    TCNT1=0;
                                
                                //      if(byte_counter==0) sbi(PORTB,PD1);
                                //        else cbi(PORTB,PD1);
                                
                                	if (((data[byte_counter] << bit_counter)&0x80)==0x00) {
                                	    if (half==0) cbi(PORTB,PB1);
                                	    if (half==1) sbi(PORTB,PB1);
                                	}
                                	else {
                                	    if (half==0) sbi(PORTB,PB1);
                                	    if (half==1) cbi(PORTB,PB1);
                                	}
                                    
                                	half++;
                                	if (half==2) {
                                	    half=0;
                                	    bit_counter++;
                                	    if (bit_counter==8) {
                                	        bit_counter=0;
                                	        byte_counter=(byte_counter+1)%8;
                                		}
                                	}
                                }
                                
                                 
                                int main(void) {
                                  int i;
                                  DDRB=0xFF;
                                  DDRC=0x00;
                                  DDRD=0x00;
                                  PORTC=0x00;
                                
                                  TCCR0 = 2;             // timer0 clock/8 
                                //  TCCR1B = 2;		 // timer1 clock/8
                                  TCCR1B = 3;
                                
                                //  OCR1A = 254;
                                //  OCR1A = 32;   //32           
                                
                                  unsigned char sel = (PINC >> 1) & 0x07;
                                  OCR1A = 30 + sel;
                                  
                                //  for (i=0;i<8;i++) data[i]=data_card[sel][i];
                                  for (i=0;i<8;i++) data[i]=data_card[0][i];
                                  
                                  sbi(TIMSK, OCIE1A);	 // timer1 Output Compare A match
                                
                                  sei();
                                
                                  while (1) {
                                
                                  }
                                    return 0;  
                                }
                                
                                



                                Makefile
                                MCU=atmega8
                                CC=avr-gcc
                                OBJCOPY=avr-objcopy
                                PROJECT=rfid
                                CFLAGS=-g -mmcu=$(MCU) -Wstrict-prototypes -Os -mcall-prologues -Wall
                                
                                #-------------------
                                all: $(PROJECT).hex
                                #-------------------
                                $(PROJECT).hex : $(PROJECT).out 
                                	$(OBJCOPY) -R .eeprom -O ihex $(PROJECT).out $(PROJECT).hex 
                                $(PROJECT).out : $(PROJECT).o 
                                	$(CC) $(CFLAGS) -o $(PROJECT).out -Wl,-Map,$(PROJECT).map $(PROJECT).o 
                                $(PROJECT).o : $(PROJECT).c 
                                	$(CC) $(CFLAGS) -Os -c $(PROJECT).c
                                asm : $(PROJECT).c 
                                	$(CC) $(CFLAGS) -O -S $(PROJECT).c
                                # you need to erase first before loading the program.
                                # load (program) the software into the EEPROM:
                                load: $(PROJECT).hex
                                	avrdude -c butterfly -P /dev/ttyUSB0 -b 115200 -p m8 -U flash\:w\:$(PROJECT).hex\:a
                                #	uisp --erase --upload --verify if=$(PROJECT).hex -dprog=avr910 -dserial=/dev/ttyUSB0 -dspeed=115200 -dpart=auto  -v=3 --hash=12 
                                
                                #-------------------
                                #	different programmers and fuse programming if necessary
                                #	uisp -dserial=/dev/ttyUSB0 -dspeed=115200 --erase --upload --verify if=$(PROJECT).hex -dprog=avr910  -v=3 --hash=12 -dpart=auto
                                #	uisp --erase --upload --verify if=$(PROJECT).hex -dprog=dapa  -v=3 --hash=12 
                                #	uisp -dprog=dapa --wr_fuse_l=0x5f --wr_fuse_h=0xd9 -v=3
                                #-------------------
                                clean:
                                	rm -f *.o *.map *.out *.hex
                                #-------------------
                                



                                В инете код не найти, автор того документа лично мне его прислал (автор чех). Сейчас ищу сайт моего приятеля из Германии, который сделал этот эмулятор со многими карточками. Его вариация схемы вот ru-radio-electr.livejournal.com/637984.html

                                Плюс ещё ищу пассивный эмулятор.
                                  +1
                                  ww1.microchip.com/downloads/en/AppNotes/00680b.pdf — отличная пдф-ка о расчётах антенн.

                                  Мой эмулятор выглядел вот так

                                  image

                                  Антенну считал и мотал к нему по мануалу. В ЖЖ можно найти описание как я это делал.

                                  Собственно говоря делался по этому мануалу

                                  Камрад из Германии скрестил эти два проекта и получил такую петрушку

                                  image

                                  Нашёл таки его сайт www.alexanderguthmann.de/en/emulator.html

                                  Инфы море, поэтому можно пошерстить мой ЖЖ по тегу dlinyj.livejournal.com/tag/rfid (там много страниц).
                                  0
                                  Про ридер я нагнал :(
                                    0
                                    Хотя вру, по моему вот этот пепелац, что на фото выше, может и читать и эмулировать
            0
            «развязочный конденсатор по питанию — и работать стало в 100% случаев. Ладно, едем дальше…» поясните пожалйста схемой что сделали. А то тоже требовалось читать с SD карты. Вдруг решит проблему.
              +1
              Все выводы питания SD-карты и ардуино собрал как можно ближе друг к другу и в этом месте поставил конденсатор 10 микрофарад:

                0
                Ещё хорошо поставить керамику 0,1 мкФ
                  +1
                  Если посмотреть на фото макетки, то она там стоит чуть левее, ближе к сопру, чтобы по звуку было меньше помех.
                    0
                    Я просто комментатору отметил, чтобы он имел это в виду.

                    Когда я был мелкий, то экономил на кондёрах по питанию. В результате всегда имел много весёлых граблей :)))
                      +1
                      Просто натыкать 0.1 не всегда полезно, а иногда и даже вредно. Зависит от спектра помех и резонансных частот конденсаторов. Вот хорошая статья: О развязке питания с примерами с интересными комментариями.
                        0
                        Я уже и забыл про него :)
                  0
                  между плюсом и минусом?
                0
                Простите, я наверно слишком молод и не понимаю классики (никогда не было спектрума), но почему нельзя просто перегнать мелодии в mp3 и слушать с буквально чего угодно? Этому есть какие-то обьективные препятствия кроме «тёплый ламповый звук»?

                Пожалуйста, поймите меня правильно — мне действительно интересно зачем нужно городить огород вокруг динамика?
                  +1
                  Это из разряда почему люди слушают музыку на ламповых усилителях, а не ставят программный звуковой фильтр «лампы» ;)
                    +1
                    Я так и подумал. А разве ардуино не разрушает «ламповость» звука? хД
                      +1
                      Тут важен оригинальный звуковой чип, который собственно говоря и используется.
                        –3
                        А разве lossless формат не дает звук лучшего качества чем может выдать чип?
                        Или это как лампы подсвеченные светодиодами дающие к окраске звука?
                          +1
                          Мне кажется вы не понимаете механизм трекерной музыки, раз задаёте такие вопросы.

                          Если вы хотите потроллить автора поста, то для начала разберитесь в матчасти.
                            +1
                            Я понимаю как работает трекерная музыка. Я не собираюсь тролить автора. Мне правда интересно есть ли разница между звучанием чипа в «живую» или записанного с него звука в lossless(она конечно будет, но будет ли она заметна). Понятно что трекерный файлик будет во много раз меньше чем lossless, но устройств которые могут его проиграть намного больше.
                              0
                              Тут мы упираемся в другую проблему — это перегонку звука. Вопрос в том, как сделать её наиболее тёплой и ламповой? А главное целессобразности этой перегонки, если для её осуществления надо делать такую же приблуду. => проще использовать данную приблуду просто из коробки, не морочась с форматами
                                0
                                Подключить к выходу этой макетки любой комп с нормальной звуковой картой и писать в lossless. Дальше загнать на любой плеер и наслаждаться. Это, если что, не руководство к действию, а просто альтернатива «спять плеер наполовину, забросить его на два года»
                                  –1
                                  Спасибо большое Капитан Очевидность. Выше я как раз об этом говорил.
                            0
                            >А разве lossless формат не дает звук лучшего качества чем может выдать чип?
                            Ещё совсем недавно люди, собирая компьютер, выбирали звуковую карту, и в многочисленных обзорах сравнивали всякие MIDI банки и их звучание… как сейчас помню AWE32 и т.п. Типа, если вы хотите насладиться настоящим качеством музыки в вашей любимой игре выбирайте супер мега карту и получите оркестровое качество. С чего бы это, если везде одна и та же цифра (и речь тут не о тёплых ламповых проводах из бумаги)?
                            Продолжаем тему: формат MOD, вот чем он отличается от Wav, к примеру?
                            Более современное: Csound http://csound.github.io/documentation.html
                            Ну а теперь переходим к чиповой музыке и, внезапно, оказывается, что это вовсе не музыка, а программа для чипа и в общем случае, мы можем только логи писать и там где было 100 байт программы, у нас запишутся мегабайты логов. Потом смотрим на параметры чипа, и выясняется ещё и что 44КГц мало, что бы эмулировать по этим логам вывод чипа и один в один всё равно не получится, то есть будет играть огромную роль, качество софта, осуществляющего приближение, то есть речь, о LossLess здесь уже не идёт, но да — можно очень точно воспроизвести этот раздутый лог.
                              0
                              Ну сейчас ясное дело цифра, хотя и с разной частотой дискритизации. А тогда если в игре были не mp3, а midi то можно было слышать или пластмассовое детское пианино или сэмплы реального. Хотя и та и та цифровая музыка.
                      +2
                      Этот вопрос можно задать большинству DIY разработок.
                      У меня два ответа, серьёзный и не очень, но гораздо ближе к правде:

                      1. Получить полезный навык и показать другим интересный несложный проект.
                      2. Мне так захотелось.
                        +1
                        Да я ж не в претензии — я просто не знал что используется оригинальный звуковой чип (к стати, совсем оригинальный, или «точно такой-же»?), как указал dlinyj

                        И оффтоп: как мне кажется, мотивация «мне так захотелось» намного более полезная чем все остальное. Разумеется, когда речь идет о чем-то более конструктивном чем тупое лежание на диване
                          0
                          Чип самый что ни на есть оригинальный.
                          Причём рядом лежит его собрат — YM2149F, который подключается похожим образом, но имеет чуть более другие звуковые оттенки, из-за которых было немало «тёплых ламповых срачей». Вот тоже буду сравнивать.
                            0
                            Вообще, я бы послушал вживую. Не могу сказать, что фанат, но интересно
                              +1
                              Вот теперь жди пока я в корпусе их оба не соберу.
                              Тебе же нельзя без корпуса :-)
                                +1
                                Вот у меня тоже уже больше года лежит YM2149F купленный наебэй под ЛПТ-проигрыватель… Готов посмотреть на вашу реализацию ;) Ардуинов и шилдов в запасах ну просто завались!
                                  0
                                  Где искать и по чём? Под линуксом интересно работает?
                                    +1
                                    Так что там смотреть — просто подключаете YM вместо AY и вперёд, номера выводов другие, а программирование точно такое же.

                                    Я вот думаю их подключить одновременно параллельно, один в левое, другой в правое ухо — тогда может и усышу разницу.
                                    0
                                    Тролинг засчитан
                          +2
                          Я, возможно, слепой, но не нашел способа написать вам в личку. Есть такой замечательных архив музыки как zxtunes dot com. Вам должно понравиться.
                            0
                            Спасибо, я знаю про этот сайт. Так как у меня громадный архив трекерной музыки, то там ничего нового я не увидел, но мне очень понравились ремиксы, особенно Putzi — Crazy Comets и Saanvi — Ramparts!
                              0
                              Хорошо. Просто авторы этого сайта стучались ко многим авторам персонально и многое из музыки на этом сайте никогда нигде не публиковалось. А ремиксы я не замечал. Действительно очень круто!
                            +1
                            Повторил на Yamaha YM2149. Всё работает.
                            Спасибо автору за интересную статью.
                            Скрытый текст

                              0
                              Спасибо что написали об этом. Приятно знать, что не зря потратил время на написание статьи.
                                0
                                Повторил на YM2149F пока на макетке, но в девайс обязательно оформлю. Доволен как слон. Спасибо за статью.

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

                            Самое читаемое