疑似コンソール (C言語編)
2023.12.01
YouTube でも紹介しています。画像をクリックすると再生できます。
以前、pythonを用いてリングバッファ擬きの疑似コンソールを作成しました。
疑似コンソール
今回は、C言語によりリングバッファを実装し、TFT LCDを疑似コンソール化してみます。

●リングバッファ

File:Ring.buffer.svg - Wikimedia Commons
リングバッファは、予め記憶領域を確保して、読み出し位置と書き込み位置をずらしながら処理を行い、領域の終端に到達したら、領域の先頭に繋げ無限ループ的に利用する仕組みです。
ここでは、疑似コンソール・クラス Console を作成して、inoファイルで使用する例を用いて説明します。
#include "Console.h"
Console console;
void setup()
{
console.print("ABC", TFT_GREEN);
console.print("DEFGHI", TFT_YELLOW);
console.print("JKL" , TFT_GREEN);
console.print("MNOP", TFT_YELLOW);
}
void loop() {}

リングバッファのサイズは下記の通りです、
(色情報(COLOR565)2バイト+桁数+終端コード1バイト)×行数
2行5桁のLCDの場合、必要となるリングバッファのサイズは
(2+5+1)×2=16バイト
初回のconsole.print()関数呼び出しでは配列の先頭から、色情報2バイト、文字列、終端(NULLコード1バイトと埋めていきます。
LCD表示用データの範囲は配列の先頭から終端コードまでになります。
2回目の関数呼び出しでは、終端コードの次の位置からデータを埋めていきます。
ここで指定した文字列を表示するためには2行必要になるため、LCD表示用データの範囲もこれに合わせてシフトさせます。
3回目の関数呼び出しで単に終端コードに続けて埋めていってしまうとオーバーフローしてしまいます。
リングバッファとはいっても、円形に配列が繋がっているわけではありません。
配列の終端までデータが埋まった時点で、配列の先頭から続きのデータで上書きしていきます。
LCD表示データの開始位置は2回目に呼び出した際の文字列の途中からになるので、表示文字列の直前に色情報も付加します。
4回目の関数呼び出しも同様に処理していきます。
このような流れになるようにプログラム要件をまとめ、疑似コンソール・クラスにコードを記述しています。
ここにソースコードも記載しておきますが、まずはご自身で処理の流れを考えてコーディングしてみてください。
意外と面倒なことに気付くと思います。
下記のコードは、横240x縦320のディスプレイに表示フォントサイズ横12x縦16pxのフォントを使用した場合です。
実行環境に応じて、変更してください。
疑似コンソール・クラス Console
#include "Console.h"
Console::Console() {
tft.init(240, 320);
tft.setRotation(2);
tft.setTextSize(2); // 12px
tft.setTextWrap(false);
tft.fillScreen(ST77XX_BLACK);
width = tft.width();
height = tft.height();
cols = width / FONT_WIDTH;
rows = height / FONT_HEIGHT;
lines = 0;
stack = (uint8_t*)malloc(STACK_SIZE);
if (!stack) {
tft.setCursor(0, 0);
tft.setTextColor(TFT_RED);
tft.println("memory allocate failed!");
while(1);
} else {
from = stack;
to = stack;
memset(stack,'\0',sizeof(char)*(cols+1)*rows);
}
}
Console::~Console() {}
void Console::backlight(uint8_t pin, uint8_t val)
{
pinMode(pin,OUTPUT);
analogWrite(pin, 64); // 0 - 255
}
void Console::print(char *text, uint16_t color)
{
int skip, cnt, line;
uint16_t c;
uint8_t *pos;
uint8_t cH, cL;
char prn_buff[512];
boolean nullflag;
if (*text == 0x0) return;
// stack text to the ring buffer
*to = (uint8_t)(color>>8);
if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;
*to = (uint8_t)(color&0xff);
skip = 0;
cnt = 0;
while (1) {
if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;
*to = *text;
if (++cnt == cols) {
skip++;
cnt = 0;
}
if (*text == 0x0) {
if (cnt > 1) skip++;
break;
}
text++;
}
if ((to+1)<(stack+STACK_SIZE)) to++; else to = stack;
lines += skip;
if (lines > rows) {
skip = lines - rows;
lines = rows;
} else {
skip = 0;
}
// shift the display start position
while (skip > 0) {
cH = *from;
if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
cL = *from;
if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
nullflag = false;
while (skip > 0) {
if (nullflag) break;
cnt = 1;
while (1) {
if (*from == 0x0) {
if (cnt > 1) --skip;
if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
nullflag = true;
break;
} else {
if (cnt == cols) {
if ((--skip) == 0) {
*from = cL;
if (from == stack) from = stack + STACK_SIZE - 1; else --from;
*from = cH;
break;
}
cnt = 0;
}
}
if ((from+1)<(stack+STACK_SIZE)) from++; else from=stack;
cnt++;
}
}
}
tft.fillScreen(ST77XX_BLACK);
line = 0;
pos = from;
while(1) {
c = *pos;
if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
c = (c<<8)&0xff00 + *pos;
if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
tft.setTextColor(c);
cnt = 1;
while (1) {
prn_buff[cnt-1] = *pos;
if (*pos == 0x0) {
if (cnt > 1) {
tft.setCursor(0, line*FONT_HEIGHT);
tft.print(prn_buff);
line++;
}
if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
break;
}
if (!(cnt%cols)) {
prn_buff[cnt] = 0x0;
tft.setCursor(0, line*FONT_HEIGHT);
tft.print(prn_buff);
line++;
cnt = 0;
}
if ((pos+1)<(stack+STACK_SIZE)) pos++; else pos=stack;
cnt++;
}
if (pos == to) break;
}
}
疑似コンソール・ヘッダーファイル
#ifndef __CONSOLE_H
#define __CONSOLE_H
#include <Adafruit_GFX.h> // Core graphics library
//#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include "stdint.h"
#define TFT_CS A10
#define TFT_RST A11
#define TFT_DC A12
#define TFT_LIT A13
#define TFT_BLACK 0x0000 /* 0, 0, 0 */
#define TFT_NAVY 0x000F /* 0, 0, 128 */
#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */
#define TFT_MAROON 0x7800 /* 128, 0, 0 */
#define TFT_PURPLE 0x780F /* 128, 0, 128 */
#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */
#define TFT_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */
#define TFT_BLUE 0x001F /* 0, 0, 255 */
#define TFT_GREEN 0x07E0 /* 0, 255, 0 */
#define TFT_CYAN 0x07FF /* 0, 255, 255 */
#define TFT_RED 0xF800 /* 255, 0, 0 */
#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */
#define TFT_WHITE 0xFFFF /* 255, 255, 255 */
#define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */
#define TFT_YELLOW 0xB7E0 /* 180, 255, 0 */
#define TFT_PINK 0xFC9F
#define TFT_AQUAMARINE 0x6675 /* 102,205,170 */
#define TFT_WHEAT 0xF756 /* 245,222,179 */
#define STACK_SIZE 512
#define FONT_WIDTH 12
#define FONT_HEIGHT 16
class Console
{
private:
uint8_t cols, rows, lines;
uint16_t width, height;
uint8_t *stack;
uint8_t *from, *to;
char prn_buff[256];
public:
Console();
~Console();
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
void print(char *text, uint16_t color);
void backlight(uint8_t pin, uint8_t val);
};
#endif /*__CONSOLE_H*/
それでは、実際に動かしてみます。
●NodeMCU-32S / HiLetgo ESP-WROOM-32 ESP-32S WiFi + Bluetooth Dev Board
今回は激安で手に入れたNodeMCU-32Sを使いました。
使用するLCDの表示サイズに合わせて、メモリを確保できるマイコンボードを用意してください。

ESP32S-HiLetgo Dev Boad with Pinout Template
●2.0" 320x240 Color IPS TFT Display with microSD Card Breakoutd
表示に使用するフォントに横12ドット、縦16ドットを使用したので、このTFTを縦置きで使用した場合に、このTFTでは横20文字、縦20行になります。

ADA4311
| ST7789 | - | NodeMCU-32S | - | Raspberry Pi |
| VIN | - | #1 3.3V | | |
| GND | - | #38 GND | | |
| SCK | - | #30 SCK | | |
| MISO | - | #31 MISO | | |
| MOSI | - | #37 MOSI | | |
| CS | - | #26 (GPIO4) | | |
| RESET | - | #25 (GPIO0) | | |
| D/C | - | #24 (GPIO2) | | |
| BACKLIGHT | - | #23 (GPIO15) | | |
| | | USB | - | USB |

ソースコードのビルドには、PlatformIOを使用しています。
Arduino開発環境構築 PlatformIO
$ pio boards NodeMCU-32S
Platform: espressif32
==================================================================
ID MCU Frequency Flash RAM Name
------------ ------- --------- ----- ----- -----------------------
nodemcu-32s2 ESP32S2 240MHz 4MB 320KB Ai-Thinker NodeMCU-32S2
nodemcu-32s ESP32 240MHz 4MB 320KB NodeMCU-32S
プロジェクトの作成
$ mkdir -p ~/NodeMCU-32S/console
$ cd ~/NodeMCU-32S/console
$ pio init -b nodemcu-32s
$ pio run -t upload
●活用例

今回の疑似コンソールを作成するにあたり、テキストに色情報を付加した理由は、
2つのシリアルポートを使って、双方向通信の情報を識別して監視するのが目的でした。
●応用
リングバッファのサイズを大きくすると、表示履歴を遡るようにすることも可能です。
|
Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」
「一念発起して何かを始めても、いつも三日坊主で終わってしまう」
「色んなことを先延ばしにしたまま、時間だけが過ぎていく」
そこで本書では、そんな著者が独自に開発した、
まったく新しい目標達成メソッド「100日デザイン」について、
その知識と技術を、余すところなくご紹介します。
まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。
ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。
今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。
ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。
29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!
【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。
基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。
Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説!
500以上のサンプルを掲載。実行結果も確認。
全24章の包括的なチュートリアル。
ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS…
全WEB媒体で効果バツグン!
カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術
小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。
Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード
電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)
|