0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ST7789 を使う 続 STM32CubeIDE編

Last updated at Posted at 2024-12-08

前記事の続きです。

せっかくの Nucleo なので、STM32CubeIDEで使いたいときもあります。
そのときにDisplayを使えるように、自分なりのテンプレートとして準備します。

動作状態を手軽に表示したいので、テキストが出れば十分ですが、
M5Stackなどで便利に使っていた Label という概念を導入します。

main.cpp(例)
	HAL_Delay(1000);
	label0.Draw((char *)"Hello L452RE");
	HAL_Delay(1000);
	label0.Draw((char *)"Update text");

この場合、2回目 Update text を書くとき、前のテキストを消しています。そうすることで重ねて表示されるのを防いでいます。簡単なようですが、このような動作をするためには仕掛けが必要です。ここでは自作の class を準備しました。

C(++)言語はかなり苦手意識がありますが、Google Gemini に相談しまくってなんとか形になりました。

使うディスプレイは前回と同じ、CSがない 1.3 240x240 です。
image.png

NUCLEO L452RE-Pを使っています。

image.png

Display SCL SDA RES DC BLK
Nucleo PB13(SCK) PB15(MOSI) PB6 PA8 3V3

ON/OFF制御しないのでBacklightは電源直結。Nucleoの内側のピンを使っていますが、L452RE-Pの場合、SCK/MOSIはSPI2に割り当てられています。

Thanks to

https://github.com/Floyd-Fish/ST7789-STM32
https://github.com/Korzhak/STM32CppProjectExample
https://qiita.com/tlab/items/f3e12a4559e883a5fb78
https://www.tohoho-web.com/ex/c-lang.html#pointer

.ioc設定

NUCLEOデフォルト設定に、PB6/PA8/PB15/PB13を追加します。

image.png

少しでも処理が早いほうが、と思い、PLLで80MHzに設定。

image.png

SPI2の設定はまず以下 8bit MSB First, Polarity=High, 1Edge
こちらの環境では、Prescaler = 2, 40Mbit/s で動作しました。

image.png

Libraryが必要としているようで、SPI2にDMAを追加します。
このことに気づくのに時間がかかりました。TXだけでOKです。

image.png

プロジェクトは c++ タイプにして、コードを発生させます。

ファイルを集める

image.png

以下4つは、上記 https://github.com/Floyd-Fish/ST7789-STM32
からもらってきて、Inc および Src フォルダーに配置します。コピーしたら右クリックして Refresh を選ぶと、ツリーにも反映されます。

Inc\fonts.h
Inc\st7789.h
Src\fonts.c
Src\st7789.c

c++ で Class を扱うので、どうせなら、と Adruino風にします。https://github.com/Korzhak/STM32CppProjectExampleを参考に、

Inc\AltMain.h
Inc\AltMain.cpp

を作成します。

コード編集

ST7789ピン設定

SPI2を指定。DMA使用を確認。CSなしに設定。
RST/DCピンを正しく記載。

st7789.h(抜粋)
/* choose a Hardware SPI port to use. */
//#define ST7789_SPI_PORT hspi1
#define ST7789_SPI_PORT hspi2
extern SPI_HandleTypeDef ST7789_SPI_PORT;

/* choose whether use DMA or not */
#define USE_DMA

/* If u need CS control, comment below*/
#define CFG_NO_CS

/* Pin connection*/
#define ST7789_RST_PORT GPIOB
#define ST7789_RST_PIN  GPIO_PIN_6
#define ST7789_DC_PORT  GPIOA
#define ST7789_DC_PIN   GPIO_PIN_8

#ifndef CFG_NO_CS
#define ST7789_CS_PORT  ST7789_CS_GPIO_Port
#define ST7789_CS_PIN   ST7789_CS_Pin
#endif

テキストラベルのクラスを定義

Label.h
#ifndef LABEL_H
#define LABEL_H

#include <altMain.h>
#include "ST7789.h"
#include "fonts.h"

class Label {
public:
    void Create(uint16_t x, uint16_t y, FontDef* font,  uint16_t fg, uint16_t bg);
    void Draw(const char* s);

private:
    uint16_t x0, y0;
    FontDef* font0;
    uint16_t fg0, bg0; // foreground color, background color
    char prev_s[20];
};

#endif // LABEL_H
Label.cpp
#include "Label.h"
#include <string.h>
#include "ST7789.h"
#include "fonts.h"

void Label::Create(uint16_t x, uint16_t y, FontDef* font, uint16_t fg, uint16_t bg) {
    x0 = x;
    y0 = y;
    font0 = font;
    fg0 = fg;
    bg0 = bg;
    strcpy(prev_s, "");
}

void Label::Draw(const char* s) {
    ST7789_WriteString(x0, y0, prev_s, *font0, bg0, bg0);
    ST7789_WriteString(x0, y0, s, *font0, fg0, bg0);
    strcpy(prev_s, s);
}

ここは個人的にかなり苦労しました。

文字を書くだけなら、たとえば ST7789_WriteString(0, 0, "Hello!", Font_16x26, WHITE, BLACK); と書いてしまえば終わりなのですが、

クラス内で扱うので、いろいろ悩まされました。Google Geminiさんに教えてもらってやっとたどり着きました。

Create() するときにフォントを指定し、それを描画のときに使うので、privateな変数でフォントを覚えておく必要があります。フォントは font.h で構造体で定義されています。

試行錯誤した結果、構造体のアドレスをポインター変数で覚えておく、という方法となりました。 * なのか & なのか、まだまだ私は修業が必要なようですね...

setup() および loop() 作成

こちらに助けていただきました。

altMain.h
#ifndef INC_ALTMAIN_HPP_
#define INC_ALTMAIN_HPP_


#ifdef __cplusplus
extern "C"
{
#endif
// START C-BLOCK IN C++ CODE

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "st7789.h"
#include "fonts.h"

/* Functions -----------------------------------------------------------------*/
void setup();
void loop();
// YOUR FUNCTIONS


// END C-BLOCK IN C++ CODE
#ifdef __cplusplus
}
#endif


#endif /* INC_ALTMAIN_HPP_ */

ここで、自分で定義した Labelクラスを使って、簡潔にテキストを表示、更新します。

altMain.cpp
#include <altMain.h>
#include "Label.h"

Label label0;
Label label1, label2, label3, label4;

void setup() {

	ST7789_Init();
	ST7789_Fill_Color(BLACK);

	label0.Create(0, 0, & Font_16x26, WHITE, BLACK);

	label1.Create(0, 30, & Font_11x18, YELLOW, BLACK);
	label2.Create(0, 50, & Font_11x18, CYAN, BLACK);
	label3.Create(0, 70, & Font_11x18, MAGENTA, BLACK);
	label4.Create(0, 90, & Font_11x18, GREEN, BLACK);

	label1.Draw((char *)"  CH1 YELLOW");
	label2.Draw((char *)"  CH2 CYAN");
	label3.Draw((char *)"  CH3 MAGENTA");
	label4.Draw((char *)"  CH4 GREEN");
}

void loop() {
	// YOUR LOOP CODE
	HAL_Delay(1000);
	label0.Draw((char *)"Hello L452RE");
	HAL_Delay(1000);
	label0.Draw((char *)"Update text");
}

main.c

こちらは #include "altMain.h"setup() および loop() を追加して放置です。.iocを編集して再generateしても大丈夫。

main.c(抜粋)

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "altMain.h"
/* USER CODE END Includes */

// (中略)

  /* USER CODE BEGIN 2 */
  setup();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  loop();
  }
  /* USER CODE END 3 */
}

おわりに

長くなりました。

Nucleoで各種テストをするとき、とくにそれが数時間とか、長時間になるときは、
Nucleoに接続した表示機があれば、状況を簡単に把握することができます。

ST7789の表示機であれば、昔ながらのキャラクターディスプレイや、よくある SSD1306 OLEDユニットよりも多くの情報が一度に確認できますね。

手軽に使うために、STM32CubeIDEで使う方法を模索しました。また、テキストで情報を簡単に表示させるため、C++のクラスの扱いを調べ、自前の簡単なライブラリーみたいなものを作ってみました。

作成するまでは苦労しましたが、やはり一度 Class をしっかり作れば、移植も簡単で、Class を使った検証用ソフトウエアも作成が早くできそうです。とても勉強になりました。

追記

main.c(抜粋)
Label label0;
label0.Create(0, 0, & Font_16x26, WHITE, BLACK);
label0.Draw((char *)"Hello L452RE");

2行目で、表示位置、フォント、色を指定します。Font_16x26 は構造体になっていますので、アドレスで渡します。

上記参考URLで、類似事例の解説がありました。ありがたいです。

main() {
    int a1 = 123;        // int変数 a1 を確保し、その値として 123 を格納
    int *a2 = &a1;       // ポインタ変数 a2 を確保し、その値として a1 のポインタを代入
    printf("%d\n", *a2); // a2 ポインタの中身(ポインタが示すメモリの中身)は 123
    *a2 = 321;           // a2 ポインタの中身(ポインタが示すメモリの中身)を 321 に変更
    printf("%d\n", a1);  // a1 の値が 321 に変わる
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?