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

« 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 | トップページ | 理科の実験 »

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

コメント

コメントを書く

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

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

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/577514/56700003

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

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