影片解碼
本節展示如何配置TouchGFX Generator,以生成具有影像解碼功能的TouchGFX HAL。
以下將詳細說明了如何使能TouchGFX HAL,以通過軟體(LibJPEG)或硬體(JPEG)支援影像解碼。 它涵蓋STM32F7和STM32H7,因為支援硬體(JPEG)解碼的STM32CubeMX配置與STM32F7略有不同。
一般情況下,LibJPEG和JPEG配置可以在中介軟體和多媒體類別的STM32CubeMX中找到:
在閱讀本文之前,請閱讀關於在TouchGFX中使用MJPEG影片的文件和關於影片小工具的文件。
請務必閱讀本文所有部分,然後繼續下一步。
Tip
軟體解碼
軟體解碼解決方案要求按照TouchGFX Generator使用者指南中指定的,從STM32CubeMX啟用LibJPEG中介軟體。 軟體解碼設置對於所有具備LibJPEG功能的MCU(如STM32F4、STM32F7、STM32H7)都是相同的。
TouchGFX軟體解碼器期望LibJPEG解碼的資料按照BGR像素排序。 如果該設置保留為RGB,R和B顏色分量將在應用中互換。 此外,每個像素的大小應該是3個位元組(4個位元組意味著影片以XRGB格式編碼)。
Caution
根據TouchGFX Generator使用者指南中所描述,一旦從STM32CubeMX啟用LibJPEG,現在可以從TouchGFX Generator啟用軟體解碼。
支援RTOS
TouchGFX Generator使用者指南提到,單和雙緩衝區解碼策略需要符合CMSIS標準的RTOS,比如FreeRTOS。 TouchGFX Generator生成一個必須與影片解碼任務關聯的入口函數videoTaskFunc
。 STM32CubeMX可以生成此配置,方法是在FreeRTOS中介軟體配置的任務和佇列選項卡中定義任務和入口函數。
影片任務的棧尺寸(在CMSIS V2的詞中定義)和RTOS堆尺寸是兩個重要參數,後者是因為用於軟體解碼的LibJPEG使用動態記憶體分配。 FreeRTOS堆對於您的一般應用 + 0xA000
應該足夠大。
基於以上配置,STM32CubeMX生成以下程式碼:
main.c
/* Definitions for VideoTask */
osThreadId_t VideoTaskHandle;
const osThreadAttr_t VideoTask_attributes = {
.name = "VideoTask",
.stack_size = 1000 * 4,
.priority = (osPriority_t) osPriorityLow,
};
void main()
{
...
/* creation of VideoTask */
VideoTaskHandle = osThreadNew(videoTaskFunc, NULL, &VideoTask_attributes);
...
}
FreeRTOSConfig.h
#define configTOTAL_HEAP_SIZE ((size_t)75000)
本節總結了用於軟體解碼的TouchGFX HAL配置。 從STM32CubeMX生成程式碼後,在從Designer使用影片小工具時,應用程式將能夠使用LibJPEG解碼影片。
影片影片
Caution
下面來自EWARM專案的程式碼片段顯示了由TouchGFX Designer創建的額外選項,以及它如何將資料放在ExtFlashSection
中,後者是在所有TouchGFX開發包的連結器腳本中定義的。 這不適用於非記憶體映射的flash或未定義此部分的專案。
ewarm_project.ewp
<option>
<name>IlinkExtraOptions</name>
<state>--image_input $PROJ_DIR$\..\TouchGFX\generated\videos\bin\washerdryer.bin,video_washerdryer_bin_start,ExtFlashSection,4</state>
<state>--keep video_washerdryer_bin_start</state>
</option>
對於影片資料位於非存儲映射的flash中的專案,請閱讀本文中的FileReader
部分。
硬體解碼
TouchGFX Generator使用者指南提到,JPEG IP必須在STM32CubeMX中啟用,以支援硬體解碼。 軟體解碼和硬體解碼有多個共同特點,而本節將概述它們之間的不同點。
支援RTOS
FreeRTOS棧尺寸可以大大低於軟體解碼的棧尺寸,因為沒有動態分配記憶體的軟體棧。 保持棧尺寸,以便能夠容納專案中的其他任務。
STM32F769-DISCO
支援JPEG功能的STM32F7系列(例如STM32F769)的JPEG配置,與STM32H7系列略有不同。 RGB_FORMAT
必須遵守TouchGFX Framebuffer的格式,即下面範例中的JPEG_RGB565
。
通過DMA設置,使用DMA將資料傳輸到JPEG外設(記憶體到外設),以及從JPEG外設接收資料(外設到記憶體)。 為IN
和OUT
各添加一個DMA請求將自動設置方向參數。
本節總結了支援JPEG功能的STM32F7(例如STM32F769)上關於硬體解碼的TouchGFX HAL配置。 從STM32CubeMX生成程式碼後,在從Designer使用影像小工具時,應用程式將能夠使用JPEG外設解碼影像。
Caution
STM32H750-DK
唯一能將(例如)STM32H750和STM32F769上的硬體解碼(支援JPEG)加以區分的因素是在STM32CubeMX中配置DMA傳輸的方式。 不僅UI不同,DMA概念也不同。
對於STM32H750,JPEG外設只能配置為使用MDMA外設(而不是常規DMA外設)。 為輸入和輸出FIFO閾值信號添加MDMA配置,如下圖所示。
Note
Caution
不幸的是,當通過STM32CubeMX為(例如)STM32H750生成程式碼時,將(上面定義的)MDMA配置為MDMA處理常式的程式碼缺失,開發人員必須手動添加突出顯示的使用者程式碼。
Core\Src\stm32h7xx_hal_msp.c
void HAL_JPEG_MspInit(JPEG_HandleTypeDef* hjpeg)
{
if(hjpeg->Instance==JPEG)
{
/* USER CODE BEGIN JPEG_MspInit 0 */
hmdma_jpeg_infifo_th.Init.Request = MDMA_REQUEST_JPEG_INFIFO_TH;
hmdma_jpeg_outfifo_th.Init.Request = MDMA_REQUEST_JPEG_OUTFIFO_TH;
/* USER CODE END JPEG_MspInit 0 */
...
FileReader
介面
當在非記憶體映射的記憶體上存儲MJPEG影像時,開發人員可以指定touchgfx::VideoDataReader
的實現,TouchGFX影片控制器可以使用它將需要解碼的資料傳輸到配置的解碼器(軟體/硬體)。 下面是此介面的簡單範例,它將影像資料從一個緩衝區複製到另一個緩衝區。
VideoView.cpp
class MyReader : public touchgfx::VideoDataReader
{
public:
MyReader() : position(0) { }
virtual uint32_t getDataLength() { return video_len; }
virtual void seek(uint32_t pos) { position = pos; }
virtual bool readData(void* dst, uint32_t bytes)
{
memcpy(dst, &video_data[position], bytes);
position += bytes;
return true;
}
private:
uint32_t position;
} myReader;
開發人員可以配置小工具以使用資料讀取器,而不是將一個影像
小工具指向映射記憶體中的影像開始部分:
VideoView.cpp
video.setVideoData(myReader);
遷移TBS,以影片解碼
如果你想要在一個專案(該專案是從一個支援影片解碼的開發板的TBS所創建)可用之前將其遷移,而且希望能夠在TouchGFX Desginer中對該專案進行“運行專案”操作,必須對GCC Makefile進行一些手動更改。 以下部分將列出所需的變更、以及進行變更的原因。 這些更改是對舊TBS中已經存在的GCC Makefile的擴展。
如上述場景所述,除了更新Makefile,您還必須在STM32CubeMX中設置影片解碼。
一般性變更
定義您專案中的LIBJPEG路徑:
# LibJPEG path
libjpeg_path := $(cubemx_middlewares_path)/Third_Party/LibJPEG
然後必須定義影片資產輸入路徑:
asset_texts_input := TouchGFX/assets/texts
asset_videos_input := TouchGFX/assets/videos
影片資產輸出路徑也必須定義在其他資產輸出路徑下面:
asset_images_output := $(asset_root_path)/images
asset_fonts_output := $(asset_root_path)/fonts
asset_texts_output := $(asset_root_path)/texts
asset_videos_output := $(asset_root_path)/videos
將影片輸出資產添加到元件清單中:
all_components := $(components) \
$(asset_fonts_output) \
$(asset_images_output) \
$(asset_texts_output)
$(asset_texts_output) \
$(asset_videos_output)
必須定義影片目的檔。 影片目的檔與已經存在的物件分離:
c_source_files := $(call find, $(source_paths),*.c) $(os_source_files) $(board_c_files)
source_files += $(board_cpp_files)
video_object_files := $(call find, $(asset_videos_output),*.o)
必須定義到影片轉換器腳本的路徑
textconvert_script_path := $(touchgfx_path)/framework/tools/textconvert
textconvert_executable := $(call find, $(textconvert_script_path), *.rb)
videoconvert_script_path := $(touchgfx_path)/framework/tools/videoconvert
可以添加一個可選的Echo,以查看所有影片目的檔。 影片目的檔必須添加到連結階段。 該行將$(video_object_files) 和其他目的檔加在一起:
$(binary_output_path)/$(target_executable): $(object_files) $(object_asm_files)
@echo Video Objects: $(video_object_files)
@echo Linking $(@)
@mkdir -p $(@D)
@mkdir -p $(object_output_path)
@$(file >$(build_root_path)/objects.tmp) $(foreach F,$(object_files) $(video_object_files),$(file >>$(build_root_path)/objects.tmp,$F))
將影片規則添加到現有資產和.PHONY:
_assets_: BitmapDatabase TextKeysAndLanguages Videos
.PHONY: BitmapDatabase TextKeysAndLanguages Videos
添加影片轉換後,接著添加影片規則:
Videos:
@ruby $(videoconvert_script_path)/videoconvert.rb $(asset_videos_input) $(asset_videos_output)
最後更新清除規則,也刪除與影片相關的輸出。
_clean_:
@echo Cleaning: $(board_name)
@rm -rf $(build_root_path)
# Do not remove gui_generated
@rm -rf $(asset_images_output)
@rm -rf $(asset_fonts_output)
@rm -rf $(asset_texts_output)
@rm -rf $(asset_videos_output)
# Create directory to avoid error if it does not exist
@mkdir -p $(asset_root_path)
# Remove assets folder if it is empty (i.e. no gui_generated folder)
@rmdir --ignore-fail-on-non-empty $(asset_root_path)
# Clean bootloader project
@$(MAKE) -r -f ExtMem_Boot/gcc/Makefile -s $(MFLAGS) clean
軟體變更
添加所有LIBJPEG路徑到包括路徑:
include_paths := $(library_includes) \
$(foreach comp, $(all_components), $(comp)/include) \
$(foreach comp, $(cubemx_components), $(comp)/Inc) \
$(foreach comp, $(touchgfx_generator_components), $(comp)/generated) \
$(framework_includes) \
$(cubemx_middlewares_path) \
$(touchgfx_middlewares_path) \
$(touchgfx_generator_components) \
LIBJPEG/Target \
$(libjpeg_path)/include \
LIBJPEG/App
必須定義LIBJPEG源路徑:
c_source_files := $(call find, $(source_paths),*.c) $(os_source_files) $(board_c_files)
source_files += $(board_cpp_files)
libjpeg_source_path = Middlewares/Third_Party/LibJPEG/source
接下來,所有LIBJPEG原始檔案必須添加到board_c_files:
board_c_files := \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_bus.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_qspi.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_sdram.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_ts.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336_reg.c \
$(Drivers_path)/BSP/Components/mt25tl01g/mt25tl01g.c \
$(Drivers_path)/BSP/Components/mt48lc4m32b2/mt48lc4m32b2.c \
$(libjpeg_source_path)/jaricom.c \
$(libjpeg_source_path)/jcomapi.c \
$(libjpeg_source_path)/jdapimin.c \
$(libjpeg_source_path)/jdapistd.c \
$(libjpeg_source_path)/jdarith.c \
$(libjpeg_source_path)/jdatasrc.c \
$(libjpeg_source_path)/jdcoefct.c \
$(libjpeg_source_path)/jdcolor.c \
$(libjpeg_source_path)/jddctmgr.c \
$(libjpeg_source_path)/jdhuff.c \
$(libjpeg_source_path)/jdinput.c \
$(libjpeg_source_path)/jdmainct.c \
$(libjpeg_source_path)/jdmarker.c \
$(libjpeg_source_path)/jdmaster.c \
$(libjpeg_source_path)/jdmerge.c \
$(libjpeg_source_path)/jdpostct.c \
$(libjpeg_source_path)/jdsample.c \
$(libjpeg_source_path)/jdtrans.c \
$(libjpeg_source_path)/jerror.c \
$(libjpeg_source_path)/jidctflt.c \
$(libjpeg_source_path)/jidctfst.c \
$(libjpeg_source_path)/jidctint.c \
$(libjpeg_source_path)/jmemmgr.c \
$(libjpeg_source_path)/jmemnobs.c \
$(libjpeg_source_path)/jquant1.c \
$(libjpeg_source_path)/jquant2.c \
$(libjpeg_source_path)/jutils.c \
LIBJPEG/App/libjpeg.c
然後將LIBJPEG原始檔案添加到現有的目的檔案中,方法與其他中介軟體原始檔案相同:
# Start converting paths
object_files := $(object_files:$(touchgfx_path)/%.cpp=$(object_output_path)/touchgfx/%.o)
object_files := $(object_files:%.cpp=$(object_output_path)/%.o)
object_files := $(object_files:$(touchgfx_middlewares_path)/%.c=$(object_output_path)/$(touchgfx_middlewares_path)/%.o)
object_files := $(object_files:$(cubemx_middlewares_path)/%.c=$(object_output_path)/$(cubemx_middlewares_path)/%.o)
object_files := $(object_files:$(libjpeg_source_path)/%.c=$(object_output_path)/$(libjpeg_source_path)/%.o)
object_files := $(object_files:$(Drivers_path)/%.c=$(object_output_path)/Drivers/%.o)
object_files := $(object_files:%.c=$(object_output_path)/%.o)
硬體變更
添加所有JPEG路徑到包括路徑:
include_paths := $(library_includes) \
$(foreach comp, $(all_components), $(comp)/include) \
$(foreach comp, $(cubemx_components), $(comp)/Inc) \
$(foreach comp, $(touchgfx_generator_components), $(comp)/generated) \
$(framework_includes) \
$(cubemx_middlewares_path) \
$(touchgfx_middlewares_path) \
$(touchgfx_generator_components) \
Utilities/JPEG
接下來,所有JPEG原始檔案必須添加到board_c_files:
board_c_files := \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_bus.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_qspi.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_sdram.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_ts.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336_reg.c \
$(Drivers_path)/BSP/Components/mt25tl01g/mt25tl01g.c \
$(Drivers_path)/BSP/Components/mt48lc4m32b2/mt48lc4m32b2.c \
Utilities/JPEG/jpeg_utils.c
將影片緩衝區置於外部記憶體中
當開發人員使用CubeMX創建新專案時,與生成的專案相關聯的連結器腳本不包含TouchGFX使用的任何可能的區域。 因此,用於解碼MJPEG影片的特定緩衝區被連結器放置在內部flash中,直至開發人員修改他們的連結器腳本,將緩衝區放置到其他地方。 如果沒有這樣的修改,開發人員將會遇到大量內部存儲被使用的情況,可能無法容納大一點的影片緩衝區,比如全螢幕影片解碼所需的緩衝區。
Tip
- STM32F746-DISCO
- STM32F769-DISCO
- STM32H750-DK
啟用影片解碼以後,TouchGFX Generator生成用於JPEG解碼的RGB緩衝區的定義。 該定義是用一個位置指示來實現的,它告訴連結器應該將緩衝區放在哪個部分。 如果連結器在連結器腳本中找不到該存儲區域,緩衝區將被放在內部存儲中。
LOCATION_PRAGMA("Video_RGB_Buffer")
uint32_t videoRGBBuffer[57600] LOCATION_ATTRIBUTE("Video_RGB_Buffer");
下面特定於編譯器的子章節描述了開發人員可以進行的修改,以實現在SDRAM中放置緩衝區。 Video_RGB_Buffer
表示用於影片解碼的緩衝區。 連結器腳本範例在SDRAM中為TouchGFX影像緩衝區保留了一些空間(從0xC0000000
開始)。
- EWARM
- STM32CubeIDE
- MDK-ARM
Further reading
下面的例子都在STM32F746G-DISCO板(0xC0000000
->0xC00FF000
)上的SDRAM開始部分保留了一些空間,允許應用程式通過位址(例如0xC0000000
)引用影像緩衝區,而不存在連結器覆寫影像緩衝區資料的風險。 每個範例都允許連結器將Video_RGB_Buffer
放入已定義的SDRAM區域。
Tip
EWARM (IAR)
stm32f746xx_flash.icf
define symbol __ICFEDIT_region_SDRAM_start__ = 0xC00FF000;
define symbol __ICFEDIT_region_SDRAM_end__ = 0xC0700FFF;
define region SDRAM_region = mem:[from __ICFEDIT_region_SDRAM_start__ to __ICFEDIT_region_SDRAM_end__];
place in SDRAM_region { first section Video_RGB_Buffer };
連結之後,EWARM\STM32F746G_DISCO\List\STM32F746G_DISCO.map
包含Video_RGB_Buffer
的以下放置資訊:
STM32F746G_DISCO.map
Video_RGB_Buffer zero 0xc00f'f000 0x3'8400 TouchGFXGeneratedHAL.o [2]
- 0xc013'7400 0x3'8400
STM32CubeIDE
STM32F746NGHX_FLASH.ld
MEMORY
{
...
SDRAM (xrw) : ORIGIN = 0xC00FF000, LENGTH = 8M
}
BufferSection :
{
*(Video_RGB_Buffer Video_RGB_Buffer.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SDRAM
編譯之後,STM32CubeIDE\Debug\STM32F746G_DISCO.map
包含Video_RGB_Buffer
的以下放置資訊:
STM32F746G_DISCO.map
BufferSection 0x00000000c00ff000 0x1c200
*(Video_RGB_Buffer Video_RGB_Buffer.*)
Video_RGB_Buffer
0x00000000c00ff000 0x1c200 Application/User/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
0x00000000c00ff000 videoRGBBuffer
MDK-ARM (Keil)
STM32F746G_DISCO.sct
LR_IROM1 0x08000000 0x00200000 { ; load region size_region
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00050000 { ; RW data
.ANY (+RW +ZI)
}
RW_SDRAM 0xC00FF000 0xC0700FFF {
*.o (Video_RGB_Buffer)
}
}
連結之後,MDK-ARM\STM32F746G_DISCO\STM32F746G_DISCO.map
包含Video_RGB_Buffer
的以下放置資訊:
STM32F746G_DISCO.map
Video_RGB_Buffer 0xc00ff000 Section 115200 touchgfxgeneratedhal.o(Video_RGB_Buffer)