質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

2回答

2574閲覧

Arduino超初心者です。IF構文の各ループ内で切替時に極短時間だけ処理(1回だけでも可)させたい記述のアイデア

azt1999

総合スコア13

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

1グッド

1クリップ

投稿2019/02/01 11:25

編集2019/02/01 12:33

先月からArduinoを始めた超初心者ですが、外部のハードウエアを制御を勉強中です。
ArduinoのdigitalPin#5に入力された信号を周波数カウンタのスケッチで処理し、6段階に制御を割り振るスケッチを
書いてますが、切替にもたつきが出てしまい苦労してます。(周波数を変数として扱い、if構文で割り振り)

周波数カウンタのライブラリはこちらを使用させて貰ってます。
http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-frequency-counter-library/
http://interface.khm.de/wp-content/uploads/2009/01/FreqCounter_1_12.zip

外部のハードウエア制御の関係上、if構文の各ループ切替時のみ極力ループさせず1回だけさせたい処理(外部のICを標準SPIライブラリでシリアルで送り、2byteでイニシャライズ)ありますが、いいアイデアが無く、今のところ、外部から負のワンショットパルスに近い信号(といっても0.5sec.程度?)を貰い、別のdigitalpin#6に入れ、Hレベルになった際、処理を力技で止めさせてますが、外部のワンショットパルスの設定幅に
自由度が無く、切替時に相当もたつき感があり、レスポンスがよろしく無いです。(切替時のイニシャライズは1回だけでも可)

変数(frq)は若干ふらつきますが(±2%程度)、変数(frq)がダイナミックに切り替わる際のみ、ソフト上でflagを立てて
スマートに制御できないか?と考えてますが、いいアイデアが思いつきません。
(boolean isFirst = true; を用い記述したところ、起動最初のループだけ上手く行き、リセットボタンを押さない限り、各ループ切替後の処理は無視されてしまいました・・・)

現行の外部からワンショットパルスを貰い制御しているスケッチを記述しますので、お知恵をお借りしたくアドバイスお願いします。

#include <FreqCounter.h>
#include <SPI.h>
#define SS_PIN 10

SPISettings mySPISettings = SPISettings(500000, MSBFIRST, SPI_MODE0);
int val = 0;
long int frq;

void setup() {
pinMode(9, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, OUTPUT);
pinMode(6, INPUT);
SPI.begin(); //SPI有効
pinMode(SS_PIN, OUTPUT);
}

void loop(){
FreqCounter::f_comp = 8; // Set compensation to 12
FreqCounter::start(100); // Start counting with gatetime of 100ms
while (FreqCounter::f_ready == 0); // wait until counter ready
frq = FreqCounter::f_freq; // read result
val = digitalRead(6);

 if(frq < 10000){
if (val == 0) {
SPI.beginTransaction(mySPISettings);//ここから
digitalWrite(10, HIGH);//
SPI.transfer(B01001000);//
SPI.transfer(B11001111);//
digitalWrite(10, LOW);//ここまでが極力ループさせず1回だけさせたい処理です
}
//通常LOOP処理1
}

 else if(frq < 20000){
if (val == 0) {
SPI.beginTransaction(mySPISettings);  
digitalWrite(10, HIGH);
SPI.transfer(B01001010);
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}  
//通常LOOP処理2
}

 else if(frq < 30000){
if (val == 0) {
SPI.beginTransaction(mySPISettings);  
digitalWrite(10, HIGH);
SPI.transfer(B01011010);
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}  
//通常LOOP処理3
}

 else if(frq < 40000){
if (val == 0) {
SPI.beginTransaction(mySPISettings);
digitalWrite(10, HIGH);
SPI.transfer(B01011010);
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}
//通常LOOP処理4
}

 else if(frq < 50000){
if (val == 0) {
SPI.beginTransaction(mySPISettings);
digitalWrite(10, HIGH);
SPI.transfer(B01011110);
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}  
//通常LOOP処理5
}
else{
if (val == 0) {
SPI.beginTransaction(mySPISettings);
digitalWrite(10, HIGH);
SPI.transfer(B11011010);
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}
//通常LOOP処理6
}
delay(100);
}

bochan2👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2019/02/01 11:56

質問文ではちと意味不明です。 短時間で処理を終わらせるようにコードを組めばいいだけじゃね?という感想しか出てきません。 もちっと具体的になにをさせたいのか説明してくだされ
thkana

2019/02/01 12:31

1回だけでもいいのなら、ループさせないで1回だけ実行すればいいと思うのだけど違うの?
azt1999

2019/02/01 12:36

y_waiwai様、thkana様 アドバイス有難うございます。補足追加しました。特定の部分だけ極力ループさせないで1回だけ実行させるやり方をご教授下さい。
thkana

2019/02/01 14:47

「ループ」っていう言葉をあなたと私で違う意味に考えているのかな? ループって、環状、つまりグルグル回る、繰り返し。 > SPI.beginTransaction(mySPISettings);//ここから > digitalWrite(10, HIGH);// > SPI.transfer(B01001000);// > SPI.transfer(B11001111);// > digitalWrite(10, LOW);//ここまでが極力ループさせず1回だけさせたい処理です ここには「ループ」は無い。「一回だけ実行」する以外にはないのだけど。
y_waiwai

2019/02/01 15:18

あなたのいうごく短時間でさせたい処理というのはSPIでのデータ送信するということ? なんか無駄なコードも多そうだし、そもそものプログラム設計が間違ってそうな匂いがプンプンするので、 まずはそもそもなにをしたいのかというのをはっきりさせましょう 間違った前提からは間違ったコードしかでてきません
azt1999

2019/02/01 18:42

y_waiwai様、thkana様 アドバイス有難うございます。シリアルで2byte送り、RF系のICをイニシャライズさせてます。返しが無い単方向通信でごくまれに取りこぼす時がある様なので、2、3回送ってやった方がいいかも?と思ってます。常時送り続けるとノイズが出てしまうのです。 for (int i=0; i<1; i++){ }で記述しましたが、再度読み込まれNGでした。他の処理は入力周波数に応じ、7~9のdigitalpinをdigitalwriteでHIGH/LOWさせてダイオードSWを介し、アナログ回路を切り替えているだけの極めてシンプルなスケッチです。今でも切替時にもたつくだけで機能的には動くことは動くのですが。
guest

回答2

0

コード ```シリアルモニタでfrqダミー値を入れて確認 frq境界をまたぐ時のみ spi_out() を出力

#include <SPI.h>
SPISettings mySPISettings = SPISettings(500000, MSBFIRST, SPI_MODE0);
long int frq = 0;
unsigned char spi_dat[]={ B01001000 , B01001010 , B01011010 , B01011010 , B01011110 , B11011010 };

void spi_out( unsigned char sel ){
if ( sel > 5 ){ return; }
SPI.beginTransaction(mySPISettings);
digitalWrite(10, HIGH);
SPI.transfer( spi_dat[sel] );
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}
void setup() {
Serial.begin( 9600 );
Serial.println("Arduino Program Start !!");
}
void loop() {
static char add = 1;
static unsigned char sel = 9;
unsigned char frqs;
if ( frq <= 0 ){ add = 1; }
if ( frq >= 60000 ){ add = -1; }
// ---------------------- 判定メイン部
frqs = frq / 10000 ; if ( frqs > 5 ){ frqs = 5; }
if ( frqs != sel ){
sel = frqs;
Serial.print(" spi_out("); Serial.print( frqs ); Serial.print(") ");
Serial.print(" frq = "); Serial.println( frq );
spi_out( frqs );
}
// ----------------------
frq += add;
}

frq_chk[] として追加してみました frqs1*4 は使ってません シュミットトリガのようにヒステリスを持たせる場合は、frq 上昇時と下降時で frq_chk[] を切り換えれば良いと思います

#include <SPI.h>
SPISettings mySPISettings = SPISettings(500000, MSBFIRST, SPI_MODE0);
long int frq = 0;
unsigned char spi_dat[]={ B01001000 , B01001010 , B01011010 , B01011010 , B01011110 , B11011010 };
#define frqs0 10000ul
#define frqs1 10884ul
long frq_chk[] = { frqs0 , frqs1 , frqs02 , frqs12 , frqs04 , frqs14 };
void spi_out( unsigned char sel ){
if ( sel > 5 ){ return; }
SPI.beginTransaction(mySPISettings);
digitalWrite(10, HIGH);
SPI.transfer( spi_dat[sel] );
SPI.transfer(B11001111);
digitalWrite(10, LOW);
}
void setup() {
Serial.begin( 9600 );
Serial.println("Arduino Program Start !!");
SPI.begin();
}
void loop() {
static char add = 1;
static unsigned char sel = 9;
unsigned char frqs;
if ( frq <= 0 ){ add = 1; }
if ( frq >= 60000 ){ add = -1; }
// ---------------------- 判定メイン部
// frqs = frq / 10000 ; if ( frqs > 5 ){ frqs = 5; }
for ( frqs = 0 ; frqs < 5 ; frqs++ ){
if ( frq < frq_chk[frqs] ){ break; }
} // for
if ( frqs != sel ){
sel = frqs;
// Serial.print(" frqs = "); Serial.print( frqs );
Serial.print(" spi_out("); Serial.print( frqs ); Serial.print(") ");
Serial.print(" frq = "); Serial.println( frq );
spi_out( frqs );
}
// ----------------------
frq += add;
}

投稿2019/02/01 20:45

編集2019/02/02 20:55
koujikuu

総合スコア401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

azt1999

2019/02/02 03:01

有難うございます。シリアルモニタで見ると下記の通り、無事frqダミー値が確認できました。 綺麗に割り切れないので実機の判定では閾値設定が必要かと思いますが、いろいろ工夫し、 後ほど実際のSPIの出力をオシロがロジアナでチェックしてみます。 Arduino Program Start !! spi_out(0) frq = 9999 spi_out(1) frq = 10000 spi_out(2) frq = 20000 spi_out(3) frq = 30000 spi_out(4) frq = 40000 spi_out(5) frq = 50000 ついでにで誠に申し訳ありませんが、別の制御応用で下記の周波数の判定を予定 してますが、10000で割り切れないので、良い判定のやり方があればアドバイス お願いします。厚かましいお願いで申し訳ありません。 frqs0,10000 frqs1,10884 frqs2,20000 // frqs0 *2 frqs3,21768 // frqs1 *2 frqs4,40000 // frqs0 *4 frqs5,43536 // frqs1 *4
azt1999

2019/02/03 00:45

有難うございます。マイコン、Arduinoも超初心者ですが、同じ処理をさせるにもここまでエレガントな記述方ができるものだと感心させられました。まだコードが全部理解できていないので、一行づつ理解してステップアップできる様頑張ります。
guest

0

ベストアンサー

あぁそうか、周波数範囲が切り替わったときだけデータ送信をしたいということか...って、最初から質問にそう書いてありますね。ボケてました。失礼しました。
でも、「ループ」という言葉はやっぱり違うと思います。

で。今のプログラムを出来るだけ活かすなら、

Arduino

1void loop() { 2 FreqCounter::f_comp = 8; // Set compensation to 12 3 FreqCounter::start(100); // Start counting with gatetime of 100ms 4 while (FreqCounter::f_ready == 0); // wait until counter ready 5 frq = FreqCounter::f_freq; // read result 6 val = digitalRead(6); 7 static int frqRange = -1; 8 9 if (frq < 10000) { 10 if (frqRange != 0) {//前回他の周波数レンジだったならSPIデバイス設定 11 SPI.beginTransaction(mySPISettings);//ここから 12 digitalWrite(10, HIGH);// 13 SPI.transfer(B01001000);// 14 SPI.transfer(B11001111);// 15 digitalWrite(10, LOW);//ここまでが極力ループさせず1回だけさせたい処理です 16 } 17 frqRange = 0;//この周波数レンジに入ったことを記憶 18 //通常LOOP処理1 19 } 20 else if (frq < 20000) { 21 if (frqRange != 1) { 22 SPI.beginTransaction(mySPISettings);    23 digitalWrite(10, HIGH); 24 SPI.transfer(B01001010); 25 SPI.transfer(B11001111); 26 digitalWrite(10, LOW); 27 } 28 frqRange = 1; 29 //以下同様につき略 30 } 31 delay(100); 32}

なんていう手もあります。ちょっと力技感はあって、koujikuuさんの示されたプログラムのほうがやっぱりスマートです。

付け加えるなら、2%のフラつきがあるのなら、周波数境界付近でのバタ付きを防ぐとかも考えたほうがいいこともあるかも知れません。
例えば、9999->10000に上がったときは周波数レンジ1に移行するけど、10000->9999に下がったときはレンジ変更しなくて、9700(3%減)まで下がったときにやっと周波数レンジ0に移行するとか。

投稿2019/02/02 11:26

thkana

総合スコア7639

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

azt1999

2019/02/02 13:29

有難うございます。static変数で周波数レンジを記憶させて、レンジが外れた時にSPIデバイスのイニシャライズを行うというロジックですね。早速試してみましたが、ハードウエアから信号を貰って制御するより反応がかなり良くなりました。助かります。信号系にノイズが乗っているのか?閾値の問題か?受け側でシリアルデータの信号の取りこぼしが時折出るようなのでSPIの転送スピード落として実験中です。有難うございました。
thkana

2019/02/02 14:00

眺めていて思ったのですが、 最後のdelay(100)って必要ですか? それと、 FreqCounter::start(100); // Start counting with gatetime of 100ms while (FreqCounter::f_ready == 0); // wait until counter ready としているので、ここでも100mS無為に待つことになっているような。応答を早くしたいのなら、この辺見直してみるのも一つの手では。 void loop() { frq = FreqCounter::f_freq; // read result FreqCounter::f_comp = 8; // Set compensation to 12 FreqCounter::start(100); // Start counting with gatetime of 100ms static int frqRange = -1; if (frq < 10000) { if (frqRange != 0) {//前回他の周波数レンジだったならSPIデバイス設定 SPI.beginTransaction(mySPISettings);//ここから digitalWrite(10, HIGH);// SPI.transfer(B01001000);// SPI.transfer(B11001111);// digitalWrite(10, LOW);//ここまでが極力ループさせず1回だけさせたい処理です } frqRange = 0;//この周波数レンジに入ったことを記憶 //通常LOOP処理1 } else if (frq < 20000) { //略 } while (FreqCounter::f_ready == 0); // wait until counter ready }
azt1999

2019/02/03 00:23

再々有難うございます。アナログ回路側の出来もお世辞にも良いとは言えず、ロックアップタイムにそれなりに時間がかかってます。アナログもデジタルも良く判ってない為、タイミングチャートを真面目に確認し、勉強しながらいろいろ試してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問