マイコンカーラリー用高速データロガー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はクロックを上げると出始め、クロックを下げるとエラーが無くなるので問題の原因はタイミングということで間違いないと思います。

 

FatFS+FreeRTOSでDiskErrorが発生

マイコンカーラリー用高速データロガー(シリアル送信データをSDカードに記録することで1mS毎のデータログを可能にするタイプ)

のプログラムを書いて実験しているとDiskErrorがランダムに出て記録が出来ないトラブルにぶちあたってしまいました。
DiskErrorはFatFSライブラリの下位レイヤーで発生するエラーなのでFatFSそのものに問題があるわけでもなさそうです。

CPUはstm32F765VGTで使っていたHAL_Driverのバージョンは
STM32CubeF7 Firmware Package V1.6.0 / 30-December-2016

STM32FCubeMX のバージョンはVer4.19.0でした。

HAL_Driverの中身まで調べるのは面倒なのでWebで同様の症状を探してみるとねむいさんが「stMicroのCommunityでF7_1.6.0はDiskErrorが出るよ。」と書いているのを見つけました。

Problem with FatFs and STM32Cube_FW_F7_V1.6.0 (Possible STM32Cube Bug)

これが原因に違いないと喜んで、ねむいさん推薦のFW_F7_V1.5.0に替えてもやはりランダムなDiskErrorが出て問題は解決しません。

それでHALライブラリのドキュメントに、

と書いてある資料を最後まで読んでみると(おいおい、最初に読めよ :-? )何となく原因がわかりました。
このアプリケーションはデバッグがやりやすいようにFreeRTOSを使って作っていたのですがFatFSはFreeRTOS対応版と通常版で別のドライバーを使うようになっていてFreeRTOS版の時はスタックサイズとヒープ領域の指定に気を付けないとエラーが起きるということでした。

FreeRTOSでエラーが起きないスタックサイズとヒープサイズを調べるのは結構面倒ですし特にFreeRTOSを使う必要があったわけではないので通常版でテストしてみるとあっさりエラー無しで動作するようになりました 8-)

ただ、SDMMCCLK clock divided factor は設定画面の注釈には

SDMMC_CK = SDMMCCLK / [CLKDIV + 2]. The output clock frequency can vary between 187 KHz and 24 MHz. It is advised to keep the default ClockDiv value (0) to have a maximum SDMMC_CK frequency of 24 MHz.

(0にするようお勧めします)

と書いてあるにもかかわらず 1 (48/(2+1)=16MHz)に設定する必要がありました。