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?

More than 3 years have passed since last update.

ArduinoのFマクロやPSTRマクロについてのメモ

Last updated at Posted at 2022-03-01

雑まとめ

  • PROGMEM
    • 各アーキテクチャがもつ「定数データをFlashROMから直接読み込む機能」をArduinoの世界で共通に使えるよう抽象化したもの
  • PSTRマクロ
    • PROGMEMを使ってASCIIZ文字列定数を書く記述をArduinoの世界で共通に使えるよう抽象化したもの。定義された値の型はPGM_Pというマクロで定義されている型になるが、AVRとESP8266とかだとconst char * になる。
  • Fマクロ
    • PSTRマクロの吐き出す結果がマクロPGM_Pで定義されている型であることは前述したが、これはコンパイラの知るところではないので、PSTRマクロの結果をconst __FlashStringHelper *型として返すことでコンパイラが認識できるようにしたもの。

はじめに

Arduinoいじっていると、FマクロとかPSTRマクロがよく出てくるんですよね。Qiitaにも記事があって、ArduinoのSRAMを節約できるF()マクロと **_P 関数 && PSTR()マクロの使い方とか。他にはGuide to PROGMEM on ESP8266 and Arduino IDEとか。

公式リファレンスPROGMEM - Arduino Referenceは説明が薄い…。

定義を見に行く

PROGMEMとPSTRマクロ

この2つはCPUアーキテクチャごとの差異を吸収するレイヤなので一緒に書く。

見るとわかりますが、それぞれ微妙にマクロの定義が違います。

ESP8266

Guide to PROGMEM on ESP8266 and Arduino IDEを見ると、こんな感じになってるらしい。

 #define PROGMEM   ICACHE_RODATA_ATTR
 #define ICACHE_RODATA_ATTR  __attribute__((section(".irom.text")))
 #define PGM_P       const char *
 #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))

AVR Atmel's SAM3X

// https://github.com/arduino/ArduinoCore-sam/blob/790ff2c852bf159787a9966bddee4d9f55352d15/cores/arduino/avr/pgmspace.h#L32-L34

 #define PROGMEM
 #define PGM_P  const char *
 #define PSTR(str) (str)

AVR libc

// excerpt from http://svn.savannah.gnu.org/viewvc/avr-libc/trunk/avr-libc/include/avr/pgmspace.h?view=annotate

 #define __ATTR_PROGMEM__ __attribute__((__progmem__))
 #define PROGMEM __ATTR_PROGMEM__
 #define PGM_P const char *
 # define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))

Fマクロ

PSTRの結果をconst __FlashStringHelper * にキャストしてる。

// https://github.com/arduino/ArduinoCore-API/blob/e03b65374c614130aa1b11597e07b3b5089a726d/api/String.h#L45

 #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

定義された値の使い方

PROGMEM / PSTRマクロ

アクセス方法がCPUアーキテクチャ依存なので、それをwrapしたライブラリ経由で使います。具体的にはavr/pgmspace.hなどで定義されてる関数を使ってFlashROMからメインメモリに引っ張ってくることになる。
たとえばpgm_read_byteで読んだり、strcpy_Pでコピったり、snprintf_Pで使ったり。

Fマクロ

文字列としてconst __FlashStringHelper *を受ける関数に渡す。

受け取った関数の中では何をしているか

受け取ってすぐPGM_Pへキャストして、strlen_PとかFlashROMのデータを処理する関数に渡してる。

// https://github.com/arduino/ArduinoCore-API/blob/e03b65374c614130aa1b11597e07b3b5089a726d/api/String.cpp#L260

String & String::operator = (const __FlashStringHelper *pstr)
{
	if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
	else invalidate();

	return *this;
}

どう使っていくか

  • 関数に定数文字列を渡すときconst __FlashStringHelper *を受け取る引数があるならFマクロで包んで渡せば良さそう。
  • 自作関数内で完結するときはPSTRマクロで包んで*_P関数に渡せば良さそう。
  • ライブラリとかで外から受け取るときはconst __FlashStringHelper *で受け取る関数をつくって、PSTRはライブラリに閉じ込めると良さそう。
    • 定数として外に置くときFマクロやPSTRマクロは使えずPROGMEMをそのまま置くしかない1
    • ESP8266のArduinoライブラリにはFPSTRマクロがあって、const __FlashStringHelper *にキャストしてくれる。
  1. 「statement-expressions are not allowed outside functions nor in template-argument lists」ってコンパイラに怒られる。 F() gives compilation errors - Programming Questions - Arduino Forumを参照。

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?