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()を使う必要がある。
コメント