目次

電子木琴

 MIDIに関係する内容を調べているとき、電子木琴を知りました。




 MIDIを使わないで、スタンドアローンの木琴は
 キットで売られているので、Arduinoではなく
 PICで実現してみます。

 半田付けすると以下。




 A/Dコンバータの1ポートを2種の情報を入力できる
 ようにしました。

 OPアンプは、LM324を使って電圧フォロワとします。



 PIC12F1501を利用するとし、4ビットをA/D変換器の
 入力に使い、1ビットを出力にします。



 プログラムは、2つのブロックで構成しました。

 入力キーの判定は、電源を入れたときに、4つの
 電圧フォロワが出力している電圧値を記憶して
 常にそれらから外れているかを計算します。

 発音は、カウンタの分周比の切り替えで実現。

 プログラムは以下。

/*
  electrical xyrophone

  system clock 16MHz

  PORTA.F0 code_in_block_0(A B)
  PORTA.F1 code_in_block_1(C D)
  PORTA.F2 code_in_block_2(E F)
  PORTA.F3 nRESET
  PORTA.F4 code_in_block_3(G A)
  PORTA.F5 sound_out

  frequency data <initial value>
    A  440Hz <65081>
    A# 466Hz <65107>
    B  493Hz <65130>
    C  523Hz <65154>
    C# 554Hz <65175>
    D  587Hz <65195>
    D# 622Hz <65214>
    E  659Hz <65233>
    F  698Hz <65249>
    F# 739Hz <65265>
    G  783Hz <65281>
    G# 830Hz <65295>
    A  880Hz <65309>

*/
typedef unsigned char UBYTE ;
typedef unsigned int  UWORD ;

/* divider */
#define  Xsound0 65081
#define  Xsound1 65130
#define  Xsound2 65154
#define  Xsound3 65195
#define  Xsound4 65233
#define  Xsound5 65249
#define  Xsound6 65281
#define  Xsound7 65309

typedef union {
  struct {
    unsigned char B0:1;
    unsigned char B1:1;
    unsigned char B2:1;
    unsigned char B3:1;
    unsigned char B4:1;
    unsigned char B5:1;
    unsigned char B6:1;
    unsigned char B7:1;
  } BIT ;
  unsigned char DR ;
} FLAGSP ;

volatile FLAGSP x_flags ;

#define EFLAG x_flags.DR.B0
#define TFLAG x_flags.DR.B1
#define SFLAG x_flags.DR.B2
#define OFLAG x_flags.DR.B3
#define XOUT  x_flags.DR.B4

#define OFF 0
#define ON  OFF+1

#define CNTBEGIN 6

volatile UBYTE cstate ;
volatile UBYTE pstate ;
volatile UBYTE xcnt ;

volatile UWORD nsx[2] ;
volatile UWORD adv[4] ;
volatile UWORD xadv[4] ;

/* function prototype */
void  init_usr(void);
UWORD get_adv(UBYTE x);

/* interrupt handler */
void interrupt(void)
{
  /* timer0 overflow */
  if ( INTCON.T0IF == ON ) {
    /* clear flag */
    INTCON.T0IF = OFF ;
    /* initialize */
    TMR0 = CNTBEGIN ;
    /* set flag */
    EFLAG = ON ;
  }
  /* timer1 overflow */
  if ( PIR1.TMR1IF == ON ) {
    /* clear flag */
    PIR1.TMR1IF = OFF ;
    /* initialize */
    TMR1H = nsx[0] ;
    TMR1L = nsx[1] ;
    /* set flag */
    TFLAG = ON ;
  }
}

void main(void)
{
  /* initialize */
  init_usr() ;
  /* endless loop */
  while ( ON ) {
    /* first code */
    if ( OFLAG == ON ) {
      /* clear flag */
      OFLAG = OFF ;
      /* get 4 pin voltage code */
      *(adv+0) = get_adv(0);
      *(adv+1) = get_adv(1);
      *(adv+2) = get_adv(2);
      *(adv+3) = get_adv(3);
      *(xadv+0) = *(adv+0);
      *(xadv+1) = *(adv+1);
      *(xadv+2) = *(adv+2);
      *(xadv+3) = *(adv+3);
    }
    /* event handling */
    if ( EFLAG == ON ) {
      /* clear flag */
      EFLAG = OFF ;
      /* update */
      *(xadv+0) = get_adv(0);
      *(xadv+1) = get_adv(1);
      *(xadv+2) = get_adv(2);
      *(xadv+3) = get_adv(3);
      /* default */
      cstate = 0 ;
      /* judge state */
      if ( *(xadv+0) < *(adv+0)-10 ) { cstate =  8 ; }
      if ( *(xadv+0) > *(adv+0)+10 ) { cstate =  9 ; }
      if ( *(xadv+1) < *(adv+1)-10 ) { cstate = 10 ; }
      if ( *(xadv+1) > *(adv+1)+10 ) { cstate = 11 ; }
      if ( *(xadv+2) < *(adv+2)-10 ) { cstate = 12 ; }
      if ( *(xadv+2) > *(adv+2)+10 ) { cstate = 13 ; }
      if ( *(xadv+3) < *(adv+3)-10 ) { cstate = 14 ; }
      if ( *(xadv+3) > *(adv+3)-10 ) { cstate = 15 ; }
      /* judge */
      if ( pstate == 0 && cstate > 0 ) {
        SFLAG = ON ;
        if ( cstate == 8 ) {
          *(nsx+0) = Xsound0 / 256 ;
          *(nsx+1) = Xsound0 % 256 ;
        }
        if ( cstate == 9 ) {
          *(nsx+0) = Xsound1 / 256 ;
          *(nsx+1) = Xsound1 % 256 ;
        }
        if ( cstate == 10 ) {
          *(nsx+0) = Xsound2 / 256 ;
          *(nsx+1) = Xsound2 % 256 ;
        }
        if ( cstate == 11 ) {
          *(nsx+0) = Xsound3 / 256 ;
          *(nsx+1) = Xsound3 % 256 ;
        }
        if ( cstate == 12 ) {
          *(nsx+0) = Xsound4 / 256 ;
          *(nsx+1) = Xsound4 % 256 ;
        }
        if ( cstate == 13 ) {
          *(nsx+0) = Xsound5 / 256 ;
          *(nsx+1) = Xsound5 % 256 ;
        }
        if ( cstate == 14 ) {
          *(nsx+0) = Xsound6 / 256 ;
          *(nsx+1) = Xsound6 % 256 ;
        }
        if ( cstate == 15 ) {
          *(nsx+0) = Xsound7 / 256 ;
          *(nsx+1) = Xsound7 % 256 ;
        }
        T1CON.TMR1ON = ON ;
      }
      if ( pstate > 0 && cstate == 0 ) {
        SFLAG = OFF ;
        T1CON.TMR1ON = OFF ;
      }
      /* update */
      pstate = cstate ;
    }
    /* sound flashing */
    if ( TFLAG == ON ) {
      /* clear flag */
      TFLAG = OFF ;
      /* increment */
      xcnt++ ;
      /* judge */
      XOUT = OFF ;
      if ( xcnt & ON ) { XOUT = ON ; }
    }
    /* sound */
    if ( SFLAG == OFF ) { XOUT = OFF ; }
    PORTA.F5 = XOUT ;
  }
}

/* define function body */
void init_usr(void)
{
  /* 16MHz */
  OSCCON = (0x0f << 3) | 0x03 ;
  /* disable D/A converter */
  DACCON0.DACEN = OFF ;
  /* disable compare module */
  CM1CON0.C1ON = OFF ;
  CM1CON0.C1OE = OFF ;
  /* initialize A/D converter */
  ADCON0.ADON = OFF ;
  ANSELA      = 0x17 ;
  ADCON2      = 0xa0 ;
  /* I/O state */
  PORTA = 0 ;
  /* I/O directions */
  TRISA = 0x1f ; /* bit5 as output , others as input */
  /* initialize Timer 0 */
  {
    /*
       16MHz/4 = 4MHz -> 4MHz/8 = 500kHz prescaler = 1:8
    */
    OPTION_REG = 0x02 ;
    /* 256 - 6 = 250 */
    TMR0 = CNTBEGIN ;
    /* enable timer0 overflow interrupt */
    INTCON.T0IE = ON ;
  }
  /* initialize Timer 1 */
  {
    /* 
       TMR1CS = 00 (16MHz/4=4MHz)
       T1CKPS = 01 (4MHz/2=2MHz)
       disable 
    */
    T1CON = 0x10 ;
    /* clear */
    TMR1H = 0 ;
    TMR1L = 0 ;
    /*
       enable overflow interrrupt
    */
    PIE1.TMR1IE = ON ;
    /* enable peripheral interrupt */
    INTCON.PEIE = ON ;
  }
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* initialize flags */
  x_flags.DR = 0 ;
  OFLAG = ON  ;
  /* initialize variables */
  xcnt = 0 ;
  cstate = 0 ;
  pstate = 0 ;
  *(adv+0) = 0 ;
  *(adv+1) = 0 ;
  *(adv+2) = 0 ;
  *(adv+3) = 0 ;
}

UWORD get_adv(UBYTE x)
{
  UWORD result ;
  UBYTe r[2] ;
  /* set channel */
  ADCON0 = (x << 2) ;
  /* start */
  ADCON0.ADON = ON ;
  /* wait */
  while ( ADCON0 & 2 ) ;
  /* get result */
  *(r+1) = ADRESL ;
  *(r+0) = ADRESH ;
  /* concatenate */
  result = *(r+0) << 8 | *(r+1) ;

  return result;
}

 タイマー割込みは、タイマー0とタイマー1を利用。
 タイマー0は、キー入力のタイミングを決めます。

 タイマー1をオーバーフローで利用して
 発音周波数の2倍でスピーカに1か0を
 出力。

 2つの状態変数pstate、cstateの比較でキーの入力と
 変更を検出します。


目次

inserted by FC2 system