回答編集履歴

5

修正

2020/09/12 22:56

投稿

thkana
thkana

スコア7703

test CHANGED
@@ -150,13 +150,13 @@
150
150
 
151
151
  完成寸前まで作っちゃったんで載せときます。nac_tnkさんのタイマー版をベースにしてます。1度単位での制御はソフトループだと苦しいので。
152
152
 
153
- 制約として、①の前半と後半の立ち上がり間隔が、①の終わりと②の始ま(②終わりと③の始まり、③の終わりと①の始まり)より短い必要があります。「一番短い立ち上がり間隔」を1の検出の要件としましたので。図が正しいなら特に問題ないでしょう。
153
+ 制約として、①の前半と後半の立ち上がり間隔が、①の後半の立ち上がりと②の立ち上がりの間隔より短い必要があります(パルス全体の幅と切れ込みの入れ方によってはその関係は崩れます)。「一番短い立ち上がり間隔」をの検出の要件としましたので。図が現物通りなら特に問題ないでしょう。
154
154
 
155
155
  あまり明確にかかれていませんが、LEDの点灯開始は各パルスの立ち上がりと勝手に解釈しました。
156
156
 
157
157
 
158
158
 
159
- LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはできているはずなので。
159
+ LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはこれきているはずなので。
160
160
 
161
161
 
162
162
 

4

画像追加

2020/09/12 22:56

投稿

thkana
thkana

スコア7703

test CHANGED
@@ -355,3 +355,5 @@
355
355
  }
356
356
 
357
357
  ```
358
+
359
+ ![実行結果](a7af8ba67ab56a12b49d386f65e81894.png)

3

これで最後、かな?

2020/09/12 11:14

投稿

thkana
thkana

スコア7703

test CHANGED
@@ -119,3 +119,239 @@
119
119
 
120
120
 
121
121
  羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。
122
+
123
+
124
+
125
+ ---
126
+
127
+ 質問情報の追記をうけての追記
128
+
129
+
130
+
131
+ > 例えば1の羽根が90度の位置に来た時にLEDを点灯させるという感じ
132
+
133
+ 消灯のタイミングはパルス幅の真ん中に来た時です
134
+
135
+
136
+
137
+ とか、「どこにいっちゃったんだぁ?」と思いますけれど。
138
+
139
+
140
+
141
+ > 回転スピードは一定していません。
142
+
143
+
144
+
145
+ とかのびっくりネタはもう隠していませんか?
146
+
147
+ 10倍のダイナミックレンジってのは結構インパクトありますよ。最初に「どのくらいの時間をみているのか」と聞きましたよね? なぜかというと、それが設計の基本的なところで押さえるべきパラメータだからです。そこを、最初に言った9msから平気で120msまでしかも可変で引っ張る、なんていうのは、「バカにしている」と怒られても文句は言えないレベルです(怒らないけど思ってます)。
148
+
149
+
150
+
151
+ 完成寸前まで作っちゃったんで載せときます。nac_tnkさんのタイマー版をベースにしてます。1度単位での制御はソフトループだと苦しいので。
152
+
153
+ 制約として、①の前半と後半の立ち上がり間隔が、①の終わりと②の始まり(②の終わりと③の始まり、③の終わりと①の始まり)より短い必要があります。「一番短い立ち上がり間隔」を1の検出の要件としましたので。図が正しいなら特に問題ないでしょう。
154
+
155
+ あまり明確にかかれていませんが、LEDの点灯開始は各パルスの立ち上がりと勝手に解釈しました。
156
+
157
+
158
+
159
+ LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはできているはずなので。
160
+
161
+
162
+
163
+ ```Arduino
164
+
165
+ #include <limits.h>
166
+
167
+ const int LEDPORT[] = {11, 12, 13};
168
+
169
+ const int TM_STOP = 0;
170
+
171
+ const int TM_RUN = 0b11;
172
+
173
+
174
+
175
+ volatile bool roundComp;
176
+
177
+
178
+
179
+ void pulse_read() {
180
+
181
+ //立ち上がり位置の記録。
182
+
183
+ unsigned int edge = TCNT1;//即座にタイマー値を取得することでばらつきを防ぐ
184
+
185
+
186
+
187
+ static unsigned int lastEdge;//前回の立ち上がり位置
188
+
189
+ static unsigned int width[4];//各パルスの幅を保存
190
+
191
+ static int cnt = 0;//パルスのインデックス
192
+
193
+
194
+
195
+ if (cnt == 3 ) {
196
+
197
+ //タイマー操作を優先するため同条件のif文が分かれている
198
+
199
+ TCCR1B = TM_STOP; //数μs狂うかも知れないけど、安全なタイマーカウンタ操作のため止める
200
+
201
+ if (OCR1A > TCNT1) { //未実行のコンペアがあったら移行する
202
+
203
+ OCR1A -= TCNT1;
204
+
205
+ }
206
+
207
+ TCNT1 = 0; //タイマーカウンタをリセット
208
+
209
+ TCCR1B = TM_RUN;
210
+
211
+ }
212
+
213
+
214
+
215
+ //バルス立ち上がりの間隔を求める
216
+
217
+ width[(cnt + 3) % 4] = edge - lastEdge;
218
+
219
+ if ( cnt == 3 ) {
220
+
221
+ roundComp = true;//一周データ取り終わったことを記録
222
+
223
+ edge = 0;
224
+
225
+ }
226
+
227
+ lastEdge = edge;
228
+
229
+
230
+
231
+ // いちばん幅が狭いところつまり1のパルスの検出。
232
+
233
+ unsigned int wMin = width[0];
234
+
235
+ int firstEdge = 0;
236
+
237
+ for (int i = 1; i < 4; i++) {
238
+
239
+ if (width[i] < wMin) {
240
+
241
+ wMin = width[i];
242
+
243
+ firstEdge = i;
244
+
245
+ }
246
+
247
+ }
248
+
249
+
250
+
251
+ //一周分のデータが揃っているならLED処理
252
+
253
+ if (roundComp ) {
254
+
255
+ // 直前での120度分の回転時間
256
+
257
+ int w_3 = width[(cnt + 3) % 4];
258
+
259
+ //1のパルスが割れている分の補正
260
+
261
+ if (cnt == (firstEdge + 2) % 4) {
262
+
263
+ w_3 += width[firstEdge];
264
+
265
+ }
266
+
267
+
268
+
269
+ if (cnt == firstEdge) {//1のセンサが発報
270
+
271
+ OCR1A = edge + w_3 * 40l / 120; //例として40度回る時間後タイマーで消灯
272
+
273
+ //計算途中でのオーバーフローを防ぐため一部をlong型にしている
274
+
275
+ digitalWrite(LEDPORT[0], HIGH);//センサが発報したら点灯
276
+
277
+ }
278
+
279
+ if (cnt == (firstEdge + 2) % 4 ) { //2のセンサが発報
280
+
281
+ OCR1A = edge + w_3 * 20l / 120; //20度
282
+
283
+ digitalWrite(LEDPORT[1], HIGH);//センサが発報したら点灯
284
+
285
+ }
286
+
287
+ if (cnt == (firstEdge + 3) % 4 ) { //3のセンサが発報
288
+
289
+ OCR1A = edge + w_3 * 60l / 120; //60度
290
+
291
+ digitalWrite(LEDPORT[2], HIGH);//センサが発報したら点灯
292
+
293
+ }
294
+
295
+ }
296
+
297
+
298
+
299
+ cnt=(cnt+1)%4;//立ち上がり取得カウンタを進めておく
300
+
301
+ }
302
+
303
+
304
+
305
+ ISR (TIMER1_OVF_vect) {//低速、停止時に0にする為
306
+
307
+ roundComp = 0;
308
+
309
+ }
310
+
311
+
312
+
313
+ ISR (TIMER1_COMPA_vect) {//OCRA一致割り込みでLED OFF
314
+
315
+ //面倒なので全部OFF
316
+
317
+ for ( auto l : LEDPORT) {
318
+
319
+ digitalWrite(l, 0);
320
+
321
+ }
322
+
323
+ }
324
+
325
+
326
+
327
+ void setup() {
328
+
329
+ Serial.begin(115200) ;
330
+
331
+ for ( auto l : LEDPORT) {
332
+
333
+ pinMode(l, OUTPUT);
334
+
335
+ }
336
+
337
+ TCCR1A = 0;
338
+
339
+ TCCR1B = TM_RUN; //1/64=4us/countでタイマー1を回す
340
+
341
+ OCR1A = 0;
342
+
343
+ OCR1B = 0;
344
+
345
+ TIMSK1 = 0b11; //必要な割り込みON
346
+
347
+ attachInterrupt(0, pulse_read, RISING);//センサーD2ピン
348
+
349
+ }
350
+
351
+
352
+
353
+ void loop() {
354
+
355
+ }
356
+
357
+ ```

2

追記

2020/09/12 09:57

投稿

thkana
thkana

スコア7703

test CHANGED
@@ -17,6 +17,10 @@
17
17
 
18
18
 
19
19
  ---
20
+
21
+
22
+
23
+ (追記)質問と条件が変わってしまいますが、センサーは羽根1枚だけを、切込み無しで検出するという前提で簡易的(=タイマ使わなくてフリーランのループ)なプログラムを試してみました。それでこれだけ出来るのですが、これじゃダメなんですか?
20
24
 
21
25
 
22
26
 
@@ -106,6 +110,10 @@
106
110
 
107
111
  これで、コメントしてあるSerial.printlnを活かすと183とか4とかの値になったので、タイマーを使わなくても2度分くらいのばらつきで動くことはできるようです。
108
112
 
113
+ (追記) 波形をみたところ、目視で100μs以下ぐらいのジッタはあるようです。要求される精度はどのくらいですか?
114
+
115
+
116
+
109
117
  タイマーを使うにしても原理としては同じようにできるでしょう。精度や分解能は随分よくなる期待が持てるかと思います。
110
118
 
111
119
 

1

軽く実験

2020/09/05 23:24

投稿

thkana
thkana

スコア7703

test CHANGED
@@ -13,3 +13,101 @@
13
13
 
14
14
 
15
15
  あるいはタイマ使わなくてフリーランのループでも測れるかなぁ? その切れ込みパルスは一周に対してどういう比率になるのでしょう?
16
+
17
+
18
+
19
+ ---
20
+
21
+
22
+
23
+ おおよそ9ms毎に1msのパルスを作って、以下のプログラムに突っ込んでみました。Arduino UNOなので16MHzのAVR 328Pです。
24
+
25
+ ```Arduino
26
+
27
+ void setup() {
28
+
29
+ pinMode(2, INPUT);
30
+
31
+ pinMode(13, OUTPUT);
32
+
33
+ Serial.begin(115200);
34
+
35
+ }
36
+
37
+ const unsigned int LED_ON_PHASE = 0;
38
+
39
+ const unsigned int LED_OFF_PHASE = 180;
40
+
41
+ unsigned int count;
42
+
43
+ unsigned int totalCount;
44
+
45
+ unsigned int dummy1, dummy2;
46
+
47
+
48
+
49
+ int lastStat;
50
+
51
+
52
+
53
+ void loop() {
54
+
55
+ int stat = digitalRead(2);
56
+
57
+ if (!stat) {
58
+
59
+ if (lastStat) {
60
+
61
+ totalCount = count;
62
+
63
+ count = 0;
64
+
65
+ // Serial.println(totalCount);
66
+
67
+ } else {
68
+
69
+ dummy1 = count;//プログラムの経路によって実行時間が変わるのを緩和するためのダミー
70
+
71
+ dummy2 = 0;
72
+
73
+ }
74
+
75
+ } else {
76
+
77
+ if (lastStat) {
78
+
79
+ dummy1 = count;
80
+
81
+ dummy2 = 0;
82
+
83
+ } else {
84
+
85
+ dummy1 = count;
86
+
87
+ dummy2 = 0;
88
+
89
+ }
90
+
91
+ }
92
+
93
+ int led = ((unsigned long)totalCount * LED_ON_PHASE / 360 < count) &&
94
+
95
+ (count < (unsigned long)totalCount * LED_OFF_PHASE / 360);
96
+
97
+ digitalWrite(13, led);
98
+
99
+ count++;
100
+
101
+ lastStat = stat;
102
+
103
+ }
104
+
105
+ ```
106
+
107
+ これで、コメントしてあるSerial.printlnを活かすと183とか4とかの値になったので、タイマーを使わなくても2度分くらいのばらつきで動くことはできるようです。
108
+
109
+ タイマーを使うにしても原理としては同じようにできるでしょう。精度や分解能は随分よくなる期待が持てるかと思います。
110
+
111
+
112
+
113
+ 羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。