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

回答編集履歴

5

修正

2020/09/12 22:56

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -74,10 +74,10 @@
74
74
  10倍のダイナミックレンジってのは結構インパクトありますよ。最初に「どのくらいの時間をみているのか」と聞きましたよね? なぜかというと、それが設計の基本的なところで押さえるべきパラメータだからです。そこを、最初に言った9msから平気で120msまでしかも可変で引っ張る、なんていうのは、「バカにしている」と怒られても文句は言えないレベルです(怒らないけど思ってます)。
75
75
 
76
76
  完成寸前まで作っちゃったんで載せときます。nac_tnkさんのタイマー版をベースにしてます。1度単位での制御はソフトループだと苦しいので。
77
- 制約として、①の前半と後半の立ち上がり間隔が、①の終わりと②の始ま(②終わりと③の始まり、③の終わりと①の始まり)より短い必要があります。「一番短い立ち上がり間隔」を1の検出の要件としましたので。図が正しいなら特に問題ないでしょう。
77
+ 制約として、①の前半と後半の立ち上がり間隔が、①の後半の立ち上がりと②の立ち上がりの間隔より短い必要があります(パルス全体の幅と切れ込みの入れ方によってはその関係は崩れます)。「一番短い立ち上がり間隔」をの検出の要件としましたので。図が現物通りなら特に問題ないでしょう。
78
78
  あまり明確にかかれていませんが、LEDの点灯開始は各パルスの立ち上がりと勝手に解釈しました。
79
79
 
80
- LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはできているはずなので。
80
+ LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはこれきているはずなので。
81
81
 
82
82
  ```Arduino
83
83
  #include <limits.h>

4

画像追加

2020/09/12 22:56

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -176,4 +176,5 @@
176
176
 
177
177
  void loop() {
178
178
  }
179
- ```
179
+ ```
180
+ ![実行結果](a7af8ba67ab56a12b49d386f65e81894.png)

3

これで最後、かな?

2020/09/12 11:14

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -58,4 +58,122 @@
58
58
 
59
59
  タイマーを使うにしても原理としては同じようにできるでしょう。精度や分解能は随分よくなる期待が持てるかと思います。
60
60
 
61
- 羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。
61
+ 羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。
62
+
63
+ ---
64
+ 質問情報の追記をうけての追記
65
+
66
+ > 例えば1の羽根が90度の位置に来た時にLEDを点灯させるという感じ
67
+ 消灯のタイミングはパルス幅の真ん中に来た時です
68
+
69
+ とか、「どこにいっちゃったんだぁ?」と思いますけれど。
70
+
71
+ > 回転スピードは一定していません。
72
+
73
+ とかのびっくりネタはもう隠していませんか?
74
+ 10倍のダイナミックレンジってのは結構インパクトありますよ。最初に「どのくらいの時間をみているのか」と聞きましたよね? なぜかというと、それが設計の基本的なところで押さえるべきパラメータだからです。そこを、最初に言った9msから平気で120msまでしかも可変で引っ張る、なんていうのは、「バカにしている」と怒られても文句は言えないレベルです(怒らないけど思ってます)。
75
+
76
+ 完成寸前まで作っちゃったんで載せときます。nac_tnkさんのタイマー版をベースにしてます。1度単位での制御はソフトループだと苦しいので。
77
+ 制約として、①の前半と後半の立ち上がり間隔が、①の終わりと②の始まり(②の終わりと③の始まり、③の終わりと①の始まり)より短い必要があります。「一番短い立ち上がり間隔」を1の検出の要件としましたので。図が正しいなら特に問題ないでしょう。
78
+ あまり明確にかかれていませんが、LEDの点灯開始は各パルスの立ち上がりと勝手に解釈しました。
79
+
80
+ LED制御ぐらいは自分でやって下さい。そもそもの質問の①を知ることはできているはずなので。
81
+
82
+ ```Arduino
83
+ #include <limits.h>
84
+ const int LEDPORT[] = {11, 12, 13};
85
+ const int TM_STOP = 0;
86
+ const int TM_RUN = 0b11;
87
+
88
+ volatile bool roundComp;
89
+
90
+ void pulse_read() {
91
+ //立ち上がり位置の記録。
92
+ unsigned int edge = TCNT1;//即座にタイマー値を取得することでばらつきを防ぐ
93
+
94
+ static unsigned int lastEdge;//前回の立ち上がり位置
95
+ static unsigned int width[4];//各パルスの幅を保存
96
+ static int cnt = 0;//パルスのインデックス
97
+
98
+ if (cnt == 3 ) {
99
+ //タイマー操作を優先するため同条件のif文が分かれている
100
+ TCCR1B = TM_STOP; //数μs狂うかも知れないけど、安全なタイマーカウンタ操作のため止める
101
+ if (OCR1A > TCNT1) { //未実行のコンペアがあったら移行する
102
+ OCR1A -= TCNT1;
103
+ }
104
+ TCNT1 = 0; //タイマーカウンタをリセット
105
+ TCCR1B = TM_RUN;
106
+ }
107
+
108
+ //バルス立ち上がりの間隔を求める
109
+ width[(cnt + 3) % 4] = edge - lastEdge;
110
+ if ( cnt == 3 ) {
111
+ roundComp = true;//一周データ取り終わったことを記録
112
+ edge = 0;
113
+ }
114
+ lastEdge = edge;
115
+
116
+ // いちばん幅が狭いところつまり1のパルスの検出。
117
+ unsigned int wMin = width[0];
118
+ int firstEdge = 0;
119
+ for (int i = 1; i < 4; i++) {
120
+ if (width[i] < wMin) {
121
+ wMin = width[i];
122
+ firstEdge = i;
123
+ }
124
+ }
125
+
126
+ //一周分のデータが揃っているならLED処理
127
+ if (roundComp ) {
128
+ // 直前での120度分の回転時間
129
+ int w_3 = width[(cnt + 3) % 4];
130
+ //1のパルスが割れている分の補正
131
+ if (cnt == (firstEdge + 2) % 4) {
132
+ w_3 += width[firstEdge];
133
+ }
134
+
135
+ if (cnt == firstEdge) {//1のセンサが発報
136
+ OCR1A = edge + w_3 * 40l / 120; //例として40度回る時間後タイマーで消灯
137
+ //計算途中でのオーバーフローを防ぐため一部をlong型にしている
138
+ digitalWrite(LEDPORT[0], HIGH);//センサが発報したら点灯
139
+ }
140
+ if (cnt == (firstEdge + 2) % 4 ) { //2のセンサが発報
141
+ OCR1A = edge + w_3 * 20l / 120; //20度
142
+ digitalWrite(LEDPORT[1], HIGH);//センサが発報したら点灯
143
+ }
144
+ if (cnt == (firstEdge + 3) % 4 ) { //3のセンサが発報
145
+ OCR1A = edge + w_3 * 60l / 120; //60度
146
+ digitalWrite(LEDPORT[2], HIGH);//センサが発報したら点灯
147
+ }
148
+ }
149
+
150
+ cnt=(cnt+1)%4;//立ち上がり取得カウンタを進めておく
151
+ }
152
+
153
+ ISR (TIMER1_OVF_vect) {//低速、停止時に0にする為
154
+ roundComp = 0;
155
+ }
156
+
157
+ ISR (TIMER1_COMPA_vect) {//OCRA一致割り込みでLED OFF
158
+ //面倒なので全部OFF
159
+ for ( auto l : LEDPORT) {
160
+ digitalWrite(l, 0);
161
+ }
162
+ }
163
+
164
+ void setup() {
165
+ Serial.begin(115200) ;
166
+ for ( auto l : LEDPORT) {
167
+ pinMode(l, OUTPUT);
168
+ }
169
+ TCCR1A = 0;
170
+ TCCR1B = TM_RUN; //1/64=4us/countでタイマー1を回す
171
+ OCR1A = 0;
172
+ OCR1B = 0;
173
+ TIMSK1 = 0b11; //必要な割り込みON
174
+ attachInterrupt(0, pulse_read, RISING);//センサーD2ピン
175
+ }
176
+
177
+ void loop() {
178
+ }
179
+ ```

2

追記

2020/09/12 09:57

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -9,6 +9,8 @@
9
9
 
10
10
  ---
11
11
 
12
+ (追記)質問と条件が変わってしまいますが、センサーは羽根1枚だけを、切込み無しで検出するという前提で簡易的(=タイマ使わなくてフリーランのループ)なプログラムを試してみました。それでこれだけ出来るのですが、これじゃダメなんですか?
13
+
12
14
  おおよそ9ms毎に1msのパルスを作って、以下のプログラムに突っ込んでみました。Arduino UNOなので16MHzのAVR 328Pです。
13
15
  ```Arduino
14
16
  void setup() {
@@ -52,6 +54,8 @@
52
54
  }
53
55
  ```
54
56
  これで、コメントしてあるSerial.printlnを活かすと183とか4とかの値になったので、タイマーを使わなくても2度分くらいのばらつきで動くことはできるようです。
57
+ (追記) 波形をみたところ、目視で100μs以下ぐらいのジッタはあるようです。要求される精度はどのくらいですか?
58
+
55
59
  タイマーを使うにしても原理としては同じようにできるでしょう。精度や分解能は随分よくなる期待が持てるかと思います。
56
60
 
57
61
  羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。

1

軽く実験

2020/09/05 23:24

投稿

thkana
thkana

スコア7738

answer CHANGED
@@ -5,4 +5,53 @@
5
5
  最初は回転数とか知りませんから、立ち上がり-立ち下がりの間隔を4回測定します。
6
6
  そのパルス幅を見れば1の羽根を判別できるでしょう。残りの羽根のパルス幅もわかります。次の周で、短いパルス幅を検出したらLEDを点灯して、先の測定で残りの羽根のパルス幅の半分でタイマとのコンペアかなにかで割り込ませてLEDを消灯すれば。もちろん、この間も「次の周」に備えて各パルス幅は測り続けます。
7
7
 
8
- あるいはタイマ使わなくてフリーランのループでも測れるかなぁ? その切れ込みパルスは一周に対してどういう比率になるのでしょう?
8
+ あるいはタイマ使わなくてフリーランのループでも測れるかなぁ? その切れ込みパルスは一周に対してどういう比率になるのでしょう?
9
+
10
+ ---
11
+
12
+ おおよそ9ms毎に1msのパルスを作って、以下のプログラムに突っ込んでみました。Arduino UNOなので16MHzのAVR 328Pです。
13
+ ```Arduino
14
+ void setup() {
15
+ pinMode(2, INPUT);
16
+ pinMode(13, OUTPUT);
17
+ Serial.begin(115200);
18
+ }
19
+ const unsigned int LED_ON_PHASE = 0;
20
+ const unsigned int LED_OFF_PHASE = 180;
21
+ unsigned int count;
22
+ unsigned int totalCount;
23
+ unsigned int dummy1, dummy2;
24
+
25
+ int lastStat;
26
+
27
+ void loop() {
28
+ int stat = digitalRead(2);
29
+ if (!stat) {
30
+ if (lastStat) {
31
+ totalCount = count;
32
+ count = 0;
33
+ // Serial.println(totalCount);
34
+ } else {
35
+ dummy1 = count;//プログラムの経路によって実行時間が変わるのを緩和するためのダミー
36
+ dummy2 = 0;
37
+ }
38
+ } else {
39
+ if (lastStat) {
40
+ dummy1 = count;
41
+ dummy2 = 0;
42
+ } else {
43
+ dummy1 = count;
44
+ dummy2 = 0;
45
+ }
46
+ }
47
+ int led = ((unsigned long)totalCount * LED_ON_PHASE / 360 < count) &&
48
+ (count < (unsigned long)totalCount * LED_OFF_PHASE / 360);
49
+ digitalWrite(13, led);
50
+ count++;
51
+ lastStat = stat;
52
+ }
53
+ ```
54
+ これで、コメントしてあるSerial.printlnを活かすと183とか4とかの値になったので、タイマーを使わなくても2度分くらいのばらつきで動くことはできるようです。
55
+ タイマーを使うにしても原理としては同じようにできるでしょう。精度や分解能は随分よくなる期待が持てるかと思います。
56
+
57
+ 羽根3枚を検出しないといけない状況があるのでしょうか? それこそ羽根の隙間を狙わなきゃいけない、とかだと真面目に羽根を検出したほうがいいかも知れませんが。