一回転が2秒ならかなりゆっくりですね。であれば、1回転したら直前の1回転分のデータをSDカードに記録するようにしてそれを9回繰り返す、と考えたほうが(3周分をまとめようとするよりも)ややこしくならないのではないかと思います。ただし、SDカードへの記録中も測定を止めてはいけない、というのが注意点。
データを貯めるエリアは2面(周)分もって、
パルスのエッジの記録は「割り込み」による。割り込みハンドラ内でmicros()の値を配列に保存
Z相が来たら記録面を切り替えて、記録済の面をSDカードに書き出す、とかいうところでしょうか。
(話だけでわかるか? とも思うけど、サンプルを書けとかいわれるとちょっと面倒だなぁ...)
面倒といいながら、SD書き込み中にちゃんと割り込みがかかるか等、色々と気になったので作ってみました。ただし、ターゲットはArduino m0/PROで、そのままDueに持っていけるかどうかは知りません。どちらもATMELのARMだし、m0の方が低クロック少メモリなので期待は出来ますが。
動作チェックとして他のマイコンで2s/555μs/694μs(それぞれ設定値、パルス同士は同期をとっていません)のパルスを作ってそれを突っ込んで大体2.0002s/22b(555)~22d(557)/ 2b6(694)~2b7(695)ぐらいの値が出ているのでそこそこかと。
SDカードの書き出しは数字で打ちたかったのですが、2秒に確実に収まるとは言えない感じだったのでバイナリにしています。書き込み時間は200~500msぐらいでばらつきます。Dueの方がCPUが早いのでもしかしたら数字でも出せるかもしれませんが、SDのI/Fが律速しているのかも。
Arduino
1//Items.h
2#include <cstddef>
3#include <SD.h>
4
5class Items {
6 //バッファ管理変数
7 volatile unsigned short *buf[2];
8//データインデックス管理
9 volatile size_t idx[2];
10//データ最大数
11 size_t bufSize;
12//周数
13 volatile unsigned int round;
14//周の最初の立ち上がりエッジ保存
15 unsigned long t0[2];
16//前回の立ち上がり時刻保存
17 volatile unsigned long tPrev[2];
18//周の初回判定
19 bool start;
20
21 public:
22 Items(size_t );
23//割り込みハンドラ本体
24 void handle();
25//周送り処理
26 void turnRound();
27//データ書き出し
28 void logWrite(File);
29};
Arduino
1//Itmes.cpp
2#include <Arduino.h>
3#include <cstddef>
4#include <SD.h>
5#include "Items.h"
6
7Items::Items(size_t sz) : bufSize(sz) {
8 start = false;
9 round = 0;
10 buf[0] = new unsigned short[bufSize];
11 buf[1] = new unsigned short[bufSize];
12};
13
14//割り込みハンドラ本体
15void Items::handle() {
16 unsigned long tt = micros();//時刻取得
17 size_t rr = round % 2; //使用バッファブロック選択
18 if (!start) { //周の初回の処理
19 idx[rr] = 0;
20 t0[rr] = tt;
21 tPrev[rr] = tt;
22 start = true;
23 }
24 if (idx[rr] < bufSize) { //バッファに余裕あるなら
25 buf[rr][idx[rr]++] = tt - tPrev[rr]; //前回との差分値を保存
26 }
27 tPrev[rr] = tt;//今回値を保存(次回差分計算用)
28}
29
30//周送り
31void Items::turnRound() {
32 round += 1;
33 start = false;
34}
35
36//データ書き出し
37void Items::logWrite(File logFile) {
38 if (round > 0) {
39 size_t rr = (round - 1) % 2;
40 //前回の周の最初の立ち上がりエッジ時刻
41 unsigned long tmp = t0[rr];
42 logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
43 //前回の周のデータ数
44 tmp = idx[rr];
45 logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
46 //データ本体書き込み
47 for (unsigned int i = 0; i < idx[rr]; i++) {
48 unsigned short tmp;
49 tmp = buf[rr][i];
50 logFile.write((uint8_t*)&tmp, sizeof(unsigned short));
51 }
52 }
53}
Arduino
1//main.ino
2#include <SPI.h>
3#include <SD.h>
4
5#include "Items.h"
6
7typedef const int Pin;
8Pin PhA = 6;
9Pin PhB = 4;//m0のD2は割り込みを受けないみたいなので変更
10Pin PhZ = 5;
11
12//Z相読み出しのレイテンシを見込んで少し余裕を持たせる
13const int RATIOA = 2880 + 32;
14const int RATIOB = 3600 + 32;
15
16Items pha(RATIOA), phb(RATIOB);
17
18volatile bool isSDtriggered = false;//カード書き込みトリガ
19
20volatile unsigned int roundZ = 0;//回転回数
21volatile unsigned long bufZ[2];//Z相時刻
22
23File logFile;
24
25//A相割り込みハンドラ
26void handleA() {
27 pha.handle();
28}
29
30//B相割り込みハンドラ
31void handleB() {
32 phb.handle();
33}
34
35//Z相割り込みハンドラ
36void handleZ() {
37 if (isSDtriggered) {
38 return; //前の処理が終わっていない
39 }
40 bufZ[++roundZ % 2] = micros();//時刻記録
41 pha.turnRound();//A相次の周へ
42 phb.turnRound();//B相次の周へ
43 isSDtriggered = true;//一周回ったので記録開始(SD操作はmainコンテキストで)
44}
45
46void setup() {
47 Serial.begin(115200);
48 pinMode(PhA, INPUT);
49 pinMode(PhB, INPUT);
50 pinMode(PhZ, INPUT);
51 //各相割り込み設定
52 attachInterrupt(digitalPinToInterrupt(PhA), handleA, RISING);
53 attachInterrupt(digitalPinToInterrupt(PhB), handleB, RISING);
54 attachInterrupt(digitalPinToInterrupt(PhZ), handleZ, RISING);
55
56 if (!SD.begin(10)) {
57 Serial.println("SD initialization failed!");
58 while (1);//カード初期化失敗したら停止
59 }
60 Serial.println("SD initialization done.");
61}
62
63void loop() {
64 if (isSDtriggered) { //SD書き込みトリガ掛かっていたら書き込み
65 if ( roundZ > 1&& roundZ<11) { //1周目完=2周目~9周目完=10周目のときSD書き込み
66 char fname[128];//ファイル名生成
67
68 Serial.println("SD start");
69 sprintf(fname, "/enc/round%d.txt", roundZ - 1); //round周数.txt
70 Serial.println("File: " + String(fname));
71 logFile = SD.open(fname, FILE_WRITE & ~O_APPEND); //ファイル先頭から書き込み
72 if (!logFile) {
73 Serial.println("File open error.");
74 while (1); //ファイルオープン失敗したら停止
75 }
76 unsigned long tmp = roundZ - 1; //現在(収集中)の前の周(収集完了)のデータ
77 logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
78 tmp = bufZ[(roundZ - 1) % 2];
79 logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
80 pha.logWrite(logFile); //A相データ書き込み
81 phb.logWrite(logFile); //B相データ書き込み
82 logFile.close();
83 Serial.println("SD done");
84 }
85 isSDtriggered = false;
86 }
87}
88/*ファイルフォーマットは
89 * 4byte 周ナンバー
90 * 4byte Z相立ち上がり時刻(micros値)
91 * 4byte A相立ち上がり時刻(micros値)
92 * 4byte A相データ数
93 * 2byte x データ数 A相間隔データ
94 * 4byte B相立ち上がり時刻(micros値)
95 * 4byte B相データ数
96 * 2byte x データ数 B相間隔データ
97 */