概要
前回から少し時間が空いてしまいましたが、いろいろ追加検証をしたので紹介したいと思います。
スピーカーについて
前回にスピーカーに接続して、簡易的に音出しまでやりましたがその後に音質が悪い原因がわかりました。

ESP32の出力波形はこちらです。それなりにきれいな440Hzのサイン波ですね。

スピーカー端子から出力された波形がこちらです。完全な矩形波になっていますね。

いろいろ調べたのですが、上記の波形をESP32から出力してみると、、、

こちらの波形になりました。実験の結果ESP32からの出力を、FM8002Aのアンプで10倍以上にしてスピーカーから出力しています。この手の場合にはゲイン1倍で、電圧増幅しなくてもいいのですがしちゃっていますね。なのでちゃんとした音声は再生できません。ブザーとして利用するのがよいと思います。
SDカード
SDシンプル利用例
#include "SD.h"
SPIClass SPI_SD;
void sd_dir(void) {
enum { spi_sck = 18, spi_miso = 19, spi_mosi = 23, spi_ss = 5 };
SPI_SD.begin(spi_sck, spi_miso, spi_mosi, spi_ss);
SD.begin(spi_ss, SPI_SD);
if (!SD.begin(spi_ss, SPI_SD)) {
Serial.println("Card Mount Failed");
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("None SD Card");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
File root = SD.open("/");
if (root) {
File file = root.openNextFile();
while (file) {
time_t t = file.getLastWrite();
struct tm *tm_t = localtime(&t);
Serial.printf("%04d-%02d-%02d %02d:%02d:%02d ", tm_t->tm_year + 1900, tm_t->tm_mon + 1, tm_t->tm_mday, tm_t->tm_hour, tm_t->tm_min, tm_t->tm_sec);
if (file.isDirectory()) {
Serial.print("<DIR>");
Serial.print(" ");
Serial.println(file.name());
} else {
// ファイル名とファイルサイズを出力
Serial.print(" ");
Serial.printf("%10d ", file.size());
Serial.println(file.name());
}
file = root.openNextFile();
}
}
}
void setup() {
Serial.begin(115200);
delay(1000);
}
void loop() {
sd_dir();
delay(5000);
}
SDカードの情報を取得するだけのサンプルです。比較的かんたんに利用することができますが、ESP32の場合にはSPIは2系統までしか同時に利用することができません。
しかしこのボードは液晶、タッチパネル、SDと3系統あります。そのためすべてを同時に使うことができないのです。
SD同時利用例
#include <Arduino.h>
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include "SD.h"
SPIClass SPI_SD(VSPI);
void sd_dir(void) {
enum { spi_sck = 18, spi_miso = 19, spi_mosi = 23, spi_ss = 5 };
SPI_SD.begin(spi_sck, spi_miso, spi_mosi, spi_ss);
SD.begin(spi_ss, SPI_SD);
if (!SD.begin(spi_ss, SPI_SD)) {
Serial.println("Card Mount Failed");
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("None SD Card");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
File root = SD.open("/");
if (root) {
File file = root.openNextFile();
while (file) {
time_t t = file.getLastWrite();
struct tm *tm_t = localtime(&t);
Serial.printf("%04d-%02d-%02d %02d:%02d:%02d ", tm_t->tm_year + 1900, tm_t->tm_mon + 1, tm_t->tm_mday, tm_t->tm_hour, tm_t->tm_min, tm_t->tm_sec);
if (file.isDirectory()) {
Serial.print("<DIR>");
Serial.print(" ");
Serial.println(file.name());
} else {
// ファイル名とファイルサイズを出力
Serial.print(" ");
Serial.printf("%10d ", file.size());
Serial.println(file.name());
}
file = root.openNextFile();
}
}
}
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ILI9341 _panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_XPT2046 _touch_instance;
public:
LGFX(void)
{
{
auto cfg = _bus_instance.config();
// SPIバスの設定
cfg.spi_host = VSPI_HOST;
cfg.spi_mode = 0;
cfg.freq_write = 40000000;
cfg.freq_read = 16000000;
cfg.spi_3wire = true;
cfg.use_lock = true;
cfg.dma_channel = SPI_DMA_CH_AUTO;
cfg.pin_sclk = 14; // 変更
cfg.pin_mosi = 13; // 変更
cfg.pin_miso = 12; // 変更
cfg.pin_dc = 2; // 変更
_bus_instance.config(cfg);
_panel_instance.setBus(&_bus_instance);
}
{
auto cfg = _panel_instance.config();
cfg.pin_cs = 15; // 変更
cfg.pin_rst = -1; // 変更
cfg.pin_busy = -1; // 変更
cfg.panel_width = 240;
cfg.panel_height = 320;
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.offset_rotation = 0;
cfg.dummy_read_pixel = 8;
cfg.dummy_read_bits = 1;
cfg.readable = true;
cfg.invert = false;
cfg.rgb_order = false;
cfg.dlen_16bit = false;
cfg.bus_shared = false; // 変更
_panel_instance.config(cfg);
}
{
auto cfg = _light_instance.config();
cfg.pin_bl = 21; // 変更
cfg.invert = false;
cfg.freq = 44100;
cfg.pwm_channel = 7;
_light_instance.config(cfg);
_panel_instance.setLight(&_light_instance);
}
{
auto cfg = _touch_instance.config();
cfg.x_min = 300; // 変更
cfg.x_max = 3900; // 変更
cfg.y_min = 3700; // 変更
cfg.y_max = 200; // 変更
cfg.pin_int = -1; // 変更
cfg.bus_shared = false; // 変更
cfg.offset_rotation = 0;
cfg.spi_host = HSPI_HOST; // 変更
cfg.freq = 1000000;
cfg.pin_sclk = 25; // 変更
cfg.pin_mosi = 32; // 変更
cfg.pin_miso = 39; // 変更
cfg.pin_cs = 33; // 変更
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
LGFX display;
void setup(void) {
Serial.begin(115200);
delay(1000);
display.init();
display.setTextSize((std::max(display.width(), display.height()) + 255) >> 8);
display.fillScreen(TFT_BLACK);
}
uint32_t count = ~0;
void loop(void) {
display.startWrite();
display.setRotation(++count & 7);
display.setColorDepth((count & 8) ? 16 : 24);
display.setTextColor(TFT_WHITE);
display.drawNumber(display.getRotation(), 16, 0);
display.setTextColor(0xFF0000U);
display.drawString("R", 30, 16);
display.setTextColor(0x00FF00U);
display.drawString("G", 40, 16);
display.setTextColor(0x0000FFU);
display.drawString("B", 50, 16);
display.drawRect(30, 30, display.width() - 60, display.height() - 60, count * 7);
display.drawFastHLine(0, 0, 10);
display.endWrite();
auto start = millis();
while ( (millis() - start) < 1000) {
int32_t x, y;
if (display.getTouch(&x, &y)) {
display.fillRect(x - 2, y - 2, 5, 5, TFT_RED);
}
}
sd_dir();
display.initBus();
}
上記が、液晶とSDを排他的に利用したサンプルです。
VSPI | 液晶 or SD |
HSPI | タッチパネル |
上記の組み合わせで使っています。VとHは逆になっても構いません。今回は液晶とSDで利用前にバスを初期化しなおすことで共存させています。タッチパネルと共存させたかったのですが、現在SDのバスを簡単に初期化する方法が提供されていませんでした。
液晶再初期化方法
display.initBus();
肝になるのは上記の処理です。これによりSDで使っていたバスを液晶用に初期化しなおしています。
SD初期化方法
SPI_SD.begin(spi_sck, spi_miso, spi_mosi, spi_ss);
SDの方は上記のように毎回利用前に初期化しなおしています。これにより液晶で使っていたVSPIを壊して、SDの初期化が動いています。
タッチパネルの通知
タッチパネルにはタッチのIRQ信号としてGPIP36があります。タッチするとLOWになるようです。このGPIOを監視することでタッチが発生したかを確認することができます。直接タッチ取得をしてもよいのですが、SPI経由の取得ですので少し重いです。とはいえ、あまり有意義に使える用途は思いつきませんでした。
割り込みの確認
QueueHandle_t xQueueTp;
void ARDUINO_ISR_ATTR isrPin() {
xQueueSendFromISR(xQueueTp, NULL, 0);
}
void taskTp(void *pvParameters) {
while (1) {
xQueueReceive(xQueueTp, NULL, portMAX_DELAY);
Serial.println("TP IRQ");
}
}
void setup(void) {
Serial.begin(115200);
delay(1000);
xQueueTp = xQueueCreate(1, 0);
xTaskCreateUniversal(
taskTp, // タスク関数
"taskTp", // タスク名(あまり意味はない)
8192, // スタックサイズ
NULL, // 引数
5, // 優先度(大きい方が高い)
NULL, // タスクハンドル
APP_CPU_NUM // 実行するCPU(PRO_CPU_NUM or APP_CPU_NUM)
);
pinMode(36, INPUT);
attachInterrupt(36, isrPin, FALLING);
}
void loop(void) {
}
上記が最低限の割り込み設定と、別タスクでの処理になります。ただし、これはあまり使い道がないかもしれません。タッチパネルに接触している間は常にLOWなので、動かしているかを判定することはできません。
SPI通信削減
if (digitalRead(36) == LOW && display.getTouch(&x, &y)) {
display.fillRect(x - 2, y - 2, 5, 5, TFT_RED);
}
上記のように36がLOWの場合だけタッチパネルの情報を取得することで、SPI通信を削減することが可能です。とはいえ、あまりこれも意味がないように思えます。
コネクタ
上記のケーブルで4ピンと2ピンを購入したところ、付属ケーブルと前回利用したスピーカーと同じコネクタだったと思います。
まとめ
実は3.5インチモデルが新発売してしまいました。今度はタッチパネルが感圧式と静電容量方式の2種類あります。そのうち購入して検証したいと思います。
コメント