B-U585I-IOT02Aディスカバリー ボードを使ってカメラ モジュールを操作する

はじめに

mb1379 b-u585i-iot02a_scaled

STMicroelectronicsのIoTディスカバリボードのコレクションに新たに加わったB-U585I-IOT02Aは、STM32U585AII6Q MCUを搭載しているのが特徴です。STM32 MCUの超低消費電力カテゴリに分類されるSTM32U5シリーズは、同クラスの製品よりも強化されたパフォーマンスとセキュリティ機能を誇っています。これらの機能を利用して、このディスカバリ ボードには、多機能で安全かつ効率的なIoTアプリケーションをいくつでも開発するために使用できるコンポーネントが豊富に含まれています。またWiFiとBLEの通信モジュール、外部メモリ、および各種環境センサを搭載しています。

また、B-U585I-IOT02Aはカメラモジュール拡張コネクタを搭載しており、外部カメラモジュールの接続が可能です。特に、B-CAMS-OMVに同梱されているカメラモジュールMB1379は、ディスカバリボードと完全互換で、人気の高いOV5640 5-Mpixel CMOSイメージセンサをベースにしています。次のチュートリアルでは、白紙のプロジェクトから開始して、このモジュールとやり取りする方法を示します。


:ただし、このコネクタのシルクスクリーン(図1)には、誤ってMB1183カメラモジュールを使用するように記載されています。これはいくつかの理由で間違っています。

  1. MB1183モジュールは、単体ではお求めいただけません。本モジュールを搭載したST EVALボードを購入することでのみ入手可能です(AN5020の表4参照)。EVALボードはディスカバリボードやNUCLEOボードよりもかなり高価なため、このモジュールを入手するのはコスト的に厳しいかもしれません。

  2. MB1183モジュールではカメラモジュールコネクタの1、2ピンに1.8Vを供給する必要がありますが、これらのピンはB-U585I-IOT02Aのテストポイントに接続されています(つまり、フローティングのままです)。そのため、このディスカバリボードでMB1183モジュールを使用することは可能ですが、まず適切な電圧を供給するために1.8Vの電圧レギュレータを追加する必要があります。図2に小型のSOT-23-3デバイスを用いた例を示します(AP2138N-1.8TRG1DICT-NDレギュレータを示しています)。

  3. MB1183モジュールは、STのサポートが不十分なS5K5CAGAというCMOSイメージセンサを採用しています。ドライバは、OV5640センサ用のドライバと同様に、STM32U5 MCUパッケージおよびGitHubから入手可能です。しかし、S5K5CAGAドライバは古く、OV5640ドライバが提供する機能の多くがありません。

また、MB1066カメラモジュールの紹介もあります(例:AN5020)。しかしこのモジュールのピンアウトは、B-U585I-IOT02Aのカメラモジュールコネクタと互換性がありません。

b-u585i-iot02a_cameraSilkscreen
図1: カメラモジュールコネクタの表記が誤って
いるシルクスクリーンマーキング    


図2: CN7 の1pinに1.8Vを供給(MB1183 カメラ
モジュールを使用する際に必要)するために
B-U585I-IOT02Aにはんだ付けした小型電圧 
レギュレータ(SOT-23-3 パッケージ)          


必要条件

このチュートリアルに沿って進めるには、以下のソフトウェアとハードウェアが必要です。

  • STM32CubeIDE(バージョン1.11.0)

    • 別のIDEを使用したい場合は、スタンドアロンのSTM32CubeMXツールを使用し、プロジェクト設定で別のToolchain/IDEを選択するオプションも用意されています。生成されたコードは、任意の開発環境にインポートすることができます。この方法でも、以下の手順はガイドラインとして使用できます。
  • B-U585I-IOT02Aディスカバリボード

  • B-CAMS-OMVバンドル

    • カメラモジュールMB1379とアダプタボードが付属しています。
  • Micro-B USBケーブル(例:993-1294-ND

手順

この手順では、STM32U5がカメラモジュールを制御して画像を読み取ることができるように、設定と初期化 を行う方法を説明します。また、カメラ本体(OV5640)のドライバを追加する方法と、コーディングをさらに簡素化するための抽象化レイヤを提供する方法も紹介しています。CubeMXの構成にこだわらず、動作する例から始めたい場合は、STM32CubeU5 MCUパッケージのBSP例があります。

ステップ1.STM32CubeIDEプロジェクトを作成する。

もちろん、既存のSTM32U5プロジェクトから始める場合は、このステップをスキップすることができます。

  1. File > New > STM32 Projectを選択して、STM32プロジェクトウィザードを開始します。

  2. STM32 Project Target Selectionツールで、Board Selectorタブを選択し、図3に示すようにB-U585I-IOT02Aボードを選択します(左側のボードフィルタを使用して検索を絞り込 みます)。Next > をクリックします。


    図3: プロジェクトのターゲットボードとして、
                           B-U585I-IOT02Aディスカバリボードを選択する。

  3. プロジェクト名(例:「b-u585i-iot02a_camera」)を入力し、Finishをクリックします。プロジェクトの作成中に、いくつかのプロンプトが表示されますので、以下のように回答してください。

    • 「Initialize all peripherals with their default Mode?」と表示されたら、Noをクリックします。

    • 「Device Configuration Tool editor is associated with Device Configuration Tool perspective. Do you want to open this perspective now?(Device Configuration Toolエディタは、Device Configuration Toolのパースペクティブに関連しています。このパースペクティブを開きますか?)」と表示されたら、Yesをクリックします。

    • 「The instruction cache (ICACHE) must be enabled to reach the maximum performance. The ICACHE can be enabled from the Pinout tab under ICACHE. Do you still want to generate code?(最大の性能を発揮するためには、命令キャッシュ(ICACHE)を有効にする必要があります。ICACHEはPinoutタブのICACHEから有効にすることができます。それでもコードを生成しますか?)」というプロンプトが表示されたら、Yesをクリックします。

ステップ2.STM32CubeMXの設定

  1. Pinout & Configurationタブで、パースペクティブの左側にあるコンポーネントリストから ICACHEコンポーネントを選択し、 Modeを「1-way(direct mapped cache)」に変更します(図4)。


    図4: 命令キャッシュ(ICACHE)を有効化する。

  2. コンポーネントリストからI2C1ペリフェラルを選択します。Modeパネルで、I2Cオプションを「I2C」に変更し、そうすることでピンアウトビューのピンPB8とPB9が緑色になることを確認します(これらには既にユーザー ラベルが付いているはずです)。オプションとして、ConfigurationパネルのParameter Settingsタブで、 I2C Speed Modeを「Fast Mode」に変更します。これらの手順は、図5に示されています。


    図5: PB8ピンと PB9ピンでI2Cバスを有効にして構成する。

  3. コンポーネントリストから、DCMIペリフェラルを選択します。

    1. ModeパネルでDCMIオプションを「Slave 8 bits External Synchro」に変更します。

    2. Configurationパネルで、Parameter Settingsタブを選択します。Pixel clock polarityを「Active on Rising Edge」に、Vertical synchronization polarityを「Active High」に、そしてHorizontal synchronization polarityを「Active High」に変更します(図6)。


      図6: OV5640イメージセンサとインターフェースするためのデジタルカメラ
      インターフェース(DCMI)ペリフェラルを有効化し設定する。

    3. ConfigurationパネルでNVIC Settingsタブを選択し、チェックボックスをオンにし「DCMI/PSSI global interrupt」を有効にします(図7)。


      図7:「DCMI/PSSI global interrupt」を有効にする。

    4. Configurationパネルで GPIO Settingsタブを選択し、図8に示すように信号表に合わせてピンの割り当てを変更します。最も簡単な方法は、STM32CubeMX User Manual (UM1718)の4.4.5節で説明されています。

      手動で機能を別のピンに再割り当てする場合は、以下の順序で行います。

      1. ピン配列図で、CTRLキーを押しながら、ピンを左クリックし、保持します。
        移設可能なピンであれば、青くハイライトされ、点滅します。

      2. 機能を目的のピンまでドラッグします。


      図8: DCMI GPIO設定をカメラモジュールのコネクタのピン
      配列に合わせる(UM2839の表27を参照)。     

  4. ピン配列図を使用して、図9に示すように、ピンPI1をGPIO_Inputに、ピンPI2とPI3をGPIO_Outputに設定します。

    configuration_gpio_pinout
    図9: 残りのカメラ制御信号を有効にする。PLUG、RSTI、
    およびXSDN(UM2839の表27を参照)。       

  5. コンポーネントリストからGPIOペリフェラルを選択し、PI1、PI2、およびPI3ピンを図10に示す信号表と一致するように設定します。


    図10: 残りのカメラ制御信号を設定する。PLUG、RSTI、および XSDN。

  6. コンポーネントリストから、GPDMA1を選択します。

    1. Modeパネルで、Channel 12 - 8 Words Internal FIFO / 2D addressingオプションを「Linked-List Mode」に変更します。

    2. ConfigurationパネルでCH12タブを選択し、Execution Mode (circular/linear) of the Linked Listオプションを「Circular」に設定します(図11)。


      図11: 汎用DMA 1(GPDMA1)ペリフェラルのチャンネル12を有効にし、設定する。

    3. Configurationパネルで、NVIC Settingsタブを選択し、「GPDMA1 Channel 12 global interrupt」を有効にするためにチェックボックスをオンにします(図12)。


      図12: 「GPDMA1 Channel 12 global interrupt」を有効にする。

  7. コンポーネントリストから、LINKEDLISTを選択します。

    1. Add Listボタンをクリックし、「YourQueueName」リストを選択します。図13のハイライトで示されるように、リストを設定します。


      図13: GPDMAリンクリストキューを設定する。

    2. S「YourNodeName」ノードを選択し、図14のハイライトで示すようにノードを設定します。


      図14: GPDMAリンクリストの最初のノードを設定する。

    3. Add Nodeボタンをクリックし、「YourNodeName」ノードを選択します。DCMINode1(図14)とまったく同じようにノードを設定しますが、ノード名に「DCMINode2」を使用することだけが違います(図15)。


      図15: GPDMAリンクリストの2番目のノードを追加し、設定する。

  8. Project Managerタブを選択し、Code Generatorオプションに切り替えます。「Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral」の横のボックスをチェックします。これは図16で示されています。


    図16: code generation(コード生成)の設定を変更し、ペリフェラル
    ごとに一組の「.c/.h」ファイルを生成する。                  

  9. *.iocファイルを保存します(File > Savee)。「Do you want to generate Code?」というプロンプトが表示されたら、Yes.をクリックします。

ステップ3:リンクリストを初期化する

ステップ2(g)でリンクリストを設定しましたが、.iocファイルを保存する際にすべての初期化コードが自動生成されるわけではありません。リンクリストキューをGPDMAのチャンネル12に関連付けるには、main.c ファイルに以下のコードスニペットを追加する必要があります。

  1. main.c ファイルで、USER CODE Includes セクションに #include "linked_list.h" という指令文を追加してください。

  2. USER CODE PV セクションに、以下のコード行を追加します。

    extern DMA_QListTypeDef DCMIQueue;
    
  3. USER CODE 2 セクションに、以下のコード行を追加します。

    MX_DCMIQueue_Config();
    HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel12, &DCMIQueue);
    __HAL_LINKDMA(&hdcmi, DMA_Handle, handle_GPDMA1_Channel12);
    

    これらのサブステップは、以下の図17にまとめられています。この時点で、プロジェクトは正常にビルドされるはずです。

    linkedlistInit_code
    図17: 以前に設定した「DCMIQueue」をGPDMA1のチャネル12に
    関連付けるように、main.c ファイルを変更する。   

ステップ4:カメラドライバを追加する

  1. まず、OV5640ドライバを入手/配置する必要があります。これはSTM32CubeU5 MCU packageに含まれており、またstm32-ov5640 GitHub repositoryからも入手可能です。しかし、プロジェクトの作成にSTM32CubeIDEを使用したため、STM32CubeU5 MCUのパッケージが自動的にダウンロードされ、ローカルのSTM32Cube Repositoryに保存されています。ローカルリポジトリがマシンのどこにあるかわからない場合は、Window > Preferencesを選択し、STM32CubeカテゴリからFirmware Updaterを選択してください。すると、図18に示すように、ファームウェアのインストールリポジトリのパスが表示されます。以下のステップ4(d)では、この場所を使用することにします。


    図18: ローカルのSTM32Cubeリポジトリの場所を決定する。

  2. Project ExplorerでDriversディレクトリを右クリックし、Import… を選択します(図19)。


    図19: Driversディレクトリにリソースをインポートする。

  3. GeneralカテゴリからFile Systemを選択します(図20)。Next > をクリックします。

    cameraDriver_import_fileSystem
    図20: インポートするリソースとして File System を選択する。

  4. STM32CubeU5 MCU PackageのDriversディレクトリに移動します(上記のステップ4(a)に示しています)。ov5640 ドライバの横のボックスをチェックして選択します(図21 を参照)。Finish.をクリックします。


    図21: STM32CubeU5 MCU PackageのComponentsドライバから
    ov5640ドライバのディレクトリを選択する。         

  5. 新しいヘッダファイルがプロジェクトに追加されたとき、コンパイラがそれらを見つけることができるように、それらを含むフォルダのパスがインクルードパスのリストに含まれている必要があります。Project Explorerで、先ほど追加したov5640ディレクトリを右クリックし、Add/remove include paths… を選択します(図22)。Select configurations to modifyダイアログボックスが表示されたら、「Debug」と「Release」の両方のオプションの横にあるボックスにチェックが入っていることを確認し、OKをクリックします。


    図22: ov5640ドライバファイルが含まれるインポートしたばかりの
        ディレクトリをインクルードパスのリストに追加する。

ステップ5:BSPドライバを追加する

  1. stripped-down B-U585I-IOT02A BSP files repositoryの内容をダウンロードし、B-U585I-IOT02Aディレクトリを<project_name>/Drivers/BSPディレクトリにコピーします。

  2. ステップ4(e)と同様に、この新しいディレクトリをインクルードパスのリストに追加します。つまり、Project ExplorerでB-U585I-IOT02Aディレクトリを右クリックし、Add/remove include paths… を選択してください。そして、OKをクリックします。この時点で、プロジェクトには図23に示すようなドライバファイルが含まれています。


    図23: ステップ4と5で説明したドライバファイルを追加
     した結果によるプロジェクトのサブ構造。

  3. 最後に main.c ファイルで、USER CODE Includes セクションに #include "b_u585i_iot02a_camera.h" という指令文を追加してください。

使用例

以上の手順で、STM32U5の設定/初期化、イメージセンサドライバの追加、BSP抽象化レイヤの組み込みを行うことで、接続したカメラモジュールのドーターボードから簡単に画像を取り込むことができます。その例として、カメラを初期化し、640x480サイズの画像を取り込み、SRAMにあるデータバッファに保存するコードを以下に示します。B-U585I-IOT02Aにはディスプレイが搭載されていないため、SRAMから画像を読み出し、PCで閲覧する方法がいくつか考えられますので、次章でご紹介します。

  1. STM32CubeIDEで main.c ファイルを開いてください。USER CODE PV セクションに、以下のコードを追加します。

    uint32_t CameraBuf[640*480/2];
    volatile uint8_t frameFlag;
    
  2. USER CODE 0 セクションに、以下のコードを追加します。

    void BSP_CAMERA_FrameEventCallback(uint32_t Instance)
    {
      frameFlag = 1;
    }
    
  3. USER CODE 2 セクションに、以下のコードを追加します。

    // Start w/ LEDs off
    HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET);
    
    // Initialize camera
    if (BSP_CAMERA_Init(0, CAMERA_R640x480, CAMERA_PF_RGB565) != BSP_ERROR_NONE)
    {
      HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_RESET);
    }
    else
    {
      HAL_Delay(1000); // give the camera time to return good images
    
      // Take snapshot
      frameFlag = 0;
      BSP_CAMERA_Start(0, (uint8_t *)CameraBuf, CAMERA_MODE_SNAPSHOT);
      while (frameFlag == 0);
      HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_RESET);
    }
    

これでプロジェクトが正常にビルドされます(Project > Build Project)。各ピクセルが2バイトのストレージを必要とする生のRGB565画像を保存しているため、RAMの使用率が非常に高いことに注意してください。したがって、640×480ピクセルには、(使用可能なSRAM768KiBのうち)600KiBのメモリが必要です。

取り込んだ画像を見る

追加ソフトウェアのインストールや追加コードの記述なしにデバイスメモリからデータを読み出す最も簡単な方法は、STM32CubeIDEデバッグツールを使用することです。

  1. MB1379カメラモジュールをB-U585I-IOT02AディスカバリボードのCN7カメラコネクタに挿し込みます。PCとディスカバリボードのCN8 ST-LINK USBコネクタの間にUSBケーブルを接続します。

  2. STM32CubeIDE プロジェクトをデバッグします(例:Run > Debug)。アプリケーションを初めて起動する場合は、Edit launch configuration properties(起動設定プロパティの編集)ダイアログボックスが表示されます。表示された場合は、OK.をクリックします。

  3. アプリケーションがロードされ、デバッグセッションが開始されると、プログラムは main() 関数の先頭で中断されます。 Run > Resume.を選択して、プログラムの実行を続行します。

  4. B-U585I-IOT02Aの緑色のユーザーLED(LD7)が点灯したら、画像の取り込みは完了です。Run > Suspend.を選択して、デバッグセッションを一時停止します。

  5. 図24に示すように、Memoryビューを開き(Window > Show View > Memory)、緑色のプラス記号をクリックして、モニタする新しいアドレスを追加します。表示されたダイアログボックスで、テキストフィールドに「CameraBuf」と入力し、OKをクリックします。

    viewCapture_memoryView
    図24: 進行中のデバッグセッションでMemoryビューにメモリモニタを作成する。

  6. MemoryビューでExportアイコンをクリックします。表示されたExport Memoryダイアログボックスで、Formatオプションを「RAW Binary」に変更します。Lengthオプションには、CameraBuf配列の長さを入力します。CameraBuf を uint32_t CameraBuf[640*480/2]; として定義したことを思い出してください。 したがって、

    \textrm{Length} = \frac{640 \cdot 480}{2} \cdot 4

    File nameフィールドに出力ファイルのパスを入力します。OKをクリックします(図25)。

    図25: 取り込んだ画像を含むSRAMのセクションをローカルファイルにエクスポートする。

  7. RGB565の生画像を使用可能な形式に変換するには、幅広く使用されているffmpegツールを使用することができます。また、GIMPを使った代替案も以下に紹介します。インストールされていることを前提に、お好みのターミナルアプリケーションを開き、ffmpegを使って、以下のコマンドでRGB565画像をPNG画像に変換してください。図26のような出力が表示されます。

    ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb565 -s 640x480 -i <input_file> -f image2 -vcodec png <output_file>
    


    図26: ffmpegを使用してRGB565の生画像をPNG形式に変換する。

    OV5640で取り込んだ画像を変換したものを図27に示します。

    capture
    図27: B-U585I-IOT02Aディスカバリボード上のMB1379カメラモジュールで取り込んだ画像例

  8. ffmpeg の代わりに、GNU Image Manipulation Program(GIMP)を使って、エクスポートされた RGB565 の生画像を表示したり操作したりすることも可能です。 拡張子.dataで画像を保存し(上の図25参照)、GIMPで開くだけです。図28に示すようなダイアログボックスが表示されます。画像の設定を図のように変更し、Openをクリックします。


    図28: GIMPを使用してRGB565の生画像を開き、表示/操作する。




オリジナル・ソース(English)