開発環境の進化はプログラミングを簡単にする

高校生モノづくりコンテスト電子回路部門は、課題プログラムに使うCPUボード、プログラミング言語と開発環境を自由に選ぶことが出来ます。

組み込み用CPUの性能と開発環境は、十年二昔の勢いで進歩していて、どのCPUや開発環境を選ぶかがコンテストの成績に大きく影響します。
野球では金属と木のバットで飛距離が違い、高校野球で使える金属バットがプロ野球で使えなかったりしますが、ものづくりコンテストのプログラムで使う開発環境の影響はそれより桁違いに大きいものです。

ですから、モノづくりコンテストにチャレンジするにあたって、開発環境として何を選ぶかが重要になります。

開発環境の進化がもたらすもの

開発環境の進化がもたらすものはプログラミングの効率化です。
開発環境が一世代進化するごとに、少ないプログラムサイズで楽に高度な処理が書けるようになってきました。

例えば、高校生モノづくりコンテスト全国大会の課題は、トップクラスのプログラミング能力を持つ生徒が、平均的な開発環境を使って、制限時間内に解くのが難しいレベルに設定される傾向があります。
課題を解くにはプログラムを書く能力だけではなく、課題を解くアルゴリズムを考える能力も必要ですが、平均的な開発環境の半分の時間でプログラムが書ける開発環境を使うのと、平均の倍の時間がかかる旧世代の開発環境を使うのでは大きな差があります。

CPU初期化プログラムの効率化

最近の組み込み用CPUは高度な周辺機能を内蔵して、高性能になった反面、周辺機能を初期化するために複雑なレジスタ設定が必要になっています。
そして、わかりにくいデータシートを見ながら、数多いCPUと接続するハードウェアに合わせて、ひとつの間違いも無くレジスタに設定する値を調整するまではCPUは正常に動作しません。
これが、組み込みプログラムが難しいと思われる理由のひとつになっていて、Arduinoはこの部分を徹底的に標準化することで、誰でも簡単に組み込みプログラムが始められるようにすることに成功しました。

CPUの性能を最大限に発揮する必要がある本格的な組み込みプログラムの分野では、標準化による初期化プログラムの簡略化という手法を取ることは出来ませんでしたが、最近になっていくつかの組み込みCPUメーカーから「GUI画面で周辺機能パラメータを設定し、自動的に初期化プログラムを生成する。」という手法を使った開発環境が提供されて、実用的レベルになってきました。

MPLAB-Xに内蔵されたMCCがそのひとつで、MCCを使えばデータシートを見なくても簡単に初期化プログラムが完成します。
簡単とはいっても周辺機能に関する知識は必要ですが、周辺機能とレジスタの対応を考えなくても初期化が出来るようになったのは非常に大きな進歩と言えます。

マイコンカーラリー用高速データロガーUART2MMC動作確認

マイコンカーラリーで1mS毎のデータ記録が出来るシリアルポート経由データロガーUART2MMCがとりあえず動作するようになりました。

マイコンカーラリーでは速度を上げるために走行中のデータを記録出来るSDカードが大活躍しています。
拡張基板のSDコネクタの信号をR8CPUに接続してプログラムでデータをSDカードに記録できるようにしていますが、走行制御の合間に記録するため10mS毎に64文字の記録しか出来ない仕様となっています。

走行制御は1mS毎の割り込みと1mS前後で繰り返す制御ループでおこなっているので走行中に起こっていることを正確に把握するためには1mS 以下のサイクルでデータの記録をおこなうのが理想です。

このボードは1mS毎にシリアル送信されたデータをSD/MMCメモリに書き込んで記録する目的で作成しました。

UART2MMCの機能一覧

1.消費電流 typ 50mA
2.通信速度 1250000bps MAX
3.LFN(長いファイル名)対応
4.ファイルオープンで自動的に新しいファイル名を作成
5.対応フォーマット FAT16, FAT32, exFAT
6.USBシリアルプログラム書き込み可能

ロータリーディップSWの選択

0: R8Cへのプログラム書き込み
1:1250000bpsデータロガー
2:USBからのコマンドでSD/MMCへアクセス

データロガー書き込みサンプル関数

/************************************************************************
 printdata : シリアルポートにデータ出力   
  iRunReady: 出力準備フラグ
     iRunReady!=0で ファイルオープンコマンドを出力し、データ出力の用意をする
	 uRunReady==0 になったらファイルクローズコマンドを出力し初期状態に戻る                       
  iRunFlag: データ出力フラグ
     データ出力(書き込み)の用意が出来ているときにiRunFlag != 0でデータ出力
  使い方の例
   1.走行開始SWの押し下げで iRunReady を1にする
    2.走行開始のタイミングで iRunFlag  を1にする
	3.停止時にiRunReadyとiRunFlag を0にする

  ※UART2MMCボードを使ったシリアルポート経由データログの注意

  最初に "open filename\n"コマンドを送りファイルをオープンし、
  最後に"close \n"コマンドでファイルを閉じる。
  ファイル名が重複する場合は最後の3文字を数値に変更してユニークなファイル名で
  ファイルが作られるので最後の3文字は"000"にしておくと良い。
  ファイル名は拡張子を含めて最大255文字までなのでコンパイル日時もファイル名に
  付加することを推奨
  通信速度に最大の1250000bpsを使った場合
 1mSで送ることの出来る(保存できる)文字数は約100文字以内
 printf()関数を使うと時間がかかるので下の例のように個別のデータ出力関数を使う。
  (uart0.cのデータ出力関数一覧)
  文字列出力 :   void uart0_puts(char far *s);
  2桁16進出力  : void uart0_puthxb(unsigned char n);
  4桁16進出力  : void uart0_puthxw(unsigned short n);
  8桁16進出力  : void uart0_puthxl(unsigned long n);
  int 10進出力 : void uart0_putint(int n);
  unsigned int 10進出力  : void uart0_putuint(unsigned int n);
  long 10進出力          : void mon_putlong(long n);
  unsigned long 10進出力 : void mon_putulong(unsigned long n);
  数値を文字列に変換して uart0から出力
   u : 出力する数値, len : 文字列の長さ, radix : 基数 2,10,16等   
                           void uart0ltoa(unsigned long u, int len, int radix);
  CRLF出力     : void uart0_putcrlf(void);
************************************************************************/

void printdata(void)
{
  static int state=0;
	
  switch( state ){
    case 0:  // アイドル状態
      if( iRunReady ) state = 10;
      break;
    case 10: // ファイルオープンコマンド出力
      if( iRunReady == 0 ){
          state = 0;
          break;
      }
      /* ファイル名にコンパイル日時を追加、
         ファイル名重複時に最後の数字'000'は自動的に置き換えられる  */
      uart0_puts( "open ");
      uart0_putfn("KENKO_H28_LogData_" );
      uart0_putfn(C_DATE);
      uart0_putfn("_");
      uart0_putfn(C_TIME);
      uart0_putfn( "_000.csv\r\n" );
      state = 20;
      errorct = 0;
      break;
    case 20:
      // ヘッダー出力
      uart0_puts( "wr Time,Pattern,Sensor,Center,Analog,Angle,"
                  "SB_M,Encoder,LF_M,RF_M,LR_M,RR_M,saka\r\n" );
      state = 30;
      break;	
    case 30: //  iRunReadyが1の間データ出力を繰り返す
      if( !iRunReady ){
          state = 40;
          break;
      }
      if( iRunFlag ){
          /*************** データ出力例  **********************/
          uart0_puts("wr ");                                 // 書き込みコマンド(必須)
          uart0_putuint(uRunTime);         uart0_putch(','); // mS単位の走行時間
          uart0_putint(pattern);           uart0_putch(','); // パターン番号
          uart0_puthxb(sensor_inp());      uart0_putch(','); // デジタルセンサ(4bit)
          uart0_putint(center_inp());      uart0_putch(','); // デジタルセンサ(中心)
          uart0_putint(getAnalogSensor()); uart0_putch(','); // アナログセンサ
          uart0_putint(getServoAngle());  uart0_putch(',');  // ボリューム(ステアリング角度)
          uart0_putint(ServoPwm_Buff); 	uart0_putch(',');    // サーボPWM
          uart0_putint(iEncoder);       uart0_putch(',');    // ロータリエンコーダ
          uart0_putint(motorLF);        uart0_putch(',');    // 左前モータ
          uart0_putint(motorRF);        uart0_putch(',');    // 右前モータ
          uart0_putint(motorLR);        uart0_putch(',');    // 左後モータ
          uart0_putint(motorRR);        uart0_putch(',');    // 右後モータ
	  uart0_putint(ad4);	// 坂センサー	
	  uart0_putcrlf();
          /*************** データ出力:ここまで  ******************************/
      }
      break;
    case 40:
      printf("close\n");
      state = 0;
      break;
    default:
      state = 0;
      break;
  } 	
}

stm32F765VGT with STM32CubeMXでFatFSが動作する条件

STM32CubeMXのHALライブラリには ELM by ChaN さんの開発したFatFSが含まれていてSDカードやUSBメモリにFAT16,FAT32,exFATが使えるファイルシステムを簡単に作ることが出来ます。

動作確認したHALライブラリのバージョン

ただし2017/02/13現在確認したところ動作したのは次のV1.5.0で

OK:  STM32CubeF7 Firmware Package V1.5.0 / 23-September-2016

Ver1.5.1とVer1.6.0ではランダムにDiskErrorが発生します。
ねむいさんによるとこれはHAL Driverのバグではないかということです。
私のように無闇に最新バージョンにアップデートすると痛い目をみることになるのでVer1.5.0を使うようにしましょう。 8-)

NG: STM32CubeF7 Firmware Package V1.5.1 / 07-October-2016

NG: STM32CubeF7 Firmware Package V1.6.0 / 30-December-2016

SDMMCのクロック

最初はClock divide factor =2(12MHz)で動作しているように思ったのですがファイル書き込みを繰り返してテストするとDiskErrorが発生、結局SDMMC1に設定する Clock divide factor は16が限界で(2.67MHz)、それ以上のクロックではDhiskErrorが発生しました。
STM32F769Discoveryでは最低でも12MHzで動作しているようなのでSTM32F765のライブラリにまだ問題があるのかもしれません。

今回の目的にはこのスピードで間に合うのでとりあえずよしとします :lol:

(追記)

古い2GのSDカードでテストしたところまたDISKERRが出てClock divide factorを25にして安定して書き込めるようになりました。
DISKERRはクロックを上げると出始め、クロックを下げるとエラーが無くなるので問題の原因はタイミングということで間違いないと思います。