「Raspberry Pi Pico」でタッチパネル液晶表示器「ILI9341」に搭載されている「SDカードリーダー」の使い方を、開発環境に「ArduinoIDE」を使って詳しく紹介します。
「SDカードリーダー」は「SPI通信」で、送受信端子を液晶画面と共通で使用できるため、せっかくならこれも使えるようにしておきたい!
ということで「SDカードリーダー」の使い方も紹介しておきます。
タッチパネル液晶表示器「ILI9341」の使い方や「ラズパイPico」の基本的な使用方法、「ArduinoIDE」を使用した開発環境の準備等は、以下のリンクで詳しく紹介しています。
1.Raspberry Pi Picoとは
2.タッチパネル液晶表示器 ILI9341とは
3.配線図
4.開発環境(ArduinoIDE)、ライブラリの準備
5.SDカードリーダーの使い方
・初期設定
・ファイル書き込み例
・ファイル読み込み例
・ファイル削除例
・JPEG画像の読み込み
6.サンプルプログラムで動作確認
・動作紹介
・サンプルプログラム(コピペ)
7.まとめ
1.Raspberry Pi Picoとは
Raspberry Pi Pico(ラズパイ Pico)とは、イギリスのRaspberry Pi財団が開発したマイコンボードで、「Python」や「C言語」でプログラムすることができます。
本体基板上の入出力端子に、スイッチやセンサ、各種制御モジュール、通信モジュールを接続することで、それらをプログムによって制御することができ、プログラミング学習やホビー、組込み用途に最適です。
「ラズパイ Pico」の端子配列は以下のようになります。
(「Pico2」やWi-Fi通信機能搭載の「Pico W」もLED以外の基本的な端子配列は同じです。)
「ラズパイ Pico」の使い方や端子機能、開発環境の準備については、以下のリンク先で詳しく紹介しています。
2.タッチパネル液晶表示器 ILI9341とは
「ILI9341」とは、タッチパネル搭載のTFT液晶表示器で、今回使用するのは2.8インチ(320x240Pixel)のものです
外観は下画像のようになります。
今回購入したものの梱包状態は上画像になります。
タッチパネル機能のない(制御ICが実装されていない)ものもあるようなので、注意して購入しましょう。
タッチペンが付属していましたが、全てに付属はしてないかもしれません。なくてもいいと思いましたが、動作確認するとスマホのような精度はないので、タッチペンは必須と思います。
裏面にタッチパネルの制御ICが実装されています。
今回実装されていた制御ICは「XPT2046」でした。
SDカードリーダーも実装されていますが、通信用の端子は実装も付属もされていません。
SDカードリーダーを使用するには、通信用端子(上画像左部)へピンヘッダー等を半田付けします。
SDカードは上画像のようにカードリーダーに差し込みますが半分以上がはみ出します。
今回購入したものは以下のアマゾンのものです。
3.配線図
配線図は下画像のようになります。
SPI通信のデータ送受信配線の「MOSI」と「MISO」クロック信号の「SCK」は液晶表示とタッチパネル、SDカードリーダーで共通で、CS端子で切り替えて使用します。
実際に配線した様子は下画像のようになります。
ブレッドボードを横に3枚連結して、ジャンパー線で接続しています。
SDカードリーダーの配線が見えないので、ILI9341を取り外すと下画像のようになります。
ブレッドボードの端子間隔を合わせるために下画像のように、ブレッドボードの電源用2列を、裏のテープをカッターで切って取り外して組み替えています。
今回使用したブレッドボードはサンハヤト製の6列タイプの以下のものです。
4.開発環境(ArduinoIDE)、ライブラリの準備
開発環境「ArduinoIDE」のインストールやライブラリの準備方法は以下のリンクで詳しく紹介しています。
使用するライブラリは以下表のようになります。
「ArduinoIDE」で事前にインストールしておいてください。
ライブラリ名 | 用途 | バージョン | 検索名 |
---|---|---|---|
Adafruit GFX Library | 文字フォントの指定や画面の チラツキ防止の「スプライト」用 | 1.11.1 | gfx |
Adafruit BusIO | Adafruit GFXライブラリを使用するために必要 | 1.16.2 | busio |
Adafruit ILI9341 | 液晶表示器ILI9341の制御用 | 1.6.1 | 9341 |
XPT2046_Touchscreen | タッチパネル制御用 | 1.4 | xpt paul |
JPEGDecoder | JPEG画像デコード処理用 | 2.0.0 | jpegdeco |
ライブラリはプログラムの初めにヘッダーとして以下のように書いて呼び出して使用します。
#include <Adafruit_GFX.h> // Adafruitのグラフィックスライブラリ
#include <Adafruit_ILI9341.h> // 液晶表示器 ILI9341 制御用ライブラリ
#include <XPT2046_Touchscreen.h> // タッチパネル制御用ライブラリ
#include <JPEGDecoder.h> // JPEG画像デコーダー用のライブラリ
5.microSDカードリーダーの使い方
microSDカードの操作はArduino標準ライブラリの「SD」ライブラリを使用します。
以下から使い方の例として、初期設定から書き込み、読み込み、削除、JPEG画像を読み込んで表示する方法を紹介します。
ここではSDカードを使用する方法について紹介します。
液晶表示やタッチパネルについての初期設定や使い方については、以下のリンク先で詳しく紹介しています。
・初期設定
以下はSDカードリーダーを使用するための初期設定の例です。
通信端子の「MOSI(送信)/MISO(受信)/SCK(クロック)」は液晶表示とタッチパネル、SDカードリーダーで共通の端子番号を設定します。
「SD_CS(チップセレクト)」端子は任意の出力端子を指定します。
この出力端子を「LOW」にすることでSDカードリーダーと通信を行うことができます。
初期設定では「SD.begin(SD_CS)」で「CS」端子番号を指定してSDカードの初期化を行なっています。
#define COMMON_SCK 18 // 液晶表示とタッチパネル、SDカードリーダー共通の SCK
#define COMMON_MOSI 19 // 液晶表示とタッチパネル、SDカードリーダー共通の MOSI
#define COMMON_MISO 16 // タッチパネル、SDカードリーダーの MISO
#define SD_CS 15 // SD(TF)カードリーダのCSピン
#define SD_FILENAME "/test.txt" // SD(TF)カードに保存するファイル名
#define JPEG_FILENAME "/raspi_pico.jpg" // JPEG画像ファイル名(SDカード内のJPEGファイル名を指定)
JPEGDecoder jpegDec; // JPEGデコーダのインスタンス(JPEG画像読み込み用)
File myFile; // Fileクラスのインスタンスを宣言
// 初期設定 ----------------------------------------
void setup() {
Serial.begin(115200); // シリアル通信初期化
//SPI0設定(TFT,TOUCH,SD共通)
SPI.setTX(COMMON_MOSI); // SPI0のTX(MOSI)
SPI.setRX(COMMON_MISO); // SPI0のRX(MISO)
SPI.setSCK(COMMON_SCK); // SPI0のSCK
// SDカードの初期化
if (!SD.begin(SD_CS)) {
Serial.println("SDカードの初期化に失敗しました");
return;
} else {
Serial.println("SDカードが初期化されました");
}
}
以下から書き込み、読み込み、削除方法を関数例としてまとめたものを紹介していきますが、いずれも上記の初期設定の例の中の以下のように「SD_FILENAME」でファイル名を指定しておきます。
#define SD_FILENAME "/test.txt" // SD(TF)カードに保存するファイル名
さらに、「File」クラスのインスタンスを「myFile」として宣言しておきます。
これにより、「myFile」にSDカードからデータを読み込んで「myFile.〜」で指定すれば、データを操作するプログラムが実行できるようになります。
File myFile; // Fileクラスのインスタンスを宣言
JPEG画像の読み込みについては、以下のように「JPEG_FILENAME」でjpeg画像のファイル名を指定しておきます。
#define JPEG_FILENAME "/raspi_pico.jpg" // JPEG画像ファイル名(SDカード内のJPEGファイル名を指定)
さらに、「JPEGDecoder」のインスタンスを「jpegDec」として宣言しておきます。
これにより、「jpegDec.〜」でjpeg画像のデコード処理(ビットマップ変換)プログラムが実行できるようになります。
JPEGDecoder jpegDec; // JPEGデコーダのインスタンス(JPEG画像読み込み用)
・ファイル書き込み例
SDカードにファイルを書き込むには以下の例のように「writeSdData()」関数を「書き込みたいデータの型」を指定して実行します。(今回は例として「”文字列”」と整数の「100」を書き込んでいます。)
writeSdData("文字列", 100); // SDカードへ「文字列」と整数100の書き込みを実行
関数例は以下になります。
void writeSdData(const char* text, int data) {
myFile = SD.open(SD_FILENAME, FILE_WRITE); // SDカードのファイルを開く
// データ書き込み
if (myFile) { // ファイルが開けたら
myFile.printf("%s : %d\n", text, data); // テキストと整数データを書き込み
myFile.close(); // ファイルを閉じる
} else { // ファイルが開けなければ
Serial.println("ファイルを開けませんでした");
dispMessage("Can't open file!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
}
}
書き込み処理は、まず「SD.open()」関数で「ファイル名」と「書き込みモード(FILE_WRITE)」を指定してファイルを開きます。
ファイルが開けたら「myFile.print()」関数で書き込みデータを指定して書き込みを実行します。
書き込みが完了したら「myFile.close()」関数でファイルを閉じます。
・ファイル読み込み例
SDカードからファイルを読み込むには以下の例のように「readSdData(“ファイル名”)」関数を、読み込みたい「ファイル名」を指定して実行します。
readSdData(SD_FILENAME); // SDカードからデータ読み込みを実行
関数例は以下になります。
void readSdData(const char* fileName) {
myFile = SD.open(fileName, FILE_READ); // SDカードのファイルを開く
if (myFile) { // ファイルが開けたら
// データ読み込み表示
while (myFile.available()) { // ファイルにデータがあれば繰り返し
char data = myFile.read(); // データを1文字づつ読み込む
Serial.print(data); // シリアル出力
}
myFile.close(); // ファイルを閉じる
} else { // ファイルが開けなければ
Serial.println("ファイルが読み込めませんでした"); // シリアル出力
}
}
読み込み処理は、まず「SD.open()」関数で「ファイル名」と「読み込みモード(FILE_READ)」を指定してファイルを開きます。(「, FILE_READ」は省略可)
ファイルが開けたら「While文」で「myFile.available()」関数を実行して、データがある間「myFile.read()」で1文字づつ読み込んで処理します。
全ての文字の読み込み処理が完了したら「myFile.close()」関数でファイルを閉じます。
・ファイル削除例
SDカードのファイルを削除するには以下の例のように「deleteSdData(“ファイル名”)」関数を、削除したい「ファイル名」を指定して実行します。
deleteSdData(SD_FILENAME) // SDカードのデータ削除を実行
関数例は以下になります。
void deleteSdData(const char* fileName) {
if (SD.exists(fileName)) { // ファイルが存在すれば
// ファイルを削除
if (SD.remove(fileName)) { // ファイル削除を実行して成功したら
Serial.println("ファイルを削除しました");
} else { // ファイル削除に失敗したら
Serial.println("ファイルの削除に失敗しました");
}
} else { // ファイルがなければ
Serial.println("ファイルが見つかりません");
}
}
ファイルを削除するにはまず「SD.exists(“ファイル名”)」関数でファイルが存在するかを確認します。
存在すれば「SD.remove(“ファイル名”)」関数でファイル名を指定して削除します。
・JPEG画像の読み込み例
SDカードに保存されたjpeg画像を読み込んで液晶画面に表示させるには以下の例のように、「jpegDraw(“ファイル名”)」関数をファイル名を指定して実行します。
jpegDraw(JPEG_FILENAME); // SDカードに保存されたJPEG画像のファイル名を指定して実行
関数例は以下になります。
void jpegDraw(const char* filename) {
JpegDec.decodeSdFile(filename); // JPEGファイルをSDカードからデコード実行
// シリアル出力、デコード画像情報(MCU [Minimum Coded Unit]:JPEG画像データの最小処理単位
Serial.printf("Size : %d x %d\nMCU : %d x %d\n", JpegDec.width, JpegDec.height, JpegDec.MCUWidth, JpegDec.MCUHeight);
Serial.printf("Components: %d\nMCU / row: %d\nMCU / col: %d\nScan type: %d\n\n", JpegDec.comps, JpegDec.MCUSPerRow, JpegDec.MCUSPerCol, JpegDec.scanType);
uint16_t *pImg; // ピクセルデータ用のポインタ
while (JpegDec.read()) { // JPEGデータを読み込む
pImg = JpegDec.pImage; // 現在のピクセルデータのポインタを取得
for (int h = 0; h < JpegDec.MCUHeight; h++) { // MCU高さ分ループ
for (int w = 0; w < JpegDec.MCUWidth; w++) { // MCU幅分ループ
// 現在のピクセルのx, y座標を計算
int x = JpegDec.MCUx * JpegDec.MCUWidth + w; // x座標
int y = JpegDec.MCUy * JpegDec.MCUHeight + h; // y座標
if (x < JpegDec.width && y < JpegDec.height) { // ピクセルが画像範囲内なら
tft.drawRGBBitmap(x, y, pImg, 1, 1); // 液晶画面にピクセルを描画
}
pImg += JpegDec.comps; // ポインタを次のピクセルデータへ進める
}
}
}
}
JPEG画像を液晶画面に表示するには、デコード処理を行います。
デコード処理は「JpegDec.decodeSdFile(“ファイル名”)」関数をファイル名を指定して実行します。
デコード処理は「MCU(Minimaum Coded Unit)」というJPEG画像の最小処理単位で行われ、今回の動作確認では「16×16」ピクセル単位で処理されていました。
液晶画面に表示するには「MCU」の高さと幅の分「for文」でループさせて、「tft.drawRGBBitmap()」関数で1ピクセルごとに点を、指定座標に表示することで画像として表示します。
ラズパイPicoの互換ボードでSDカードリーダーを搭載した「RP2040-GEEK」を使用したmicroSDカードにデータ保存、読み込み、削除をする方法も、以下リンク先でサンプルプログラムで紹介しています。
6.サンプルプログラムで動作確認
実際にSDカードへのデータの読み書きや削除、JPEG画像データの読み込み表示行う「サンプルプログラム」を下の方に準備しましたので、動作確認してみましょう。
ここで使用するJPEG画像データは下画像のような「ラズパイPico」のイラスト(160×240ピクセル)です。
以下からダウンロードできます。
・動作紹介
動作については以下のようになります。
「サンプルプログラム」を書き込んで実行すると、下画像のような画面が表示されます。
画面を左右2画面に分割して、左側をデータ表示、右側を操作画面として使用します。
画面の右上にはタッチ位置確認のための座標「x, y」と、電源ONからの経過時間が表示されます。
[Write]ボタンを押すごとに経過時間がSDカード内の「指定ファイル(test.txt)」に書き込まれます。
書き込んだデータは、左側のデータ表示画面に表示されます。
[Read]ボタンを押すと、SDカード内の「指定ファイル(test.txt)」が読み込まれて、保存されている「テキストデータ」が表示されます。
[Delete]ボタンを押すと、SDカード内の「指定ファイル(test.txt)」が削除されます。
[JPEG]ボタンを押すと、SDカード内の「JPEG画像データ(raspi_pico.jpg)」が読み込まれて画面に表示されます。
ファイルが読み込めなかったり、ファイルが存在しない等のエラー表示も下画像のように表示されるようにしています。
・サンプルプログラム(コピペ)
サンプルプログラムの動作について、基本的にはここまで紹介してきた内容の応用になります。
サンプルプログラムは以下になります。
コピペで貼り付けて書き込んでください。コピーは下の黒塗り部右上のアイコンクリックでもできます。
#include <Adafruit_GFX.h> // Adafruitのグラフィックスライブラリ
#include <Adafruit_ILI9341.h> // 液晶表示器 ILI9341 制御用ライブラリ
#include <XPT2046_Touchscreen.h> // タッチパネル制御用ライブラリ
#include <JPEGDecoder.h> // JPEG画像デコーダー用のライブラリ
#include <Fonts/FreeSans12pt7b.h> // フォントを読み込み
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#define TFT_WIDTH 320 // 画面幅
#define TFT_HEIGHT 240 // 画面高さ
#define TFT_ROTATION 3 // 画面の回転(タッチパネルの回転と合わせる)
#define COMMON_SCK 18 // 液晶表示とタッチパネル、SDカードリーダー共通の SCK
#define COMMON_MOSI 19 // 液晶表示とタッチパネル、SDカードリーダー共通の MOSI
#define COMMON_MISO 16 // タッチパネル、SDカードリーダーの MISO
#define TOUCH_CS 17 // タッチパネルの CS
#define TFT_DC 20 // 液晶画面の DC
#define TFT_RST 21 // 液晶画面の RST
#define TFT_CS 22 // 液晶画面の CS
#define SD_CS 15 // SD(TF)カードリーダのCS
#define SD_FILENAME "/test.txt" // SD(TF)カードに保存するファイル名
#define JPEG_FILENAME "/raspi_pico.jpg" // JPEG画像ファイル名
XPT2046_Touchscreen ts(TOUCH_CS); // タッチパネルのインスタンスを作成
Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST); // ILI9341ディスプレイのインスタンスを作成
JPEGDecoder jpegDec; // JPEGデコーダのインスタンス
File myFile; // Fileクラスのインスタンスを宣言
// スプライト(メモリ描画領域から一括表示)をcanvas1(操作パネル画面)、canvas2(データ表示画面)として2画面準備
// 画面表示をtftではなくcanvas1,2で指定して一括描画することでチラツキなく表示できる
GFXcanvas16 canvas1(TFT_WIDTH/2, TFT_HEIGHT); // 操作画面用スプライト(オフスクリーンバッファ)
GFXcanvas16 canvas2(TFT_WIDTH/2, TFT_HEIGHT); // データ表示画面用スプライト(オフスクリーンバッファ)
// 変数宣言
float timeData; // SDカードに保存するダミーデータ(電源ONからの経過時間)
bool writeButtonState; // 書き込みボタン状態格納用
bool readButtonState; // 読み込みボタン状態格納用
bool deleteButtonState; // 削除ボタン状態格納用
bool jpegButtonState; // Jpeg画像読み込みボタン状態格納用
/*********************** 液晶画面メッセージ表示関数 **********************/
void dispMessage(const char* text, uint16_t collor) {
canvas2.fillScreen(collor); // 背景の塗りつぶし
canvas2.setCursor(5, 120); // カーソル位置
canvas2.print(text); // メッセージ表示
}
/************************* SDカード データ書き込み関数 ************************/
void writeSdData(const char* text) {
// SDカードのファイルを開く
myFile = SD.open(SD_FILENAME, FILE_WRITE);
// データ書き込み
if (myFile) { // ファイルが開けたら
myFile.printf("%s : %.1f\n", text, timeData); // テキストと経過時間(秒)をファイルに書き込み
myFile.close(); // ファイルを閉じる
// 書き込みデータ表示
canvas2.fillScreen(0x3103); // 背景の塗りつぶし
canvas2.setCursor(0, 18); // カーソル位置
canvas2.printf("%s,%.1f\n", text, timeData); // テキストと経過時間(秒)を画面に表示
Serial.printf("%s,%.1f\n", text, timeData); // シリアル出力
} else { // ファイルが開けなければ
Serial.println("ファイルを開けませんでした");
dispMessage("Can't open file!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
}
}
/********************* SDカード データ読み込み関数 ********************/
void readSdData(const char* fileName) {
myFile = SD.open(fileName); // SDカードのファイルを開く
if (myFile) { // ファイルが開けたら
canvas2.fillScreen(0x3103); // 背景の塗りつぶし
canvas2.setCursor(0, 18); // カーソル位置
// データ読み込み表示
while (myFile.available()) { // ファイルにデータがあれば繰り返し
char data = myFile.read(); // データを1文字づつ読み込む
canvas2.print(data); // TFT出力
Serial.print(data); // シリアル出力
}
myFile.close(); // ファイルを閉じる
} else { // ファイルが開けなければ
Serial.println("ファイルが読み込めませんでした"); // シリアル出力
dispMessage("Can't read file!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
}
}
/********************* SDカード データ削除関数 ********************/
void deleteSdData(const char* fileName) {
if (SD.exists(fileName)) { // ファイルが存在すれば
if (SD.remove(fileName)) { // ファイル削除を実行して成功すれば
Serial.println("ファイルを削除しました");
canvas2.fillScreen(0x3103); // 背景の塗りつぶし
canvas2.setCursor(0, 18); // カーソル位置
canvas2.println("Delete File"); // TFT出力
canvas2.println(fileName); // TFT出力
} else { // ファイル削除に失敗したら
Serial.println("ファイルの削除に失敗しました");
dispMessage("Delete Failed!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
}
} else { // ファイルがなければ
Serial.println("ファイルが見つかりません");
dispMessage("File not found!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
}
}
//********************* JPEG画像デコード処理関数 *********************//
void jpegDraw(const char* filename) {
JpegDec.decodeSdFile(filename); // JPEGファイルをSDカードからデコード実行
// シリアル出力、デコード画像情報(MCU [Minimum Coded Unit]:JPEG画像データの最小処理単位、ここでは16x16ピクセル)
Serial.printf("Size : %d x %d\nMCU : %d x %d\n", JpegDec.width, JpegDec.height, JpegDec.MCUWidth, JpegDec.MCUHeight);
Serial.printf("Components: %d\nMCU / row: %d\nMCU / col: %d\nScan type: %d\n\n", JpegDec.comps, JpegDec.MCUSPerRow, JpegDec.MCUSPerCol, JpegDec.scanType);
uint16_t *pImg; // ピクセルデータ用のポインタ
while (JpegDec.read()) { // JPEGデータを読み込む
pImg = JpegDec.pImage; // 現在のピクセルデータのポインタを取得
for (int h = 0; h < JpegDec.MCUHeight; h++) { // MCU高さ分ループ
for (int w = 0; w < JpegDec.MCUWidth; w++) { // MCU幅分ループ
// 現在のピクセルのx, y座標を計算
int x = JpegDec.MCUx * JpegDec.MCUWidth + w; // x座標
int y = JpegDec.MCUy * JpegDec.MCUHeight + h; // y座標
if (x < JpegDec.width && y < JpegDec.height) { // ピクセルが画像範囲内なら
// tft.drawRGBBitmap(x, y, pImg, 1, 1); // 有効にすると液晶画面に直接描画されるのが確認できる
canvas2.drawRGBBitmap(x, y, pImg, 1, 1); // canvas2にピクセルを描画
}
pImg += JpegDec.comps; // ポインタを次のピクセルデータへ進める
}
}
}
}
/******************** テキスト描画関数 ********************/
void drawText(int16_t x, int16_t y, const char* text, const GFXfont* font, uint16_t color) {
canvas1.setFont(font); // フォント
canvas1.setTextColor(color); // 文字色
canvas1.setCursor(x, y); // 表示座標
canvas1.println(text); // 表示内容
}
/******************** ボタン描画関数 ********************/
void drawButton(int x, int y, const char* label, const GFXfont* font, uint16_t bgColor, uint16_t labelColor) {
int16_t w = 150, h = 42; // ボタン幅, 高さ
canvas1.fillRect(x, y, w, h, ILI9341_DARKGREY); // 外枠
canvas1.fillRect(x + 3, y + 3, w-6, h-6, ILI9341_WHITE); // 境界線
canvas1.fillRect(x + 6, y + 6, w-12, h-12, bgColor); // 操作部
// テキストの幅と高さを取得
canvas1.setFont(font); // 表示ラベル
int16_t textX, textY; // テキスト位置取得用
uint16_t textWidth, textHeight; // テキストサイズ取得用
canvas1.getTextBounds(label, x, y, &textX, &textY, &textWidth, &textHeight); // テキストの境界を取得する関数
// 中央揃えのための新しいx, y座標の計算
int16_t centeredX = x + (w - textWidth) / 2; // xを中央へ
int16_t centeredY = y + (h - textHeight) / 2 + textHeight; // yを下げて中央へ
canvas1.setTextColor(labelColor); // 文字色
canvas1.setCursor(centeredX, centeredY); // 新しいカーソル位置を設定
canvas1.print(label); // テキストを描画
}
// 初期設定 ----------------------------------------
void setup() {
Serial.begin(115200); // シリアル通信初期化
//SPI0設定(TFT,TOUCH,SD共通)
SPI.setTX(COMMON_MOSI); // SPI0のTX(MOSI)
SPI.setRX(COMMON_MISO); // SPI0のRX(MISO)
SPI.setSCK(COMMON_SCK); // SPI0のSCK
//液晶表示初期設定
tft.begin(); // TFTを初期化
tft.setRotation(TFT_ROTATION); // TFTの回転を設定(0-3)
tft.setTextSize(1); // テキストサイズ(倍率)
canvas1.fillScreen(ILI9341_BLACK); // canvas1の背景色初期化
canvas2.fillScreen(0x3103); // canvas2の背景色初期化
canvas2.setFont(&FreeSans9pt7b); // canvas2のフォント
//タッチパネル初期設定
ts.begin(); // タッチパネル初期化
ts.setRotation(TFT_ROTATION); // タッチパネルの回転を設定(液晶画面と合わせる)
// SDカードの初期化
if (!SD.begin(SD_CS)) {
Serial.println("SDカードの初期化に失敗しました");
dispMessage("SD card not found!", ILI9341_RED); // 液晶画面メッセージ表示関数呼び出し
return;
} else {
Serial.println("SDカードが初期化されました");
}
}
// メイン -----------------------------------------
void loop() {
timeData = (float)millis()/1000.0; // 経過時間を算出(秒に換算)
canvas1.fillScreen(ILI9341_BLACK); // 画面クリア
//タッチパネル処理
canvas1.setTextColor(ILI9341_WHITE); // 文字色
canvas1.setCursor(7, 16); // 座標設定
canvas1.setFont(&FreeSans9pt7b); // フォント
if (ts.touched() == true) { // タッチされていれば
TS_Point tPoint = ts.getPoint(); // タッチ座標を取得
// タッチx座標をTFT画面の座標に換算
int16_t x = (tPoint.x-400) * TFT_WIDTH / (4095-550); // タッチx座標をTFT画面の座標に換算
int16_t y = (tPoint.y-230) * TFT_HEIGHT / (4095-420); // タッチy座標をTFT画面の座標に換算
canvas1.printf("x%d y%d", x, y); // タッチ座標表示
// ボタンタッチエリア検出(各処理関数実行)
if (x >= 177 && x <= 303 && y >= 71 && y <= 95 && !writeButtonState) { // 書き込みボタン範囲内なら
writeButtonState = true; // ボタン状態をtrueへ
writeSdData("Time(s)"); // 書き込みデータ名を指定してSDデータ書き込み関数呼び出し
}
if (x >= 177 && x <= 303 && y >= 115 && y <= 139 && !readButtonState) { // 読み込みボタン範囲内なら
readButtonState = true; // ボタン状態をtrueへ
readSdData(SD_FILENAME); // ファイル名を指定してSDデータ読み出し関数呼び出し
}
if (x >= 177 && x <= 303 && y >= 159 && y <= 183 && !deleteButtonState) { // 削除ボタン範囲内なら
deleteButtonState = true; // ボタン状態をtrueへ
deleteSdData(SD_FILENAME); // ファイル名を指定してSDデータ削除関数呼び出し
}
if (x >= 177 && x <= 303 && y >= 203 && y <= 227 && !jpegButtonState){ // JPEG画像読み込みボタン範囲内なら
jpegButtonState = true; // ボタン状態をtrueへ
jpegDraw(JPEG_FILENAME); // JPEG画像デコード処理関数呼び出し
}
// Serial.printf("x=%d, y=%d\n", x, y); // シリアル出力確認用
} else { //タッチされていなければ座標表示なし
canvas1.print("x - y -"); // 文字表示
readButtonState = false; // ボタン状態をfalseへ
writeButtonState = false;
deleteButtonState = false;
jpegButtonState = false;
}
canvas1.setCursor(95, 16); // 座標設定
canvas1.printf("%.1fs", timeData); // 経過時間表示
// 線描画
canvas1.drawFastVLine(0, 0, 240, ILI9341_WHITE); // 線(指定座標から垂線)
canvas1.drawFastVLine(1, 0, 240, ILI9341_WHITE); // 線(指定座標から垂線)
canvas1.drawFastHLine(0, 24, 160, ILI9341_WHITE); // 線(指定座標から平行線)
canvas1.drawFastHLine(0, 25, 160, ILI9341_WHITE); // 線(指定座標から平行線)
canvas1.drawFastHLine(0, 60, 160, ILI9341_WHITE); // 線(指定座標から平行線)
canvas1.drawFastHLine(0, 61, 160, ILI9341_WHITE); // 線(指定座標から平行線)
// 文字描画(x, y, 内容, フォント, 文字色)
drawText(10, 55, "SD TEST", &FreeSans18pt7b, ILI9341_ORANGE); // タイトル表示
// ボタン描画(x, y, ラベル, フォント, ボタン色(ON/OFFで分岐), ラベル色)
drawButton(7, 65, "Write", &FreeSans12pt7b, (writeButtonState) ? ILI9341_DARKGREY : ILI9341_BLUE, ILI9341_WHITE); // 書き込みボタン
drawButton(7, 109, "Read", &FreeSans12pt7b, (readButtonState) ? ILI9341_DARKGREY : ILI9341_DARKGREEN, ILI9341_WHITE); // 読み込みボタン
drawButton(7, 153, "Delete", &FreeSans12pt7b, (deleteButtonState) ? ILI9341_DARKGREY : ILI9341_RED, ILI9341_WHITE); // 削除ボタン
drawButton(7, 197, "JPEG", &FreeSans12pt7b, (jpegButtonState) ? ILI9341_DARKGREY : ILI9341_PURPLE, ILI9341_WHITE); // Jpeg画像読み込みボタン
// スプライト(メモリ内に描画した画面)をTFTに描画
tft.drawRGBBitmap(160, 0, canvas1.getBuffer(), TFT_WIDTH/2, TFT_HEIGHT); // 操作パネル画面表示
tft.drawRGBBitmap(0, 0, canvas2.getBuffer(), TFT_WIDTH/2, TFT_HEIGHT); // データ表示エリア画面表示
}
7.まとめ
「Raspberry Pi Pico」でタッチパネル液晶表示「ILI9341」に搭載されている「SDカードリーダー」の使い方を詳しく紹介しました。
「SDカードリーダー」はSPI通信で制御され、通信に使用する端子は「液晶表示」と「タッチパネル」共通で使用でき、CS端子で切り替えて使用します。
今回使用した「ILI9341」には「SDカードリーダー/液晶表示/タッチパネル」が1枚の基板に実装されています。
通信端子を共有すれば、最小構成で10本の配線で全ての制御を行うことができるため「ラズパイPico」に限らず、多くのマイコンボードで制御することができます。
「SDカード」へのデータ読み書きや削除、JPEG画像データの読み出しは「Arduino」のライブラリを使用すれば、比較的簡単に行うことができるため、測定データを記録するデータロガーやJPEG画像を利用したディスプレイ広告、案内板等を安価に実現することができます。
タッチパネル操作画面の作成には、ボタン等部品の配置や座標の設定等に手間がかかりますが、操作パネルの写真や画像、手書きの操作画面を画像として読み込んでタッチ座標だけ設定すれば、タッチパネル式の操作画面も手軽に作成できます。
小規模な用途であれば、シーケンサとGOT(グラフィックオペレーションターミナル)を組み合わせたものと同じ機能が遥かに安価で実現でき、SDカードへのデータ記録もできるため、使いこなせば色々な用途に使用できると思います。
コメント