Dフリップフロップを制御
秋月電子で購入した「2回路Dフリップフロップ TC74HC7」をArduinoから制御する。たまたまであるが、FreeRTOSを使うことになった。
データシート情報
ここにデータシートあり。
ピンアサイン
真理値表
準備
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の立ち上がりで変化となる。真理値表とおり。
すべての状態変化の組み合わせの結果については略。
おわりに
単純ではあるが、これがコンピューターのハードウェアの基本らしい。