weather-plugin の時のように、動作に支障をきたした、とかではなく。単に新しいバージョンが出ていたので。

 

 

 

LinuxMint18ではDEにxfce4を選択していても、このpluginは導入されていないようです。インストールできないことも無いと思いますが(※)、万が一挙動不審になっても困るのでパスして、今回はdebian9(stable32bit)で試してみました。
なお、debianはtestingで用意されているようです。そのうちパッケージになるでしょう。

新しいバージョンで追加になったのはマイクロフォンのコントロール。試したマシン(Panasonic CF-R2)に内蔵マイクはついていないので、特に有り難みはありません。

 

※追記
LinuxMint18ではGTK3のバージョンが追いつかずビルドできませんでした。

 

 

インストール上の注意点

  • 既存のバージョンを外そうとするとDE(xfce4)全体を外そうとするので、そのままにして上書きインストールする。
  • ./configure に --libdir=/usr/lib/(arch)を忘れないこと。

 

見た目が少し格好良くなったかも。

 

 

以前、leafpad に lua5.1をくっつけてluaの実行環境(luapad)を作りました。
それなりに重宝して使っていたのですが、時々何も言わずに実行を終了することがあることに気づきました。

 

計算結果が表示されずに終了している。よく見ると5行目にカーソルが移動しているので、ここで何らかの不具合が発生していることは判る。

 

起きるのはwebからコードをコピペ、あるいはコピペした後修正して実行した時が多いので、文字コードの問題ではなかろうか、と思っていました。実際、コピペを止めて同じ内容を手打ちすると通常の動作を示します。

 

取り敢えず手打ちで回避できるので今まで放ったらかしにしていたのですが、時間が出来たのでバグ潰しに掛かりました。

 

このバグが発生するまでの流れは次の通りです。

 

  1. gtksourcebuffer上のテキストを取り出し(text)、
  2. err = luaL_loadstring(L,text)||lua_pcall(L,0,LUA_MULTRET,0);でluaにかける。
  3. text上にコンパイル出来ない文字が含まれているとluaは通常の手順(luaスタックにエラーメッセージを積んでエラーコードを返す)で終了する。
  4. errをチェックし、エラーであればluaスタックからエラーメッセージを取り出す。また、gtksourceview上の該当行にカーソルを移動して行をハイライト表示する。
  5. 実行結果表示用のgtktextbufferにエラーメッセージを流し込んでエラー表示しようとする。
  6. しかし、メッセージにUTF-8で許されない文字が含まれていると何も表示せずに終わる。この際、luapadを端末から起動していると、g_utf8_validateが失敗した、とのメッセージが出ます。


対策としては、luaが返してくるメッセージをUTF-8に変換してgtkに渡せば良いのですが、gtkに用意されている関数 g_convertは元々の文字セットが何であるか分かっていないと変換できません。
主だった文字コードを総当りでチェックしようかと思いましたが、たかがエラーメッセージを出すのに大げさ過ぎるので、自家製のメッセージを表示することでお茶を濁すことにしました。

 

gtktextbuffer的には「変なもの飲ませるな!」ということ何でしょうが、「文字化けでも何でもいいから表示するオプション」が欲しかったです。

 

発生している状況をそのままメッセージにしたが、使う側としては然程アリガタミのある内容ではない。

 

移動平均を使ったLPFの実験の続き。ウィキペディアの記事にあった、単純移動平均を2回適用するフィルタ関数を書いてみました。

 

コードの一部を示します。

 

typedef struct {
            uint8_t buff[MAXBUFFS];
            uint8_t pos;
} FILTER;

 

extern uint8_t filter_width;    // カットオフ周波数設定用
static FILTER f_l1, f_l2;

 

static uint8_t _smafilter_n (FILTER *f, uint8_t v, uint8_t n)
{
    uint8_t i;
    uint16_t j;
    
    if (n < 2) return v;
    
    f->buff[f->pos] = v;
    f->pos++;
    if (f->pos > n - 1) f->pos = 0;
    
    j = 0;
    for (i = 0; i < n; i++) {
        j += f->buff[i];
    }
    return (uint8_t)(j/n);
}

 

uint8_t smafilter_n (uint8_t v)
{
    return _smafilter_n (&f_l2,
                _smafilter_n (&f_l1, v, filter_width), filter_width);
}

 

void TIMER_RC_intr(void)
{
    //~ TRCGRD.WORD = smafilter_n (adc_read () >> 2);
    asm ("pushm     r0,r1,r2,r3,a0,a1"  );
    asm ("jsr.a     _adc_read"          );
    asm ("mov.w     r0, r1"             );
    asm ("shl.w     #-2, r1"            );
    asm ("jsr.a     _smafilter_n"       );
    asm ("mov.b     #0, r0h"            );
    asm ("mov.w     r0, 0xf0"           );
    asm ("popm      r0,r1,r2,r3,a0,a1"  );
}


PWM用のタイマRC割り込みを使って、AD変換を行い、移動平均をとってPWMにセットします。当初、ADとPWMの割り込みをコンペアA一致で起こせないか試したのですがうまく行きませんでした。また、入出力各々にリングバッファを用意して割り込みで動かし、フィルター処理はフォアグラウンドで行おうともしたのですが、時間がかかって正しく音にならなかったので、結局一番原始的な方法にしています。

 

サンプリング周波数は16kHzです。

 

割り込みハンドラはgccで書くと、使いもしない内部作業域の退避と復帰をやったりして大変効率が悪いので、アセンブラ(インラインアセンブラ)で書き直しました。

 

フォアグランドでは以前作成したモニタプログラムを走らせています。これはUARTでPCとやりとりしているのですが、割り込みを使っていないのでPWM/AD側との干渉を気にしなくて済みます。

モニタを使って上記コードの変数filter_width(変数名は気分です、LPFなので幅とか関係ないです。)を書き換えて、動作中にカットオフ周波数を変更することが可能です。

 

フィルターとしての切れは良くありませんが、LPFとしてきちんと動作しています。単純移動平均はプログラムも容易で、ディジタルフィルタの実験としてはゲルマラジオ並の簡便さといえるでしょう。

 

既知の問題点


ノイズが多い。


ディジタル周りではなく、単純に信号源由来のものや、出力側のICアンプで拾ったり生じたりしているものが多い。ブレッドボード上のバラック配線なので仕方ないのだが。

 

 

入力レベルの適正値はどの辺なのか


最初、レベル不足を警戒してADの前にOPアンプを入れたのだが、うまく動かなかった(どうもOPアンプを壊したらしい)のでコンデンサを挟んでR8Cと直結したら音が出た。PCのヘッドフォン出力と市販ラジオの録音用出力を試してみたが、後者のほうが良い結果が得られた(レベル調整ができないにも関わらず、過不足による歪が生じなかった)。
実用するにはこの辺は結構重要になると思われる。現在、ビットレートを8ビットとしているので尚更。

 

 

ステート数を把握していない

 

PWMの割り込みは1250クロック(1クロックは1/20MHz)毎に発生するので、フィルター処理に使えるのはこれが上限となる。
割り込みハンドラが実行されるまで20クロックかかる。更にAD変換を待ちループで行う、プログラムの見通しを良くするためサブルーチンの呼び出しが数段に及ぶなど、時間がかかる傾向にある。

 

タイマRCによる割り込みでADCを読んでリングバッファに格納する、というルーチン

 

void ADC_intr(void)
{
        P1.BIT.P1_0 = 0;    // LED ON

    if (((buff_write+1) & 63) == buff_read) {
        ;
    }
    else {
        // 下位2ビットはノイズとして捨てる
        sio_buff[buff_write] = (adc_read () >> 2);
        buff_write++;
        buff_write &= 63;
    }
}

 

これのgcc出力

 

000086f7 <_ADC_intr>:
    86f7:    ec fc           pushm r0,r1,r2,r3,a0,a1
    86f9:    7c f2 02        enter #0x2
    86fc:    75 4f 1d 03     push.w:g 0x31d
    8700:    75 4f 1f 03     push.w:g 0x31f
    8704:    75 4f 21 03     push.w:g 0x321
    8708:    75 4f 23 03     push.w:g 0x323
    870c:    75 4f 25 03     push.w:g 0x325
    8710:    75 4f 27 03     push.w:g 0x327
    8714:    75 4f 29 03     push.w:g 0x329
    8718:    75 4f 2b 03     push.w:g 0x32b
    871c:    7e 8f 78 05     bclr:g 0,0xaf
    8720:    0b a6 03        mov.b:s 0x3a6,r0l
    8723:    b3              mov.b:z #0,r0h
    8724:    73 02           mov.w:g r0,r2
    8726:    c9 10           add.w:q #1,r0
    8728:    77 20 3f 00     and.w:g #63,r0
    872c:    72 f2 5e 03     mov.b:g 0x35e,r1l
    8730:    d8 03           mov.b:q #0,r1h
    8732:    c1 10           cmp.w:g r1,r0
    8734:    6a 1d           jeq 8752 <_ADC_intr+0x5b>
    8736:    73 2b fe        mov.w:g r2,-2[fb]
    8739:    fd b5 82 00     jsr.a 82b5 <_adc_read>
    873d:    e9 90           shl.w:q #-2,r0
    873f:    73 b2 fe        mov.w:g -2[fb],r2
    8742:    73 24           mov.w:g r2,a0
    8744:    72 0c 65 03     mov.b:g r0l,0x365[a0]
    8748:    0b a6 03        mov.b:s 0x3a6,r0l
    874b:    84 01           add.b:s #1,r0l
    874d:    94 3f           and.b:s #63,r0l
    874f:    03 a6 03        mov.b:s r0l,0x3a6
    8752:    75 df 2b 03     pop.w:g 0x32b
    8756:    75 df 29 03     pop.w:g 0x329
    875a:    75 df 27 03     pop.w:g 0x327
    875e:    75 df 25 03     pop.w:g 0x325
    8762:    75 df 23 03     pop.w:g 0x323
    8766:    75 df 21 03     pop.w:g 0x321
    876a:    75 df 1f 03     pop.w:g 0x31f
    876e:    75 df 1d 03     pop.w:g 0x31d
    8772:    7b f4           stc fb,a0
    8774:    7a d4           ldc a0,sp
    8776:    eb 73           popc fb
    8778:    ed 3f           popm r0,r1,r2,r3,a0,a1
    877a:    fb              reit
    877b:    04              nop
    877c:    04              nop
    877d:    04              nop   


0x31dから0x32b迄の退避と復旧という、無駄ルーチンが生成されている。

 

この領域のmapファイル上の該当部分

 

.bss           0x000000000000031d       0x10 /usr/local/m32c-elf/lib/gcc/m32c-elf/4.9.4/libgcc.a(__m32c_memregs.o)
                0x000000000000031d                mem0
                0x000000000000031e                mem1
                0x000000000000031f                mem2
                0x0000000000000320                mem3
                0x0000000000000321                mem4
                0x0000000000000322                mem5
                0x0000000000000323                mem6
                0x0000000000000324                mem7
                0x0000000000000325                mem8
                0x0000000000000326                mem9
                0x0000000000000327                mem10
                0x0000000000000328                mem11
                0x0000000000000329                mem12
                0x000000000000032a                mem13
                0x000000000000032b                mem14
                0x000000000000032c                mem15


参考に別ルーチンを示す。タイマRC割り込みでリングバッファを読んでPWMにセットする。

 

void TIMER_RC_intr(void)
{
    if (((buff_read+1) & 63) == buff_write) {
        ;
    }
    else {
        //~ TRCGRD.WORD = (uint16_t)(sio_buff[buff_read]<<2);
        TRCGRD.WORD = (uint16_t)(sio_buff[buff_read]);
        //~ TRCGRD.WORD = (uint16_t)(sio_buff[buff_read] + pcm_value_ofset);
        buff_read++;
        buff_read &= 63;
    }
}

 

 

gccの出力

 

000086c6 <_TIMER_RC_intr>:
    86c6:    ec e8           pushm r0,r1,r2,a0
    86c8:    0b 5e 03        mov.b:s 0x35e,r0l
    86cb:    b3              mov.b:z #0,r0h
    86cc:    73 04           mov.w:g r0,a0
    86ce:    eb 08 01        mova 0x1[a0],r0
    86d1:    77 20 3f 00     and.w:g #63,r0
    86d5:    72 f2 a6 03     mov.b:g 0x3a6,r1l
    86d9:    d8 03           mov.b:q #0,r1h
    86db:    73 12           mov.w:g r1,r2
    86dd:    c1 10           cmp.w:g r1,r0
    86df:    6a 14           jeq 86f4 <_TIMER_RC_intr+0x2e>
    86e1:    72 c0 65 03     mov.b:g 0x365[a0],r0l
    86e5:    b3              mov.b:z #0,r0h
    86e6:    73 0f f0 00     mov.w:g r0,0xf0
    86ea:    0b 5e 03        mov.b:s 0x35e,r0l
    86ed:    84 01           add.b:s #1,r0l
    86ef:    94 3f           and.b:s #63,r0l
    86f1:    03 5e 03        mov.b:s r0l,0x35e
    86f4:    ed 17           popm r0,r1,r2,a0
    86f6:    fb              reit


まあ、これはこんなもんでしょう。

 

 

件の領域は、割り込みルーチン向けに用意されている、という訳ではなさそう。

謎だ。

 

R8C/M12Aで音が出せるようになったので、その応用を考えてみました。

 

まず思いつくのがオーディオフィルターです。クロック20MHzの汎用マイコンでどの程度の処理ができるか不明ですが、とりあえず方法だけでもと思いネットを漁ってみました。

 

デジタル信号処理は勉強したことがないので全くの未知ですが、それでも移動平均がローパスフィルターとして使えることが分かりました。移動平均なら統計でグラフを描いたりする時に使ったことがあります。

あんな簡単な処理でローパスフィルターになるのかしらんと思いましたが(グラフで使ったのは凸凹を均したかった為)、やればわかるさ迷わずやれよ、とばかりにプログラムして動かしてみたのですね。

 

「あー、確かにローパスだわ、これ」

 

歪っぽいのは相変わらずですが、スピーカーから出てくる音ははっきりと変化しました。調子に乗って三角移動平均というのも試してみましたが、さらに音が変わりました。まあ、籠もった上に歪むので実用性はいまいちですけど。

 

カットオフ周波数(-3dB)は
0.443 ÷ sqrt(n×n-1) × fs (サンプリング周波数)
だそうです。

 

11000Hz で n を2とか3とかにすると2800〜1700Hzになって、スピーカーから出てくる音もそんな感じがします。

nには最低でも5 とか使いたいところですが、そうするとサンプリング周波数は30000Hz以上ないと実用的なカットオフ周波数になりません。

 

R8C/M12AはA/D変換器も内蔵していて、タイマRCでトリガできます。タイマRCはPWMで使っているので、もし、オーディオフィルターを作るとなると入出力とも同じサンプリング周波数ということになりそうです。デジタル信号処理ってやったことないので、これで正しいのか判りませんが。

 

試しにタイマRCの割り込み周期を25kHzまで上げてPWMを動かしたところ、数秒間再生したところでフリーズしました。 恐らくステート溢れで暴走しているものと思います。  割り込み頻度が上がったので、リングバッファ周りでお見合いになっているのかも。24kHz程度まで下げたら動作しましたが、この辺がこのマイコンの限界かも知れません。割り込み処理の他には前述の移動平均処理とUARTからのデータ受け取りしか動かしていませんし。

 

 

R8C/M12Aのアプリケーションとして音響モノをやってみることにしました。

 

といっても、BEEP音とか出しても余り面白くありません(というか、面白くするには相当の技術を要する)。今回はPCMで行きます。

 

R8C/M12Aは内蔵ペリフェラルにPWM(タイマRC)を持っているので、これを使うと簡単です。

 

プログラムの動作としては、サンプリング周波数で割り込みをかけて新しくデータを貰ってきてPWMにセットするだけです。

 

問題はそのデータをどうするかで、R8C/M12Aの表・裏両方のROMを使っても数秒分しか格納できません。(リニアPCM/8bit/モノラル/8kHzの場合)

 

何らかの圧縮を用いれば多少は改善すると思いますが、音を出すのが目的なので取り敢えず外部から流しこむことにしました。

 

といっても、拡張メモリとか使いたくないので単にPCからシリアルで送り込むだけです。

 

処理を単純にするためにデータ仕様は次のようにしました。

 

リニアPCM、8ビットモノラル、サンプリング周波数 11520Hz

 

データ転送にはPCのシリアルターミナルソフトのファイル転送機能を使っています。厳密にはヘッダ部分が不要なのですが、この程度のPCMファイルであれば特にノイズが目立つということもありません。


サンプリング周波数が半端ですが、シリアル転送速度からこの辺が上限のようです。115200bps8ビットでストップビット1だと12800バイト/秒なので最初12800Hzにしたのですが、処理が追いつかなくて 転送が追いつかなくてテンポ遅れが生じました。

 

R8C側のPWM周波数は16kHzにしました。これより低いと、いわゆるモスキート音が耳障りで使いにくくなります。取り除くにはきちんとしたフィルターが必要になります。

ハードのほうは最初PWMの出力にRCのLPFを入れて圧電スピーカを繋いだのですが、まったく音がでませんでしたので、ICアンプを入れています。

 

動かしてみたところ、音割れが酷くて実用性は今ひとつといったところです。RCフィルターのカットオフ周波数を下げるとそれなりに改善はされますが、同じファイルをPC上で再生するのとでは雲泥の差です。

 

ここからどう改良していくか。先は長いな。

 

 

以下、コードを少し。ペリフェラル設定用のシンボルはH8/300Hライクです。

 

タイマRCの設定

pcm_value_ofset = 500; // PCM出力調整 0から TRCGRA設定値-255まで任意
ILVL3.BYTE = 0x30;  // 優先レベル2

/* タイマRCをPWMに設定 */
MSTCR.BIT.MSTTRC = 0;   // タイマRC アクティベート
TRCMR.BYTE = 0b01111111;// カウント停止, PWM, TRCGRC, TRCGRDはバッファ
TRCCR1.BYTE = 0x80;   // TRCCNTクリア、カウンタソース f1 (20MHz)
TRCGRA.WORD = 1250-1; // 20MHz / 2500 = 8kHz   1562 = 12kHz
TRCGRB.WORD = pcm_value_ofset;
TRCGRC.WORD = 1250-1; // TRCGRA の次回更新値
TRCGRD.WORD = 0x80 + pcm_value_ofset; // TRCGRBの次回更新値
TRCIER.BYTE = 0b01110001; // コンペアA一致割り込みを許可
TRCCR2.BYTE = 0b00011000; // コンペアA一致後カウント継続、TRCIOB出力 L アクティブ
TRCOER.BIT.EB = 0; // TRCIOB出力許可

 

 

割り込みハンドラ

 

void TIMER_RC_intr(void)
{
    if (((buff_read+1) & 63) == buff_write) {
        ;
    }
    else {
        TRCGRD.WORD = (uint16_t)(sio_buff[buff_read] + pcm_value_ofset);
        buff_read++;
        buff_read &= 63;
    }
}

 

 

メインルーチン

なお、データの受け渡しはリングバッファ(64バイト)を使っています。

 

uint8_t sio_buff[64];

buff_read = 0;
buff_write = 32;
for (;;) {
    if (((buff_write+1) & 63) == buff_read) {
        ;
    }
    else {
        sio_buff[buff_write] = sci_getchar ();
        buff_write++;
        buff_write &= 63;
    }
}

 

短波を聴く際、混信から逃れる為にIFフィルターを狭くしてから同調をずらし、上下何れかの側帯波を復調することがある。
こうすると当然復調音も変わる。低いほうが切れて了解度が良くなるような気がする一方、信号に伝播ノイズが乗っているとそのノイズも高域が強調されてしまう。

こういう時、オーディオフィルターがあるといいなあ、と思うのですよ。ローパスではなく、ノッチが欲しいですね。

 


Search

Calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
<< June 2018 >>

Archive

Mobile

qrcode

Selected Entry

Link

Profile

Search

Other

Powered

無料ブログ作成サービス JUGEM