• スタートアップスクリプトを見てみる

    突然ですが、main関数は一体誰が呼んでいるのでしょうか。
    普通に考えるとプログラムはROMの0番地から始まりますから0番地にmainがあるのでしょうか。
    違います。mainは別の場所にあります。0番地ではない。
    実は今まで僕らがマイコンに書き込んできたプログラムには「main関数を呼ぶ別のプログラム」がまざっていて、そいつが最初に動いています。
    しかもそいつはクロックの源を決めたり速度まで変更してくれちゃってます。
    正体を確かめておきましょう。短いプログラムです。

    犯人は誰なのか

    CMSISを覚えてるでしょうか。
    僕が「CoIDEってすごいよ!」とか騒いでる時に、プロジェクトを作って最初にやったことです。ここです。
    ここで外部からプログラムを取り込みました。前回まで使ってたプロジェクトのプロジェクトナビゲーションにも入っています。

    stm32f4_project

    これらは僕らのmain.cといっしょにマイコンに書き込まれます。
    既にstm32f407xx.hについてはレジスタが沢山defineされてるんだよ〜っていう話をしました。
    他にもいろいろいますが、大体.hというヘッダファイルですから定義であってプログラムではありません。
    ただ、そんな中

    • startup_stm32f407xx.S
    • system_stm32f4xx.c

    という連中がいます。上はアセンブリ言語、下はC言語のプログラムです。
    実はこのアセンブリがmainを呼ぶ実体です。

    startup_stm32f407xx.Sを見る

    このアセンブリを開いて見てみるとどうやら上から順番に実行されるわけではありません。
    「この辺は0番地にこの辺は100番地に」という番地指定がされているのでややこしくなっています。実際のプログラムは0番地から実行されるわけですから0番地から見てみましょう。

    .word _eram
    .word Reset_Handler
    .word NMI_Handler
    .word HardFault_Handler
    .word MemManage_Handler
    .word BusFault_Handler
    .word UsageFault_Handler
    .word 0
    .word 0
    .word 0
    .word 0
    .word SVC_Handler
    .word DebugMon_Handler
    .word 0
    .word PendSV_Handler
    .word SysTick_Handler
    
    /* External Interrupts */
    .word WWDG_IRQHandler /* Window WatchDog */
    .word PVD_IRQHandler /* PVD through EXTI Line detection */
    .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
    .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
    
    

    途中を省略していますがこれが0番地からのプログラムです。
    実はこれプログラムではないです。
    0番地は _eramで指定される32bitの数字を、1番地にはReset_handelrで指定される32bitの数字を置いておくというただの固定値です。
    これは割り込みベクターテーブルと言って「割り込みが起きた時にどのアドレスに飛ぶか」を示したものです。他のマイコンを触ったことがある人なら知っているはず。
    ここではそれがこんな風に書かれているわけです。
    ちなみに最初の_eramというのはスタックポインタの初期値です。
    リンク時にスタックポインタの最初のアドレスが決まりそれが_eramとなります。
    ARM-Cortex-Mでは0番地はプログラムでなくスタックポインタのアドレスというのは決まっています。起動した直後からスタックを使えるようにするため特別になっています。
    さて、ROMの上ののほうが割り込みベクタなのはわかりました。ではマイコンは起動後どう動くかというと

    1. 0番地のアドレスを読みスタックポインタを設定する
    2. 割り込みベクターテーブルのリセットハンドラへ飛ぶ

    ここで割り込みベクターテーブルのリセットハンドラとはリセット割り込みがかかった時にジャンプする先のことで、ベクターテーブルの一番上です。つまりResetHandlerのことです。
    これはラベルと言ってC言語では関数みたいなもんです。
    これはどこなのかとこのアセンブリを調べると上の方にありました

    Reset_Handler:
    ldr sp, =_eram /* set stack pointer */
    
    /* Copy the data segment initializers from flash to SRAM */
    movs r1, #0
    b LoopCopyDataInit
    
    CopyDataInit:
    ldr r3, =_sidata
    ldr r3, [r3, r1]
    str r3, [r0, r1]
    adds r1, r1, #4
    
    LoopCopyDataInit:
    ldr r0, =_sdata
    ldr r3, =_edata
    adds r2, r0, r1
    cmp r2, r3
    bcc CopyDataInit
    ldr r2, =_sbss
    b LoopFillZerobss
    /* Zero fill the bss segment. */
    FillZerobss:
    movs r3, #0
    str r3, [r2], #4
    
    LoopFillZerobss:
    ldr r3, = _ebss
    cmp r2, r3
    bcc FillZerobss
    
    /* Call the clock system intitialization function.*/
    bl SystemInit
    /* Call static constructors */
    bl __libc_init_array
    /* Call the application's entry point.*/
    bl main
    bx lr
    
    

    こっちはちゃんとプログラムです。
    見えてきましたね。アセンブリが詳しくない人のために解説するとここでやっていることは

    1. 初期値のあるグローバル変数の値をセット
    2. 初期値のないグローバル変数に0をセット
    3. SystemInit を呼ぶ
    4. __libc_init_arrayを呼ぶ
    5. mainを呼ぶ

    となっています。そう!下の方にmain!。いや〜、mainを読んでいるところを見つけましたね!
    ただ、mainまでにもいろいろやってるみたいです。
    まず最初の1と2はRAMに初期値を入れていますね。
    変数はRAMなんですが、RAMというのは起動後の値は不明な値が入ってます。0ですらないし、もちろん自分の欲しい値を入れておくためにはどっかでセットしないといけないのです。
    グローバル変数(C言語でどの関数からも使える変数)に初期値がある場合はmainが始まっちゃう前にセットしないといけないわけです。
    なのでその値はROMにセットされていて(この辺はリンカがやってくれる)起動後のこのプログラムでRAMにセットするようになっています。
    そして初期値のないグローバル変数には0を入れる処理が入っています。

    SystemInit

    グローバル変数の初期化が終わったらmainを呼べるのですが、ここでSystemInitってのを呼んでます。これはsystem_stm32f4xx.cの中にある関数です。
    このcファイルはは基本的に「クロックを決める」機能しかありません。
    実際system_stm32f4xx.hをみるとこの中にあるのは

    • SystemCoreClock コアのクロック数が保存してあるグローバル変数
    • SystemInit クロックの設定用関数
    • SystemCoreClockUpdate 呼ぶと各種レジスタを読んでSystemCoreClock変数をupdateする

    となっています。さっそくSystemInit関数を見てみると

    void SystemInit(void)
    {
      /* FPU settings ------------------------------------------------------------*/
    #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
    #endif
    /* Reset the RCC clock configuration to the default reset state ------------*/
    /* Set HSION bit */
    RCC->CR |= (uint32_t)0x00000001;
    
    /* Reset CFGR register */
    RCC->CFGR = 0x00000000;
    
    /* Reset HSEON, CSSON and PLLON bits */
    RCC->CR &= (uint32_t)0xFEF6FFFF;
    
    /* Reset PLLCFGR register */
    RCC->PLLCFGR = 0x24003010;
    
    /* Reset HSEBYP bit */
    RCC->CR &= (uint32_t)0xFFFBFFFF;
    
    /* Disable all interrupts */
    RCC->CIR = 0x00000000;
    
    #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
    SystemInit_ExtMemCtl();
    #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
    
    /* Configure the Vector Table location add offset address ------------------*/
    #ifdef VECT_TAB_SRAM
    SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
    #else
    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
    #endif
    }
    

    などといろいろなレジスタをいじっていて、いろいろな設定を指定ますが基本は
    内臓クロックをメインクロックとして利用する
    という設定をしています。

    Discoveryボードには外付けの8Mhzクリスタルがあるのに、マイコンに内蔵されているクロックを使うようになってるわけです。そして、この設定が終わったらアセンブリに一度戻ってmainに戻っています。

    結局起動するまでにしていること

    mainまでの流れを見てみると

    1. (startup_stm32f407xx.S)グローバル変数の値をセット(初期値のあるものと0のもの)
    2. (system_stm32f4xx.c)SystemInitでクロックなどを設定
    3. (startup_stm32f407xx.S)main関数を呼ぶ

    となっていたわけですね。
    これでCMSISで持ってきたこのプログラムの意味と最初のクロックの設定がわかりました。

    そしてどうやらクロックは内臓のものをつかうようになっているようですし、168Mhzでもうごいてなさそうです。
    次回はせっかくある外部クロックへの切り替えと168Mhzで動かすための設定をしてみましょう。

    まとめ

    • startupスクリプトとしてアセンブリとcのファイルがある
    • アセンブリは割り込みベクターテーブルとグローバル変数の初期化がある
    • cのsysteminit関数がmainの前に呼ばれている

     

  • GPIOを使ってボタン入力をする

    前回はマイコンのピンの電圧制御にはGPIOペリフェラルをいじれば良いということがわかりました。
    ODRレジスタの対応するビットを操作することでONOFFしていましたが、今回は逆に入力をやってみましょう。

    入力とは

    STM32F4Discoverにはボタンが2つあり、うち1つは自由に使うことが出来ます。

    IMG_06703

    この青いボタンがそうです。このボタンがマイコンのポートAの0につながっています。

    refmanu_btn
    出典: ReferenceManual

    回路図としてはこんな感じです。
    ボタンが押されていない時はPA0は220kΩでGNDに繋がれ0Vになり、押されるとVDDに繋がり3.3Vになる(VDDは3.3Vです)感じです。
    それがGPIOのポートAの0につながっています。
    GPIOは1か0かでピンの電圧を3.3Vか0Vかにプログラムから切り替えられましたが、逆に高いか低いかを1か0かで入力する機能もあります。

    MODERをもう一度見てみる

    ここで、前回出てきたGPIOのMODERレジスタをもう一度見てみましょう。

    Screen Shot 2016-01-05 at 22.16.17

    前回はこれを元に青色LEDが繋がっている場所を10(出力モード)に切り替えました。これによると

    • 00 入力(reset state)
    • 01 出力
    • 10 AlternativeFunction
    • 11 アナログ

    となっています。実は元々GPIOは全てのピンが最初から入力モードになっていて、ピンの状態をプログラムから読み取れる状態になっています。
    では、読み取った値は出力を決めるのに使っていたODRに入るのかというと、そうではなく別の今度はIDRというレジスタに入ります。

    Screen Shot 2016-01-23 at 18.00.00
    出典:ReferenceManual

    IDRのそれぞれの1bitがそのポートの状態を表しています。
    今回ならGPIOAの0ですから、ボタンが押されていなければ(0V) GPIOAのIDRの0bit目は0。
    押されていれば(3.3V)0bit目は1となります。

    ボタンにあわせてLEDをつける

    では、早速試してみましょう。
    ボタンが押されていたらLEDをつけるようにします。

    #include "stm32f4xx.h"
    
    int main(void)
    {
      RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
      RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
    
      GPIOA->MODER = 0;
      GPIOD->MODER = 0x40000000;
      while(1) {
        GPIOD->ODR = GPIOA->IDR << 15;
      };
    }
    

    まず、GPIOAのクロックを有効にして使えるようにします。
    そしてMODERに0を代入して入力にします。元々起動した時から0になっていて入力なので、このプログラムは別に無くても問題無いです。
    そして、while(1)の無限ループの中で前回1<<15;としてLEDのところに1をセットしていましたが、そこをGPIOA->IDRとしています。
    こうすることで、GPIOAの0bit目がGPIODの15bit目に挿入されます。

    IMG_0671

    もちろんGPIODの16bit目にもGPIOAの1bit目が入るなど、他のところにも影響がありますが、今回は無視しています。

    すごく簡単でしたね。
    他にもボタンが押された時に何かのプログラムを動かしたいときは

    if ( GPIOA->IDR & 1) {
    //押された時のプログラム
    }
    
    

    ってすれば出来ますし、いろいろ使えます。
    (ちなみにプログラムが苦手な人へ、何故while(1)という無限ループの中にボタンの入れたか謎に感じてると思います。
    GPIOD->ODR = GPIOA->IDR << 15;
    while(1);
    これでいいじゃないかと。
    ただ、これだとダメです。何故って、このODRにセットするプログラムは起動後一回呼ばれてその後はすぐwhileで無限に何もしないということをするわけですから、どんなにボタンを押してIDRを変えてもODRにはセットされないわけです。ボタンを押しながらマイコンの電源を入れた時だけ意味が出てきます。

    GPIOの他のレジスタ

    ここまでで

    • MODER ピンのモードを決める
    • ODR 出力値を決める
    • IDR 入力された値

    という3つのレジスタがあることを知りました。
    GPIOには他にもレジスタがあります。

    OTYPER

    GPIOの出力タイプをプッシュプルかオープンドレインかから選べます。
    起動後はプッシュプルになっています。
    プッシュプルというのはトランジスタ2つをつないでどっちかだけONにすることで3.3Vか0Vかを出力するというものです。普通ですね。

    IMG_0672

    オープンドレインというのはトランジスタ1つだけを使うものでGNDにつなぐかOFFかとなります。違う電圧っで出力したい時などに使います。例えばこの出力をつなぐ先が5Vで動作する他のマイコンだとすると、左のプッシュプルだとONにしても3.3Vにしかなりません。これだと相手の入力としては足りないです。しかしオープンドレインならこの出力を抵抗で5Vに繋げば 5Vか0Vかという出力ができます。

    OSPEEDR

    ピンの動作速度を4つから選べます。実はプログラムでON-OFF-ON,,,と動かしてもそれにはちょっと追いつけないのです。起動後はLowSpeedモードになっています(全部じゃないけどね)。動作速度を上げたいときはここを設定します

    PUPDR

    実は内部にプルアップ抵抗とプルダウン抵抗が入っています。それを有効化することが出来ます。

    BSRR

    ODRレジスタを操作すれば出力値は変えられます。
    今回はLEDが1つですが、何個もついてくるとLEDをONにするときに間違ってほかをOFFにしないかなどちょっと気にしてしまいます。
    BSRRはODRのセットリセットレジスタとなっていて、下位16bitはONにしたいbitを1にするとそこだけ1にしてくれて、0のbitは何も操作しないです。
    また、上位16bitは0にしたいbitを1にするとそこだけ0にしてくれます。

    LCKR

    GPIOの設定をロックして間違ってプログラムで変えないようにするためのレジスタです

    AFRLとAFRH

    MODERでは4つのモードが選べましたが、AlternativeFunctionというのは、GPIOではない例えばUARTとかUSBとかそういった別の機能のことです。
    それぞれのピンでAlternativeFunctionは16個選択できるようになっています。どれがなんなのかは既に決まっていて、好きに選ぶことは出来ないです。
    例えば「USBはGPIOAの8がいい!」と思ってもGPIOAの8ではUSBは選択できないかもしれません。
    それぞれのピンでどのAlternativeFunctionにするかはこのAFRLとAFRHで対応する場所を編集します。stm32f4_gpio_afr

    AFRLとAFRHの2つに分かれているのは1つのピンで4bitもあるので2つのレジスタに分かれているだけです。

    以上で全てです。これ以外にはありません。
    GPIOでできることはこれだけ。
    どんなに複雑そうに思えるシステムでもレジスタ以上のことは出来ません。
    つまり、どんなレジスタがあるのだけ見れば何が出来るかがわかります。これは重要な事です

    今回のまとめ

    • MODERにて入力モードにすればIDRレジスタにピンの状態が入る

    また、GPIOのレジスタは

    • MODER ピンのモードを決める
    • ODR 出力値を決める
    • IDR 入力された値
    • OTYPER 出力タイプを選ぶ
    • OSPEEDR 速度を選ぶ
    • PURDR プルアップ・プルダウンを選ぶ
    • BSRR ODRのセットリセットレジスタ
    • LCKR このGPIOレジスタ全体を触らせないためのロック
    • AFRLとAFRH MODERでAlternativeFunctionを選んでいる時どのAFにするかの選択

     

    次回は今後必要になってくるスタートアップスクリプトを見てみましょう。

  • STMのGPIOはどうなってるのか(LEDをつける)

    前々回CoIDEを使ってボタンに合わせてLEDを点灯させるプログラムを書いて実行しました。
    プログラムはこれでしたね。

    #include "stm32f4xx.h"
    
    int main(void)
    {
      RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
    
      GPIOD->MODER = 0x40000000;
      GPIOD->ODR = 1 << 15;
      while(1);
    }
    

    このプログラムでなぜLEDが点灯するのでしょうか。
    では、今回はこれがどうなっているのか順番に見ていきたいと思います。
    ただし、C言語についてはある程度分かるものとして話を進めますね。

    GPIOとは

    STMマイコンには外部とシリアル通信するためのUARTモジュール、アナログ信号をデジタルに変換するA/D変換モジュールなどがありプログラムから使えます。
    こういうプログラムから(CPUから)利用できるモジュールのことをペリフェラル(Peripheral)といいCPUと一緒にあの黒いチップの中に入っています。
    これでさっきのを言い直すとUARTペリフェラルだし、A/Dペリフェラルとなります。
    LEDをつけたり消したりといった、マイコンのピンの電圧を単純に上げたり下げたりするのもペリフェラルでGPIO(General Purpose IO)ペリフェラルといいます。
    つまりプログラムからピンの電圧を上げたり下げたりするというのは、プログラムからそのマイコンのGPIOペリフェラルを制御するということになります。
    ちなみに世の中にはいろんなマイコンがありますが、CPUの速さの違いなどもありますが、多くはペリフェラルの違いです。
    下にあるのはSTM32F40xxxシリーズのブロックダイアグラムというチップの内部構成です。

    Screen Shot 2015-12-29 at 12.20.34出典: STM DataSheetDM00037051

    左上にある「ARM Cortex-M4 168Mhz FPU」というのがいわゆるCPUで、左中央にある 「GPIO PORT A」とかがGPIOペリフェラルです。
    もちろん初めから見て「なるほど分かった」と分からなくても大丈夫。
    ただ、CPUとペリフェラルが沢山一つのチップに入っているんだなというのがイメージしてもらえればOKです。

    で?どうやってLEDをつけるのさ

    ペリフェラルの使い方はメーカーであるSTMが出しているリファレンスマニュアルに全て書いてあります。一度ダウンロードしておいてください。残念ながら全部英語です。

    http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf

    さて、GPIOを使ってLEDをONにしましょう。読める人はリファレンスマニュアルのGPIOのページを開きながら。

    レジスタ ODR がその人

    まず、LEDはどこにつながってるのでしょうか。Disocoveryボードのユーザーマニュアルによると

    Screen Shot 2016-01-05 at 21.28.47

    PD12〜PD15に緑、オレンジ、赤、青のLEDがつながっています。
    青のLEDを使うことにします。
    これによると青のLEDは62番ピンにつながっていることがわかります。
    ただ、これだけ分かってもプログラムからLEDをONにはできません。
    GPIO-62 = 1;
    みたいなのはできません。プログラムから見るのはPDってやつです。
    PDっていうのはさっきのGPIOの話で、このチップには沢山のピンがあって管理が大変なので、ピンをポートと言う単位で分けています。PDとはポートDの略です。
    つまり実際の62版とかでなく青いLEDを点灯させるためにはGPIOで管理されてるポートDの15というのをいじればいいわけです。
    プログラムからいじるのはODRというレジスタです。
    62番ピンのLEDをONOFFするためにはODRレジスタの対応するビットを1にしたり0にしたりすればOKです。1にするとON(3.3V)になり0にするとOFF(0V)になります。
    ちょっとReferenceManualのODRのページを見てみましょう。

    Screen Shot 2016-01-05 at 21.19.17出典: ReferenceManual

    ここにあることを読むとこういうことになります。
    「ポートDの15をHighにしたければポートDのODRの下から15bit目を1にしろ」
    ということです。こんな感じ。

    (PORTDのODR) = 1 << 15;
    

    さて、この(PORTDのODR)はどうしたらいいんでしょう。
    先に正解をお知らせすると

    *(0x40020C14) = 1 << 15;
    

    こうなります。
    レジスタというのはROMやRAMといっしょにアドレス上に存在します。
    PORTDのODRというのは32bitのレジスタで、0x40020C14番地にあります。つまりポートDのODRの32bitを全部0にしたければ0x40020C14に0を書き込めばいいですし、全部1にしたければ0x40020C14に0xFFFFFFFFを書き込めばいいことになります。
    今回1にしたい(ONにしたい)のはポートDの15ビット目なので上にあるようなコードになるわけです。

    便利なstm32f407xx.h

    ただ、毎回「じゃあ今度はポートAのODRをいじりたいけどアドレスは、、、」と調べるのは大変です。
    ここで使えるのがCMSISで入れたstm32f407xx.hです。includeしているstm32f4xx.hのなかでincludeされています。
    この中にはほぼ全部のアドレスがdefine文で分かりやすい文字になっています。
    なので、さっきの文はこのように書けます。

    [c]
    GPIOD->ODR = 1 << 15;
    [/c]

    これはただのdefineと構造体を使っているだけなので、コンパイル時にただのアドレスに変換されますので、速度的な問題はありません。

    出力か入力か

    では、さっそくLEDをつけてみましょう

    [c]

    #include “stm32f4xx.h”

    int main(void)
    {
    GPIOD->ODR = 1 << 15;
    while(1);
    }

    [/c]

    ただ、これだとLEDはつきません。
    何故かと言うとGPIOは起動直後は入力モードになっているからです。
    GPIOは出力だけでなく入力にも出来て、それは同時には使えないようになっています。
    入力か出力かはピン1つ1つ設定できます。ポートDの15は出力で14は入力ということもできます。
    そのモード設定はMODERレジスタでできます。

    Screen Shot 2016-01-05 at 22.16.17
    出典: ReferenceManual

    1つのピンの設定に2bitつかうようです。
    ポートDの15はMODERの30,31bitで設定します。組み合わせは4パターンで
    00 入力
    01 出力
    10 外部機能
    11 アナログ入力
    となっています。
    11はともかく10は何かというとGPIOでない例えばSPIとかUSBとかをつなぐときに使います。別の回でまた紹介します。
    で、今回は出力なので01にしましょう。

    [c]

    #include “stm32f4xx.h”

    int main(void)
    {

    GPIOD->MODER = 0x40000000;
    GPIOD->ODR = 1 << 15;
    while(1);
    }

    [/c]

    これでいけるはず!
    ただ、これでも点灯はしません。

    ペリフェラルのクロック

    点灯しない理由は「GPIOの電源が入っていないから」です。
    実はSTMマイコンではペリフェラルごとに電源をONOFF出来るようになっています。これにより使っていないペリフェラルの電源を切っておけば少ない電流でも動かせるわけです。
    そして、起動直後は基本的に全てのペリフェラルはOFFになっています。
    なのでレジスタに何を書き込んでも何の反応もないわけです。
    ちなみに”電源”と僕は言いましたが、正確に言うと電源自体は来ているのですが、クロックが供給されない状態になります。
    これがSTM32F4系のクロック図です。今回の話に関係あるところだけ抜き取っています。

    Screen_Shot_2016-01-05_at_23_08_20出典: ReferenceManual

    マイコンのクロック源はマイコンの外側についている8Mhzのクリスタルです。
    それを内部で100倍したりして使っています。ただ全部の場所で同じクロックというわけじゃなくて、場所によってクロックを分周して使っています。CPUのクロックは矢印で示した上の方です、で、今回関係あるGPIOは下の矢印で示したAPBx ペリフェラルクロックというやつです。
    APBxはCPUのクロックと同じか一番遅くて1/16となることがわかります。
    そして、APBxにつながったペリフェラルごとそれぞれON/OFFができます。
    APBはAPB1とAPB2があるのですが、GPIODはAPB1につながっています。ONにするにはRCC_AHB1ENRレジスタを使います。

    Screen Shot 2016-01-05 at 23.13.58出典: ReferenceManual

    APB1においてGPIODを有効にするかどうかはODRのように対応するビットを1にすることで決められます。この図によると GPIODはAHB1ENRの3bit目です。
    このbitは最初0でこれを1にすればGPIODにクロックを供給できます。
    クロックが供給できればレジスタの書き込みん対してちゃんと動作するようになります。(同時に何もしてなくてもそれなりの(本当にわずかな)消費電流を食うことになります)
    これをレジスタに書き込む前に行えば、完成です。

    [c]

    #include “stm32f4xx.h”

    int main(void)
    {
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;

    GPIOD->MODER = 0x40000000;
    GPIOD->ODR = 1 << 15;
    while(1);
    }

    [/c]

    このRCC_AHB1NERGPIODNENというのは((uint32_t)0x00000008)のことです。ちょうど3bit目だけ1の数字です。この方が分かりやすいですよね、これもstm32f407xx.hで定義されています。
    これをor演算でAHB1ENRにセットしています。(or演算なのは、他の既に1になっているものに影響を与えずにここだけ追加で1にするため)
    どうでしょう、これで青色LEDが点灯するようになったと思います。

    まとめ

    • マイコンの中にはペリフェラルというのがあり、いろいろな機能を持っている
    • GPIOはピンの電源ON,OFFなどを管理しているピンのペリフェラル
    • GPIOではピンはポートで管理されている
    • ピンがhighかlowかはそのポートのODRレジスタでセットできる
    • ピンが出力か入力かなどはそのポートのMODERレジスタでセットする
    • ペリフェラルごとにクロックの供給はON,OFF出来て、基本はOFFなので最初にONにしないといけない

    どうでしょうか、ただLEDを点灯するだけでもしらないといけないことが多くて大変ですよね。
    ただ、進んでいくうちに段々と楽になっていきます。
    次回は入力にしてボタンを押したらLEDをつける。といったことをやってみましょう。

  • ST純正のST-Link Utilityを入れる

    ST-Link UtilityはSTMが出しているST-Linkを使ってプログラムを書いたり消したりするためのライターソフトです。これを使うとST-Link自体のプログラム(ファームウェア)も最新のものにできます。
    http://www.st-japan.co.jp/web/jp/catalog/tools/PF251168
    ここからダウンロードします。

    stlink000

    このページの下の方に
    STSW-LINK004というのがあります、この先にダウンロードボタンがありますのでこれを開きます。

    stlink00

    開いたらダウンロード押してダウンロードします。
    完了したらインストーラーを開いてください。

    stlink0

    stlink1

    Nextを押して進めていきます。利用規約も出てきますので同意の場合は「Yes」です。

    stlink2

    無事に進めていくと、完了直前でドライバーのインストーラーが立ち上がります。ST-LInkをWindowsで使うためのものです。僕の場合何故か2個も同じものが出てきました。
    「次へ」でインストールします。僕みたいに2つ出た場合は2つともやっちゃえばOKです。

    stlink4

    そして大元のインストーラーも「Finish」で完了します。
    するとデスクトップにアイコンが出てきます。
    stlink5

    早速開きましょう。

    stlink6

    こんな画面が出てくるとおもいます。
    今回やりたいのはCodeIDEで使うためのファームウェアアップデートです。

    ファームウェアアップデート

    DiscoveryなどST-Linkを繋いだ状態で、まず認識するか確かめます。コネクトボタンを押して接続を確認します。確認できれば以下の様な画面になります。

    stlink7

    では、アップデートします「ST-Link」から「Firmware update」を押します。

    stlink8

    Upgrade画面が出てきますので「Device Connect」を押します。

    stlink9

    ここで、以下の様なエラーが出ることがありますが、こういうときはケーブルを一回抜いて再度繋げばOKです

    stlink10

    正常に接続できたら今のバージョンと最新のバージョンがでてきます。
    この場合明らかに古いので、最新にします「Yes>>>>」ボタンを押すとアップデートされます

    stlink11

    正常に書き込めたらまたCoIDEにもどって書き込んでみましょう。

    CoIDEでもう一回やってみる

    前回の通り、もう一度フラッシュ書き込みをしてみましょう。
    以下のように成功して、基板上の青いLEDがつくと思います。

    stlink12

    これで完了です!

    目次へ戻る

  • CoIDEでSTMF4Discovery用プロジェクトを作成してみる

    では、早速プログラムを書いてみましょう。CoIDEを起動してください。プロジェクトの作成は「Project」の「New Project」から行います。

    codeide9

    するとCPUを選択する画面になります。

    codeide10

    僕らが使うSTM32F4Discoveryは「STM32F407VG」ですのでそれを探します

    codeide11

    見つけたらクリックします。すると選択肢が出てきます

    codeide12

    ここで「New Project」を選択します。すると、どこにプロジェクトを置くのか聞かれます。
    どこでもいい場合はこんな感じで設定しちゃいましょう。

    codeide13

    保存が完了するとこんな画面になるかと思います。
    左側がプロジェクトの画面で早速main.cが用意されています。

    codeide14

    CMSISの追加

    やはり、CMSISはあるととても便利です。
    これはメーカーから公開されているプログラムなどをプロジェクトに取り込む作業です。
    上の画像にある画面から追加削除できます。(家のアイコンのボタンを押せばこの画面を開けます)
    今回はとりあえず動かすための最低限なので

    • cmsis_core
    • STM32F407xx_cmsisboot

    の2つを追加します。Addボタンを押せば追加できます。

    codeide15

    追加するとプロジェクトに現れます。こんな感じです。

    codeide16

    何故か追加していないHAL_Driverが入っていますが、依存関係でしょうか。。。
    不思議な事にcmsisbootいかにもhalって言うディレクトリがあるので同じファイルが2つあることになってしまいます。
    案の定、試しにビルドボタンを押してみると(左から3つ目のアイコン、虫アイコンの2つ左)同じファイルが2つあるエラーになってしまいました。なので、HAL_Driverの方を右クリックしてDeleteから削除します。

    codeide18

    こうします。おそらくCoIDEのバグかと思いますが、これで正常にビルドできるようになりました。

    試しにプログラムを書き込んでみましょう。
    main.cに以下のプログラムを書き込みます。ただ、LEDをボタンに合わせて点灯させるだけのものです。

    [c]

    #include “stm32f4xx.h”

    int main(void)
    {
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;

    GPIOD->MODER = 0x40000000;
    GPIOD->ODR = 1 << 15;;
    while(1);
    }

    [/c]

    そしてビルドします。
    ビルドしたらフラッシュボタンを押します。このIDEからプログラムも書き込めるのは素晴らしいですね。書き込みは虫アイコンの右側です。STM32F4 DiscoveryをUSBでWindowsに繋いでください。
    これで書き込めるんですが、しかし、押すとこのようなエラーが。

    [shell]
    &lt;div align=”left”&gt;&lt;span style=”font-family: Courier New; font-size: small;”&gt;C:\CooCox\CoIDE_V2Beta&gt;”C:/CooCox/CoIDE_V2Beta/bin\coflash.exe” erase STM32F407VG –adapter-name=ST-Link –port=SWD –adapter-clk=500000 –driver=”C:\Users\yuki\AppData\Roaming\CooCox\CoIDE\config\flash\CooCox-Flash\CoIDE_STM32F4xx_1024K\STM32F4xx_1024.elf”&nbsp;&lt;/span&gt;&lt;/div&gt;
    &lt;div align=”left”&gt;&lt;span style=”font-family: Courier New; font-size: small;”&gt;Chip Erase:&nbsp; Failed&lt;/span&gt;&lt;/div&gt;
    &lt;div align=”left”&gt;&lt;span style=”color: #ff0000; font-family: Courier New; font-size: small;”&gt;Error: Flash driver function execute timeout&lt;/span&gt;&lt;/div&gt;
    &lt;div align=”left”&gt;&lt;/div&gt;
    [/shell]

    codeide19

    調べたところ、どうやらDiscoveryボード上のST-Linkチップのファームウェアが古いようです。最新にUpdateしましょう。
    ST-LinkのファームウェアをUpdateするにはSTMが出しているST-Link Utilityを使います。

    ちょっと長くなるので次回にしましょう。
    目次へ戻る