目次
前
次
マイコンミュージックシーケンサ
アナログシンセサイザーの派生のひとつに
ミュージックシーケンサがあります。
音の周波数を電圧で変えるのは、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文字コマンドを
次のように決めました。
- ? ヘルプ(コマンドのリスト表示)
- B 音出し開始
- b 音出し停止
- D 音の持続時間設定(100msの整数倍)
- d 音の持続時間表示(100msの整数倍)
- V 1バイトのD/A変換値設定(10進数)
- v 8バイトのD/A変換値表示
コマンドインタプリタは、以下のように定義。
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です。
目次
前
次