勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

マイコンと加速度センサでタコメータを作る - 8. 加速度ログ取得変更

前回までの解析を踏まえて、いくつかマイコンプログラムの変更を行います。

もう一度GitHubのリンクを貼っておきます。

GitHub - hubnoki/RXtacho: Designing tachometer using RX220

まずは現状のプログラムを説明してから変更内容を書いていきたいと思います。


ログ取得処理

このプログラムでは、以下のように加速度ログ取得を行っています。

  1. 加速度ログ用のバッファを用意
  2. ユーザのボタン操作でログ取得開始
  3. まずはログ記録用のファイルをSDカード上にオープン、ヘッダを書き込む
    ・ChaN氏のFatFsを使わせていただいています。
    ・FatFsオブジェクトを確保、ファイルオブジェクトも確保、オープンします。
  4. 加速度センサからの割り込みでバッファに書き込み
    ・書き込んだデータが読み出されないうちにバッファを1周してしまった場合、上書きしてオーバランフラグを立てます。
  5. main()関数内で、バッファに新しいデータが入っているのを検知したらファイルオブジェクトに書き込み
  6. ユーザのボタン操作でログ取得終了

これらの処理は、src/RXtacho.cに記述しています。

対応するコードは以下の通りです。


加速度ログバッファ

以下のvarsは内のグローバル変数になっていて、現在の状態を示す色々な変数を入れています。

typedef struct{
    signed short g;
    uint16_t flags;
} LOG_UNIT;

#define N_LOGBUF (2*sizeof(FIX_T)*N_GBUF / sizeof(LOG_UNIT))

static struct{

    ... 省略 ...

    union{
        LOG_UNIT g_buf[N_LOGBUF];
        struct{
            FIX_T g_buf_a[N_GBUF];
            FIX_T g_buf_b[N_GBUF];
        };
    };
    uint8_t        g_buf_sel; // Buffer side to save G sensor data
    uint16_t       g_buf_wp_a;
    uint16_t       g_buf_wp_b;
    uint16_t       g_buf_wcnt_a; // For logging mode
    uint16_t       g_buf_rp;
    uint16_t       g_buf_rcnt; // For logging mode
    uint8_t        g_overrun;
    uint8_t        logging;

    ... 省略 ...

}vars;
  • この中のg_bufが加速度ログ用バッファになっています。
    FFT用のバッファと共用の領域に確保しています。
    RAM容量かつかつなので…
  • 書き込みポインタと読み出しポインタ(g_buf_wp_a, g_buf_wcnt_a, g_buf_rp)を用意していて、これで書き込むべきデータがあること、オーバランが発生していることを検出します。オーバランフラグ(g_overrun)もここにあります。


ユーザのボタン操作でログ取得開始

main()関数内の無限ループでログ取得処理関数のproc_logging()を繰り返し呼んでいます。この関数の引数で、プッシュボタンの状態を知らせます。
フラグ分ける必要はなかったかな…

static void proc_logging(uint8_t spush, uint8_t lpush);

... 省略 ...

int main(void)
{
    char s[50];
    int i;
    unsigned long psw_cnt;
    uint8_t psw_spush, psw_lpush;

//---------------------------------------------------------

... 初期化処理色々 ...

    while (1) {

        //// Serial commands ////
        ... UARTコマンド受信処理 ...

        //// Check for PSW ////
        ... プッシュスイッチ状態確認 ...
        詳細は省略するが、psw_spush(短プッシュ)、psw_lpush(長プッシュ)フラグをセットする
        
        //// Process for each mode ////
        //---------
        if(vars.mode == MODE_LOGGING){
            proc_logging(psw_spush, psw_lpush);
        }
        //---------
        else if(vars.mode == MODE_FFT){

        ... まだ続く ...


ファイルオープン、ヘッダ書き込み

ログ取得を行っていないときに短プッシュされると、ログ書き込み用のファイルをオープンして、ヘッダを書き込みます。
また、加速度センサの動作を開始します。
先ほどのproc_logging()関数に記述してあります。


//-------------------------------------------------------------------------------------------
// Process for logging mode 
//-------------------------------------------------------------------------------------------
static unsigned long tm, tmax; // For measuring process time

static void proc_logging(uint8_t spush, uint8_t lpush)
{
    ... 省略 ...

    if(vars.logging == 0){
        // Start logging
        if(spush){
            fres = 0;

            fres |= get_file_sqno("adxl345_log_*", &(vars.log_sqno));
            fres |= (mount_SD(&(wk.fatfs)) != FR_OK);

            if(vars.log_format == LOG_FMT_CSV)
                sprintf(wk.fname, "adxl345_log_%03d.csv", vars.log_sqno);
            else
                sprintf(wk.fname, "adxl345_log_%03d.bin", vars.log_sqno);
            fres |= (f_open(&(wk.fl), wk.fname, FA_WRITE | FA_CREATE_NEW) != FR_OK);

            lcdc_fill_area(LCDC_BLACK, 0, LCDC_ROW-1, 0, LCDC_COL-1);
            if(fres){
                lcdc_puts("File open failed", LCDC_WHITE, 0, 0);
                lcdc_puts("Short push to start", LCDC_WHITE, 0, 10);
            }
            else{
                sprintf(wk.fname, "Logging ... (%d)", vars.log_sqno);
                lcdc_puts(wk.fname, LCDC_WHITE, 0, 0);
                lcdc_puts("Short Push to stop ", LCDC_WHITE, 0, 10);

                // Log file header
                if(vars.log_format == LOG_FMT_CSV){
                    fres = (
                        f_printf(&(wk.fl), "rate:%d, range:%d\r\n"
                            , value_list_rate[vars.cur_value_idx[STG_RATE]].val
                            , value_list_range[vars.cur_value_idx[STG_RANGE]].val
                        )
                    == EOF);
                }
                else{
                    // Header identifier
                    tmp = 'LOGH';
                    fres |= f_write(&(wk.fl), &tmp, sizeof(int), &btw);
                    // Header version
                    tmp = 1;
                    fres |= f_write(&(wk.fl), &tmp, sizeof(int), &btw);
                    // Header contents size (in bytes)
                    tmp = 8;
                    fres |= f_write(&(wk.fl), &tmp, sizeof(int), &btw);
                    // Header contents
                    // Rate setting
                    tmp = value_list_rate[vars.cur_value_idx[STG_RATE]].val;
                    fres |= f_write(&(wk.fl), &tmp, sizeof(int), &btw);
                    // Range setting
                    tmp = value_list_range[vars.cur_value_idx[STG_RANGE]].val;
                    fres |= f_write(&(wk.fl), &tmp, sizeof(int), &btw);
                }
                vars.logging = 1;
                vars.g_buf_wp_a = 0;
                vars.g_buf_wcnt_a = 0;
                vars.g_buf_rp = 0;
                vars.g_buf_rcnt = 0;
                adxl345_start();
            }

            ... 続く ...


バッファ書き込み

加速度センサからのピン割り込みでバッファへの書き込みを行います。
読み出しポインタも確認して、オーバランフラグをセットします。 ピン割り込みの使い方、加速度センサの設定はまた余裕があったら説明します。
下のadxl345_int_routine()関数は、割り込み発生時に実行されるよう設定してあります。

void adxl345_int_routine()
{
    signed short d[3];
    FIX_T f;
    LOG_UNIT log_d;

    ADXL345_get(d);

    // Put data into buffer
    if(vars.logging){

        log_d.g = d[vars.axis_sel];
        // Flags
        //     overrun - Overrun occurred, overwriting data which are not read
        log_d.flags = (vars.g_overrun = (vars.g_buf_wcnt_a - vars.g_buf_rcnt) >= N_LOGBUF-1);

        vars.g_buf[vars.g_buf_wp_a] = log_d;

        if(++vars.g_buf_wp_a >= N_LOGBUF)
            vars.g_buf_wp_a = 0;
        vars.g_buf_wcnt_a++;
    }
    else{

        ... FFTモード用の処理 ...

    }
}


ファイル書き込み

前に出たproc_logging()関数の続きです。

  • 書き込みポインタと読み出しポインタを比較して、一致していなければデータ書き込みが行われたと判断し、ファイルオブジェクトへの書き込みを行います。
  • ファイル関係のエラー処理も行っています。SDカードを使用しているので、途中で抜けてしまったりというエラーが考えられます。エラーが出たらログ記録を停止します。
… 続き…

        // Continue logging
        else{
            td = timer_soft_count(&tm);
            timer_soft_reset(&tm);
            if(td > tmax)
                tmax = td;

            if(vars.g_buf_wcnt_a != vars.g_buf_rcnt){ // There are rest of data to read
                ovrn = vars.g_overrun;
                if(vars.log_format == LOG_FMT_CSV){
                    fres = (
                            f_printf(&(wk.fl), "%d,%d\r\n"
                            , vars.g_buf[vars.g_buf_rp].g, vars.g_buf[vars.g_buf_rp].flags)
                        == EOF);
                }
                else if(vars.log_format == LOG_FMT_BIN){
                    fres = f_write(&(wk.fl), &(vars.g_buf[vars.g_buf_rp]), sizeof(LOG_UNIT), &btw);
                }

                if(ovrn == 1){
                    vars.g_buf_rp = vars.g_buf_wp_a;
                    vars.g_buf_rcnt = vars.g_buf_wcnt_a;
                    vars.g_overrun = 0;
                }
                else{
                    if(++vars.g_buf_rp >= N_LOGBUF) vars.g_buf_rp = 0;
                    vars.g_buf_rcnt++;
                }

                if(fres){ // f_write() or f_printf() failed
                    adxl345_stop();
                    vars.logging = 0;
                    f_close(&(wk.fl));
                    umount_SD();
//                 lcdc_puts("File write failed", LCDC_WHITE, 0, 0);
                    sprintf(wk.fname, "File write failed:%d", fres);
                    lcdc_puts(wk.fname, LCDC_WHITE, 0, 0);
                    lcdc_puts("Short push to start", LCDC_WHITE, 0, 10);

                }
            }
        }
    }

}


ログ取得終了

ログ取得中に短プッシュでログ取得を終了します。終了時、ファイルを閉じてSDカードをアンマウントします。

        // Stop logging
        if(spush){
            adxl345_stop();
            vars.logging = 0;
            f_close(&(wk.fl));
            umount_SD();

            PRINTF("Max process interval : %d\r\n", tmax);

            vars.mode_init = 1; // Return to the initial step
        }


次回に続く

長くなってしまったので、今回の記事はここまでにします。
次回は、現状すでに時間計測の仕組みが入っているので、これを説明したいと思います。