주요 내용으로 건너뛰기

메모리 매핑 모드가 아닌 플래시를 사용하여 이미지 저장

이 섹션에서는 메모리 매핑 모드가 아닌 플래시에 저장할 수 있는 바이너리 파일에 모든 이미지를 연결하는 방법과 런타임에서 비트맵 캐시와 함께 해당 파일을 사용하는 방법을 알아봅니다. TouchGFX는 메모리 매핑 모드가 아닌 플래시에 저장된 비트맵에 대해 그리기 작업을 수행할 수 없지만, RAM에 비트맵을 캐싱하면 애플리케이션에서 비트맵을 사용 가능하게 만들 수 있습니다.

비트맵 캐시에 대한 일반적인 설명은 비트맵 캐싱을 참조하십시오.

이 문서에서는 비트맵 캐시를 설정했고 메모리 매핑 모드가 아닌 플래시에 비트맵을 저장한다고 가정합니다. USB 스토리지, NAND 플래시 등이 여기에 해당합니다.

이미지를 특정 주소에 연결하고, 이미지를 파일로 복사하며, TouchGFX가 파일을 캐시로 복사하도록 돕는 것이 목적입니다.

플래시에서 캐시로 비트맵 데이터 복사

비트맵을 캐싱할 때 TouchGFX는 원래 위치에서 비트맵 캐시로 픽셀을 복사합니다.

이러한 복사 작업은 HAL 클래스에서 이 메서드를 호출하여 수행됩니다.

HAL.hpp
bool HAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);

비트맵이 주소 지정이 가능한 일반 플래시(예: 내부 플래시 또는 메모리 매핑 모드의 외부 플래시)에 저장되어 있는 경우에는 TouchGFX 라이브러리에서 제공되는 일반적인 blockCopy 함수로도 충분하기 때문에 다른 조치를 취할 필요가 없습니다.

반면에 비트맵이 파일 시스템과 같이 주소 지정이 불가능한 스토리지에 저장되는 경우에는 일반적인 구현으로는 불충분하므로 특정 플래시 스토리지에서 읽기가 가능하도록 업데이트된 버전을 제공해야 합니다.

하지만 우선은 비트맵이 고정 주소에 링크되어 있는지 확인해야 합니다.

BitmapDatabase 테이블

TouchGFX의 모든 비트맵은 generated/images/src 폴더의 .cpp 파일에 생성됩니다. 여기서 비트맵은 바이트 배열들의 형태로 표현됩니다.

이 배열들은 C++ 컴파일러에 의해 다른 소스 코드 파일로 컴파일된 다음, 애플리케이션에 링크됩니다.

다음은 회전하는 로고를 보여주는 버튼과 텍스처 매퍼가 포함된 간단한 애플리케이션의 스크린샷입니다.

버튼과 텍스처 매퍼

이 애플리케이션에서는 Button_Pressed, Button_Released, Logo의 세 가지 이미지를 사용하고 있습니다.

이러한 세 개의 비트맵은 .cpp 파일로 변환되고 애플리케이션의 일부로서 링크됩니다. 이미지는 bitmap_database라는 테이블에서 참조됩니다. 이 테이블은 BitmapDatabase.cpp 파일에 저장되어 있습니다. 다음은 위의 예제에서 가져온 테이블입니다(일부 세부 정보는 제거).

BitmapDatabase.cpp
extern const unsigned char _blue_buttons_round_edge_small[];
extern const unsigned char _blue_buttons_round_edge_small_pressed[];
extern const unsigned char _blue_logo_touchgfx_logo[];

const touchgfx::Bitmap::BitmapData bitmap_database[] =
{
{ _blue_buttons_round_edge_small, ... },
{ _blue_buttons_round_edge_small_pressed, ... },
{ _blue_logo_touchgfx_logo, ... }
};

먼저 선언된 배열들은 개별 비트맵의 픽셀을 포함하고 있습니다. 이 배열들은 다른 .cpp 파일에 정의되어 있습니다. bitmap_database 배열에는 이 배열들의 주소가 보관되어 있습니다. TouchGFX는 이 배열을 사용하여 비트맵의 픽셀의 주소를 찾습니다.

프로그래머가 비트맵 캐싱을 요청하면 TouchGFX는 플래시(bitmap_database 배열)에서 비트맵의 주소를 찾아서 여기에서 데이터를 복사합니다.

링커 스크립트 수정

링커는 비트맵의 주소를 선택합니다. 이는 비트맵이 저장된 섹션을 토대로 합니다. TouchGFX의 모든 비트맵은 기본적으로 ExtFlashSection에 저장됩니다.

표준 링커 스크립트(여기서는 GCC용)는 다른 읽기 전용 데이터와 함께 이 섹션을 플래시에 저장합니다.

이 예제에서는 주소 0x24000000의 ExtFlashSection에 이미지 데이터를 저장해보겠습니다. 사용되고 있지 않은(코드 또는 데이터 주소 공간에 포함되지 않은) 주소를 선택할 수 있습니다.

먼저, 일반적인 내부 FLASH 및 RAM 영역에 더해 새 메모리 영역(주소 0x24000000의 USB 플래시)을 정의합니다.

STM32F746.ld
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
USB(r) : ORIGIN = 0x24000000, LENGTH = 1M
}

그런 다음, USB 영역에 ExtFlashSection을 저장하도록 링커에 지시합니다.

STM32F746.ld
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
} >USB

링크 이후에 맵 파일(application.map)을 검사하여 비트맵의 주소를 확인할 수 있습니다. 해당 부분은 다음과 같습니다.

application.map
ExtFlashSection
0x24000000 0x23ec0
*(ExtFlashSection ExtFlashSection.*)
ExtFlashSection
0x24000000 0x10000 TouchGFX/build/.../Blue_Logo_touchgfx_logo.o
0x24000000 _blue_logo_touchgfx_logo
ExtFlashSection
0x24010000 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small.o
0x24010000 _blue_buttons_round_edge_small
ExtFlashSection
0x24019f60 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small_pressed.o
0x24019f60 _blue_buttons_round_edge_small_pressed

여기를 보면 이미지의 전체 크기가 0x23ec0 = 147.136 바이트임을 알 수 있습니다. 비트맵을 보유하고 있는 세 개의 배열이 주소 0x24000000부터 순차적으로 위치해 있습니다.

비트맵 데이터를 SD 카드로 옮긴다고 가정해 보겠습니다. 간단한 objcopy 명령을 사용하여 .elf 파일에서 비트맵에 대한 이진 데이터를 추출할 수 있습니다.

$ arm-none-eabi-objcopy.exe --dump-section ExtFlashSection=images.bin TouchGFX/build/bin/target.elf
$ ls -l images.bin
-rw-r--r-- 1 christef Administrators 147136 Feb 20 11:56 images.bin

이렇게 하면 이미지 바이트 배열만 포함하는 파일(images.bin)이 생성됩니다. 이 파일은 SD 카드인 USB 플래시에 복사하거나, 플래시 칩에 프로그래밍할 수도 있습니다.

이제는 TouchGFX가 주소가 0x24000000보다 위인 데이터를 요청할 때 SD 카드의 images.bin 파일에서 데이터를 가져올 수 있습니다.

BlockCopy 함수 수정

비트맵을 RAM에 캐싱하면 TouchGFX가 HAL::BlockCopy를 호출하여 데이터를 가져온다는 점을 기억하시기 바랍니다.

이를 SD 카드의 데이터에 링크시키기 위해 특정 HAL 클래스에서 새로운 BlockCopy를 구현할 수 있습니다. 여기서는 이 클래스를 TouchGFXHAL(TouchGFX Generator에서 생성)이라고 칭합니다.

TouchGFXHal.hpp
class TouchGFXHAL : public TouchGFXGeneratedHAL
{
public:
...
virtual bool blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
}
TouchGFXHal.cpp
// This function is called whenever a bitmap is cached. Must copy a number of bytes from the (non-memory-mapped) source
// to the cache.
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
// If requested data is located within the memory block we have assigned for ExtFlashSection,
// provide an implementation for data copying.
if (src >= 0x24000000 && src < 0x24100000)
{
void* dataOffset = src - 0x24000000;
// In this example we assume graphics is placed in SD card, and that we have an appropriate function
// for copying data from there.
sdcard_read(dest, dataOffset, numBytes);
return true;
}
else
{
// For all other addresses, just use the default implementation.
// This is important, as blockCopy is also used for other things in the core framework.
return HAL::blockCopy(dest, src, numBytes);
}
}

이제 SD 카드에서 비트맵 캐싱을 시작할 수 있습니다.

TouchGFX가 캐싱되지 않은 비트맵에 대한 그리기를 시도하는 경우 bitmap_database 테이블에 있는 주소에서 픽셀을 읽어올 것입니다. 이 주소는 0x24000000 - 0x24100000 범위 내에 있기 때문에 읽기 작업에서 예외가 발생합니다.

RAM에 이미지 링크

가용 RAM의 용량이 모든 이미지를 저장할 만큼 충분히 큰 경우(위의 예제의 경우 147,136바이트 이상), 이미지를 복사하기 위해 비트맵 캐시를 사용할 필요가 없습니다.

전략은 다음과 같습니다.

  • 이미지에 대한 고정 주소 및 RAM 범위 선택
  • 링커 스크립트의 RAM 영역에서 해당 범위를 제거
  • 선택한 주소와 크기로 새로운 IMAGES 영역 생성
  • IMAGES 영역에 ExtFlashSection을 배치
  • 애플리케이션을 링크하고 .map 파일을 확인
  • application.elf에서 images.bin 파일 생성
  • TouchGFX가 시작되기 전에 전체 images.bin 파일을 SD 카드에서 RAM의 선택 주소로 복사

이 솔루션은 간단하지만 다음과 같이 몇 가지 단점이 있습니다.

  • 가용 RAM의 용량이 모든 이미지를 저장할 수 있을 만큼 커야 함
  • SD 카드에서 복사가 이뤄지기 때문에 시작 시간이 더 길어짐(메가 바이트인 경우 몇 초가 소요됨)