カスタマーサポートセンター

FAQ~よくある質問~ | FAQマスタ詳細表示

FAQ詳細情報

ID 10810925
FAQカテゴリ(大) コンパイラ
最終更新日 2016-01-02

質問内容

TN86174: AVR用 IAR Embedded Workbench での文字列の使用

回答内容

ターゲット: AVR
コンポーネント: コンパイラ
公開: 2003/08/01 11:38

 

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

 

配置可能な場所とその特徴
AVR用 IAR Embedded Workbench では、文字列は 3 (4) 種類の場所に格納することができます。

1. 外部 ROMのデータ空間
2. 内部 RAM
3. フラッシュ
加えて、考慮すべき少なくとも 1 つのハイブリッドもあります。

4. オンデマンドで文字列をフラッシュからRAMにコピーします。

 

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

デフォルトポインタはフラッシュとRAMの両方を指し示すことができます。但し、このためには、ランタイムでポイント先がフラッシュ空間かRAM空間かの判定をする必要があります。これは、コードサイズと実行時オーバーヘッドの両方のコストがかかります。

 

データ領域に配置
ISO/ANSI C のプログラムでは 1. と 2. がデータ空間です。多くのアプリケーションでは外部ROMはありません。内部 RAMは常に存在しますが、小さいため文字列を置くのは難しいです。

 

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

もし、ISO/ANSI C 準拠プログラムが必要でフラッシュメモリがない場合、文字列データのために外部ROMを準備するか、RAMを使用してください。RAM を使用する場合、文字列を短く保ち、少ない数にして全部格納できるようにしてください。

標準(relax) ISO/ANSI C 準拠の場合、フラッシュ char 配列変数を定義することで文字列を格納することができます。通常のCライブラリ関数に文字列を渡すために、データ空間に格納します。フラッシュの文字列を使用するには、フラッシュ文字列を受け付ける代替ライブラリ ルーチンを使用する必要があります。pgmspace.h ファイルにいくつかの代替機能が提供されています。これらは、いくつかの一般的な C ライブラリの関数のためのフラッシュ用の代替関数で、名前の後に _P 拡張子が付けられています。__generic 用の _G 拡張子をつけた代替関数も pgmspace.h に準備されています。ユーザコードの関数間で文字列を渡すときに __flash  キーワードを常に使用できます。

 

...外部 ROM に文字列を配置するには、
使用している AVR に外部バスがあり、コンパイラをコマンドラインから起動している場合、これがデフォルトです。IDE を使用する場合は、チェックボックス"Place string literals and constants in initialized RAM"を外すのを忘れないようにします。このチェックボックスは、

Project>Options>C/C++ Compiler

の、Codeタブの中にあります。最後に文字列と定数を作成し、 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 -- 型の衝突
}

キャストを追加することでコンパイラを黙らせることはできますが、プログラムは、文字列がプログラム空間にあるため動作しません printf() は、ポインタはデータ空間にある文字列を指していると思っているからです。

#include <stdio.h>
void emit_time (int hour, int minute)
{
 printf((char*) "%d:%d",
        hour,
        minute);  // ランタイムで失敗する
}

前に、これを動かすために 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"
};

使用する場合 (mega128) のように-v3 を書かなければならないので、文字列リテラルは __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() 呼出しにハンドルを渡します。

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]));
}

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

 

 

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

参考資料URL