フォト
無料ブログはココログ

MyList

« MAKE: vol12 | トップページ | 理科の実験 »

2013年2月 8日 (金)

avr-gcc constをプログラム・メモリに配置

 Charlieplexingを拡張して 8x8マトリックスLEDを9本のI/Oで駆動するプログラムをAVR AT90S2313で作っている。
 シリアルで通信できるようにして printfデバッグしているのだが(printf()は使えないけど)、デバッグメッセージを増やすとデータが受信できなくなって、メッセージが表示されなくなってしまいには暴走するようになる。

 スタックがデータ領域を喰っているのだろうと見当をつけmapファイルを覗いてみると、初期化済変数領域(data)がやたら多い。

調べてみて分かったこと

  const char str[] = "abc";
   や
  puts("ABC");

と書くと、"abc"や"ABC"は自動的にプログラム・メモリ領域に書かれるのかと思っていたら、.dataセクションに配置されるらしく、プログラム・メモリ領域に配置するには変数の後に__atrrbute(__progmem__)が必要らしい。

 avr/pgmspace.h にプログラム・メモリ領域を使うためのマクロがある。

#define PROGMEM  __atrrbute__((__progmem__))
#define PSTR(s)     ((const PROGMEM char *)(s))
#define pgm_read_byte(address_short)         pgm_reaa_byte_near(address_short)
#define pgm_read_byte_near(address_hosrt)  __LPM((uint16_t)(address_short))

typedef char PROGMEM  prog_char;
#define PGM_P const prog_char *

など

 プログラム・メモリ領域の変数は

const char str[] PROGMEM = "abc";

で宣言し、文字列リテラルはPSTR()マクロを使って

puts(PSTR("ABC"));

のように使う。
プログラム・メモリに取ったデータはpgm_read_byte()で読むらしい。

PGM_P p = str;
char c = pgm_read_byte(str);

###

↓のようなテストプログラムtest.cで確認してみた

/*
* test.c
* Feb. 2013/ Yoshi
*/
#include <avr/io.h>
const char str[] = "0123456789ABCDEF";
int main(void)
{
const char *p;
for (p=str;*p;p++)
{
putchar(*p);
}
for (;;);
}

 

↓test.map 初期化済変数 dataセクションと初期化していない変数 bssセクション
  test.o内の変数 strは dataセクションに取られている。

.data           0x00800060       0x12 load address 0x000000d2
                0x00800060                PROVIDE (__data_start, .)
*(.data)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/crts2313.o
.data          0x00800060       0x11 test.o
                0x00800060                str
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_exit.o)
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_copy_data.o)
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(putchar.o)
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(fputc.o)
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(iob.o)
.data          0x00800071        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_clear_bss.o)
*(.data*)
*(.rodata)
*(.rodata*)
*(.gnu.linkonce.d*)
                0x00800072                . = ALIGN (0x2)
*fill*         0x00800071        0x1 00
                0x00800072                _edata = .
                0x00800072                PROVIDE (__data_end, .)

 

.bss            0x00800072        0x6
                0x00800072                PROVIDE (__bss_start, .)
*(.bss)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/crts2313.o
.bss           0x00800072        0x0 test.o
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_exit.o)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_copy_data.o)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(putchar.o)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(fputc.o)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(iob.o)
.bss           0x00800072        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_clear_bss.o)
*(.bss*)

↓test.lst test.cのアセンブリ出力は

                 .global str
                 .data
                 str:
   0000 3031 3233 .string "0123456789ABCDEF"
        3435 3637
        3839 4142
        4344 4546
        00

やっぱり変数 str はSRAM上の dataセクションに配置されている。

↓avr/pgmspace.hをインクルードして、PRGMEM修飾し、プログラム・メモリへのアクセスは pgm_read_byte()を使う。

/*
* test1.c
* Feb. 2013/ Yoshi
*/
#include <avr/io.h>
#include <avr/pgmspace.h> const char str[] PROGMEM = "0123456789ABCDEF";
int main(void)
{
PGM_P p;
for (p=str; pgm_read_byte(p); p++)
{
putchar(pgm_read_byte(p));
}
for (;;);
}

 

↓test1.map  test1.oの.data領域が0byteになった。

.data           0x00800060        0x0 load address 0x000000d2
                0x00800060                PROVIDE (__data_start, .)
*(.data)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/crts2313.o
.data          0x00800060        0x0 test1.o
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_exit.o)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(putchar.o)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(fputc.o)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(iob.o)
.data          0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_clear_bss.o)
*(.data*)
*(.rodata)
*(.rodata*)
*(.gnu.linkonce.d*)
                0x00800060                . = ALIGN (0x2)
                0x00800060                _edata = .
                0x00800060                PROVIDE (__data_end, .)

 

.bss            0x00800060        0x6
                0x00800060                PROVIDE (__bss_start, .)
*(.bss)
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/crts2313.o
.bss           0x00800060        0x0 test1.o
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_exit.o)
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(putchar.o)
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(fputc.o)
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib\libc.a(iob.o)
.bss           0x00800060        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3\libgcc.a(_clear_bss.o)
*(.bss*)

↓test1.lst  .progmem.dataセクションに配置されるようだ

                 .global str
                 .section .progmem.data,"a",@progbits
                 str:    0000 3031 3233 .string "0123456789ABCDEF"         3435 3637
        3839 4142
        4344 4546
        00
          (
           )
                .L2:
  0010 EC2F       mov r30,r28
  0012 FD2F       mov r31,r29
  0014 C895       lpm
  0016 802D       mov r24, r0

ちょいハマり

const char str[] PROGMEM = "0123456789ABCDEF";
    (
     )
PGM_P p;
for (p=str; *p; p++)
{
putchar(pgm_read_byte(p));
{

ついつい↑のように書いてしまいがちだけれど、*p ではなく pgm_read_byte()を使う必要がある。

 

« MAKE: vol12 | トップページ | 理科の実験 »

日記・コラム・つぶやき」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: avr-gcc constをプログラム・メモリに配置:

« MAKE: vol12 | トップページ | 理科の実験 »