SSブログ

Digisparkで音楽演奏【PLL OC1A編】 [Arduino]

以前のスケッチを手直し。
ボードマネージャは、ATTinyCore でも Digistump AVR Boards でも行けるようにしてみた。
8MHzでもなんとか遅延はなさそうで、乾電池2本でも動作できるはず。

dsoutput.jpg
結局もう1つ改造Digispark作成。
壊れたイヤホンからケーブルをリユースして録音用パーツを作成。



以下、スケッチ。
//  Digispark version Score Replay Sketch
//    Clock : 16.5/16/8 MHz, PLL : 66/64 MHz
#include "notes2.h"                     // Definition data of notes ( pitch (scale & octave), and note value (length))
#include "Kewpie3min.h"                 // "The Parade of the Tin Soldiers" by Leon Jessel
#define MAX_TRACK   4                   // Maximum number of tracks

// Number of cycles that are the wavelength of the basic 12-note scale (from C0 to B0) (@ 8 MHz, with an interrupt every 256 clocks)
static const uint16_t CYC_SCALE[] = { 1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204, 1136, 1073, 1012 };

void setup(){
  pinMode( 1, OUTPUT );                 // Audio output (PB1,OC1A)
  pinMode( 0, INPUT_PULLUP );           // Switch       (PB0)
  
  // *** Register Settings (Timer1, for DAC)  *** PLL 64MHz, No prescaling. 8bit=256clock to make PWM, so 250kHz. 
  TIMSK  &= ~_BV(TOIE1);        // TIMSK(T/C Interrupt Mask Register)      - TOIE1(T/C1 Overflow Interrupt Enable) CLEAR : Disable ISR(TIMER1_OVF_vect) (for Digistump AVR Boards)
  PLLCSR |=  _BV(PCKE);         // PLLCSR(PLL Control and Status Register) - PCKE(PCK Enable) SET : PCK clock(fast 64 MHz) is used as T/C1 clock source
  TCCR1   = B01100001;          // TCCR1(T/C1 Control Register) : PWM A Enable. OC1A cleared on compare match. OC1A not connected. No prescale
  OCR1A   = 0;                  // OCR1A(T/C1 Output Compare RegisterA) : Set output to 0
  OCR1C   = 255;                // OCR1C(T/C1 Output Compare RegisterC) : Set upper limit to 255
  // *** Register Settings (Timer0, for Data) *** Data processing interval 31.25kHz(256clk@8MHz or 512clk@16MHz)
  TIMSK  &= ~_BV(TOIE0);        // TIMSK(T/C Interrupt Mask Register)      - TOIE0(T/C0 Overflow Interrupt Enable) CLEAR : Disable ISR(TIMER0_OVF_vect) (for ATTinyCore)
  TCCR0A  = B00000011;          // TCCR0A(T/C0 Control Register A) :  OC0A/OC0B disconnected, Fast PWM
  TCCR0B  = B01001010;          // TCCR0B(T/C0 Control Register B) :  Fast PWM, TOP:OCR0A, clk/8 (From prescaler)
  OCR0A   = F_CPU / 250000;     // OCR0A (T/C0 Output Compare A Register) : TOP 8MHz:32, 16MHz:64, 16.5MHz:66
}

void loop(){
  while(  digitalRead(0) );             // Wait until the switch is pressed
  while( !digitalRead(0) );             // Wait until the switch is released
  playDsPLL( Kewpie3min );              // Playback
}

void playDsPLL(const uint16_t *d){      // Digispark PLL version (16.5/16/8 MHz support)
  uint8_t  Tracks;                      // Number of tracks
  const uint16_t *NoteP[MAX_TRACK];     // Pointer for each track of the score
  uint16_t NoteCycle, n;                // Number of interrupt cycles required for the length of the reference note (96th note) and its counter (n)
  uint16_t note;                        // Note (pitch + note value) information read from PROGMEM
  uint8_t  len[MAX_TRACK];              // Length of note (how many 96th notes) (subtraction counter)
  uint16_t cyc[MAX_TRACK], c[MAX_TRACK];// Number of interrupt cycles (cyc) required for one sound wavelength cycle and its counter (c)
  uint16_t env[MAX_TRACK];              // Sound amplitude (envelope)
  uint16_t out[MAX_TRACK];              // Output value
  uint8_t  lap = 20;                    // Sound transitional cycles(laptime) (for Attenuation calculator)

  // *** Preparing Scores ***
  NoteCycle = 8000000 / 256 *4*60 / pgm_read_word_near(d++) / MIN_NOTE; // Number of cycles required for reference note
  for( Tracks = 0; Tracks < MAX_TRACK; ) {              // Get the number of tracks and the start position of each track from the song data
    if( pgm_read_word_near(d++) != 0 ) continue;        // Skip until the break comes
    if( pgm_read_word_near(d)   == 0 ) break;           // If two zeros follow, end of data
    len[ Tracks ] = 1;                                  // Initialize the note length subtraction counter to the remaining 1
    NoteP[ Tracks++ ] = d;                              // Get location in memory, Count up the number of tracks
  }
  n = Tracks;                           // Initialize the score processing so that it can be performed immediately after the start of the do loop

  // *** Playback ***
  do {
    // * Processing Scores *
    if( --n < Tracks ) {                // Processing of score for each reference note length
      if( !--len[n] ) {
        note   = pgm_read_word_near( NoteP[n]++ );
        len[n] = (uint8_t) note;        // The lower 8 bits are the length of the note (how many times the length of a 96th note)
        cyc[n] = (note>>8) ? CYC_SCALE[ (note>>8) & 0xf ] >> (note>>12) : 0;    // Upper 4 bits are octave, next 4 bits are pitch class (0-11), 0 for rests
          c[n] = 0;                     // Initialize the counter for the number of cycles to create one square wave cycle
        env[n] = 0xffff;                // Initially, the maximum amplitude
      }
      if( !n ) n = NoteCycle;
    }
    // * Waveform Processing / Output *
    switch( Tracks ) {                  // Create output data, Square wave with duty ratio of 1:1
      case 4:   out[3] = ( ( c[3] = (++c[3]==cyc[3]) ? 0 : c[3] ) < (cyc[3]>>1) ) ? env[3] : 0;   [[fallthrough]];
      case 3:   out[2] = ( ( c[2] = (++c[2]==cyc[2]) ? 0 : c[2] ) < (cyc[2]>>1) ) ? env[2] : 0;   [[fallthrough]];
      case 2:   out[1] = ( ( c[1] = (++c[1]==cyc[1]) ? 0 : c[1] ) < (cyc[1]>>1) ) ? env[1] : 0;   [[fallthrough]];
      case 1:   out[0] = ( ( c[0] = (++c[0]==cyc[0]) ? 0 : c[0] ) < (cyc[0]>>1) ) ? env[0] : 0;
    }
    switch( Tracks ) {                  // OC1A(ATtiny85:PB1) Change output by number of tracks
      case 1:   OCR1A =   out[0] >>8;                                   break;
      case 2:   OCR1A = ((out[0]>>1) + (out[1]>>1)) >>8;                break;
      case 3:   OCR1A = ((out[0]>>1) + (out[1]>>2) + (out[2]>>2)) >>8;  break;
      case 4:   OCR1A = ((out[0]>>2) + (out[1]>>2) + (out[2]>>2) + (out[3]>>2)) >>8;
    }
    switch( --lap ) {                   // Amplitude attenuation (Distribute processing per loop)
      case 4:   env[3] -= (env[3]>>9);    break;
      case 3:   env[2] -= (env[2]>>9);    break;
      case 2:   env[1] -= (env[1]>>9);    break;
      case 1:   env[0] -= (env[0]>>9);    break;
      case 0:   lap = 20;               // every 640usec(32usec*20) @16MHz (Adjustable)
    }
    while( !(TIFR & _BV(TOV0)) );       // Waiting for Timer0 overflow (every 32usec)
    TIFR |= _BV(TOV0);                  // Clear T/C0 Overflow Flag
  //if( ! digitalRead(0) ) break;       // Ends when PB0 is pressed.
    if( !(PINB & _BV(0)) ) break;       // Ends when PB0 is pressed.
  } while( note );                      // Exit if note data is 0
  OCR1A = 0;                            // Set output to 0
}

次は、矩形波じゃないやつで。
タグ:Digispark 音楽 PLL
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。