目次
前
次
電子木琴
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の比較でキーの入力と
変更を検出します。
目次
前
次