目次

カメラユニット開発

 2月24日の懇親会で出た話題を思い出し
 高校生がMCR_VCに参加できるようにカメラ
 OV7670とマイコンSTM32M3Discoveryを合体
 したカメラユニットを開発することに。



 OV7670は¥1950で入手できますし、STM32M3Discoveryは
 ¥950です。送料を含めると¥3000を超えますが、高校生
 が入手できる範囲でしょう。

 ファームウエアから利用することを考えるとAPI
 (Application Program Interface)を用意して
 おいた方が、ハードウエアやデバイスドライバ
 を定義するためのハードルが低くなります。

 6つのAPIを考えます。

 各APIの内容を定義します。

  init_camera
  カメラを初期化します。
  パラメータはないですが、別に配列を定義して
  その内容をOV7670にSCCBインタフェースで転送
  します。

  get_line_sensor_data
  line_numberで指定するラインの8ビットデータを出力します。
  8ビットデータは、1ラインにあるピクセルから、生成します。
  詳細は、別に定義します。
  OV7670は、VGAサイズの画像データを取得するので
  line_numberの最大値は、479となります。

  get_line_width
  line_numberで指定するラインの、センターライン中央
  の幅を出力します。
  幅は、0あるいは正の整数で最大65535とします。
  OV7670は、VGAサイズの画像データを取得するので
  line_numberの最大値は、479となります。

  get_line_center
  line_numberで指定するラインの、センターライン中央
  のピクセル位置を出力します。
  ピクセル位置は、0あるいは正の整数で最大639となります。
  存在しない場合、9999を返します。
  OV7670は、VGAサイズの画像データを取得するので
  line_numberの最大値は、479となります

  get_line_zp
  line_numberで指定するラインの情報を2値化し
  0→1になる位置を出力します。
  0あるいは正の整数で最大9999とします。
  OV7670は、VGAサイズの画像データを取得するので
  line_numberの最大値は、479となります。
  ピクセル位置は、0から639になりますが、範囲外が
  あるので9999を使います。

  get_line_pz
  line_numberで指定するラインの情報を2値化し
  1→0になる位置を出力します。
  0あるいは正の整数で最大9999とします。
  OV7670は、VGAサイズの画像データを取得するので
  line_numberの最大値は、479となります。
  ピクセル位置は、0から639になりますが、範囲外が
  あるので9999を使います。

 ここまで考えて、秋月電子から基板を入手し
 翼のごとく、拡張基板を半田付けします。



 3.3V、5Vの電源が基板から供給されると
 緑、赤のLEDが点灯します。

 電源を供給すると、サークル状に配置された8個の
 LEDが点滅していきます。これでハードウエアには
 問題がないことがわかります。


開発環境  STM32F3Discoveryの開発環境を入手しなければ  ファームウエアを開発できません。入手可能な  開発環境を調べてみました。  開発環境は、以下の4種あるとか。  評価版であるため、KeilとAtollicの場合  コンパイル、リンクできるコードサイズが  最大32kバイトに制限されています。  ADuC7026(Analog DevicesのARM)で、Keilの  開発環境を利用した経験から、これを入手し  使うことにしました。最大容量の制限までの  コードにはならないだろうと、期待して動作  を確認することに。  開発環境のダウンロード時に、氏名、所属企業、Email  アドレス等を入力しなければなりません。所属企業に  関しては、学校名でも大丈夫でした。  STM32F3DiscoveryのARMは、Cortex-Mシリーズなので  ダウンロード前にサポートされているかを確認です。  ダウンロードし、インストールすると以下のショート  カットができました。  ショートカットをクリックすると、KeilのIDEが  開きます。  GUIは、ADuC7026(Analog DevicesのARM)で使っている  Version3と同じで、MicrosoftのVC++、XilinxのISEと  似ていて、自分には慣れた環境です。  最初は、円形に配置されているLEDの点滅プログラム  を作成してみます。  プロジェクトを作成します。  ゼロからプロジェクトを作成するのは、面倒なので  Demoの中に入っていた拡張子がuvprojであるファイル  の名前と内容を変更して対応します。  Demo.uvprojからknigt.uvprojへと内容をそっくり  コピーしておきます。  プロジェクトを統括するプロジェクトファイルの  内容は、必要項目をタグを使い設定しています。  テキストエディタを利用して、次の項目を  修正します。  StartupFileが、指定ディレクトリに存在しないときは  インストールした開発環境の中にあるディレクトリから  startup_stm32f30x.sを探し出し、指定ディレクトリに  コピーします。  プロジェクトのディレクトリに、必要なファイルを  コピーします。  knightというプロジェクトには、ディレクトリknightと  そのサブディレクトリknightを用意します。  サブディレクトリknightには、stm32f3discovery_fw.zipの中にある  次のディレクトリ中のCソースコードとヘッダファイルをコピーします。   STM32F3-Discovery_FW_V1.1.0/Project/Demonstration  さらに次のディレクトリにあるCソースコードとヘッダファイルをコピーします。   STM32F3-Discovery_FW_V1.1.0/Libraries/STM32_USB-FS-Device_Driver   STM32F3-Discovery_FW_V1.1.0/Libraries/STM32F30x_StdPeriph_Driver   STM32F3-Discovery_FW_V1.1.0/Libraries/CMSIS/Device/ST/STM32F30x   STM32F3-Discovery_FW_V1.1.0/Utilities/STM32F3_Discovery  こうして準備したファイルのうち、main.cだけは「読み取り専用」の  属性チェックを外しておきます。  stm32f3discovery_fw.zipは、STMicroelectronicsのサイトから  ダウンロードできます。  すべての準備が整ったなら、次の手順でコンパイル、リンクして  HEX形式ファイルを作成します。  開発環境の起動  プロジェクト選択   メニューバー中の「Project」をクリック。   ディレクトリを移動し、対象となるプロジェクト   ファイルを探します。   プロジェクトファイルを開くと、左に次の   グループ分けした構成が表示されます。  コンパイル、リンク   メニューバーで、「Project→Build target」とクリック   します。   HEXファイルを生成するためには、次のように   メニューバーで、「Project→Options」と   クリックして、表示されるタブからOutputを   選んで、チェックを入れます。  LED点滅に利用したソースコードは、以下。 /* Includes ------------------------------------------------------------------*/ #include "main.h" /* define data types */ typedef unsigned char UBYTE ; /* Private variables ---------------------------------------------------------*/ RCC_ClocksTypeDef RCC_Clocks; __IO uint32_t TimingDelay = 0; __IO uint32_t UserButtonPressed = 0; __IO uint8_t DataReady = 0; __IO uint8_t PrevXferComplete = 1; __IO uint32_t USBConnectTimeOut = 100; #define CNTMAX 16 #define OFF 0 #define ON OFF+1 /* function prototype */ void init_leds(void) ; void clear_leds(void) ; void update_leds(UBYTE x) ; int main(void) { UBYTE count ; /* SysTick end of count event each 10ms */ RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 100); /* Initialize LEDs on STM32F3-Discovery board */ init_leds() ; /* Infinite loop */ clear_leds() ; count = 0 ; /* endless loop */ while (ON) { /* impress */ update_leds(count) ; /* increment */ count++ ; /* judge */ if ( count == CNTMAX ) { count = 0 ; } /* delay */ Delay(5) ; } } /* set directions */ void init_leds(void) { STM_EVAL_LEDInit(LED3); STM_EVAL_LEDInit(LED5); STM_EVAL_LEDInit(LED7); STM_EVAL_LEDInit(LED9); STM_EVAL_LEDInit(LED10); STM_EVAL_LEDInit(LED8); STM_EVAL_LEDInit(LED6); STM_EVAL_LEDInit(LED4); } /* turn off all LEDs */ void clear_leds(void) { STM_EVAL_LEDOff(LED3); STM_EVAL_LEDOff(LED5); STM_EVAL_LEDOff(LED7); STM_EVAL_LEDOff(LED9); STM_EVAL_LEDOff(LED10); STM_EVAL_LEDOff(LED8); STM_EVAL_LEDOff(LED6); STM_EVAL_LEDOff(LED4); } void update_leds(UBYTE x) { /* turn off all LEDs */ clear_leds(); /* turn on target LED */ if ( x == 0 || x == 15 ) { STM_EVAL_LEDOn(LED3) ; } if ( x == 1 || x == 14 ) { STM_EVAL_LEDOn(LED5) ; } if ( x == 2 || x == 13 ) { STM_EVAL_LEDOn(LED7) ; } if ( x == 3 || x == 12 ) { STM_EVAL_LEDOn(LED9) ; } if ( x == 4 || x == 11 ) { STM_EVAL_LEDOn(LED10); } if ( x == 5 || x == 10 ) { STM_EVAL_LEDOn(LED8) ; } if ( x == 6 || x == 9 ) { STM_EVAL_LEDOn(LED6) ; } if ( x == 7 || x == 8 ) { STM_EVAL_LEDOn(LED4) ; } } /** * @brief Inserts a delay time. * @param nTime: specifies the delay time length, in 10 ms. * @retval None */ void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); } /** * @brief Decrements the TimingDelay variable. * @param None * @retval None */ void TimingDelay_Decrement(void) { if (TimingDelay != 0x00) { TimingDelay--; } } /** * @brief Basic management of the timeout situation. * @param None. * @retval None. */ uint32_t LSM303DLHC_TIMEOUT_UserCallback(void) { return 0; } /** * @brief Basic management of the timeout situation. * @param None. * @retval None. */ uint32_t L3GD20_TIMEOUT_UserCallback(void) { return 0; } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #endif  ライブラリにある関数を利用しているので、オブジェクト  指向の記述になっています。また、デバッグ用処理コード  が入っているので、少し冗長になっています。  基板にダウンロードし、動作確認します。  ST-LINK Utility   STMicroelectronicsのサイトから、次のZip形式ファイル   をダウンロードし、展開後、インストールします。    st-link_v2_usbdriver.zip   インストールすると、デスクトップに次のアイコンが   できます。   アイコンのダブルクリックで、次のGUIが開きます。  ボードと接続   USBケーブルを接続し、リンクするには   ソケットアイコンをクリックします。   リンク接続に成功すると、諸元を表示します。  HEXファイル指定   メニューバーのキャビネットアイコンを   クリックします。   ディレクトリをたどって、目的のHEXファイル   を探します。   HEXファイルをクリックすると、ST-LINK   の環境にロードされます。  ファイル転送   メニューバーから「Target→Program...」をクリック。   ダウンロード動作画面が現れたなら、「start」をクリック。   ステータスで、ダウンロードの成功、失敗を確認できます。   これで、STM32F3Discovery基板上のLEDが次のように   点滅を繰り返すことを確認できます。   リンク切断には、メニューバーの「disconnect」アイコンを   クリックします。
カメラとの接続  環境操作がわかったなら、早速、コンパイル、リンクして  となりそうですが、OV7670をどこに接続するか決めてから  半田付けになります。  STM32F3Discoveryのチップは、STM32F303VCBT6と  型番がついたいます。  このチップにはピンが100本あります。  ポートはPA、PB、PC、PD、PE、PFでした。  PAからPEが各16ピン、PFが8ピンになっています。  ワンチップマイコンでは、兼用ピンになっている  ことが多いので、GPIO(General Purpose Input and Output)で利用できるポートを、データシートから  拾ってみると、PDの16ピンをまるまる使えそうです。  OV7670は、16ピンのコネクタで接続するので電源を  除いて、14ピンあれば使えます。16ピンを用意して  おけば、大丈夫だろうと判断しました。  動作周波数は、最大72MHzとなっていますが、OV7670  と同期を取りながら処理するには、タイマー割込み  を多用するのが自分流。タイマーに関しての調査を  しました。  タイマーは、全部で13あります。この中で12個の  タイマーは16ビット、ひとつだけが32ビットでした。  PWM波形生成、外部信号キャプチャに使うには、充分  な個数のタイマーを持っていると言えるでしょう。  他の周辺モジュールに関してもデータシートでみると  12ビットのA/Dコンバータ、12ビットのD/Aコンバータ  DMACがあります。DMACを使うと、内蔵SRAMにデータを  入れながら、他の処理もという並列動作ができそう。  OV7670とSTM32F3Discoveryを接続するために  ピンアサインを決めます。  16ピンの割当てを次のようにしてみます。  CLKは20MHzほどが必要です。  タイマー割込みを利用するのは、面倒なので  24.576MHzのクリスタルを発振させ、ここに  接続します。  また、PCLKはOV7670が出力してくるので  一度設定がうまくいけば、不要になるので  接続なしとします。  もう一度、ピン割当てを見直し、次のように  しました。  ピン割当てを確定したので、インタフェース  回路を考え、次のようにしました。  実際に半田付けする段になると、拡張コネクタは  不規則な配置になっていたので、次のように変更  しました。  PE0は、外部割込みピンに割当てられて  いるので、VSYNCの変化を捕らえて同期  処理するのによいと判断しました。  この変更で、接続は次のようにしました。  基板に必要な部品を接続すると、以下の様に。  ハードウエアが出来たので、3レイヤーで処理  コードを考えていきます。 (under construction)
目次

inserted by FC2 system