IAR C/C++ 컴파일러 for AVR에서 문자열 사용법
기술노트 86174
아키텍처:
AVR
컴포넌트:
compiler
업데이트:
2021-07-15 오후 6:57
소개
본 기술 노트는 문자열의 위치(다른 메모리 영역)에 대해 설명합니다.
가능한 위치 및 측면
IAR C/C++ Compiler for AVR을 사용할 때 문자열이 저장되는 세, 네 가지의 가능한 위치가 있습니다.
- 외부 ROM의 데이터 영역
- 내부 RAM
- 플래시.
이 외에도 하나 이상의 (고려할 만한)복합적인 경우가 있습니다. - 필요 시 문자열을 플래시에서 RAM으로 복사
플래시 메모리에 배치하는 것의 문제점 (CODE 영역)
플래시는 문자열의 가장 논리적인 위치로 들릴 수 있지만, AVR의 하버드 아키텍처(Harvard Architecture)에서는 잘 작동하지 않습니다. 기본 포인터(확장 키워드가 없는 포인터)는 확장 키워드 없이 정의된 개체를 가리킬 수 있어야 합니다. 문자열은 이러한 객체의 좋은 예입니다. 만일 우리가 (확장 키워드가 없는) 문자 포인터를 플래시를 가리키도록 허용한다면, 그것은 또한 RAM 메모리(및 스택)가 플래시 메모리 공간에 있어야 한다는 것을 의미할 것이다. 이는 물론 RAM이 데이터 공간에만 존재하기 때문에 AVR에서는 불가능하다.
기본 포인터는 플래시와 RAM을 모두 가리키도록 만들 수 있지만, 포인터가 실제로 플래시를 가리키는지 또는 데이터 공간을 가리키는지 런타임에 확인할 수 있도록 포인터에 태그를 지정해야 합니다. 이것은 코드 크기와 런타임 오버헤드에서 모두 많은 비용이 들 것이다.
데이터 영역에 배치
ISO/ANSIC 프로그램의 경우, 데이터 공간의 메모리인 대체 1 또는 대체 2가 남아 있음을 의미합니다. 많은 응용 프로그램의 경우 외부 ROM이 없습니다. 내부 RAM은 항상 존재하지만 부족한 리소스가 될 수 있으므로 문자열을 넣기가 어려울 수 있습니다.
고려사항
이런 배경에서 이제 우리는 어디로 가야 할까요? AVR에서는 문자열을 전혀 사용할 수 없습니까? 물론, 그것은 단지 우리가 조금 조심하고 가능성을 고려할 것을 요구한다.
ISO/ANSIC 호환 프로그램을 사용하려면 플래시를 사용할 수 없으며 외부 ROM을 제공하거나 문자열에 RAM을 모두 사용해야 합니다. RAM을 사용하는 경우 문자열을 짧게 유지하고 몇 개만 사용해야 모두 맞출 수 있습니다.
만약 ISO/ANSI C 준수를 완화할 수 있다면, 문자열을 플래시 char 배열 변수로 선언함으로써 플래시에 집어넣을 수 있습니다. 일반적인 C 라이브러리 함수에 전달되는 문자열은, 데이터 영역에 존재해야 합니다. 플래시화된 문자열을 사용하기 위해, 플래시 문자열을 기대하기 위해 대체 라이브러리 루틴을 사용해야 합니다. 우리는 pgmspace.h 헤더 파일에 몇가지 대체 함수들을 제공하고 있습니다. 확장자가 _P인 일부 공통 C 라이브러리 함수에 대한 플래시 대안입니다. pgmspace.h 헤더 파일에는 _G 확장자를 가진 __generic counterparts도 있습니다. 사용자 자신의 코드에 대해서는 함수 간에 문자열을 전달할 때 항상 __flash 키워드를 사용할 수 있습니다.
어떻게 문자열을...
...외부 ROM에 넣는가
당신의 파생 모델이 외부 버스를 가지거나 커맨드 라인으로 컴파일을 하는 경우 기본값입니다. 만약 IDE를 사용한다면, Code 탭의 Project>Options>C/C++ Compiler 하위에서 "Place string literals and constants in initialized RAM"이라는 체크박스를 체크해제합니다. 목적은 문자열과 상수를, NEAR_C, FAR_C, HUGE_C 처럼, 상수 세그먼트로 만드는 것입니다.
...내부 RAM에 넣는가
이는 GUI 및 명령줄에 외부 버스가 없는 모든 파생 모델의 기본값입니다. 명령줄에서 수동으로 활성화하려면 명령줄 옵션 -y를 사용하십시오.
부연으로, 이것은 또한 문자열을 쓸 수 있게 만들 것입니다. 이것은 약간의 맛없는 것으로 여겨질 수 있습니다. 왜냐하면 이것은 런타임에 당신의 프로그램을 수정하기 때문입니다. 또한 컴파일러가 메모리를 공유하기 위해 동일한 텍스트를 가진 문자열 리터럴을 허용한다면 문제가 될 것이다. 가끔 당신은 이것을 하는 타당한 이유를 가질 수 있지만, 그렇다면 당신은 RAM에 당신의 string이 필요합니다.
...플래시에 넣는가
여기서 아이디어는 코드의 각 문자열을 재정의하여 플래시가 되도록 하는 것입니다.
__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 혹은 상위 버전)
2.28A에서 소개한, --string_literals_in_flash 명령줄 키워드를 사용하세요. 문자열 리터럴을 플래시 세그먼트 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를 사용하면 단일 정의에 문자열 배열을 생성하고 모든 것을 플래시에 저장할 수 있습니다. 다음은 -v1 칩(예: 8515)에서 수행하는 방법입니다:
__flash char __flash *messages[5]=
{
"Message 1",
"Message 2",
"Message 3",
"Message 4"
};
-v3(mega128 처럼)를 사용하는 경우 문자열 리터럴이 __farflash 대신 __farflash에 배치되므로 다음을 기록해야 합니다:
__flash char __farflash *messages[5]=
{
"Message 1",
"Message 2",
"Message 3",
"Message 4"
};
flash에서 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]));
}
이것은 완전히 다시 입력되며, 기능을 종료할 때 버퍼가 할당 해제됩니다.
모든 제품 이름은 해당 소유자의 상표 또는 등록 상표입니다.