ここから本文です
S.A.の狂気の科学実験室
Mad Science Technology
 とうとう授業が始まってしまいました。忙しくなりそうです。まあプログラミングと電気回路、電子回路はどう考えても「お遊び」なんですがw

 で、唐突ですがレーザー加工機用の台がある程度完成しました。
イメージ 2
実に作業時間2時間くらいです。ボルトの長さを間違えたせいでワッシャーを通せなくなり時間がかかったんですがそれでもこの速さ。こういう工作は楽ですね。この机もどきはおおよそ1200*900で総工費\7.7kぐらいです。ボルトは後日ちゃんとしたのを買いに行くつもりなのでもう少し増えますが、コストもかなり抑えられました。とりあえず来週までにはキャスターつけたりして完成されときます。

 レーザー加工機のほうも進捗があります。
イメージ 3
3次ミラー及びレンズです。一番設計がめんどくさいところなので先にやりました。レンズはボルトスロットで高さが調整できるようになっていて、水道管のコネクタみたいなやつを回すことで微調整できます。我ながらいいアイデアだったと思ってます。2次、1次ミラーはまだ設計してませんがまあすぐに終わるでしょう(希望)。ちなみにXYはもう完全に動きます。部品さえあればサクッとできますね。

 サークルの方でも実は頼まれたことがあったのでそれもやってました。なんでもひずみゲージ用の測定回路を作ってほしいとか。エアクラ(名大の鳥人間部)はアナログ回路に詳しい人が誰もいないという状態で信号の増幅ができなかったそうです。それ故に32bitADCで直接読み取ろうというのには驚かられましたが(うまくいかなかったようです)
イメージ 1
 というわけで計装アンプで1000倍ぐらいに増幅して32bitADCで読み取ってArduinoにSPIで送るというのをやりました。なんでArduinoなんか使うのかと言ったらサークルにマイコンを使ってる人がいないからです。自分しか理解できないプログラム書いて渡してもしょうがないですからね。ちなみに3日で終わりました。Arduinoはやはりうわさに聞くほど簡単でしたね。ネットに落ちてるライブラリ拾って完成とか・・・。ちなみに3日かかったのはなぜかADCのADS1262が22ピンをGNDに落とさないと正常に動かないからでした。ここanalog inputだよなぁ・・・。ひょっとすると中国の「型番書き換え」ICの可能性???

 まあ、いろいろありましたがかなり作業がはかどりました。サークルの新歓にも先輩として出ました。入ったの10月だからあんまり先輩じゃないけどw やる気のある1年が入ると面白そうですね。とりあえずテスラコイル作ってたりする変人はいませんでしたがw
 では、また今度。

この記事に

 こんばんわー。突然ですがタイトル通り、MIDIインタラプタを作ったのでプログラムを公開します。プログラムは一個前の記事にあります。
 まず初めに、タイマ割込みのサブルーチンは金魚氏のプログラムを参考にしていま
す。これのおかげで和音を作ることが初めてできました!この場を借りて感謝します。

以下注意事項です。
・転載する際は一言言ってください。
・これを参考にして改造、改良を行ったプログラムを公開する場合もぜひお声掛けください。
・動作を保証するわけではありません。
・このプログラムに起因する事故等の責任は一切取りません。

まあバグってテスラコルのIGBTがお亡くなりになっても文句を言いに来たりしないで、という感じです。転載等に関してごちゃごちゃ書いてるのは「改良してほしい」からです。ぜひいじってください。
プログラム中のコメントは私が理解するためにつけてるだけです。基本無視してください。(若干英語になってるのは全角半角切り替えるのがめんどくさかったからです。英語ができるわけではありません(できません))

では、プログラムについてだらだら説明させてもらいます。

0.事前説明
//debugや//for debugは削除してもらってOKです。
実行環境は
windows10 AtmelStudio7.0(設定はデフォルト)
AVRマイコン:ATmega88V 外付けクリスタル20MHz
6和音まで。処理中のデータが16個を超えるとノートオフできなくなります。

1.main()
 信号の処理を行っています。MIDIデータは8bit = 1byte単位のデータで、基本的に何のデータかを示すステータスバイト、2つのデータバイトからなっています。私のプログラムが処理するのはステータスバイトが0x8n(音を止めろ),0x9n(音を鳴らせ)の2種類だけです。本当はもっとしっかりデコードするべきなんですがめんどくさいのでこれだけとします。nはチャンネル(0~F)です。ここでは無視しています。
 まず、ノートオン(0x9n)は、音程のデータを読み取って0x7nに書き換えます。MIDI規格にはこんなデータはありえないんですが、ここでは「処理済みのノートオン」ということにします。こうしないと2重、3重と音を鳴らしてしまいます。
 ノートオフ(0x8n)は、それに対応する「処理済みノートオン」をさがして、そのデータを解放(0x00)します。0x00のデータは処理されませんし、新しい信号を受信したときにデータを格納できます。そして最後に自身も解放します。0x00もMIDI規格にはありません。

2.ISR(USART_RX_vect)
 信号を受信したときの割込みルーチンです。複雑ですがやることは簡単で、MIDIはステータスバイト、第1データバイト、第2データバイトの順に送られてくるので一個受信したら次のデータ用のフラグを立てて受信してるだけです。一回一回割込みルーチンから抜け出せるようにしたかったのでこうなりました。めんどくさいと思う方は
MIDIstatus = temp;
while(!(UCSR0A&0x80));
MIDIdata1 = UDR0;
while(!(UCSR0A&0x80));
MIDIdata2 = UDR0;
なんていう風でもいいと思います。若干無駄がありますが・・・

3.ISR(timer0_COMPA_vect)
 8usごとに呼び出されるタイマー0比較A一致割込みルーチンです。まず、16bitのカウント変数WGcntが毎回インクリメントされます。16bitタイマーをCPUにやらせてるようなもんですね。で、WGcmpという変数と比較しています。この比較値と一致するたびに周期分値を加算しています。これで周期ごとにうまいこと一致するわけですね。オーバーフローしてもカウント変数もオーバーフローするので関係ないです。この仕組みは実に感動しました。なお、呼び出されて出力ピンがhighになっても次に呼び出されたときにlowになるのでパルス幅はこのルーチンが呼び出されるインターバルと一致します。
 この処理が特に重いのでベロシティ値を反映したりするには和音数を下げるか、もっと処理能力の高いマイコンを使う必要がありそうです。
 あと、使用中か否かの判別方法がこのままだと8和音までしか上げられないので注意です。

ざっとこんなところです。質問、提案などは大歓迎です。いじればいろいろできるんじゃないかと思います。和音数下げてベロシティ値反映させたのを現在模索中です。

レーザー加工機はベルトが届かないのでまだX軸動いてないです・・・

では、おやすみなさい。

この記事に

MIDI_interrupter_ver3.1

#include <avr/io.h>
#define F_CPU 20000000UL
#include <avr/interrupt.h>

/*period == period[Note Number]*8(us) */
const int period[128]={
0x3BB9,0x385E,0x3534,0x3238,0x2F66,0x2CBD,0x2A3A,0x27DC,0x259F,0x2382,0x2184,0x1FA3,0x1DDC,0x1C2F,0x1A9A,0x191C,
0x17B3,0x165E,0x151D,0x13EE,0x12CF,0x11C1,0x10C2,0x0FD1,0x0EEE,0x0E17,0x0D4D,0x0C8E,0x0BD9,0x0B2F,0x0A8E,0x09F7,
0x0967,0x08E0,0x0861,0x07E8,0x0777,0x070B,0x06A6,0x0647,0x05EC,0x0597,0x0547,0x04FB,0x04B3,0x0470,0x0430,0x03F4,
0x03BB,0x0385,0x0353,0x0323,0x02F6,0x02CB,0x02A3,0x027D,0x0259,0x0238,0x0218,0x01FA,0x01DD,0x01C2,0x01A9,0x0191,
0x017B,0x0165,0x0151,0x013E,0x012C,0x011C,0x010C,0x00FD,0x00EE,0x00E1,0x00D4,0x00C8,0x00BD,0x00B2,0x00A8,0x009F,
0x0096,0x008E,0x0086,0x007E,0x0077,0x0070,0x006A,0x0064,0x005E,0x0059,0x0054,0x004F,0x004B,0x0047,0x0043,0x003F,
0x003B,0x0038,0x0035,0x0032,0x002F,0x002C,0x002A,0x0027,0x0025,0x0023,0x0021,0x001F,0x001D,0x001C,0x001A,0x0019,
0x0017,0x0016,0x0015,0x0013,0x0012,0x0011,0x0010,0x000F,0x000E,0x000E,0x000D,0x000C,0x000B,0x000B,0x000A,0x0009
};

/*受信したMIDIデータの保存用*/
/*status byte の値が0x00(MIDIとしてありえない)の場合、使用中でない(解放中)とする*/
/*status byte の値が0x7n(MIDIとしてありえない)の場合、処理済みのノートオン信号とする(n:channel)*/
#define DATA_M 16 //maximum of MIDIdata
volatile unsigned char MIDIstatus[DATA_M] ={0x00}; //status byte
volatile unsigned char MIDIdata1[DATA_M]; //1st data byte
volatile unsigned char MIDIdata2[DATA_M]; //2nd data byte
volatile unsigned char MIDIWGuse[DATA_M]; //第nビットが1 => 第nWGを使用中

/*発音用変数*/
/*Wave Generator => WG*/
#define WG_M 6 //maximum of WG
volatile unsigned short WGcnt; //カウンター
volatile unsigned short WGcmp[WG_M]; //比較用変数
volatile unsigned short WGper[WG_M]; //周期記憶用
volatile char WGdet=0b11000000; //使用中/解放中判別用(第nビットが0/1 => 第nWGが解放中/使用中) 8個までしか増やせない
volatile char PORTB_temp; //PORTB値一時記憶用


/*8bit timer0 cmp_A interrupt*/
ISR(TIMER0_COMPA_vect)
{
PORTB = PORTB_temp;
WGcnt++;
PORTB_temp &= 0b11111110;
//WG0
if(WGdet&0b00000001){
if(WGcmp[0] == WGcnt){
WGcmp[0] += WGper[0];
PORTB_temp |= 0b00000001;
}
}
//WG1
if(WGdet&0b00000010){
if(WGcmp[1] == WGcnt){
WGcmp[1] += WGper[1];
PORTB_temp |= 0b00000001;
}
}
//WG2
if(WGdet&0b00000100){
if(WGcmp[2] == WGcnt){
WGcmp[2] += WGper[2];
PORTB_temp |= 0b00000001;
}
}
//WG3
if(WGdet&0b00001000){
if(WGcmp[3] == WGcnt){
WGcmp[3] += WGper[3];
PORTB_temp |= 0b00000001;
}
}
//WG4
if(WGdet&0b00010000){
if(WGcmp[4] == WGcnt){
WGcmp[4] += WGper[4];
PORTB_temp |= 0b00000001;
}
}
//WG5
if(WGdet&0b00100000){
if(WGcmp[5] == WGcnt){
WGcmp[5] += WGper[5];
PORTB_temp |= 0b00000001;
}
}
}

/*USART receive interrupt*/
ISR(USART_RX_vect)
{
unsigned char temp; //UDR0の値を記憶
static unsigned char stemp; //status byte仮取得用
static unsigned char i=0;
static char flag0; //status byte取得フラグ
static char flag1; //1st data byte取得フラグ
temp=UDR0;
/*tempがstatus byteの場合*/
if(temp&0x80){
/*解放中のmidi[i]を探索*/
while(1){
if(MIDIstatus[i] == 0x00){
stemp=temp; //status byte仮取得
flag0=1; //status byte取得フラグを立てる
return;
}
i++;
if(i>=DATA_M){
i=0;
flag0=0;
flag1=0;
return;
}
}
}
/*tempがdata byteの場合*/
else{
/*status byte取得済みの場合*/
if(flag0){
MIDIdata1[i]=temp; //1st data byte取得
flag0=0; //status byte取得フラグを降ろす
flag1=1; //1st data byte取得フラグを立てる
return;
}
/*1st data byte取得済みの場合*/
if(flag1){
MIDIdata2[i]=temp; //2nd data byte取得
flag1=0; //1st data byte取得フラグを降ろす
MIDIstatus[i]=stemp; //status byte本取得
i=0;
return;
}
}
}
/*UDR0は一回読み取ると空になるので注意*/
/*status byteは先に本取得してしまうとdata byteがないままmain関数で処理されてしまう*/

int main(void)
{
/*PORT config*/
DDRB = 0b00000001; //PortB(PB0:output)
DDRC = 0b00011111; //for debug
DDRD = 0b00000010; //PD(PD0:RXD)
/*8bit timer0 config*/
TCCR0A = 0b00000010; //CTCmode(TOP:OCR0A)
TCCR0B = 0b00000010; //clk_div:1/8 => ++ per 0.4(us)
TIMSK0 = 0b00000010; //cmp_A_int:allowed
OCR0A = 19; //int per 8.0(us)
/*USART config*/
UBRR0 = 39; //31250bps@freq=20MHz
UCSR0A = 0b00000000; //received => 0b1xxxxxxx
UCSR0B = 0b10010000; //受信割り込み許可(7)、受信許可(4)
UCSR0C = 0b00000110; //非同期、パリティ禁止、停止ビット1bit、データサイズ8bit
unsigned char i=0,j; //counter
unsigned char chvm; //channel voice message
sei(); //全体割込み許可
while(1)
{
/*not free data*/
if(MIDIstatus[i]){
chvm = (MIDIstatus[i]&0xF0); //get chvm(ch:0)
/*processed noteON*/
if(chvm == 0x70){
//ignore
}
/*noteOFF*/
else if(chvm == 0x80){
/*探索*/
for(j=0; j<DATA_M; j++){
/*1st data byteが同じ場合*/
if(MIDIdata1[j] == MIDIdata1[i]){
/*status byteがprocessed noteONかつ同チャンネルか確認*/
if(MIDIstatus[j] == ((MIDIstatus[i]&0x0F)|0x70)){
/*ここまで来たならmidi[i]はmidi[j]に対するノートオフ信号確定*/
WGdet &= (~MIDIWGuse[j]); //WGnを解放
MIDIstatus[j] = 0x00; //midi[j]解放
break;
}
/*status byteがnoteONかつ同チャンネルか確認*/
if(MIDIstatus[j] == ((MIDIstatus[i]&0x0F)|0x90)){
/*ここまで来たならmidi[i]はmidi[j]に対するノートオフ信号確定*/
/*発音できなかった場合はこのスコープに入る*/
MIDIstatus[j] = 0x00; //midi[j]解放
break;
}
}
}
MIDIstatus[i] = 0x00; //release midi[i]
}
/*noteON*/
else if(chvm == 0x90){
/*ベロシティ0の場合*/
if(MIDIdata2[i] == 0x00){
MIDIstatus[i] = 0x80; //midi[i]をノートオフ信号に変換
}
/*WGがすべて使用中の場合*/
else if(WGdet == 0b11111111){
//ignore
}
/*解放中WG探索*/
else{
//WGの空きに合わせて値を生成 => MIDIWGuse[i]に代入
for(j=0,MIDIWGuse[i]=1; MIDIWGuse[i]&WGdet; j++,MIDIWGuse[i] <<= 1);
WGcmp[j] = WGcnt+5; //比較値初期化
WGper[j] = period[MIDIdata1[i]]; //周期を代入
WGdet |= MIDIWGuse[i]; //対応するWGを使用中
MIDIstatus[i] &= 0x0F; //処理済みサイン
MIDIstatus[i] |= 0x70; //処理済みサイン
}
}
/*the others*/
else{
MIDIstatus[i] = 0x00; //release midi[i]
}
}
i++;
if(i >= DATA_M) i=0;
}
}

この記事に

前回の記事は嘘です

 前回の記事はclass内は嘘ですので気にしないでくださいね。妙にリアリティある風に書いたせいか本当にできたのかと思う人がいたみたいなのでなんだか申し訳ないです。(いや、ちょっとふざけただけなんだよ…

 さて、実際にはこんな感じです↓
イメージ 1
イメージ 2
イメージ 3
とりあえずY軸は上限スイッチ以外完成していて動かせます。X軸はベアリングとベルト(追加分)が届かなくて残念なことになってます。アクリルの変な部品はX軸のベルトテンショナーです。部品が届けばX軸はすぐできるんじゃないかと・・・。

しかし全体像を把握せずに部分的に設計していったためにどうあがいてもX軸をフルで使えない雰囲気になってきました。ワークスペース800*900のつもりでしたが700*900ぐらになりそうです。まあ、個人的には十分なんですがなんかプライドが許さない(笑)
でも無理なもんは無理なんで諦めます。
 そして相変わらず何で覆うかで困ってます。薄い鉄板落ちてないかな。

 あと、MIDIインタラプタの方は正直全くバグの理由がわからず混乱状態なのでちょっと無理かもしれないです。信号のデコードはできてるし処理落ちでもないし・・・なんなんでしょう?音を鳴らすのに問題があることになりますが別に動いてるんですよね。なんか信号がたくさんやってくると(和音ではない)たまにしか音が出なくなります。もしいじってみたいマニアがいましたらどうぞ↓
デコードは割とてきとうです。あと、タイマは強制開放しないと音が変わりません。
ATmega88V@20MHz AtmelStudio7.0
/*-----------------------------------------------------------------------*/
deleted
/*-----------------------------------------------------------------------*/

コードが汚いのはC言語独学&初心者なので勘弁してください。「この書き方おかしいのでは」とかあったらコメントしてください。なおテキストボックスの作り方がわからなかった模様w

とりあえずこんなところですね。来週の火曜から授業なのでそれまでにXYが完璧に完成してるといいな。

追記:
//debugとなっているところは無視でOK
もともと8MHzのプログラムをコピペしていじってるから数値がおかしい場所があるかも
追記2:
追記を書くのもめんどくさくなる位いろいろ不具合が見つかったので削除しました。

この記事に

エイプリルフール

public class AprilFool{
いや〜長かったけどレーザー加工機が完成しました。X軸Y軸動かすどころかここまでできちゃいましたよ。
まあまだ加工はこれと言ってしていないですが切断はテストがてら少々行いました。ベルトテンショナーの構造が結構ややこしいんですが個人的にはできに満足しています。光学関係の調整は死ぬほどだるかったですが頑張りました。焦点はレンズを容易に動かせる(Z軸)ようにしたのでたいしたことはないんですがX軸方向のレーザーを反射する2次ミラーの調整で苦戦しました。二度とやりたくねぇ・・・
これからいろいろ加工して写真もアップします。全体像も次回うpしますね。
}

 あと、最近java始めました。なんか分かるようなわからんような感じで進めております。なお本は買ってない模様。とりあえずなんかアプリ作りたいですね。それと、本日踏切一時不停止で捕まりました。あほすぎですね。反省します。皆さんもぜひ気を付けてください。(これで警察が張っているのは有名なので捕まるのはよほどの阿保だけですが…)

 きょうはこんな感じで締めたいと思います。ノシ

この記事に

[ すべて表示 ]

本文はここまでですこのページの先頭へ
みんなの更新記事