0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

エレキ素人が何か考える(その14)&FreeRTOS理解:番外編3:Dフリップフロップ

Posted at

Dフリップフロップを制御

秋月電子で購入した「2回路Dフリップフロップ TC74HC7」をArduinoから制御する。たまたまであるが、FreeRTOSを使うことになった。

データシート情報

ここにデータシートあり。

ピンアサイン

真理値表

TruthTable.png
この表を確認する。

準備

Arduinoとの配線

ここでは、2nd D Flip Flopを利用。

Arduino D Flipflop
2 2Q
4 2CLR~
5 2PR~
6 2CLK
7 2D

ソフトウェアで「Q」の変化を検知(インターラプトルーチン利用)したいため、「Q」を、Arduinoのピン2にアサインした。(Arduino Unoでのインターラプトルーチン利用は、ピン2及びピン3に限られる。)

下記のような状況。中央に見えるものが、D Flip Flopである。

ソースコード

PC(シリアル)から入力した数値により、入力ピンを制御する。

#include <Arduino_FreeRTOS.h>

#define PIN_Q   2     // in addition, for Arduino UNO interrupt
#define PIN_CLR 4
#define PIN_PR  5
#define PIN_CLK 6
#define PIN_D   7
#define DELAY   2000  // for clock (approximately 2000 ms)

TaskHandle_t xClk, xInput, xMonitor;  // for task
uint8_t cur_d, cur_clr, cur_pr;       // current value
volatile bool flag;                   // for interrupt routine
char *msg[] = {                       // command from PC
  "< Set LOW to D >", "< Set HIGH to D >", "< Set LOW to CLR~ >", "< Set HIGH to CLR~ >",
  "< Set LOW to PR~ >", "< Set HIGH to PR~ >", "< Read Q value >"};
  • ピンアサイン
  • クロック用変数(Delay)、ここではおよそ4秒で1サイクル
  • タスク用変数、現在の入力信号値(D, CLR~, PR~)を保持する変数
  • インターラプトごとに反転するフラグ
  • PCからのシリアル入力数値に対応する名称(コマンド)
uint8_t readQvalue() {
  uint8_t val = digitalRead(PIN_Q);
  return(val);
}

void setDvalue(uint8_t val) {
  cur_d = (val == 0)? LOW: HIGH;
  digitalWrite(PIN_D, cur_d);
}

void setClear(uint8_t val) {
  cur_clr = (val == 2)? LOW: HIGH;
  digitalWrite(PIN_CLR, cur_clr);
}

void setPrset(uint8_t val) {
  cur_pr = (val == 4)? LOW: HIGH;
  digitalWrite(PIN_PR, cur_pr);
}
  • readQvalue():現在のQをread
  • setDvalue():Dへのセット及び値の保持
  • setClear():CLR~へのセット及び値の保持
  • setPrset():PR~へのセット及び値の保持
void detection() {
  flag = !flag;
}

インターラプトルーチンであり、フラグを反転させる。

void TaskClock(void *arg) {
  pinMode(PIN_CLK, OUTPUT);
  for (;;) {
    digitalWrite(PIN_CLK, HIGH);
    Serial.println("***** Clock Rising *****");
    vTaskDelay(DELAY/portTICK_PERIOD_MS);
    digitalWrite(PIN_CLK, LOW);
    vTaskDelay(DELAY/portTICK_PERIOD_MS);
  }
}

CLK入力用タスク。CLK HIGH、DELAY sleep、CLK LOW、DELAY Sleepを繰り返す。1サイクルは、およそ、2 x DELAY msである。クロックの立ち上がりで、D値を読み取るので、長いサイクル(周期)の方が状況を把握しやすい。そのため、FreeRTOSを利用した。

void TaskInput(void *arg) {
  char buf[32];
  for (;;) {
    if (Serial.available() > 0) {
      String data = Serial.readStringUntil('\n');
      uint8_t val = data.toInt();
      if (val > 5) {
        val = 6;
      }
      Serial.println(msg[val]);
      if ((val == 0) || (val == 1)) {         // Set LOW(val=0) or HIGH(val=1) to D
        setDvalue(val);
      } else if ((val == 2) || (val == 3)) {  // Set LOW(val=2) or HIGH(val=3) to Clear~
        setClear(val);
      } else if ((val == 4) || (val == 5)) {  // Set LOW(val=4) or HIGH(val=5) to Prset~
        setPrset(val);            
      } else {                                // Read Q value
        sprintf(buf, "Q:%s", ((readQvalue() == 0)? "LOW": "HIGH"));
        Serial.println(buf);
      }
      sprintf(buf, "D:%s  CLR~:%s  PR~:%s\n", ((cur_d == LOW)? "LOW": "HIGH"),
        ((cur_clr == LOW)? "LOW": "HIGH"), ((cur_pr == LOW)? "LOW": "HIGH"));
      Serial.println(buf);
    }
    vTaskDelay(1); 
  }
}

シリアル入力タスクである。

  • 0, 1:Dへの入力(LOW, HIGH)
  • 2, 3:CLR~への入力(LOW, HIGH)
  • 4, 5:PR~への入力(LOW, HIGH)
  • それ以外の数値:Qを読み込み(入力値のエラー処理は未実施)
  • デバッグ用出力(各信号)
void TaskMonitor(void *arg) {
  bool current = flag;
  for (;;) {
    if (current != flag) {
      char buf[32];
      Serial.println("< Q value change >");
      sprintf(buf, "Q:%s", ((readQvalue() == 0)? "LOW": "HIGH"));
      Serial.println(buf);
      sprintf(buf, "D:%s  CLR~:%s  PR~:%s\n", ((cur_d == LOW)? "LOW": "HIGH"),
        ((cur_clr == LOW)? "LOW": "HIGH"), ((cur_pr == LOW)? "LOW": "HIGH"));
      Serial.println(buf);  
      current = flag;
    }
    vTaskDelay(1);
  }
}

インターラプト検知時のタスク(インターラプトルーチンは短時間で処理するのが基本のため)。デバッグ用出力(各信号)実施。

void setup() {
  Serial.begin(9600);
  pinMode(PIN_CLR, OUTPUT);
  pinMode(PIN_PR, OUTPUT);
  pinMode(PIN_D, OUTPUT);
  cur_clr = HIGH; digitalWrite(PIN_CLR, cur_clr);
  cur_pr = HIGH; digitalWrite(PIN_PR, cur_pr);
  cur_d = HIGH; digitalWrite(PIN_D, cur_d);
  
  pinMode(PIN_Q, INPUT);  // INPUT_PULLUP
  flag = true;
  attachInterrupt(digitalPinToInterrupt(PIN_Q), detection, CHANGE);

  xTaskCreate(TaskClock, "TaskClock", 128, NULL, 1, &xClk);
  xTaskCreate(TaskInput, "TaskInput", 128, NULL, 1, &xInput);
  xTaskCreate(TaskMonitor, "TaskMonitor", 128, NULL, 1, &xMonitor);
    
  vTaskStartScheduler();
}

void loop() {
  // Nothing
}

Arduinoお決まりの部分。

  • 各種ピン(信号)のモード設定及び初期化
  • インターラプトルーチンの設定
  • タスク起動およびスケジューリング

実験

以下、シリアル表示(ログ)。下記点がポイント。

  • 「***** Clock Rising *****」は定期的に表示される
  • 「< Set LOW to X >」および「< Read Q value >」はPCから指示されて表示される
  • 「< Q value change >」は「Q」の値が変化したときのみ表示される
14:46:45.092 -> ***** Clock Rising *****
14:46:48.115 -> < Read Q value >
14:46:48.161 -> Q:HIGH
14:46:48.161 -> D:HIGH  CLR~:HIGH  PR~:HIGH
14:46:48.208 -> 
14:46:49.472 -> ***** Clock Rising *****
14:46:51.529 -> < Set LOW to D >
14:46:51.529 -> D:LOW  CLR~:HIGH  PR~:HIGH
14:46:51.575 -> 
14:46:52.459 -> < Read Q value >
14:46:52.459 -> Q:HIGH
14:46:52.459 -> D:LOW  CLR~:HIGH  PR~:HIGH
14:46:52.506 -> 
14:46:53.814 -> ***** Clock Rising *****
14:46:53.862 -> < Q value change >
14:46:53.862 -> Q:LOW
14:46:53.909 -> D:LOW  CLR~:HIGH  PR~:HIGH
  • 初期状態のQはHIGH。
  • DをLOWにセット。
  • 直後にQを読むとまだHIGHのまま。
  • Clockの立ち上がりで、Qが変化し、LOWとなる。期待どおり。
  • Clockの1サイクルの実測値は、約4.4秒程度。各種オーバーヘッドあり。
14:47:06.924 -> ***** Clock Rising *****
14:47:06.971 -> < Q value change >
14:47:06.971 -> Q:HIGH
14:47:06.971 -> D:HIGH  CLR~:HIGH  PR~:HIGH
14:47:07.019 -> 
14:47:11.276 -> ***** Clock Rising *****
14:47:12.999 -> < Set LOW to CLR~ >
14:47:12.999 -> D:HIGH  CLR~:LOW  PR~:HIGH
14:47:13.046 -> 
14:47:13.046 -> < Q value change >
14:47:13.093 -> Q:LOW
14:47:13.093 -> D:HIGH  CLR~:LOW  PR~:HIGH
  • QがHIGHの状態。
  • CLR~をLOWにセット。
  • 即、QがLOWになる(Clockの立ち上がりを待たずに)。真理値表を確認すると、これも期待どおり。
  • 下記に続く。
14:47:21.085 -> < Set HIGH to CLR~ >
14:47:21.132 -> D:HIGH  CLR~:HIGH  PR~:HIGH
14:47:21.132 -> 
14:47:24.391 -> ***** Clock Rising *****
14:47:24.391 -> < Q value change >
14:47:24.437 -> Q:HIGH
14:47:24.437 -> D:HIGH  CLR~:HIGH  PR~:HIGH
  • QがLOWの状態で、CLR~をLOWからHIGHに戻す。
  • ここでは、即QがHIGHにならず。
  • Clockの立ち上がりで、Qが変化し、HIGHとなる。
14:47:44.738 -> < Set LOW to D >
14:47:44.738 -> D:LOW  CLR~:HIGH  PR~:HIGH
14:47:44.785 -> 
14:47:46.189 -> ***** Clock Rising *****
14:47:46.235 -> < Q value change >
14:47:46.235 -> Q:LOW
14:47:46.235 -> D:LOW  CLR~:HIGH  PR~:HIGH
14:47:46.281 -> 
14:47:50.539 -> ***** Clock Rising *****
14:47:52.644 -> < Set LOW to PR~ >
14:47:52.691 -> D:LOW  CLR~:HIGH  PR~:LOW
14:47:52.691 -> 
14:47:52.691 -> < Q value change >
14:47:52.738 -> Q:HIGH
14:47:52.738 -> D:LOW  CLR~:HIGH  PR~:LOW
14:47:52.738 -> 
14:47:54.929 -> ***** Clock Rising *****
14:47:59.270 -> ***** Clock Rising *****
14:48:00.725 -> < Set HIGH to PR~ >
14:48:00.771 -> D:LOW  CLR~:HIGH  PR~:HIGH
14:48:00.771 -> 
14:48:03.660 -> ***** Clock Rising *****
14:48:03.660 -> < Q value change >
14:48:03.708 -> Q:LOW
14:48:03.708 -> D:LOW  CLR~:HIGH  PR~:HIGH

CLR~やPR~のLOW/HIGHは、LOWの場合Qが即変化、HIGHの場合Clockの立ち上がりで変化となる。真理値表とおり。

すべての状態変化の組み合わせの結果については略。

おわりに

単純ではあるが、これがコンピューターのハードウェアの基本らしい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?