質問するログイン新規登録

回答編集履歴

2

サンプル追加

2021/01/31 13:23

投稿

thkana
thkana

スコア7739

answer CHANGED
@@ -4,4 +4,198 @@
4
4
  パルスのエッジの記録は「割り込み」による。割り込みハンドラ内でmicros()の値を配列に保存
5
5
  Z相が来たら記録面を切り替えて、記録済の面をSDカードに書き出す、とかいうところでしょうか。
6
6
 
7
- (話だけでわかるか? とも思うけど、サンプルを書けとかいわれるとちょっと面倒だなぁ...)
7
+ (話だけでわかるか? とも思うけど、サンプルを書けとかいわれるとちょっと面倒だなぁ...)
8
+
9
+ ---
10
+
11
+ 面倒といいながら、SD書き込み中にちゃんと割り込みがかかるか等、色々と気になったので作ってみました。ただし、ターゲットはArduino m0/PROで、そのままDueに持っていけるかどうかは知りません。どちらもATMELのARMだし、m0の方が低クロック少メモリなので期待は出来ますが。
12
+
13
+ 動作チェックとして他のマイコンで2s/555μs/694μs(それぞれ設定値、パルス同士は同期をとっていません)のパルスを作ってそれを突っ込んで大体2.0002s/22b(555)~22d(557)/ 2b6(694)~2b7(695)ぐらいの値が出ているのでそこそこかと。
14
+
15
+ SDカードの書き出しは数字で打ちたかったのですが、2秒に確実に収まるとは言えない感じだったのでバイナリにしています。書き込み時間は200~500msぐらいでばらつきます。Dueの方がCPUが早いのでもしかしたら数字でも出せるかもしれませんが、SDのI/Fが律速しているのかも。
16
+
17
+ ```Arduino
18
+ //Items.h
19
+ #include <cstddef>
20
+ #include <SD.h>
21
+
22
+ class Items {
23
+ //バッファ管理変数
24
+ volatile unsigned short *buf[2];
25
+ //データインデックス管理
26
+ volatile size_t idx[2];
27
+ //データ最大数
28
+ size_t bufSize;
29
+ //周数
30
+ volatile unsigned int round;
31
+ //周の最初の立ち上がりエッジ保存
32
+ unsigned long t0[2];
33
+ //前回の立ち上がり時刻保存
34
+ volatile unsigned long tPrev[2];
35
+ //周の初回判定
36
+ bool start;
37
+
38
+ public:
39
+ Items(size_t );
40
+ //割り込みハンドラ本体
41
+ void handle();
42
+ //周送り処理
43
+ void turnRound();
44
+ //データ書き出し
45
+ void logWrite(File);
46
+ };
47
+ ```
48
+ ```Arduino
49
+ //Itmes.cpp
50
+ #include <Arduino.h>
51
+ #include <cstddef>
52
+ #include <SD.h>
53
+ #include "Items.h"
54
+
55
+ Items::Items(size_t sz) : bufSize(sz) {
56
+ start = false;
57
+ round = 0;
58
+ buf[0] = new unsigned short[bufSize];
59
+ buf[1] = new unsigned short[bufSize];
60
+ };
61
+
62
+ //割り込みハンドラ本体
63
+ void Items::handle() {
64
+ unsigned long tt = micros();//時刻取得
65
+ size_t rr = round % 2; //使用バッファブロック選択
66
+ if (!start) { //周の初回の処理
67
+ idx[rr] = 0;
68
+ t0[rr] = tt;
69
+ tPrev[rr] = tt;
70
+ start = true;
71
+ }
72
+ if (idx[rr] < bufSize) { //バッファに余裕あるなら
73
+ buf[rr][idx[rr]++] = tt - tPrev[rr]; //前回との差分値を保存
74
+ }
75
+ tPrev[rr] = tt;//今回値を保存(次回差分計算用)
76
+ }
77
+
78
+ //周送り
79
+ void Items::turnRound() {
80
+ round += 1;
81
+ start = false;
82
+ }
83
+
84
+ //データ書き出し
85
+ void Items::logWrite(File logFile) {
86
+ if (round > 0) {
87
+ size_t rr = (round - 1) % 2;
88
+ //前回の周の最初の立ち上がりエッジ時刻
89
+ unsigned long tmp = t0[rr];
90
+ logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
91
+ //前回の周のデータ数
92
+ tmp = idx[rr];
93
+ logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
94
+ //データ本体書き込み
95
+ for (unsigned int i = 0; i < idx[rr]; i++) {
96
+ unsigned short tmp;
97
+ tmp = buf[rr][i];
98
+ logFile.write((uint8_t*)&tmp, sizeof(unsigned short));
99
+ }
100
+ }
101
+ }
102
+ ```
103
+ ```Arduino
104
+ //main.ino
105
+ #include <SPI.h>
106
+ #include <SD.h>
107
+
108
+ #include "Items.h"
109
+
110
+ typedef const int Pin;
111
+ Pin PhA = 6;
112
+ Pin PhB = 4;//m0のD2は割り込みを受けないみたいなので変更
113
+ Pin PhZ = 5;
114
+
115
+ //Z相読み出しのレイテンシを見込んで少し余裕を持たせる
116
+ const int RATIOA = 2880 + 32;
117
+ const int RATIOB = 3600 + 32;
118
+
119
+ Items pha(RATIOA), phb(RATIOB);
120
+
121
+ volatile bool isSDtriggered = false;//カード書き込みトリガ
122
+
123
+ volatile unsigned int roundZ = 0;//回転回数
124
+ volatile unsigned long bufZ[2];//Z相時刻
125
+
126
+ File logFile;
127
+
128
+ //A相割り込みハンドラ
129
+ void handleA() {
130
+ pha.handle();
131
+ }
132
+
133
+ //B相割り込みハンドラ
134
+ void handleB() {
135
+ phb.handle();
136
+ }
137
+
138
+ //Z相割り込みハンドラ
139
+ void handleZ() {
140
+ if (isSDtriggered) {
141
+ return; //前の処理が終わっていない
142
+ }
143
+ bufZ[++roundZ % 2] = micros();//時刻記録
144
+ pha.turnRound();//A相次の周へ
145
+ phb.turnRound();//B相次の周へ
146
+ isSDtriggered = true;//一周回ったので記録開始(SD操作はmainコンテキストで)
147
+ }
148
+
149
+ void setup() {
150
+ Serial.begin(115200);
151
+ pinMode(PhA, INPUT);
152
+ pinMode(PhB, INPUT);
153
+ pinMode(PhZ, INPUT);
154
+ //各相割り込み設定
155
+ attachInterrupt(digitalPinToInterrupt(PhA), handleA, RISING);
156
+ attachInterrupt(digitalPinToInterrupt(PhB), handleB, RISING);
157
+ attachInterrupt(digitalPinToInterrupt(PhZ), handleZ, RISING);
158
+
159
+ if (!SD.begin(10)) {
160
+ Serial.println("SD initialization failed!");
161
+ while (1);//カード初期化失敗したら停止
162
+ }
163
+ Serial.println("SD initialization done.");
164
+ }
165
+
166
+ void loop() {
167
+ if (isSDtriggered) { //SD書き込みトリガ掛かっていたら書き込み
168
+ if ( roundZ > 1&& roundZ<11) { //1周目完=2周目~9周目完=10周目のときSD書き込み
169
+ char fname[128];//ファイル名生成
170
+
171
+ Serial.println("SD start");
172
+ sprintf(fname, "/enc/round%d.txt", roundZ - 1); //round周数.txt
173
+ Serial.println("File: " + String(fname));
174
+ logFile = SD.open(fname, FILE_WRITE & ~O_APPEND); //ファイル先頭から書き込み
175
+ if (!logFile) {
176
+ Serial.println("File open error.");
177
+ while (1); //ファイルオープン失敗したら停止
178
+ }
179
+ unsigned long tmp = roundZ - 1; //現在(収集中)の前の周(収集完了)のデータ
180
+ logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
181
+ tmp = bufZ[(roundZ - 1) % 2];
182
+ logFile.write((uint8_t*)&tmp, sizeof(unsigned long));
183
+ pha.logWrite(logFile); //A相データ書き込み
184
+ phb.logWrite(logFile); //B相データ書き込み
185
+ logFile.close();
186
+ Serial.println("SD done");
187
+ }
188
+ isSDtriggered = false;
189
+ }
190
+ }
191
+ /*ファイルフォーマットは
192
+ * 4byte 周ナンバー
193
+ * 4byte Z相立ち上がり時刻(micros値)
194
+ * 4byte A相立ち上がり時刻(micros値)
195
+ * 4byte A相データ数
196
+ * 2byte x データ数 A相間隔データ
197
+ * 4byte B相立ち上がり時刻(micros値)
198
+ * 4byte B相データ数
199
+ * 2byte x データ数 B相間隔データ
200
+ */
201
+ ```

1

表記変更

2021/01/31 13:23

投稿

thkana
thkana

スコア7739

answer CHANGED
@@ -4,4 +4,4 @@
4
4
  パルスのエッジの記録は「割り込み」による。割り込みハンドラ内でmicros()の値を配列に保存
5
5
  Z相が来たら記録面を切り替えて、記録済の面をSDカードに書き出す、とかいうところでしょうか。
6
6
 
7
- (話だけでわかるかとも思うけど、サンプルを書けとかいわれるとちょっと面倒だなぁ...)
7
+ (話だけでわかるか? とも思うけど、サンプルを書けとかいわれるとちょっと面倒だなぁ...)