兎にも角にもESP32系マイコンを理解するにはLチカ!というわけで色々なやり方でチカチカさせてみましょう。できればloop関数にdelay()を入れずに光らせたいと思いまとめてみました。
サンプルコードを使って知りたい事
- タイマー処理
- マルチスレッド処理
- LedcWrite
詳しくは説明できませんが、回路を実際に組んでコードを書いて理解できればいいかな?
そのレベルでの記事で備忘的な内容になっています。間違っていたりしたらコメントお願いします。🙇
Lチカを色んなパターンでチカらせてみる
まずは動画で確認してみましょう。5つのLEDを4つの方法で光らせています。
- Loop関数の中でDelayを使う方法(pin D8)
- Timerを使う方法(pin D6,D7)
- ledc,ledcFadeを使う方法(pin D5)
- マルチスレッド処理を使う方法(pin D4)
Loop()関数でチカチカ
void setup() {
pinMode(D8, OUTPUT);
digitalWrite(D8, HIGH);
}
void loop() {
digitalWrite(D8, !digitalRead(D8));
delay(5000);
}
動画でのpin D8はloop()関数でdigitalWriteで自分のpinを読んでオンオフを反転させて5秒待機させています。
一番簡単なやり方ですね、理解しやすいしお手軽でいいのですがループにdelay()を入れるとloop()内でのdelay以下の処理がdelayで指定した秒数だけ止まってしまうのでできれば使いたくないですね。
サンプルコードではD8ピンが5秒ごとに点いたり消えたりします。
Timerでチカチカ
動画のD7,D6PinのLEDはタイマーを使ってチカチカさせています。
ネットで調べてみるとTimerの情報はUnoの情報であったり古い情報(apiバージョン2.0→3.0時点で大きな仕様変更があったので情報をあさる時はこの点に注意)であったりと日本語のサイトではなかなかこれ!という情報を見つけることができませんでしたが、ESP32の場合は公式情報のサンプルコードを試してみるといいかもしれません。
EspressifのLibraries>APIs からTimerをして内容を確認してみてください。
ESP32C3を使ってサンプルを動かしていますが、タイマーは2つあるので二つ使ってLチカしてみましょう。
全体
hw_timer_t *timer = NULL;
hw_timer_t *timer2 = NULL;
void ARDUINO_ISR_ATTR onTimer() {
fromOntimerProcess();
}
void ARDUINO_ISR_ATTR onTimer2() {
fromOntimerProcess2();
}
void setup() {
pinMode(D7, OUTPUT);
digitalWrite(D7, HIGH);
pinMode(D6, OUTPUT);
// Set timer frequency to 1Mhz
timer = timerBegin(1000000);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarm(timer, 1000000, true, 0);//every 1.0 second
// Set timer2 frequency to 1Mhz
timer2 = timerBegin(1000000);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer2, &onTimer2);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarm(timer2, 20000, true, 0);//every 0.1 second
}
void fromOntimerProcess() {//1秒間隔でチカチカ
Serial.print("called process\n");
digitalWrite(D7, !digitalRead(D7));
}
void fromOntimerProcess2() {//0.1秒間隔でチカチカ
Serial.print("called process2\n");
digitalWrite(D6, !digitalRead(D6));
}
宣言
hw_timer_t *timer = NULL;
hw_timer_t *timer2 = NULL;
void ARDUINO_ISR_ATTR onTimer() {
fromOntimerProcess();
}
void ARDUINO_ISR_ATTR onTimer2() {
fromOntimerProcess2();
}
hw_timer_t変数を二つ空で宣言しておき、タイマー発動時に呼び出すonTimer()関数をARDUINO_ISR_ATTERをつけて確保された領域(都合の良いメモリー領域ですかね)で呼び出します。その中でfromOntimeProcess()を呼ぶ流れです。
// Set timer frequency to 1Mhz
timer = timerBegin(1000000);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarm(timer, 1000000, true, 0);//every 1.0 second
timerBegin(1000000)で1Mhz周期のタイマーをセットし、その周期をtimerAlarmで指定したタイミングでtimerAttachInterruptの割り込みでonTimer関数を起動するようにします。
timerAlarmの発動指定はマイクロセカンド単位なので、1,000,000は1秒ごとにタイマーが起動するということになります。
タイマーによって割り込みが発生するとARDUINO_ISR_ATTR onTimer()が呼び出されてそこからfromOntimerProcess()を呼び出すというながれです。
なお、ESP32C3の場合はタイマーが2つしかないので三つ設定しても挙動がおかしくなるだけなので、使用するマイコンの仕様をあらかじめしらべておくと良いでしょう。
ledcでチカチカ (ledcFadeを使ってみる)
ここではESP32で使えるledcWrite系のledcFadeを使ったチカチカ?ゆっくり点灯ゆっくり消灯をやってみます。
考え方としては割り込みを使ったPWMでLEDの明るさを調節する要領ですね。
公式の解説は以下のリンクにありますので確認しておきましょう。
宣言
// use 12 bit precision for LEDC timer
#define LEDC_TIMER_12_BIT 12
// use 5000 Hz as a LEDC base frequency
#define LEDC_BASE_FREQ 5000
// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
#define LED_PIN D5
// define starting duty, target duty and maximum fade time
#define LEDC_START_DUTY (0)
#define LEDC_TARGET_DUTY (4095)
#define LEDC_FADE_TIME (3000)
bool fade_ended = false; // status of LED fade
bool fade_on = true;
PWMな使い方なので、いわゆるデューイを表現するためのビット数を12bitで宣言して0から4095の値で明るさを調整していく感じで各々宣言していきます。
void setup() {// Setup timer with given frequency, resolution and attach it to a led pin with auto-selected channel
ledcAttach(LED_PIN, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT);
// Setup and start fade on led (duty from 0 to 4095)
ledcFade(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME);
Serial.println("LED Fade on started.");
// Wait for fade to end
delay(LEDC_FADE_TIME);
// Setup and start fade off led and use ISR (duty from 4095 to 0)
ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
}
setup()ではledcAttachでピン、周波数、ビット数を指定しておいて、いったん割り込みなしで明るさ0から4095まで3秒間で光らせていきます。その後のledcFadeWithInterruptで割り込み指定付きで3秒かけて光を暗くしていきます。
割り込み時に呼び出されるのはLED_FADE_ISRとなり、その中でfade_onフラグを見ながら同じく割り込み付きの設定で3秒fadeと3秒fadeoutを繰り返すようにしています。
なおloop()関数内では何も操作していません。
void ARDUINO_ISR_ATTR LED_FADE_ISR() {
fade_ended = true;
fade_ended = false;
// Check if last fade was fade on
if (fade_on) {
ledcFadeWithInterrupt(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
Serial.println("LED Fade off started.");
fade_on = false;
} else {
ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
Serial.println("LED Fade on started.");
fade_on = true;
}
}
割り込み時に呼び出されるLED_FADE_ISR()ではフラグの制御と繰り返しのためledcFadeWithInterruptでタイミングをとりながらゆっくり点灯とゆっくり消灯を割り込み設定の繰り返しという流れです。
本来であれば、タイマー割り込みを使ったフラグや変数の更新はセマフォを使った排他制御下で行うのが望ましいとされていて、オフィシャルのサンプルコードでも排他制御されていますが、この記事ではその辺りは扱っていません。
マルチスレッドでチカチカ
ESP32C3はコアを一つしか持ちませんのでマルチコアは使えませんがマルチスレッドで処理を進めることができます。タイマー的な方法ですね。ESP32のESP-IDFにはFreeRTOSが組み込まれているのでそちらの資料のリンクを下に掲載しておきます。
宣言
TaskHandle_t thp[1];
タスクハンドルの変数を宣言しておきます。
void setup() {
xTaskCreatePinnedToCore(Core0a, "Core0a", 4096, NULL, 3, &thp[0], 0);
}
xTaskCreatePinnedToCoreは特定のコアに固定された新しいタスクを作成するとFreeRTOS補足資料に記載されています。
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode, const char *const pcName, constuint32_t ulStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *constpxCreatedTask, const BaseType_t xCoreID)
- pxTaskCode — タスク入力関数へのポインタ。
- pcName — タスクのわかりやすい名前。
- ulStackDepth — バイト数として指定されたタスクスタックのサイズ。これはバニラFreeRTOSとは異なることに注意してください。
- pvParameters — 作成されるタスクのパラメータとして使用されるポインタ。
- uxPriority — タスクを実行する優先順位。
- pxCreatedTask — 作成したタスクを参照できるハンドルを渡すために使用されます。
- xCoreID — タスクがピン留めされているコア、またはタスクにコアアフィニティがない場合はtskNO_AFFINITY。
void Core0a(void *args) {
while (1) {//無限ループにする
delay(1);//この処理は必須
digitalWrite(D4, !digitalRead(D4));
delay(500);
}
}
呼び出されるループ関数をCore0aで作成したのでこちらのなかで無限ループを作成すると、マルチスレッド処理を実装することができます。
動画のソースコード
学習用に上記の内容をまとめたソースコードを掲載していきます。あまりこのような実装はないとおもいますが少し理解が深まるかもしれません。
/*
Repeat timer example
This example shows how to use hardware timer in ESP32. The timer calls onTimer
function every second. The timer can be stopped with button attached to PIN 0
(IO0).
This example code is in the public domain.
*/
// Stop button is attached to PIN D0
#define BTN_STOP_ALARM D0 //ESP32C3 PinD0
hw_timer_t *timer = NULL;
hw_timer_t *timer2 = NULL;
void ARDUINO_ISR_ATTR onTimer() {
fromOntimerProcess();
}
void ARDUINO_ISR_ATTR onTimer2() {
fromOntimerProcess2();
}
// ************************************//
// use 12 bit precision for LEDC timer
#define LEDC_TIMER_12_BIT 12
// use 5000 Hz as a LEDC base frequency
#define LEDC_BASE_FREQ 5000
// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
#define LED_PIN D5
// define starting duty, target duty and maximum fade time
#define LEDC_START_DUTY (0)
#define LEDC_TARGET_DUTY (4095)
#define LEDC_FADE_TIME (3000)
bool fade_ended = false; // status of LED fade
bool fade_on = true;
void ARDUINO_ISR_ATTR LED_FADE_ISR() {
fade_ended = true;
fade_ended = false;
// Check if last fade was fade on
if (fade_on) {
ledcFadeWithInterrupt(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
Serial.println("LED Fade off started.");
fade_on = false;
} else {
ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
Serial.println("LED Fade on started.");
fade_on = true;
}
}
// ************************************//
TaskHandle_t thp[1];
void setup() {
pinMode(D8, OUTPUT);
digitalWrite(D8, HIGH);
pinMode(D7, OUTPUT);
digitalWrite(D7, HIGH);
pinMode(D6, OUTPUT);
pinMode(D4, OUTPUT);
digitalWrite(D4, HIGH);
Serial.begin(115200);
while (!Serial) {
delay(10);
}
// Set BTN_STOP_ALARM to input mode
pinMode(BTN_STOP_ALARM, INPUT_PULLUP);
// Set timer frequency to 1Mhz
timer = timerBegin(1000000);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarm(timer, 1000000, true, 0);//every 1.0 second
// Set timer frequency to 1Mhz
timer2 = timerBegin(1000000);
// Attach onTimer function to our timer.
timerAttachInterrupt(timer2, &onTimer2);
// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
timerAlarm(timer2, 20000, true, 0);//every 0.1 second
// ************************************//
// Setup timer with given frequency, resolution and attach it to a led pin with auto-selected channel
ledcAttach(LED_PIN, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT);
// Setup and start fade on led (duty from 0 to 4095)
ledcFade(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME);
Serial.println("LED Fade on started.");
// Wait for fade to end
delay(LEDC_FADE_TIME);
// Setup and start fade off led and use ISR (duty from 4095 to 0)
ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
Serial.println("LED Fade off started.");
// ************************************//
xTaskCreatePinnedToCore(Core0a, "Core0a", 4096, NULL, 3, &thp[0], 0);
}
void loop() {
digitalWrite(D8, !digitalRead(D8));
delay(5000);
// digitalWrite(D8, HIGH);
// delay(5000);
// digitalWrite(D8, LOW);
// delay(5000);
// If button is pressed
if (digitalRead(BTN_STOP_ALARM) == LOW) {
Serial.print("**Button Pushed**");
// If timer is still running
if (timer) {
// Stop and free timer
timerEnd(timer);
timer = NULL;
}
if (timer2) {
// Stop and free timer
timerEnd(timer2);
timer2 = NULL;
}
}
// ************************************//
// // Check if fade_ended flag was set to true in ISR
// if (fade_ended) {
// Serial.println("LED fade ended");
// fade_ended = false;
//
// // Check if last fade was fade on
// if (fade_on) {
// ledcFadeWithInterrupt(LED_PIN, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
// Serial.println("LED Fade off started.");
// fade_on = false;
// } else {
// ledcFadeWithInterrupt(LED_PIN, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR);
// Serial.println("LED Fade on started.");
// fade_on = true;
// }
// }
// ************************************//
}
void fromOntimerProcess() {
Serial.print("called process\n");
// if (digitalRead(D7)) {
// digitalWrite(D7, LOW);
// } else {
// digitalWrite(D7, HIGH);
// }
digitalWrite(D7, !digitalRead(D7));
}
void fromOntimerProcess2() {
Serial.print("called process2\n");
// if (digitalRead(D6)) {
// digitalWrite(D6, LOW);
// } else {
// digitalWrite(D6, HIGH);
// }
digitalWrite(D6, !digitalRead(D6));
}
void Core0a(void *args) {//サブCPU(Core0)で実行するプログラム
while (1) {//ここで無限ループを作っておく
//サブで実行するプログラムを書く
delay(1);//1/1000秒待つ
// a++;//aに1を加算する
digitalWrite(D4, !digitalRead(D4));
delay(500);
// If button is pressed
if (digitalRead(BTN_STOP_ALARM) == LOW) {
Serial.print("**Button Pushed**");
// If timer is still running
if (timer) {
// Stop and free timer
timerEnd(timer);
timer = NULL;
}
if (timer2) {
// Stop and free timer
timerEnd(timer2);
timer2 = NULL;
}
}
}
}
コメント