目次

マイコンミュージックシーケンサ

 アナログシンセサイザーの派生のひとつに
 ミュージックシーケンサがあります。



 音の周波数を電圧で変えるのは、VCO(Voltage Controlled Oscillator)
 で、NE555が担当しています。回路は以下。




 可変抵抗器がたくさん並んでいるのは、造形と
 しては面白いのですが、動かすたびに、異なる
 周波数になるのでは使いにくいと考えました。

 電圧を可変にするには、D/A変換器を使えばよいと
 考えて、D/A変換器を持ったマイコンを利用すれば
 ミュージックシーケンサになるはずと思ってました。

 D/A変換器を内蔵するマイコンとして、H8/3052FOneが
 あったので、ミュージックシーケンサに仕立ててみます。





 可変抵抗器に相当するD/A変換器が出力する値を
 格納しておくため、8バイトの配列を用意。

 GDLでの開発は、プログラミング言語としてCを
 使うので、次のようにデータタイプを再定義し
 利用します。

typedef unsigned char UBYTE ;

UBYTE xdac[8];

 H8内部にある、8バイトの配列にデータを格納する
 ために、シリアルインタフェースを利用。
 システム形態は、以下としておきます。



 シリアルでD/A変換器から出力する電圧の換算値を
 設定しなければならないので、1文字コマンドを
 次のように決めました。

 コマンドインタプリタは、以下のように定義。


 NE555をVCOとして、音出しをするには
 D/A変換値を、送り出す必要があります。

 自作のRTOS(Real Time Operating System)である
 USOを使い、音出しを担当させます。

 RTOSでは、タスクに機能を割り振ります。
 次のように音出しタスクを定義。

void tsk0_proc(void)
{
  /* impress */
  DAC0 = *(xdac+state);
  /* update */
  state++ ;
  /* judge */
  state &= 7 ;
  /* delay */
  wai_tsk( bcnt * 100 );
}

 時間待ちは、システムコール wai_tsk に
 任せてしまうので、単純です。

 コマンドインタプリタを、常時動いている
 タスクの中に入れ、音出しタスクを動かす
 停止するのいずれかを指定します。

 この方式で、タスク中にコマンドインタプリタ
 を入れると、次のようになります。

    /* command interpreter */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* enable */
    if ( cmd == 'B' ) {
      state = 0 ;
      /* enable */
      rsm_tsk( TSK_ID0 );
    }
    /* disable */
    if ( cmd == 'b' ) {
      /* disable */
      sus_tsk( TSK_ID0 );
      rsm_tsk( TSK_ID1 );
    }
    /* set delay counter */
    if ( cmd == 'D' ) {
      /* clear */
      bcnt = 0 ;
      /* calculate value */
      for ( i = 0 ; i < 3 ; i++ ) {
        /* get value */
        tmp = *(sbuf+1+i) ;
        /* judge */
        if ( tmp == '\r' ) break ;
        /* convert */
        bcnt = bcnt * 10 + get_hex( tmp );
      }
    }
    /* show delay counter */
    if ( cmd == 'd' ) {
      show_value( bcnt );
      rs1_crlf();
    }
    /* set dac value */
    if ( cmd == 'V' ) {
      /* get location */
      l = get_hex( *(sbuf+1) ) ;
      /* get value */
      v = 0 ;
      for ( i = 0 ; i < 3 ; i++ ) {
        /* get value */
        tmp = *(sbuf+2+i) ;
        /* judge */
        if ( tmp == '\r' ) break ;
        /* convert */
        v = v * 10 + get_hex( tmp );
      }
      /* store */
      *(xdac + l) = v ;
    }
    /* show dac value */
    if ( cmd == 'v' ) {
      /* DAC */
      for ( i = 0 ; i < 8 ; i++ ) {
        /* prompt */
        rs1_putchar( '0' + i );
        rs1_putchar( '-' );
        rs1_putchar( ' ' );
        /* get values */
        v = *(xdac+i) ;
        /* show */
        show_value( v ) ;
        /* new line */
        rs1_crlf();
      }
    }

 音出しを停止するには、音出しタスクを止めると
 ともに、D/A変換器からの電圧出力をゼロにする
 タスクを動かしています。
 このタスクの内容は、以下。

void tsk1_proc(void)
{
  /* impress */
  DAC0 = 0;
  /* exit */
  slp_tsk();
}

 システム動作を決めたので、2チャネルのVCOを
 制御するとして、まとめます。

#include "3052.h"

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;
typedef unsigned long  ULONG ;
typedef   signed char  SBYTE ;
typedef   signed short SWORD ;

#define NO  0
#define YES NO+1

typedef struct {
  void  (*tsk)(void);
  UWORD wcount ;
} TCBP ;

#define TSK_ID_MAX 3

#define TSK_ID0 0
#define TSK_ID1 1
#define TSK_ID2 2

#define XDEBUG TSK_ID8

#define TTS_SUSPEND 0
#define TTS_WAIT    TTS_SUSPEND+1
#define TTS_READY   TTS_SUSPEND+2

TCBP tcb[TSK_ID_MAX];

/*----------------*/
/* user variables */
/*----------------*/
#define ITU1_AREG 24999

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

FLAGSP xflags ;

ULONG timcnt ;

#define UFLAG xflags.BIT.B0
#define WFLAG xflags.BIT.B1

#define P1DDR P1.DDR
#define P1DR  P1.DR.BYTE
#define P2DDR P2.DDR
#define P2DR  P2.DR.BYTE
#define P3DDR P3.DDR
#define P3DR  P3.DR.BYTE
#define P4DDR P4.DDR
#define P4DR  P4.DR.BYTE
#define P5DDR P5.DDR
#define P5DR  P5.DR.BYTE
#define P6DDR P6.DDR
#define P6DR  P6.DR.BYTE
#define P8DDR P8.DDR
#define P8DR  P8.DR.BYTE
#define P7DR  P7.DR.BYTE
#define P9DDR P9.DDR
#define P9DR  P9.DR.BYTE
#define PADDR PA.DDR
#define PADR  PA.DR.BYTE
#define PBDDR PB.DDR
#define PBDR  PB.DR.BYTE

#define DAC0  DA.DADR0
#define DAC1  DA.DADR1

#define MASKFFFF 0xffff
#define MASKFF   0xff
#define MASKCF   0xcf
#define MASK0F   0x0f
#define MASK80   0x80
#define MASK03   0x03

#define OFF      0
#define ON       OFF+1

#define MASKF0 0xf0

UBYTE timeoutcnt ;

UBYTE state  ;

UBYTE run_tsk ;
UWORD ready   ;
UWORD suspend ;
UWORD waitq   ;
UWORD bpat[16] ;

void init_sci_1(TBaudRate x);
void rs1_putchar(UBYTE x);
void rs1_crlf(void);
void rs1_puts(UBYTE *x);
void show_help(void);
void show_value(UBYTE x);

UBYTE sindex ;
UBYTE sbuf[16];
UBYTE cmd ;
UBYTE asc_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} ;
UBYTE xdac[8] ;
UBYTE ydac[8] ;
UBYTE bcnt ;

/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void  init_usr(void);
void  init_timer1(void);
UBYTE get_hex(UBYTE x);

/*-------------*/
/* system call */
/*-------------*/
void  cre_tsk(UBYTE tid,void (*tsk)(void));
void  rsm_tsk(UBYTE tid);
void  sus_tsk(UBYTE tid);
void  slp_tsk(void);
void  wai_tsk(UWORD x);
void  init_os(void);
void  timer_handler(void);
UBYTE is_tsk_ready(UBYTE tid);

/*------*/
/* task */
/*------*/
void  tsk0_proc(void);
void  tsk1_proc(void);
void  tsk2_proc(void);

/*------*/
/* main */
/*------*/
int main(void)
{
  TCBP pcur_tsk ;
  /* disable interrupt */
  DI ;
  /* initialize */
  init_usr();
  /* initialize monitor */
  init_os();
  /* enable interrupt */
  EI ;
  /* opening message */
  rs1_puts("Hello");
  rs1_crlf();
  /* endless loop */
  run_tsk = TSK_ID0 ;
  while ( ON ) {
    /* RTOS */
    pcur_tsk = tcb[run_tsk] ;
    if ( is_tsk_ready( run_tsk ) ) { (*(pcur_tsk.tsk))(); }
    run_tsk++;
    if ( run_tsk == TSK_ID_MAX ) { run_tsk = TSK_ID0 ; }
    /* 10ms handling */
    if ( WFLAG == ON ) {
      WFLAG = OFF ;
      timer_handler();
    }
  }
  return 0 ;
}

/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void init_usr(void)
{
  UBYTE ii ;
  /* PORT 3 */
  P3DR  = 0x00 ;
  P3DDR = MASKFF ; /* all outputs */
  /* PORT 4 */
  P4DR  = 0x00 ;
  P4DDR = MASKFF ; /* all outputs */
  /* PORT 6 */
  P6DR  = 0x00 ;
  P6DDR = 0x00 ; /* all inputs */
  /* PORT A */
  PADR  = 0      ;
  PADDR = MASKFF ; /* all outputs */
  /* PORT B */
  PBDR  = 0xc0 ;
  PBDDR = 0xcf ; /* PB5,PB4 are inputs , others are outputs */
  /* initialize */
  init_timer1();
  /* clear flags */
  xflags.DR = 0 ;
  /* enable DAC */
  DA.DACR.BYTE |= 0xe0 ;
  /* clear */
  for ( ii = 0 ; ii < 8 ; ii++ ) {
    *(xdac+ii) = 0 ;
    *(ydac+ii) = 0 ;
  }
  /* initialize PWM */
  {
    ITU.TSTR.BIT.STR3 = (UBYTE)0x00; /* Stop the counter */
  }
  /* clear SCI buffer */
  *(sbuf+0) = 0 ;
  sindex = 0 ;
  /* initialize */
  timcnt = 0 ;
  /* SCI */
  init_sci_1(br9600);
  /* others */
  state = 0 ;
  /* initialize counter */
  bcnt = 1 ;
}

void init_timer1(void)
{
  /* stop timer */
  ITU.TSTR.BIT.STR1 = OFF ;
  /* TOER : Timer Output Enable Register
        7 **** -> 0
        6 **** -> 0
        5 EXB4 -> 0
        4 EXA4 -> 0
        3 EB3  -> 0
        2 EB4  -> 0
        1 EA4  -> 0
        0 EA3  -> 0
  */
  ITU.TOER.BYTE = 0 ;
  /* TIOR : Timer I/O Control Register
        7 **** -> 0
        6 IOB2 -> 0 GRB is not output compare match register
        5 IOB1 -> 0
        4 IOB0 -> 0
        3 **** -> 0
        2 IOA2 -> 0 GRA is not output compare match register
        1 IOA1 -> 0
        0 IOA0 -> 0
  */
  ITU1.TIOR.BYTE = 0 ;
  /* TCR : Timer Control Register
        7 ****  -> 0
        6 CCLR1 -> 0 clear TCNT if GRA = TCNT
        5 CCLR0 -> 1
        4 CKEG1 -> 0 rising edge
        3 CKEG0 -> 0
        2 TPSC2 -> 0 φ利用
        1 TPSC1 -> 0
        0 TPSC0 -> 0
  */
  ITU1.TCR.BYTE = 0x20 ;
  /* TIER : Timer Interrupt Enable Register
        7 ****  -> 0
        6 ***   -> 0
        5 ***   -> 0
        4 ***   -> 0
        3 ***   -> 0
        2 OVIE  -> 0
        1 IMIEB -> 0
        0 IMIEA -> 1 select compare match interrupt
  */
  ITU1.TIER.BIT.IMIEA = ON ;
  /* reference */
  ITU1.GRA = ITU1_AREG ;
  ITU1.GRB = MASKFFFF ;
  /* counter */
  ITU1.TCNT = 0 ;
  /* start timer */
  ITU.TSTR.BIT.STR1 = ON ;
}

/*+++++++++++++++++++++++++++++++++++++*/
/* ITU1 interrupt with compare match A */
/*                     1ms interval */
/*+++++++++++++++++++++++++++++++++++++*/
void int_imia1(void)
{
  UBYTE dummy ;
  /* clear flag */
  dummy = ITU1.TSR.BIT.IMFA ;
  ITU1.TSR.BIT.IMFA = OFF ;
  /* increment */
  timcnt++ ;
  /* judge 10ms passed */
  if ( timcnt & 10 ) { WFLAG = ON ; }
  /* judge 1000ms passed */
  if ( (timcnt & 1023) == 100 ) {
    timeoutcnt++ ;
  }
}

/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void  init_sci_1(TBaudRate x)
{
  volatile UWORD i;
  /* SCR : Serial Control Register
    7 bit TIE  -> 0 Transmit Interrupt Enable(disable)
    6 bit RIE  -> 0 Receive  Interrupt Enable(disable)
    5 bit TE   -> 0 Transmit Enable(disable)
    4 bit RE   -> 0 Receive  Enable(disable)
    3 bit MPIE -> 0 Multi Processor Interrupt Enable(disable)
    2 bit TEIE -> 0 Transmit End Interrupt Enable(disable)
    1 bit CKE1 -> 0 Clock Source (Use Internal Baud Rate Generator)
    0 bit CKE0 -> 0 
  */
  SCI1.SCR.BYTE = 0 ;
  /* SMR : Serial Mode Register
    7 bit C/nA -> 0 Communication Mode(Asynchronous)
    6 bit CHR  -> 0 data Charactor (8 bits)
    5 bit PE   -> 0 Parity Enable(disable)
    4 bit O/nE -> 0 Parity Mode(even)
    3 bit STOP -> 0 Stop Bit(1 bit)
    2 bit MP   -> 0 Multi Processor(disable)
    1 bit CKS1 -> 0 Clock Source ( φ )
    0 bit CKS0 -> 0 
  */
  SCI1.SMR.BYTE = 0 ;
  /* data transfer speed */
  SCI1.BRR = x ;
  /* wait 1 frame */
  for (i = 0; i < 3000 ; i++) ;
  /* enable Transmmit and Receive with interrupt */
  SCI1.SCR.BYTE = 0x70 ;
}

/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void int_rxi1(void)
{
  volatile UBYTE ch,dummy ;
  /* clear flag */
  dummy = SCI1.SSR.BYTE ;
  SCI1.SSR.BIT.RDRF = OFF ;
  /* get a character */
  ch = SCI1.RDR ;
  /* store */
  *(sbuf+sindex) = ch ;
  sindex++ ;
  /* check */
  if ( ch == '\r' ) {
    *(sbuf+sindex) = 0 ;
    sindex = 0 ;
    UFLAG  = ON ;
  }
}

/*+++++++++++++++*/
/* SCI_1 putchar */
/*+++++++++++++++*/
void rs1_putchar(UBYTE x)
{
  /* wait data transfer */
  while ( SCI1.SSR.BIT.TDRE == OFF ) ;
  /* put */
  SCI1.TDR = x ;
  SCI1.SSR.BIT.TDRE = OFF ;
}

/*++++++++++++*/
/* SCI_1 puts */
/*++++++++++++*/
void rs1_puts(UBYTE *x)
{
  while ( *x ) {
    /* send 1 charactors */
    rs1_putchar(*x);
    x++ ;
  }
}

/*++++++++++++*/
/* SCI_1 crlf */
/*++++++++++++*/
void rs1_crlf(void)
{
  rs1_putchar('\r');
  rs1_putchar('\n');
}

/*++++++++++++++++++++*/
/* SCI_1 command help */
/*++++++++++++++++++++*/
void show_help(void)
{
  rs1_puts("? help")                 ; rs1_crlf();
  rs1_puts("B enable")               ; rs1_crlf();
  rs1_puts("b disable")              ; rs1_crlf();
  rs1_puts("D set delay counter")    ; rs1_crlf();
  rs1_puts("V set dac value (#0)")   ; rs1_crlf();
  rs1_puts("v show dac values (#0)") ; rs1_crlf();
  rs1_puts("W set dac value (#1)")   ; rs1_crlf();
  rs1_puts("w show dac values (#1)") ; rs1_crlf();
}

UBYTE get_hex(UBYTE x)
{
  UBYTE result ;
  /* default */
  result = 0 ;
  /* convert */
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }

  return result ;
}

void show_value(UBYTE x)
{
  UBYTE xmsg[4] ;
  UBYTE xx ;
  UBYTE i ;
  UBYTE j ;
  /* copy */
  xx = x ;
  /* default */
  *(xmsg+3) = '\0' ;
  /* separate */
  for ( i = 0 ; i < 3 ; i++ ) {
    j = 2 - i ;
    *(xmsg+j) = xx % 10 ;
    xx /= 10 ;
  }
  /* convert */
  for ( i = 0 ; i < 3 ; i++ ) {
    j = *(xmsg+i) ; 
    *(xmsg+i) = *(asc_hex+j) ;
  }
  /* show */
  rs1_puts( xmsg );
}

/* system call */
void cre_tsk(UBYTE tid,void (*tsk)(void))
{
  tcb[tid].tsk = tsk;
  tcb[tid].wcount = 0;
}

void sta_tsk(UBYTE tid,UBYTE tst)
{
  UWORD xtmp ;
  xtmp = (1 << tid) ;
  if ( tst == TTS_READY   ) { ready   |= xtmp ; }
  if ( tst == TTS_SUSPEND ) { suspend |= xtmp ; }
  if ( tst == TTS_WAIT    ) { waitq   |= xtmp ; }
}

void rsm_tsk(UBYTE tid)
{
  UWORD tmp ;
  UWORD tmpx ;
  /* get bit pattern */
  tmp  = *(bpat+tid);
  tmpx = tmp ^ MASKFFFF ;
  /* bit operation */
  ready   |= tmp;
  suspend &= tmpx;
  waitq   &= tmpx;
}

void sus_tsk(UBYTE tid)
{
  UWORD tmp ;
  UWORD tmpx ;
  /* get bit pattern */
  tmp  = *(bpat+tid);
  tmpx = tmp ^ MASKFFFF ;
  /* bit operation */
  ready   &= tmpx;
  suspend |= tmp ;
  waitq   &= tmpx;
}

void slp_tsk(void)
{
  sus_tsk(run_tsk);
}

void wai_tsk(UWORD x)
{
  UWORD tmp ;
  UWORD tmpx ;
  /* get bit pattern */
  tmp  = *(bpat+run_tsk);
  tmpx = tmp ^ MASKFFFF ;
  /* bit operation */
  ready   &= tmpx;
  suspend &= tmpx;
  waitq   |= tmp ;
  /* update counter */
  tcb[run_tsk].wcount = x ;
}

void init_os(void)
{
  UBYTE i;
  /* initialize queue */
  ready   = 0;
  suspend = 0;
  waitq   = 0;
  /* initalize pattern */
  for ( i = 0 ; i < 16 ; i++ ) { *(bpat+i) = (1 << i) ; }
  /* create task */
  cre_tsk(TSK_ID0,tsk0_proc); 
  cre_tsk(TSK_ID1,tsk1_proc);
  cre_tsk(TSK_ID2,tsk2_proc);
  /* start task state */
  sta_tsk(TSK_ID0,TTS_SUSPEND);
  sta_tsk(TSK_ID1,TTS_SUSPEND);
  sta_tsk(TSK_ID2,TTS_READY);
}

void  timer_handler(void)
{
  UBYTE i ;

  for ( i = 0 ; i < TSK_ID_MAX ; i++ ) {
    /* judge WAIT state */
    if ( waitq & *(bpat+i) ) {
      tcb[i].wcount-- ;
      if ( tcb[i].wcount == 0 ) { rsm_tsk(i); }
    }
  }
}

UBYTE is_tsk_ready(UBYTE tid)
{
  return( ready & *(bpat+tid) ) ;
}

/* send_dac_value */
void tsk0_proc(void)
{
  UBYTE tmp ;
  /* impress */
  DAC0 = *(xdac+state) ;
  DAC1 = *(ydac+state) ;
  /* update */
  state++ ;
  /* judge */
  state &= 7 ;
  /* delay */
  wai_tsk( bcnt * 100 ) ;
}

/* impress zero */
void tsk1_proc(void)
{
  /* send zero */
  DAC0 = 0 ;
  DAC1 = 0 ;
  /* exit */
  slp_tsk();
}

/* execute debug */
void tsk2_proc(void)
{
  UBYTE tmp ;
  UBYTE i ;
  UBYTE l ;
  UBYTE v ;
  if ( UFLAG == ON ) {
    /* clear */
    UFLAG = OFF ;
    /* new line */
    rs1_crlf();
    /* command interpreter */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* enable */
    if ( cmd == 'B' ) {
      state = 0 ; 
      rsm_tsk( TSK_ID0 ) ;
    }
    /* disable */
    if ( cmd == 'b' ) {
      sus_tsk( TSK_ID0 ) ;
      rsm_tsk( TSK_ID1 ) ;
    }
    /* set delay counter */
    if ( cmd == 'D' ) {
      /* clear */
      bcnt = 0 ;
      /* calculate value */
      for ( i = 0 ; i < 3 ; i++ ) {
        /* get value */
        tmp = *(sbuf+1+i) ;
        /* judge */
        if ( tmp == '\r' ) break ;
        /* convert */
        bcnt = bcnt * 10 + get_hex( tmp );
      }
    }
    /* show delay counter */
    if ( cmd == 'd' ) {
      show_value( bcnt );
      rs1_crlf();
    }
    /* set dac value */
    if ( cmd == 'V' || cmd == 'W' ) {
      /* get location */
      l = get_hex( *(sbuf+1) ) ;
      /* get value */
      v = 0 ;
      for ( i = 0 ; i < 3 ; i++ ) {
        /* get value */
        tmp = *(sbuf+2+i) ;
        /* judge */
        if ( tmp == '\r' ) break ;
        /* convert */
        v = v * 10 + get_hex( tmp );
      }
      /* store */
      if ( cmd == 'V' ) { *(xdac + l) = v ; }
      if ( cmd == 'W' ) { *(ydac + l) = v ; }
    }
    /* show dac value */
    if ( cmd == 'v' || cmd == 'w' ) {
      /* DAC */
      for ( i = 0 ; i < 8 ; i++ ) {
        /* prompt */
        rs1_putchar( '0' + i );
        rs1_putchar( '-' );
        rs1_putchar( ' ' );
        /* get values */
        l = *(xdac+i) ;
        v = *(ydac+i) ;
        /* show */
        show_value( l ) ;
        rs1_putchar( ':' );
        rs1_putchar( ' ' );
        show_value( v ) ;
        /* new line */
        rs1_crlf();
      }
    }
  }
}

 RTOSのコードを入れてあるので、複雑に見えますが
 肝になるのは、タスクを扱う3つの関数だけ。

 3関数は、tsk0_proc、tsk1_proc、tsk2_procです。


目次

inserted by FC2 system