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

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

ただいまの
回答率

87.50%

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,446

score 13

先月から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);
}

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • thkana

    2019/02/01 23:47

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

    キャンセル

  • y_waiwai

    2019/02/02 00:18

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

    キャンセル

  • azt1999

    2019/02/02 03:42

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

    キャンセル

回答 2

+2

コード

シリアルモニタで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 , frqs0*2 , frqs1*2 , frqs0*4 , frqs1*4 };
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/02 12: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

    キャンセル

  • 2019/02/03 09:45

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

    キャンセル

checkベストアンサー

+1

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

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

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);
  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) {
    if (frqRange != 1) {
      SPI.beginTransaction(mySPISettings);   
      digitalWrite(10, HIGH);
      SPI.transfer(B01001010);
      SPI.transfer(B11001111);
      digitalWrite(10, LOW);
    }
    frqRange = 1;
    //以下同様につき略
  }
  delay(100);
}


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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/02 22:29

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

    キャンセル

  • 2019/02/02 23: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
    }

    キャンセル

  • 2019/02/03 09:23

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

    キャンセル

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

  • ただいまの回答率 87.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

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