IAR Embedded Workbench for AVR での文字列の使用

テクニカル・ノート 86174

アーキテクチャ:

AVR

コンポーネント:

compiler

更新日:

2018/02/07 9:08

ほじめに

このテクニカルノートは(異なるメモリ領域での)文字列の配置について説明します。

配置可能な場所とその特徴

AVR用IAR C/C++ Compiler を使用する場合、文字列を格納できる場所は、以下の3(4)箇所です:

  1. データ空間内の 外部ROM
  2. 内部 RAM
  3. フラッシュ
    これに加えて、少なくとも1つのハイブリッド空間も考えられます:
  4. 必要に応じてフラッシュからRAMへのコピー

フラッシュメモリに配置することの欠点(CODE空間)

フラッシュは文字列を配置する最も論理的な場所と思われますが、ハーバードアーキテクチャのAVではうまく動作しません。 デフォルトのポインタ (拡張キーワードを持たないポインタ) は、拡張キーワード無しで定義されたどのようなオブジェクトもポイントできなければなりません。文字列は、そのようなオブジェクトのよい例です。 (拡張キーワード無しで) char ポインタにフラッシュをポイントできるようにするには、RAMメモリ (およびスタック) がフラッシュメモリ空間になければならないということになります。AVRではRAMはデーター空間にのみ存在するので、これは不可能です。

デフォルトポインタはフラッシュとRAMの両方をポイントできますが、そのためには、実行時にポインタ先が実際にフラッシュ空間かデータ空間かを判断できるようにポインタにタグを付ける必要があります。これは、コードサイズと実行時オーバーヘッドの両方においてコストがかかります。

データ空間への配置

ISO / ANSI Cプログラムの場合、これは、データスペース内のメモリである外部ROMか内部RAMを選択しなければならないことを意味します。 多くのアプリケーションでは、外部ROMはありません。内部RAMは常に存在しますが、小さなリソースの可能性があるので、文字列を配置するのは難しいかもしれません。

考察

このような背景で、結局どのようにすれば良いのでしょうか?AVR で文字列を使用するのは不可能なのでしょうか? もちろん可能ですが少し注意を払い可能性を検討する必要があります。

ISO / ANSI C準拠のプログラムを使用したい場合、フラッシュは不可能です。文字列データのために外部ROMを用意するかRAMを使用する必要があります。RAMを使用する場合は、文字列がすべて格納できるように短く少なくする必要があります。

ISO / ANSI C準拠を緩和できる場合は、フラッシュchar配列変数を定義して文字列をフラッシュに配置できます。 通常のCライブラリ関数に渡される文字列の場合、文字列はデータ空間になければなりません。 フラッシュ内の文字列を使用するためには、フラッシュ文字列を受け付ける代替ライブラリ ルーチンを使用する必要があります。このような代替関数をpgmspace.hヘッダファイルにいくつか提供します。これらは、拡張子が_Pの共通のCライブラリ関数のいくつかのフラッシュ用の代替関数です。pgmspace.hヘッダファイル内には、_G拡張子を持つ__generic用の代替関数もあります。ユーザコードにおいては、関数間で文字列を渡すときには、常に__flashキーワードを使用できます。

どのように配置するか ...

...外部 ROM に文字列を配置するには

使用している AVR に外部バスがあり、コンパイラをコマンドラインから起動している場合、これがデフォルトです。IDE を使用する場合は、Project>Options>C/C++ Compiler の Code タブ中の"Place string literals and constants in initialized RAM"のチェックを外してください。これにより文字列と定数をNEAR_C、FAR_C、HUGE_Cのような固定値セグメントにすることができます。

...内部 RAM に文字列を配置するには

GUIおよび外部バスを持たないすべてのAVRのコマンドラインにおけるデフォルトです。コマンドラインからこれをイネーブルするには、コマンドラインオプション -y を使用します。

ちなみにこれは、書き込み可能な文字列となります。プログラム実行中に文字列の内容が書き換えられることもあるため少し都合が悪いかもしれません。また、コンパイラが同じテキストの文字列リテラルにメモリを共有を許可すると、問題になるかもしれません。しかしこれを行うための十分な理由がある場合、文字列をRAMに配置する必要があります。

...フラッシュ内に文字列を配置するには

ここでの考え方は、コード内の各文字列をオーバーライドして、フラッシュに格納することです。

__flash char str1[] = "abcdef";
__flash char str2[] = "ghi";
__flash char __flash * pVar[] = { str1, str2 };

2.28Aより前のバージョンでは文字列リテラルを自動的にフラッシュに入れることはできませんが、代わりにローカルの静的変数を使用することができます

#include <pgmspace.h>
void f (int i)
{
static __flash char sl[] = "%d cookies\n";
printf_P(sl, i);
}

これは、文字列リテラルがフラッシュ内で許可されている場合と比較して、これ以上のコードは発生しません。少し多くのタイプ入力が必要なだけです。

2.28A以降を使用している場合でも、上記のやり方を好むかもしれません。何をしているかがより明示的です

追加情報

フラッシュ内の文字列リテラル (2.28A 以降)

コマンドラインキーワード --string_literals_in_flash (2.28Aで導入された) を使用してください。これは、文字列リテラルをNEAR_F または FAR_Fのフラッシュセグメントに格納します。

これは、文字列リテラルオブジェクトの型が文字列がプログラム空間に存在することを反映して変化することを意味します。型が変更されると、以下の有効な ISO / ANSI C プログラムは動作しなくなります:

#include <stdio.h> 
void emit_time (int hour, int minute)
{
printf("%d:%d",
hour,
minute); // error -- type conflict
}

キャストを追加することでコンパイラは終了しますが、printf()はデータ空間の文字列へのポインタを取得すると考えているため、プログラム空間に文字列があると実行時には機能しません。:

#include <stdio.h> 
void emit_time (int hour, int minute)
{
printf((char*) "%d:%d",
hour,
minute); // fails at runtime
}

前と同じように、printf_P()を使って動作させる必要があります:

#include <pgmspace.h> 
void emit_time (int hour, int minute)
{
printf_P("%d:%d",
hour,
minute);
}

--string_literals_in_flash を使用すると、1 つの定義文字列の配列が作成されフラッシュに格納されます。以下が -v1 チップ (例えば 8515)で行う方法です:

__flash char __flash *messages[5]=
{
"Message 1",
"Message 2",
"Message 3",
"Message 4"
};

-v3(mega128のような)を使用している場合、文字列リテラルは__flashの代わりに__farflashに置かれるので、次のように書く必要があります:

__flash char __farflash *messages[5]=
{
"Message 1",
"Message 2",
"Message 3",
"Message 4"
};

フラッシュからRAMへの文字列のコピーの例

フラッシュに格納された文字列をフラッシュから直接使用する代わりに、必要に応じてフラッシュからRAMにコピーして使用することもできます。適切なサイズの RAM バッファが必要で、簡単なコピー関数を準備する必要があります。次のコード例では、このアイデアを示しています。:

__x_z char* string_access (char** handle, char __flash * p)
{
char* result = *handle;
char* d = result;
while (*d++ = *p++)
;
*handle = d;
return result;
}

これを使用するには、適切なサイズのバッファをスタックに確保し、各string_access() 呼出しにhandleを渡す必要があります:

void use_it (__flash char __flash * array[4])
{
char buffer[32];
char* bufptr = buffer;
char** handle = &bufptr;
static __flash char formatter[] = "%s:%s-%s:%s";

printf(string_access(handle, formatter),
string_access(handle, array[0]),
string_access(handle, array[1]),
string_access(handle, array[2]),
string_access(handle, array[3]));
}

これは完全にリエントラントで、バッファは関数を抜けるときに解放されます。


全ての製品名は、それぞれの所有者の商標または登録商標です

申し訳ございませんが、弊社サイトではInternet Explorerをサポートしていません。サイトをより快適にご利用いただくために、Chrome、Edge、Firefoxなどの最新ブラウザをお使いいただきますようお願いいたします。