マイコンの記録

今はとある組込系開発の活動記録です。

割込みが使えない時に気にすること

今年こそは記事を続けるぞ、と毎年思うのに、1つか2つ書いたらついついそれで満足してしまい、今回も着実に日が空いてしまっているな。いつもすみません、誰にだ。

前回の記事に出てきた「Cプログラムでいきなりマイコン制御」を読みながら、まずはマイコンのGPIOの使い方を覚え、GPIOはただの接点入出力だと思っていたのだけど、設定すればいろんなことに使えるんだ、ということを知る。オンボードのLEDはすぐにLチカできるようになり、適当に短絡して入力できるようにもなった。今取り組んでいる開発は、電圧値のアナログ入力を行って、それをモーターを制御するためのPWMに変えて出力する、ジャイロのデータも使って4つのPWMを微妙に制御する、みたいなことをやろうとしている。僕個人的には、小学生の頃好きだったFM音源のドライバをまた作りたいな。YAMAHAさまのIC(2608と2610、どちらも取扱超難らしい)とDACはもうたくさんあるので早くつなぎたい。

ちなみにボードは、例えば秋月電子であれば2019年2月現在2500円くらいで手に入る。開発環境はEclipseベースで、たくさんのサンプルとともにインストールされるので、たぶんすぐに使えるようになるだろう。でもちょっとだけハマったところがあるのでまとめておこうと思う。

 

f:id:loopsketch:20190214161507j:plain
LPC-1347全体。下は制御する周辺機器がつながるボード

自分がちょっと苦労したのは、開発環境上で、まっさらなプロジェクトを作る方法。既述の本にプロジェクトウィザードを使った流れで細かく書いてあって、その通りに作れば基本的には動くよ。でも、ADCとか、タイマーを使おうとして、いざ割込みを使おうとなったら一向に動かなくて悩んだ。ADCはポーリングで順番に各chを見に行くと動くけど、バーストモードを使おうと思うとダメ。USBは基本割込でみたいで、まったく使えない。デバッガで起動しても、ブート直後にダンマリとなり、強制的にBREAKかけると、IntDefaultHandlerにいらっしゃることが多い。SysTickだけを使うような定期的なLチカ点滅とかは問題無かったのだけど、他のペリフェラルを使おうとすると途端にダメになる感じ。なのに、サンプルのコードは問題無く動く。main関数の内容は説明に必要なものだけ書いたものが下記。

int main(void) {
    SystemCoreClockUpdate();
    SysTick_Config(SystemCoreClock / 10000);

    Chip_GPIO_Init(LPC_GPIO_PORT);
    Chip_GPIO_SetPinDIROutput(LPC_GPIO_PORT, 0, 7);
 
    USBInit();
 
    while (1) {
        Chip_GPIO_WritePortBit(LPC_GPIO_PORT, 0, 7, true);
        delay(1);
        Chip_GPIO_WritePortBit(LPC_GPIO_PORT, 0, 7, false);
        delay(1000);
        /** ここにUSBの処理いろいろ */
    }
}

ここで、8行目のUSBInit()を呼ぶとこの症状が出るが、呼ばないと出ない。もちろん呼ばないとUSBの処理いろいろは動かない。症状が出る状態だと、14行目のdeley()あたりで止まってしまう。delay()はSysTickを使ってウェイトをしている関数で、どうやらハンドラが全然飛ばなくなり、カウンタが進まなくなる。ちなみに、割込みテーブルみたいなものは、プロジェクトを作成した時にウィザードが自動的に作成し、cr_startup_lp13xx.cというファイルになる。ブレイクしたときも、このファイルのIntDefaultHandlerの中でwhileしている。いろいろ調べて行くと、ADCやUSBのサンプルコードのプロジェクトは動くのに、今回まっさらから作ったプロジェクトは動かない、ということに気がついた。「あれ、もしかして、割込みテーブルって既述違う?」

サンプルのプロジェクトにあるcr_startup_lp13xx.cに書かれている割込みテーブルって、チップごとにdefineでいろいろ定義がかかれている。でも、まっさらから作ったプロジェクトの割込みテーブルには、そういった既述が少なく、SysTick_Handler以降の並びも大きく違った。ちょっと引用長いかもしれないが、自分で忘れないように、の意味もこめて。

プロジェクトウィザード製 cr_startup_lp13xx.c

void (* const g_pfnVectors[])(void) = {
        &_vStackTop, // The initial stack pointer
        ResetISR, // The reset handler
        NMI_Handler, // The NMI handler
        HardFault_Handler, // The hard fault handler
        MemManage_Handler, // The MPU fault handler
        BusFault_Handler, // The bus fault handler
        UsageFault_Handler, // The usage fault handler
        __valid_user_code_checksum,  // LPC MCU Checksum
        0, // Reserved
        0, // Reserved
        0, // Reserved
        SVC_Handler, // SVCall handler
        DebugMon_Handler, // Debug monitor handler
        0, // Reserved
        PendSV_Handler, // The PendSV handler
        SysTick_Handler, // The SysTick handler
        /** ここから */
        WAKEUP_IRQHandler, // PIO0_0  Wakeup
        WAKEUP_IRQHandler, // PIO0_1  Wakeup
              :
        WAKEUP_IRQHandler, // PIO3_2  Wakeup
        WAKEUP_IRQHandler, // PIO3_3  Wakeup
        /** ここまでのIRQHandlerが凄く多い */
        I2C_IRQHandler, // I2C0
        TIMER16_0_IRQHandler, // CT16B0 (16-bit Timer 0)
        TIMER16_1_IRQHandler, // CT16B1 (16-bit Timer 1)
        TIMER32_0_IRQHandler, // CT32B0 (32-bit Timer 0)
        TIMER32_1_IRQHandler, // CT32B1 (32-bit Timer 1)
        (以下省略)


サンプルコードに含まれている cr_startup_lp13xx.c

void (* const g_pfnVectors[])(void) = {
        &_vStackTop,            //  initial stack pointer
        ResetISR,               // The reset handler
        NMI_Handler,            // The NMI handler
        HardFault_Handler,      // The hard fault handler
        MemManage_Handler,      // The MPU fault handler
        BusFault_Handler,       // The bus fault handler
        UsageFault_Handler,     // The usage fault handler
        __valid_user_code_checksum, // LPC MCU Checksum
        0,                      // Reserved
        0,                      // Reserved
        0,                      // Reserved
        SVC_Handler,            // SVCall handler
        DebugMon_Handler,       // Debug monitor handler
        0,                      // Reserved
        PendSV_Handler,         // The PendSV handler
        SysTick_Handler,        // The SysTick handler
        /** ここから */
        PIN_INT0_IRQHandler,    // All GPIO pin can be routed ...
        PIN_INT1_IRQHandler,    // ... to PIN_INTx
        PIN_INT2_IRQHandler,
        PIN_INT3_IRQHandler,
        PIN_INT4_IRQHandler,
        PIN_INT5_IRQHandler,
        PIN_INT6_IRQHandler,
        PIN_INT7_IRQHandler,
        GINT0_IRQHandler,
        GINT1_IRQHandler,       // PIO0 (0:7)
        0,
        0,
        /** ここまでが圧倒的に少ないし、以降defineは省略したが有効なものの並びも大分異なる */
        RIT_IRQHandler,
        0,
        SSP1_IRQHandler,        // SSP1
        I2C_IRQHandler,         //  I2C
        TIMER16_0_IRQHandler,   // 16-bit Counter-Timer 0
	TIMER16_1_IRQHandler,   // 16-bit Counter-Timer 1
	TIMER32_0_IRQHandler,   // 32-bit Counter-Timer 0
	TIMER32_1_IRQHandler,   // 32-bit Counter-Timer 1
        (以下省略)

バージョンとか、いろいろあるのかもしれない。実は割込みテーブルも自分で書くモノ、とかかもしれない。でもリザーブとかも書かれてる位だから、何番目は何の割込みであること、って決まってるに違いないし、こんなに違っちゃいけない気がする。ユーザマニュアルのNVICの章をみたところ、同じような並びのテーブルがあった、正解はサンプルコードの方だ。そこで、サンプルコードの cr_startup_lpc13xx.c を拝借しまっさらなプロジェクトに上書きしたところ問題無く動作した。ウィザードでボードの型番とか使うライブラリとか指定するのに、ちゃんと正解のテーブルにならないのなんでだろう。ちなみに、プロジェクト作成の操作手順は冒頭の参考図書に書いてあり(ちょっと増えてる画面もあるけど)、その手順で行っているので、今やろうとしてて同じ状況の人は意外と居るかもしれない。

とりあえず割込み問題は解決できたようなので、先に進めることにしようと思う。