ゾックス パソコンでワンセグテレビを楽しめるUSB接続ワンセグチューナー ブラック DS-DT305BK -

5.0 / 5.0

ワンセグチューナーでソフトウェアラジオをやるのが流行ってるらしくて、やってみたかったので、DS-DT305BK というのを買ってやってみた。Realtek RTL2832U というチップを使っているやつならなんでもいいっぽいけど、日本で買えるのはあまりないっぽい。Mac でも簡単にいけるかな〜と思ったけど超簡単だった……

でもって以下のように rtl-sdr というのを入れる。OSS のデバイスドライバ

git clone git://git.osmocom.org/rtl-sdr.git
cd rtl-sdr/
mkdir build
cd build
cmake ../
make
make install

で /usr/local に入る。

rtl_test -s 3.2e6

とかやるとなんか動いてるふう。何の問題もなく動いてる感じ……

しかしこれだと音聞いたりがめんどうなので GUI も入れてみる。

http://gqrx.dk/ を入れると GUI でウォーターフォール画面とかが見れるようになる。これもビルド済みのがあるので入れるだけで良い。

  1. トップ
  2. tech
  3. 900円でソフトウェアラジオ入門
  1. トップ
  2. ham
  3. 900円でソフトウェアラジオ入門

去年歯医者で一通りの治療が終わったあと、フロスの必要性をかなりしつこく言われたので、最初は糸ようじを使い、途中から普通の糸フロスを使ってみたりしていた。

しかし普通の糸フロスはめちゃくちゃむずかしくて挫折、フロスホルダーという糸ようじみたいにフロスを固定するものを使いはじめたけど、これも糸をうまくピンと張れなかったり、なんか微妙に使いにくかったりして、だんだんフロスをするのがおろそかになっていった。

結果、半年後には虫歯が2つ発見される (1つは神経ギリギリ・もう1つは軽め) というハメになった。まさにこのザマである。

結局結論としては糸ようじが最強ということがわかった。糸1本1本が細いので歯並びが悪くてもだいたいの歯間に入れることができ、なおかつ複数本の糸の塊になっているので汚れがじゃんじゃかとれる感じがキモチいい。

糸ようじ 60本 -

5.0 / 5.0

さすがにもう歯を削ったりしたくないので、毎日やっていきたい。今のところ毎日できている。

[tech] 割込みと WFI 命令を使った sleep の実装 | Wed, Feb 26. 2014 - 氾濫原 で WFI 命令があるのでそれ使えばよさそうみたいなことを書いたけど、Which architectures support the WFI instruction? を読んでいたら、Raspberry Pi が WFI 命令をサポートしていないことに気付いてしまった……

Raspberry Pi は ARMv6K というアーキテクチャの ARM1176JZ-F というプロセッサらしい。無印の ARMv6 は WFI 命令をサポートしない。ただ、ARM1176JZ-F は別の方法で使うことができる。

いろいろ書いてあってややこしいが、重要なのはここ

ARMv6K and ARMv6T2 include the WFI instruction, meaning that these processors do not cause an undefined instruction exception when the WFI instruction is executed. However, the ARM1136J(F)-S rev 1 and ARM1176JZ(F)-S (architecture ARMv6K), as well as the ARM1156T2(F)-S (architecture ARMv6T2) treat the WFI as a NOP, and implement the CP15 method for entering "wait for interrupt" mode.

なので、WFI 命令は NOP として扱われてた…… 気を使って NOP でも普通に動くコードを書いた結果気付きにくいバグを作っていた。

mov r1, #0;
mcr p15, #0, r1, c7, c0, #4;

とすれば WFI 相当のことができるみたい。コプロセッサってなんだよって感じだけど、書いてある通り書いたらエラーはでなかった。

mcr 命令は ARM レジスタからコプロセッサへデータを転送する命令らしい。上の mcr 命令の場合

  • C15 (p15) コプロセッサの
  • レジスタ7 (c7) = レジスタ 7: キャッシュ管理機能 に対し
  • 割込み待ち (c0, #4 ) を転送する

転送する値はなんでもいい?のかな。r1 に転送する値を入れるけど 0 にしてる。mcr の第2引数もコプロセッサのオペコードっっぽいけどよくわからない。

  1. トップ
  2. tech
  3. 続・割込みと WFI 命令を使った sleep の実装

この GCC (ビルド済み) を使えば fpu がどうとかも何も問題なくうまくいく。

ただし、gdb の simulator がついてないのが不便…… だけど、むしろ gdb でやるより、qemu とかでエミュレーションしたほうがよさそう…… 環境つくるのめんどいけど

  1. トップ
  2. tech
  3. arm-none-eabi クロスコンパイル環境

Raspberry Pi で bare metal をやっているこのコードを読んで理解したいと思います。README に殆ど書いてありますが、ちょっとよくわからないところがあったのでさらに詳しくしてみます。

前提として、ld スクリプト の通り、このプログラムの冒頭は 0x8000 からはじまっています。

割込みハンドラの実行

まず _start の最初には割込みハンドラの定義が書いてあります。

_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    ldr pc,irq_handler
    ldr pc,fiq_handler

reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word fiq

これは、README によるとちょっとしたハックになっていて、若干トリッキーな動きをします。

まず最初に _start が実行されはじめると、最初にあるのが ldr pc,reset_handler なので、すぐに reset: に実行が移り、後続のコードは実行されません。

そして reset: の冒頭では以下のようになっています (コメントはこちらでつけたものです)

reset:
    /* Set interrupt handler to radical address from 0x8000
     */
    mov r0,#0x8000
    mov r1,#0x0000
    /* copy machine code 32bytes bytes at once */
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9} /* load machine code from 0x8000 */
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9} /* store code to 0x0000 */
    /* more 32 bytes */
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}

割込みハンドラは本来、0x0000 から初まるアドレスに指定通り配置する必要があります (すなわり、割込みが入ると、決め打ちのアドレスが実行される)。これは ARM のアーキテクチャマニュアルに書いてあるマジックナンバーです。なので、0x0000 へ、割込みハンドラをコピーする必要があり、それがこの部分になっています。

ldmia は LDM 命令 + IA (インクリメントアフター) という命令で、指定されたメモリアドレス (ここでは r0!) から、指定したレジスタリストに値をロードします。r0 はロードされるごとにインクリメントされます。

そして stmia は STM 命令 + IA という命令で、指定したメモリアドレス (ここでは r1!) に、指定したレジスタリストを書きこみます。ここでは一括して1度にレジスタ8個に対し処理が行われているので、8 * 4bytes = 32bytes がコピーされます。1セット目の ldmia/stmia で割込みハンドラ8個分がコピーされ、2セット目でその後に続くハンドラのアドレスリストがコピーされています。

C的には memcpy ですかね。

なぜこのようなハックが必要か?というと、プログラムが必ず 0x8000 にロードされて、直接 0x0000 には書きこめないからみたいです。

スタックポインタの初期化

続いて以下のような似たような3つの塊がでてきます。

    /* IRQ Mode (0b11010010),   */
    mov r0,#0xD2
    msr CPSR_c,r0
    mov sp,#0x8000

    /* FIQ Mode (0b11010001) */
    mov r0,#0xD1
    msr CPSR_c,r0
    mov sp,#0x4000

    /* Supervisor Mode (0b11010011) */
    mov r0,#0xD3
    msr CPSR_c,r0
    mov sp,#0xF000000

msr 命令はプログラムステータスレジスタに値を書きこむ命令で、ここでは特にモードの切り替えを行っています。CPSR_c は カレントプログラムステータスレジスタの下位 8bit に書きこむという意味です。

カレントプログラムステータスレジスタの下位 8bit は以下のような構造になっています。

    /* 
     *  CPSR register lowest 8bit: (page A2-11)
     *      I    [7]   -> IRQ disabled (set 1 to disable)
     *      F    [6]   -> FIQ disabled (set 1 to disable)
     *      T    [5]   -> Always set 0 in ARM state
     *      MODE [4:0] -> Mode bit
     */

下位4bitでモードを指定するようになっており、それぞれコメントにあるようなモードに遷移します。

ARM では例外モードと呼ばれるモードそれぞれに対して sp レジスタは別々に存在しています。ここでは sp をそれぞれ別のアドレスを示すように初期化しているようです。

notmain の呼び出し

その後、スーパーバイザーモードのまま、C で定義された notmain 関数が呼ばれて終わりです。

本来、.bss の初期化などがあるはずですが、この例では使ってないので書いてないようです。

  1. トップ
  2. tech
  3. Raspberry Pi で bare metal している blinker05 の ARM ブートコードを読む

mruby で Raspberry Pi の GPIO をいじるというやつで、sleep をビジーループにしていたのがどうしてもひっかかっていた。

どうも ARM には割込みが起きるまで眠る命令があるみたいなので、それを使ってみることにした。コード全体

static mrb_value mrb_mruby_raspberrypi_gpio_gem_delay_us(mrb_state* mrb, mrb_value self) {
	mrb_int delay;
	mrb_get_args(mrb, "i", &delay);

	// Reset timer flags
	PUT32(ARM_TIMER_CONTROL, 0x3E0020);
	// Load count down timer value
	PUT32(ARM_TIMER_LOAD, delay-1);
	PUT32(ARM_TIMER_RELOAD, delay-1);
	// predevider = (apb_clk - freq) / freq
	PUT32(ARM_TIMER_PRE_DIVIDER, 250 - 1);
	PUT32(ARM_TIMER_IRQ_CLEAR_ACK, 0);
	PUT32(ARM_TIMER_CONTROL,
		(0x3E<<16) | // default free-running pre-scaler
		(1<<7)     | // timer enabled
		(1<<5)     | // timer interrupt enabled
		(1<<1)       // 23-bit counter
	);

	// Enable ARM Timer IRQ
	PUT32(ARM_INTERRUPT_ENABLE_BASIC_IRQS, 1);
	
	while ((GET32(ARM_INTERRUPT_IRQ_BASIC_PENDING) & 1) == 0) {
		// Waiting For Interrupt
		asm volatile ("wfi");
	}

	// Disable ARM Timer IRQ
	PUT32(ARM_INTERRUPT_DISABLE_BASIC_IRQS, 1);

	return mrb_nil_value();
}

コメントにある通りだけど、割込みを設定して、ARM_INTERRUPT_IRQ_BASIC_PENDING のフラグを見つつ、セットされるまでは wfi 命令で継続的に眠る、というようにしてみた。他に割込みを設定していないので、while は1回で抜けるつもり……

wfi 命令はオプショナルな命令らしく、ハードによっては nop として解釈されるらしい。なのでこのような実装の場合、フラグをポーリングするようなコードを併用したほうが安全そう。

というか、実際 wfi 命令がちゃんと動いているかを確かめる方法が面倒くさい。電流を測るしかなさそう。電流を今回測るところまでやってないので、ちゃんと動いてないのかもしれない。ただ、挙動として割込みをポーリングで待つ、というのはできいてるっぽい。

どうでもいいけど wfi で検索しても wifi 扱いされてだいぶウザい。

  1. トップ
  2. tech
  3. 割込みと WFI 命令を使った sleep の実装

//#!gcc -O0

#include <stdio.h>

int main (int argc, char *argv[]) {

        int a;
        int b;

        a = 0; b = 3;

        asm volatile (
                "mov r0, %[x];"
                "mov %[x], %[y];"
                "mov %[y], r0;"
                : [x] "+r" (a), [y] "+r" (b)
                :
                : "r0"
        );

        printf("a=%d, b=%d\n", a, b);

        return 0;
}

asm でやる必要は全然ないけど inline asm の文法が意味不明なのでちょっと書いてみた。

asm( code : output list : input list : clobber list)

という文法らしいけど、パっと見さっぱりわからない。上記の例(値の交換)だと

  • a と b どちらも読み書きが必要なので、output リストのほうで + (read/write) をつける。r はレジスタの意味らしい。
  • operand の指定は名前で行っている。角括弧を使うと名前で指定できるみたい
  • input list は空 (output list で read/write を指定しているので input として指定する必要はない)
  • clobber list というで、このコードによって上書きされるレジスタを指定する
    • これを指定しないと、意図せず他の変数のレジスタを破壊してしまったりする (GCC はどのレジスタが破壊されるかがわからないから、破壊されるレジスタに変数を割り当ててしまう)
    • 指定することで、GCC はそのレジスタを一旦退避させたりできる

このページが比較的わかりやすい。

  1. トップ
  2. tech
  3. ARM inline asm で値の交換を書いてみる