第7章 ThreadX GUIX移植到STM32F429(MDK AC5)
本章節将為大家介紹ThreadX GUIX的MDK AC5方式移植和設計架構,理論上不建議初學者直接學習,因為本章節涉及到的知識點很多,建議對GUIX的應用有一些了解後再來看,這樣将事半功倍。但是本章的工程模闆架構一定要學習。本章節提供的移植方法支援RGB565和ARGB8888兩種顔色格式的實作。同時可以自适應我們生産的4.3寸,5寸和7寸的電阻屏和電容屏。
雖然本章節是以我們開發闆為例進行移植的,但是教會大家如何移植到自己的闆子上以及移植過程中的注意事項是本章節的重點。
7.1初學者重要提示
7.2移植前的準備工作以及移植GUIX的流程
7.3第1步:ThreadX核心模闆架構設計
7.4第2步:GUIX模闆架構設計(重要)
7.5第3步:下載下傳GUIX庫并添加到ThreadX核心工程模闆
7.6第4步:SDRAM驅動的實作
7.7第5步:LTDC涉及到的引腳配置和時序配置
7.8第6步:電阻屏和電容屏觸摸驅動的實作
7.9第7步:GUIX底層接口函數和配置
7.10第8步:添加GUI應用進行測試。
7.11顯示屏閃爍檔案解決方法
7.12避免顯示屏上電瞬間高亮和撕裂感
7.13實驗例程
7.14總結
7.1 初學者重要提示
- 學習本章節前,務必保證已經學習了本教程的第4章,第5章和第6章,這三章是移植前的必備知識。
- 注意ThreadX MDK AC5工程提供的Port檔案問題,移植必讀:http://www.armbbs.cn/forum.php?mod=viewthread&tid=99306 。
- 為了友善大家移植,推薦直接添加我們的工程檔案到自己的工程或者直接使用我們的工程模闆,按照本章的修改說明移植即可。
- 提供了ARGB8888和RGB565兩種顔色格式的移植工程,移植方法是一樣的,僅添加的接口檔案不同。
- 本章節是以移植到ThreadX上為例進行說明的,移植到其它小型RTOS方法,後面章節再為大家介紹。
- 由于開發闆要自适應4.3寸,5寸和7寸顯示屏,而且還分電阻觸摸和電容觸摸,是以移植過程中添加的檔案稍多。雖然移植是以我們的開發闆為例進行講解的,但是重點依然是告訴大家如何移植自己的闆子以及移植過程中需要注意的事項。
- 對于本章節的移植,我們需要先從整體上把控。由于開發闆已經把需要移植的檔案都整理好了,使用者僅需添加檔案就可以使用。我們這裡着重介紹如何移植到自己的闆子上面,這個才是本章節的重點。
- 顯示屏的移植
GUIX需要的底層接口函數已經全部內建在gx_display_driver_stm32f4_24xrgb.c檔案和gx_display_driver_stm32f4_565rgb.c裡面。對于這兩個檔案,使用者僅需學會使用裡面的兩個宏配置以及LTDC涉及到的引腳和時序配置函數,這個是需要使用者自己去實作的,配置方法已經在本章節的7.7小節進行講解。
另外還有一個顯示屏背光調節函數LCD_SetBackLight的調用,其它都不用做任何修改。這三個地方都設定了,GUIX的顯示屏移植就完成了。
- 觸摸的移植
電容觸摸的移植比較容易,因為電容觸摸晶片可以自動觸摸校準,是以僅需配置完觸摸晶片後将觸摸晶片傳回的觸摸坐标(電容觸摸晶片傳回的就是實際的坐标值),按下,松手和移動三種狀态發送給GUIX即可。
電阻觸摸的移植要稍麻煩些,由于電阻觸摸闆的線性度不是很好,如果不做觸摸校準和濾波處理會有點選不準确和飛點問題。目前配套2點和4點觸摸校準算法,大家可以根據需要選擇,預設是用的2點觸摸校準算法。其中觸摸濾波方法是檢測到觸摸後先延遲30ms,消除抖動,然後采集10組坐标值做升序排列,去掉最大的幾組坐标和最小的幾組坐标,對中間的幾組求平均作為最終的數值(電容觸摸晶片傳回的是ADC數值,不是實際坐标值)。然後将最終的數值代入通過觸摸校準建立的線性公式來獲得實際的坐标值,此時就可以将觸摸坐标和觸摸按下,松手和移動狀态發送給GUIX。
7.2 移植前的準備工作以及移植GUIX的流程
移植前注意以下兩個問題:
- 本章節的IDE開發環境務必是MDK5.30及其以上版本,鏡像下載下傳位址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=96992 。
- 準備一個簡單的ThreadX工程,越簡單越好,我們就在這個簡單的工程上面移植即可:
配套模闆名稱:V6-2004_ThreadX Kernel Template
GUIX的移植通過以下8步完成,下面各個小節詳細講解每一步:
- 第1步:ThreadX核心模闆架構設計
- 第2步:GUIX模闆架構設計(重要)
- 第3步:下載下傳GUIX庫并添加到ThreadX核心工程模闆
- 第4步:SDRAM驅動的實作
- 第5步:LTDC涉及到的引腳配置和時序配置
- 第6步:電阻屏和電容屏觸摸驅動的實作
- 第7步:GUIX底層接口函數和配置
- 第8步:添加GUI應用進行測試。
7.3 第1步:了解ThreadX核心模闆架構設計
移植GUIX前,我們優先了解下ThreadX核心模闆程式的框圖。
7.3.1 準備一個ThreadX核心工程模闆
首先準備好一個簡單的ThreadX工程模闆,工程模闆的制作在ThreadX核心教程裡面有詳細說明,這裡的重點是教大家移植GUIX,對應的例子名稱:V6-2004_ThreadX Kernel Template。準備好的工程模闆如下圖所示(特别注意,我們這個模闆已經添加裸機LCD操作所需的檔案)。

7.3.2 核心架構整體把控(重要)
為了幫助大家更好的了解ThreadX核心例子模闆,專門制作了一個框圖,可以讓大家整體把控模闆設計:
下面把幾個關鍵點逐一為大家做個說明。
7.3.3 各種頭檔案彙總includes.h
這個檔案主要實作工程中各種頭檔案的彙總,大家用到的都可以将其放到這個頭檔案裡面。其它應用源檔案有調用到的,直接調用這個頭檔案includes.h即可。
使用這個頭檔案主要是友善各種頭檔案的管理。
/*
*********************************************************************************************************
* 标準庫
*********************************************************************************************************
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
*********************************************************************************************************
* OS
*********************************************************************************************************
*/
#include "tx_api.h"
#include "tx_timer.h"
/*
*********************************************************************************************************
* APP / BSP
*********************************************************************************************************
*/
#include <bsp.h>
/*
*********************************************************************************************************
* 變量和函數
*********************************************************************************************************
*/
/* 友善RTOS裡面使用 */
extern void SysTick_ISR(void);
#define
7.3.4 TheadX配置檔案tx_user.h
此檔案主要用于ThreadX核心的配置,核心相關的幾個宏配置基本都已經整理到這個檔案裡面。
/*
*********************************************************************************************************
* 宏定義
*********************************************************************************************************
*/
/*
最快速度優化需要開啟的選項 :
TX_MAX_PRIORITIES 32
TX_DISABLE_PREEMPTION_THRESHOLD
TX_DISABLE_REDUNDANT_CLEARING
TX_DISABLE_NOTIFY_CALLBACKS
TX_NOT_INTERRUPTABLE
TX_TIMER_PROCESS_IN_ISR
TX_REACTIVATE_INLINE
TX_DISABLE_STACK_FILLING
TX_INLINE_THREAD_RESUME_SUSPEND
最小代碼優化需要開啟的選項:
TX_MAX_PRIORITIES 32
TX_DISABLE_PREEMPTION_THRESHOLD
TX_DISABLE_REDUNDANT_CLEARING
TX_DISABLE_NOTIFY_CALLBACKS
TX_NOT_INTERRUPTABLE
TX_TIMER_PROCESS_IN_ISR
*/
/* 覆寫tx_port.h 裡面的宏定義 */
/*
#define TX_MAX_PRIORITIES 32
#define TX_MINIMUM_STACK ????
#define TX_THREAD_USER_EXTENSION ????
#define TX_TIMER_THREAD_STACK_SIZE ????
#define TX_TIMER_THREAD_PRIORITY ????
*/
/*
确定定時器是否到期的處理,比如應用定時器,溢出時間和函數tx_thread_sleep調用等,是在系統定時器任務裡面還是在定時器中斷裡面調用。
預設是在定時任務裡面,當定義了下面函數後,将直接在定時器中斷裡面處理,可以去掉定時器任務所消耗資源。 */
//#define TX_TIMER_PROCESS_IN_ISR
/* 用于設定定時器激活是否采用内聯方式,預設此功能是關閉的。如果使能後,内聯方式的執行速度快,但增加代碼量 */
//#define TX_REACTIVATE_INLINE
/* 用于設定是否關閉棧填充,預設情況下是使能的,所有任務的棧空間全部填充為0xEF,
* 帶有ThreadX調試元件或者運作時棧檢測會用到。
*/
//#define TX_DISABLE_STACK_FILLING
/* 用于使能棧檢測,預設是關閉的。此選項使能後,而TX_DISABLE_STACK_FILLING沒使能時,棧填充将開啟,友善棧檢測 */
//#define TX_ENABLE_STACK_CHECKING
/* 用于設定是否關閉搶占閥值,預設是開啟的。如果應用程式不需要此功能,關閉後可以降低代碼需求,提升性能 */
//#define TX_DISABLE_PREEMPTION_THRESHOLD
/* 用于設定是否清零ThreadX全局變量,如果編譯器啟動代碼在ThreadX運作前清除了.bss段,那麼可以關閉不必要的清零 */
//#define TX_DISABLE_REDUNDANT_CLEARING
/* 确定是否不需要定時器組,禁止後需要使用者注釋掉tx_initialize_low_level檔案裡面tx_timer_interrupt的調用。
另外,禁止後,必須使能TX_TIMER_PROCESS_IN_ISR */
/*
#define TX_NO_TIMER
#ifndef TX_TIMER_PROCESS_IN_ISR
#define TX_TIMER_PROCESS_IN_ISR
#endif
*/
/* 用于設定是否關閉通知回調,預設是使能的。如果應用程式沒有用到消息回調,關閉掉後可以減小代碼,并且可以提升性能。 */
//#define TX_DISABLE_NOTIFY_CALLBACKS
/* 使能tx_thread_resume和tx_thread_suspend使用内聯代碼,優勢是提升這兩個函數的執行性能,劣勢是增加代碼量 */
//#define TX_INLINE_THREAD_RESUME_SUSPEND
/* 設定TreadX核心不可中斷,好處是降低處理負擔,并且産生的代碼小。但增加鎖時間 */
//#define TX_NOT_INTERRUPTABLE
/* 使能事件Trace,會稍微增加點代碼 */
//#define TX_ENABLE_EVENT_TRACE
/* 使能BLOCK_POOL資訊擷取 */
//#define TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
/* 使能BYTE_POOL資訊擷取 */
//#define TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
/* 使能事件标志資訊擷取 */
//#define TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
/* 使能互斥信号量資訊擷取 */
//#define TX_MUTEX_ENABLE_PERFORMANCE_INFO
/* 使能消息對象資訊擷取 */
//#define TX_QUEUE_ENABLE_PERFORMANCE_INFO
/* 使能信号量資訊擷取 */
//#define TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
/* 使能任務資訊擷取 */
//#define TX_THREAD_ENABLE_PERFORMANCE_INFO
/* 使能定時器資訊擷取 */
//#define TX_TIMER_ENABLE_PERFORMANCE_INFO
7.3.5 系統時鐘節拍配置tx_initialize_low_level.s
這個彙編檔案裡面有個重要參數需要大家配置,即晶片主頻和系統時鐘節拍。
SYSTEM_CLOCK EQU 168000000
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 1000) -1)
168000000是系統時鐘主頻,1000對應的就是系統時鐘節拍,這裡1000就表示1000Hz。
7.3.6 TheadX任務管理main.c
ThreadX所有任務基本都在main.c裡面建立,友善統一管理。如果有GUIX,FileX等元件的任務需要運作,實際運作函數會在其它源檔案裡面,并将這個函數extern到main.C檔案裡面,放到相應的任務裡面執行。
另外,任務優先級,任務棧大小,任務控制塊等也都放到main.C檔案裡面,友善管理:
/*
*********************************************************************************************************
* 任務優先級,數值越小優先級越高
*********************************************************************************************************
*/
#define APP_CFG_TASK_START_PRIO 2u
#define APP_CFG_TASK_MsgPro_PRIO 3u
#define APP_CFG_TASK_USER_IF_PRIO 4u
#define APP_CFG_TASK_COM_PRIO 5u
#define APP_CFG_TASK_STAT_PRIO 30u
#define APP_CFG_TASK_IDLE_PRIO 31u
/*
*********************************************************************************************************
* 任務棧大小,機關位元組
*********************************************************************************************************
*/
#define APP_CFG_TASK_START_STK_SIZE 4096u
#define APP_CFG_TASK_MsgPro_STK_SIZE 4096u
#define APP_CFG_TASK_COM_STK_SIZE 4096u
#define APP_CFG_TASK_USER_IF_STK_SIZE 4096u
#define APP_CFG_TASK_IDLE_STK_SIZE 1024u
#define APP_CFG_TASK_STAT_STK_SIZE 1024u
/*
*********************************************************************************************************
* 靜态全局變量
*********************************************************************************************************
*/
static TX_THREAD AppTaskStartTCB;
static uint64_t AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE/8];
static TX_THREAD AppTaskMsgProTCB;
static uint64_t AppTaskMsgProStk[APP_CFG_TASK_MsgPro_STK_SIZE/8];
static TX_THREAD AppTaskCOMTCB;
static uint64_t AppTaskCOMStk[APP_CFG_TASK_COM_STK_SIZE/8];
static TX_THREAD AppTaskUserIFTCB;
static uint64_t AppTaskUserIFStk[APP_CFG_TASK_USER_IF_STK_SIZE/8];
static TX_THREAD AppTaskIdleTCB;
static uint64_t AppTaskIdleStk[APP_CFG_TASK_IDLE_STK_SIZE/8];
static TX_THREAD AppTaskStatTCB;
static uint64_t AppTaskStatStk[APP_CFG_TASK_STAT_STK_SIZE/8];
7.3.7 TheadX啟動任務
啟動任務裡面主要做了四個工作:
- 優先執行一次任務統計OSStatInit。
- 外設初始化bsp_Init。
- 任務建立AppTaskCreate和通信元件建立AppObjCreate。
- 需要周期性處理的程式bsp_ProPer1ms,對應裸機工程調用的SysTick_ISR。這個的實作非常重要,這樣之前裸機裡面使用的API,就可以直接在ThreadX裡面直接調用。
代碼如下:
/*
*********************************************************************************************************
* 函 數 名: AppTaskStart
* 功能說明: 啟動任務。
* 形 參: thread_input 是在建立該任務時傳遞的形參
* 返 回 值: 無
優 先 級: 2
*********************************************************************************************************
*/
static void AppTaskStart (ULONG thread_input)
{
(void)thread_input;
/* 先挂起定時器組 */
#ifndef TX_NO_TIMER
tx_thread_suspend(&_tx_timer_thread);
#endif
/* 優先執行任務統計 */
OSStatInit();
/* 恢複定時器組 */
#ifndef TX_NO_TIMER
tx_thread_resume(&_tx_timer_thread);
#endif
/* 核心開啟後,恢複HAL裡的時間基準 */
HAL_ResumeTick();
/* 外設初始化 */
bsp_Init();
/* 建立任務 */
AppTaskCreate();
/* 建立任務間通信機制 */
AppObjCreate();
while (1)
{
/* 需要周期性處理的程式,對應裸機工程調用的SysTick_ISR */
bsp_ProPer1ms();
tx_thread_sleep(1);
}
}
7.3.8 HAL庫時間基準stm32f4xx_hal_timbase_tim.c
ThreadX系統時鐘節拍預設是用的滴答定時器,STM32的HAL庫時間基準也是用的滴答定時器。對于這種情況,我們一般的情況下是使用其他的通用定時器替代,不過要額外的占用一點系統性能。簡單的處理辦法是重新實作下面兩個函數即可,讓HAL庫和ThreadX都使用滴答定時器:
/*
*********************************************************************************************************
* 函 數 名: HAL_Delay
* 功能說明: 重定向毫秒延遲函數。替換HAL中的函數。因為HAL中的預設函數依賴于Systick中斷,如果在USB、SD
* 卡中斷中有延遲函數,則會鎖死。也可以通過函數HAL_NVIC_SetPriority提升Systick中斷
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void HAL_Delay(uint32_t Delay)
{
bsp_DelayMS(Delay);
}
HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority)
{
return HAL_OK;
}
uint32_t HAL_GetTick (void)
{
static uint32_t ticks = 0U;
uint32_t i;
if (_tx_thread_system_state == TX_INITIALIZE_IS_FINISHED)
{
return ((uint32_t)_tx_time_get());
}
/* 如果ThreadX還沒有運作,采用下面方式 */
for (i = (SystemCoreClock >> 14U); i > 0U; i--)
{
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
}
return ++ticks;
}
7.3.9 ThreadX使能硬體浮點
這個是移植的坑王,大家移植後,可以測試下多任務的FPU計算是否有異常。比如兩個任務運作相同的浮點運算和重新整理速度,看看兩個任務的輸出是否同步變化,這個測試非常重要:
那麼問題來了,正确的使能姿勢是什麼?務必保證C和彙編的預定義宏裡面都使能。
C裡面對應的使能:
彙編裡面對應的使能:
7.4 第2步:了解GUIX模闆架構設計
以往我們做教程都是先介紹如何移植,然後看最後的移植效果。這次我們反過來,先看移植完成的效果,然後移植。
7.4.1 GUIX工程模闆
GUIX工程模闆移植完成後,大體是下面這種效果:
7.4.2 GUIX架構整體把控(重要)
為了幫助大家更好的了解GUIX核心例子模闆,專門制作了一個框圖,可以讓大家整體把控模闆設計:
下面把幾個關鍵點逐一為大家做個說明。
7.4.3 GUIX配置檔案gx_user.h
此檔案主要用于GUIX的配置,GUIX相關的宏定義配置非常多,目前先把這個檔案預留出來,随着後面章節的進行,用到那些宏定義了再添加。
7.4.4 外設驅動初始化檔案bsp.c
使用GUIX,主要涉及到SDRAM初始化,觸摸初始化,LTDC初始化(放到了GUIX底層驅動接口檔案裡面了),背光開啟和EEPROM初始化(用于存儲電阻觸摸屏校準參數):
/*
*********************************************************************************************************
* 函 數 名: bsp_Init
* 功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
bsp_InitDWT(); /* 初始化DWT時鐘周期計數器 */
bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
bsp_InitUart(); /* 初始化序列槽 */
bsp_InitExtIO(); /* 初始化FMC總線74HC574擴充IO. 必須在 bsp_InitLed()前執行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitTimer(); /* 初始化滴答定時器 */
bsp_InitExtSDRAM(); /* 初始化SDRAM */
bsp_InitI2C(); /* 初始化I2C總線,用于EEPROM */
TOUCH_InitHard();
/* 延遲200ms再點亮背光,避免瞬間高亮 */
bsp_DelayMS(200);
LCD_SetBackLight(255);
}
7.4.5 外設驅動頭檔案彙總bsp.h
此檔案主要用于添加了那些外設驅動檔案後,使能相應頭檔案,特别工程GUIX工程裡面添加的一批LCD驅動和觸摸驅動檔案,支援的頭檔案如下:
/* 通過取消注釋或者添加注釋的方式控制是否包含底層驅動子產品 */
//#include "bsp_msg.h"
//#include "bsp_user_lib.h"
#include "bsp_timer.h"
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_dwt.h"
//#include "bsp_cpu_rtc.h"
//#include "bsp_cpu_adc.h"
//#include "bsp_cpu_dac.h"
#include "bsp_uart_fifo.h"
//#include "bsp_uart_gps.h"
//#include "bsp_uart_esp8266.h"
//#include "bsp_uart_sim800.h"
//#include "bsp_spi_bus.h"
//#include "bsp_spi_ad9833.h"
//#include "bsp_spi_ads1256.h"
//#include "bsp_spi_dac8501.h"
//#include "bsp_spi_dac8562.h"
//#include "bsp_spi_flash.h"
//#include "bsp_spi_tm7705.h"
//#include "bsp_spi_vs1053b.h"
#include "bsp_fmc_sdram.h"
//#include "bsp_fmc_nand_flash.h"
//#include "bsp_fmc_ad7606.h"
//#include "bsp_fmc_oled.h"
#include "bsp_fmc_io.h"
#include "bsp_i2c_gpio.h"
//#include "bsp_i2c_bh1750.h"
//#include "bsp_i2c_bmp085.h"
#include "bsp_i2c_eeprom_24xx.h"
//#include "bsp_i2c_hmc5883l.h"
//#include "bsp_i2c_mpu6050.h"
//#include "bsp_i2c_si4730.h"
//#include "bsp_i2c_wm8978.h"
#include "bsp_tft_429.h"
#include "bsp_tft_lcd.h"
#include "bsp_ts_touch.h"
#include "bsp_ts_ft5x06.h"
#include "bsp_ts_gt811.h"
#include "bsp_ts_gt911.h"
#include "bsp_ts_stmpe811.h"
#include "bsp_beep.h"
#include "bsp_tim_pwm.h"
//#include "bsp_sdio_sd.h"
//#include "bsp_dht11.h"
//#include "bsp_ds18b20.h"
//#include "bsp_ps2.h"
//#include "bsp_ir_decode.h"
//#include "bsp_camera.h"
//#include "bsp_rs485_led.h"
//#include "bsp_can.h"
7.4.6 LCD裸機驅動檔案
STM32F429的LCD控制器LTDC控制有兩個相關的驅動檔案,即bsp_tft_429.c和bsp_tft_lcd.c。移植GUIX時,這兩個檔案基本用不上了,已經把這兩個檔案實作的LTDC配置全部內建到了GUIX的底層接口驅動檔案裡面,友善大家移植。
7.4.7 電阻和電容觸摸驅動檔案
電阻和電容觸摸主要用的以下幾個檔案:
- 總觸摸檔案,用于識别各種觸摸:bsp_ts_touch.c
- 電容觸摸檔案bsp_ts_ft5x06.c
- 電容觸摸檔案bsp_ts_gt811.c
- 電容觸摸檔案bsp_ts_gt911.c
- 電阻觸摸檔案bsp_ts_stmpe811
7.4.8 電阻觸摸校準參數存儲到EEPROM
EEPROM的驅動主要涉及到兩個檔案:
- bsp_i2c_gpio.c
配置EEPROM用到的兩個引腳。
- bsp_i2c_eeprom_24xx.c
EEPROM的讀寫API。
而觸摸校準參數的實作是在bsp_ts_touch.c檔案末尾封裝好的兩個函數裡面:
/*
*********************************************************************************************************
* 函 數 名: TOUCH_SaveParam
* 功能說明: 儲存校準參數 s_usAdcX1 s_usAdcX2 s_usAdcY1 s_usAdcX2
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
static void TOUCH_SaveParam(void)
{
g_tTPParam.TouchDirection = g_LcdDirection;/* 2014-09-11 添加螢幕方向, 用于螢幕旋轉時無需再次校準 */
#if 1
/* 寫入EEPROM */
ee_WriteBytes((uint8_t *)&g_tTPParam, TP_PARAM_EE_ADDR, sizeof(g_tTPParam));
#else
/* 寫入CPU Flash */
bsp_WriteCpuFlash(TP_PARAM_FLASH_ADDR, (uint8_t *)&g_tTPParam, sizeof(g_tTPParam));
#endif
}
/*
*********************************************************************************************************
* 函 數 名: TOUCH_LoadParam
* 功能說明: 讀取校準參數
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
static void TOUCH_LoadParam(void)
{
#if 1
/* 讀取EEPROM中的參數 */
ee_ReadBytes((uint8_t *)&g_tTPParam, TP_PARAM_EE_ADDR, sizeof(g_tTPParam));
#else
/* 讀取CPU Flash中的參數 */
bsp_ReadCpuFlash(TP_PARAM_FLASH_ADDR, (uint8_t *)&g_tTPParam, sizeof(g_tTPParam
#endif
if (g_tTPParam.TouchDirection > 4)
{
g_tTPParam.TouchDirection = 0;
TOUCH_SaveParam();
}
}
7.4.9 GUIX底層驅動接口檔案
目前制作了兩個底層驅動接口檔案:
- gx_display_driver_stm32h7_565rgb.c 對應硬體RGB565接口。
- gx_display_driver_stm32h7_24xrgb.c 對應硬體RGB888接口。
大家可以根據需要選擇相應驅動。
7.4.10 GUIX源碼檔案
GUIX的源碼檔案非常多,一個檔案一個API,有1200個左右,大家移植的時候最好都加上。
7.4.11 GUIX應用檔案
幾個應用檔案的作用如下:
随着後面的章節的學習,逐漸就熟練了。
7.5 第3步:添加GUIX庫所有相關檔案到ThreadX核心工程模闆
了解了ThreadX核心架構和GUIX架構後,介紹下如何将GUIX移植到ThreadX核心工程模闆裡面。我們這裡一步到位,直接把所有相關的檔案都加上,然後再介紹如何修改,友善大家移植到自己的闆子上。
7.5.1 第3.1步,下載下傳GUIX源碼包
按照第2章2.3.1小節講解的方法下載下傳GUIX軟體包guix-6.0.1_rel(如果軟體包更新了,數字6.0.1略有不同),下面是GUIX軟體包内容:
主要用到兩個檔案夾:
common檔案夾裡面是源碼檔案。
ports檔案夾裡面是移植檔案。
7.5.2 第3.2步,建立GUIX檔案夾到工程模闆
在工程模闆建立GUIX檔案夾
GUIX的M4核心port檔案夾裡面隻有一個GNU(對應路徑ports\cortex_m4),為了友善我們管理,再建立IAR,AC5和AC6三個檔案夾,檔案夾裡面的内容和GNU裡面的一樣,并且再添加一個src檔案夾,整體效果就是下面這樣:
建立的src檔案夾是用來存放GUIX底層驅動接口檔案用。
7.5.3 第3.3步,添加底層驅動接口檔案
添加驅動接口檔案到第3.2步建立的ac5->src和ac5-inc檔案夾裡面(h檔案添加到inc檔案夾,c檔案放到src檔案夾)
為了移植友善,大家直接複制本周教程配套例子在此檔案夾下的檔案即可。
7.5.4 第3.4步,添加Port檔案和源碼檔案到工程
将源碼檔案和ports檔案添加到MDK的工程項目中,添加後的效果如下:
推薦使用下面的方法添加,否則MDK會非常卡:
7.5.5 第3.5步,添加配置檔案gx_user.h
在User檔案夾下添加檔案gx_user.h,直接從本章節教程配套例子的User檔案夾複制即可。此檔案主要用于GUIX配置。
為了友善管理,我們這裡将路徑GUIX\ports\cortex_m4\ac5\inc裡面的gx_port.h檔案也添加進來了。
7.5.6 第3.5步,添加GUIX應用檔案
在User檔案夾添加檔案夾GUIX,直接從本章節教程配套例子的User檔案夾複制即可,此檔案夾主要是GUIX的應用部分,内容如下:
7.5.7 第3.6步,添加BSP驅動檔案
需要添加的BSP驅動檔案如下:
除了SDRAM,LTDC,EEPROM和觸摸兩個的檔案以外,還要添加bsp_tim_pwm.c,這個是用于設定PWM背光用的。
另外注意一點,bsp_tft_lcd.c檔案還關聯了一些字庫檔案,大家最好也将其添加到工程裡面。這些字庫檔案位于本章配套例子的User檔案夾下,大家直接複制到自己工程的工程裡面添加即可,添加後效果如下:
7.5.8 第3.7步,添加HAL庫檔案
相關BSP驅動關聯到的HAL庫檔案都添加了進來,簡單省事些,大家也可以把HAL庫所有檔案都添加進來:
7.5.9 第3.8步,添加預定義宏
C/C++檔案中添加的預定義宏如下:
- USE_HAL_DRIVER
- STM32F429xx
- USE_FULL_LL_DRIVER
- TX_ENABLE_FPU_SUPPORT 用于支援硬體FPU
- TX_ENABLE_STACK_CHECKING 用于棧檢測
- TX_INCLUDE_USER_DEFINE_FILE 用于包含tx_user.h
- GX_INCLUDE_USER_DEFINE_FILE 用于包含gx_user.h
ASM彙編檔案裡面添加的宏定義:
- TX_ENABLE_FPU_SUPPORT
7.5.10 第3.9步,添加頭檔案路徑
需要添加的路徑如下:
至此,我們需要的GUIX檔案都已經添加完畢。下面為大家介紹如何修改用于自己的闆子。
7.6 第4步:SDRAM驅動實作(用于顯存,動态記憶體和畫布)
一定要保證SDRAM大批量讀寫資料時是正常的,SDRAM的測試可以自己專門做一個工程測試下。對于SDRAM的驅動實作,可以學習我們寫的BSP驅動教程第39章:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 。
不管你使用的是鎂光的,海力士的,三星的,ISSI的或者華邦的,基本實作方法都是一樣的。教程配套的闆子使用的是鎂光的32位帶寬的SDRAM,如果想最大限度發揮STM32F429驅動SDRAM的性能,強烈建議使用32位帶寬的SDRAM,或者兩個16位SDRAM組成32位帶寬的SDRAM也是可以的。那SDRAM主要起到什麼作用呢?作用有二:
- 用作顯示屏的顯存
STM32F429的LTDC外接RGB接口屏是沒有顯存的,是以需要SDRAM用作顯存。如果使用者選擇STM32F429 LTDC的顔色格式是32位色ARGB8888,那麼所需要顯存大小(機關位元組)是:顯示屏寬 * 顯示屏高 * (32/8), 其中32/8是表示這種顔色格式的一個像素點需要4個位元組來表示。又比如配置顔色格式是16位色的RGB565,那麼需要的顯存大小是:顯示屏寬 * 顯示屏高 * (16/8),其中16/8是表示這種顔色格式的一個像素點需要2個位元組來表示。其它的顔色格式,依此類推。
- 用作GUIX動态記憶體和canvas幕布
GUIX要做的炫酷,是比較消耗動态記憶體的,是以使用者可以将SDRAM除了用于顯存以外的所有記憶體全部用作GUIX動态記憶體和canvas幕布。
============================================================
如果SDRAM的驅動測試已經沒有問題了,就可以将其添加到工程裡面了,開發闆使用的SDRAM驅動檔案是bsp_fmc_sdram.c。
添加到工程裡面後要配置設定SDRAM的使用,教程配套開發闆使用的是16MB,32位帶寬的SDRAM。
7.7 第5步:LTDC涉及到的引腳配置和時序配置
7.7.1 LTDC時序配置
使用者僅需配置LTDC涉及到的引腳和時序即可,配置函數封裝到下面兩個接口檔案的末尾。
- gx_display_driver_stm32f4_565rgb.c對應硬體RGB565接口。
- gx_display_driver_stm32f4_24xrgb.c對應硬體RGB888接口。
另外,由于開發闆配套了4.3寸,5寸和7寸屏顯示屏,是以要對這幾種尺寸的顯示屏做自适應,每個屏的時序配置都是不一樣的,具體實作在gx_display_driver_stm32f4_565rgb.c和gx_display_driver_stm32f4_24xrgb.c接口檔案末尾的,即函數LCD_LL_Init。大家在給自己的顯示屏移植時主要修改這個函數即可,引腳配置需要在這個函數裡面實作。下面我們再結合函數LCD_LL_Init的實作,講解下配置時要注意的一些問題,具體代碼如下:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: LCD_LL_Init
4. * 功能說明: 配置LTDC
5. * 形 參: 無
6. * 返 回 值: 無
7. * 筆 記:
8. * LCD_TFT 同步時序配置(整理自官方做的一個截圖,言簡意赅):
9. * ----------------------------------------------------------------------------
10. *
11. * Total Width
12. * <--------------------------------------------------->
13. * Hsync width HBP Active Width HFP
14. * <---><--><--------------------------------------><-->
15. * ____ ____|_______________________________________|____
16. * |___| | | |
17. * | | |
18. * __| | | |
19. * /|\ /|\ | | | |
20. * | VSYNC| | | | |
21. * |Width\|/ |__ | | |
22. * | /|\ | | | |
23. * | VBP | | | | |
24. * | \|/_____|_________|_______________________________________| |
25. * | /|\ | | / / / / / / / / / / / / / / / / / / / | |
26. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
27. * Total | | | |/ / / / / / / / / / / / / / / / / / / /| |
28. * Heigh | | | |/ / / / / / / / / / / / / / / / / / / /| |
29. * |Active| | |/ / / / / / / / / / / / / / / / / / / /| |
30. * |Heigh | | |/ / / / / / Active Display Area / / / /| |
31. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
32. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
33. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
34. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
35. * | | | |/ / / / / / / / / / / / / / / / / / / /| |
36. * | \|/_____|_________|_______________________________________| |
37. * | /|\ | |
38. * | VFP | | |
39. * \|/ \|/_____|______________________________________________________|
40. *
41. *
42. * 每個LCD裝置都有自己的同步時序值:
43. * Horizontal Synchronization (Hsync)
44. * Horizontal Back Porch (HBP)
45. * Active Width
46. * Horizontal Front Porch (HFP)
47. *
48. * Vertical Synchronization (Vsync)
49. * Vertical Back Porch (VBP)
50. * Active Heigh
51. * Vertical Front Porch (VFP)
52. *
53. * LCD_TFT 視窗水準和垂直的起始以及結束位置 :
54. * ----------------------------------------------------------------
55. *
56. * HorizontalStart = (Offset_X + Hsync + HBP);
57. * HorizontalStop = (Offset_X + Hsync + HBP + Window_Width - 1);
58. * VarticalStart = (Offset_Y + Vsync + VBP);
59. * VerticalStop = (Offset_Y + Vsync + VBP + Window_Heigh - 1);
60. *
61. ******************************************************************************************************
62. */
63. static void LCD_LL_Init(void)
64. {
65. /* 配置LCD相關的GPIO */
66. {
67. /* GPIOs Configuration */
68. /*
69. +------------------------+-----------------------+----------------------------+
70. + LCD pins assignment +
71. +------------------------+-----------------------+----------------------------+
72. | LCD429_TFT R0 <-> PI.15 | LCD429_TFT G0 <-> PJ.07 | LCD429_TFT B0 <-> PJ.12 |
73. | LCD429_TFT R1 <-> PJ.00 | LCD429_TFT G1 <-> PJ.08 | LCD429_TFT B1 <-> PJ.13 |
74. | LCD429_TFT R2 <-> PJ.01 | LCD429_TFT G2 <-> PJ.09 | LCD429_TFT B2 <-> PJ.14 |
75. | LCD429_TFT R3 <-> PJ.02 | LCD429_TFT G3 <-> PJ.10 | LCD429_TFT B3 <-> PJ.15 |
76. | LCD429_TFT R4 <-> PJ.03 | LCD429_TFT G4 <-> PJ.11 | LCD429_TFT B4 <-> PK.03 |
77. | LCD429_TFT R5 <-> PJ.04 | LCD429_TFT G5 <-> PK.00 | LCD429_TFT B5 <-> PK.04 |
78. | LCD429_TFT R6 <-> PJ.05 | LCD429_TFT G6 <-> PK.01 | LCD429_TFT B6 <-> PK.05 |
79. | LCD429_TFT R7 <-> PJ.06 | LCD429_TFT G7 <-> PK.02 | LCD429_TFT B7 <-> PK.06 |
80. -------------------------------------------------------------------------------
81. | LCD429_TFT HSYNC <-> PI.12 | LCDTFT VSYNC <-> PI.13 |
82. | LCD429_TFT CLK <-> PI.14 | LCD429_TFT DE <-> PK.07 |
83. -----------------------------------------------------
84. */
85. GPIO_InitTypeDef GPIO_Init_Structure;
86.
87. /*##-1- Enable peripherals and GPIO Clocks #################################*/
88. /* 使能LTDC和DMA2D時鐘 */
89. __HAL_RCC_LTDC_CLK_ENABLE();
90. __HAL_RCC_DMA2D_CLK_ENABLE();
91.
92. /* 使能GPIO時鐘 */
93. __HAL_RCC_GPIOI_CLK_ENABLE();
94. __HAL_RCC_GPIOJ_CLK_ENABLE();
95. __HAL_RCC_GPIOK_CLK_ENABLE();
96.
97. /* GPIOI 配置 */
98. GPIO_Init_Structure.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
99. GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
100. GPIO_Init_Structure.Pull = GPIO_NOPULL;
101. GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
102. GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
103. HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);
104.
105. /* GPIOJ 配置 */
106. GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \
107. GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | \
108. GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \
109. GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
110. GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
111. GPIO_Init_Structure.Pull = GPIO_NOPULL;
112. GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
113. GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
114. HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure);
115.
116. /* GPIOK 配置 */
117. GPIO_Init_Structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \
118. GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
119. GPIO_Init_Structure.Mode = GPIO_MODE_AF_PP;
120. GPIO_Init_Structure.Pull = GPIO_NOPULL;
121. GPIO_Init_Structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
122. GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;
123. HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure);
124. }
125.
126. /*##-2- LTDC初始化 #############################################################*/
127. {
128. LTDC_LayerCfgTypeDef pLayerCfg = {0};
129. uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP;
130. RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
131.
132. /* 支援6種面闆 */
133. switch (g_LcdType)
134. {
135. case LCD_35_480X320: /* 3.5寸 480 * 320 未使用 */
136. Width = 480;
137. Height = 272;
138. HSYNC_W = 10;
139. HBP = 20;
140. HFP = 20;
141. VSYNC_W = 20;
142. VBP = 20;
143. VFP = 20;
144. break;
145.
146. case LCD_43_480X272: /* 4.3寸 480 * 272 */
147. case LCD_50_480X272: /* 5.0寸 480 * 272 */
148. Width = 480;
149. Height = 272;
150.
151. HSYNC_W = 40;
152. HBP = 2;
153. HFP = 2;
154. VSYNC_W = 9;
155. VBP = 2;
156. VFP = 2;
157.
158. /* LCD 時鐘配置 */
159. /*
160. PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz
161. PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 1 * 420 = 420MHz
162. PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 420 / 7 = 60MHz
163. LTDC 時鐘 = PLLLCDCLK / RCC_PLLSAIDivR = 60 / 4 = 15MHz
164. */
165. /*
166. 重新整理率 = 15MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP))
167. = 15000000/((480 + 40 + 2 + 2)*(272 + 9 + 2 + 2))
168. = 15000000/(524*285)
169. = 100Hz
170.
171. */
172. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
173. PeriphClkInitStruct.PLLSAI.PLLSAIN = 420;
174. PeriphClkInitStruct.PLLSAI.PLLSAIR = 7;
175. PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_4;
176. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
177. {
178. Error_Handler(__FILE__, __LINE__);
179. }
180. break;
181.
182. case LCD_50_800X480: /* 5.0寸 800 * 480 */
183. case LCD_70_800X480: /* 7.0寸 800 * 480 */
184. Width = 800;
185. Height = 480;
186.
187. HSYNC_W = 96;
188. HBP = 10;
189. HFP = 10;
190. VSYNC_W = 2;
191. VBP = 10;
192. VFP = 10;
193.
194. /* LCD 時鐘配置 */
195. /*
196. PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz
197. PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 1 * 420 = 420MHz
198. PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 420 / 7 = 60MHz
199. LTDC 時鐘 = PLLLCDCLK / RCC_PLLSAIDivR = 60 / 4 = 15MHz
200. */
201. /*
202. 重新整理率 = 15MHz /((Width + HSYNC_W + HBP + HFP)*(Height + VSYNC_W + VBP + VFP))
203. = 15000000/((800 + 96 + 10 + 10)*(480 + 2 + 10 + 10))
204. = 15000000/(916*502)
205. = 32Hz
206.
207. 24位或者32位色選擇LTDC輸出15MHz,16位或者8位30MHz
208. */
209. PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
210. PeriphClkInitStruct.PLLSAI.PLLSAIN = 420;
211. PeriphClkInitStruct.PLLSAI.PLLSAIR = 7;
212. PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2;
213. if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
214. {
215. Error_Handler(__FILE__, __LINE__);
216. }
217. break;
218.
219. case LCD_70_1024X600: /* 7.0寸 1024 * 600,未使用 */
220. Width = 1024;
221. Height = 600;
222.
223. HSYNC_W = 2;
224. HBP = 157;
225. HFP = 160;
226. VSYNC_W = 2;
227. VBP = 20;
228. VFP = 12;
229.
230. break;
231.
232. default: /* 未使用 */
233. Width = 800;
234. Height = 480;
235.
236. HSYNC_W = 80;
237. HBP = 10;
238. HFP = 10;
239. VSYNC_W = 10;
240. VBP = 10;
241. VFP = 10;
242.
243. break;
244. }
245.
246. g_LcdHeight = Height;
247. g_LcdWidth = Width;
248.
249. /* 配置信号極性 */
250. hLTDC.Init.HSPolarity = LTDC_HSPOLARITY_AL; /* HSYNC 低電平有效 */
251. hLTDC.Init.VSPolarity = LTDC_VSPOLARITY_AL; /* VSYNC 低電平有效 */
252. hLTDC.Init.DEPolarity = LTDC_DEPOLARITY_AL; /* DE 低電平有效 */
253. hLTDC.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
254.
255. /* 時序配置 */
256. hLTDC.Init.HorizontalSync = (HSYNC_W - 1);
257. hLTDC.Init.VerticalSync = (VSYNC_W - 1);
258. hLTDC.Init.AccumulatedHBP = (HSYNC_W + HBP - 1);
259. hLTDC.Init.AccumulatedVBP = (VSYNC_W + VBP - 1);
260. hLTDC.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1);
261. hLTDC.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1);
262. hLTDC.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1);
263. hLTDC.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1);
264.
265. /* 配置背景層顔色 */
266. hLTDC.Init.Backcolor.Blue = 0;
267. hLTDC.Init.Backcolor.Green = 0;
268. hLTDC.Init.Backcolor.Red = 0;
269.
270. hLTDC.Instance = LTDC;
271.
272. /* 開始配置圖層 ------------------------------------------------------*/
273. /* 視窗顯示區設定 */
274. pLayerCfg.WindowX0 = 0;
275. pLayerCfg.WindowX1 = Width;
276. pLayerCfg.WindowY0 = 0;
277. pLayerCfg.WindowY1 = Height;
278.
279. /* 配置顔色格式 */
280. pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
281.
282. /* 顯存位址 */
283. pLayerCfg.FBStartAdress = FrameBufer;
284.
285. /* Alpha常數 (255 表示完全不透明) */
286. pLayerCfg.Alpha = 255;
287.
288. /* 無背景色 */
289. pLayerCfg.Alpha0 = 0; /* 完全透明 */
290. pLayerCfg.Backcolor.Blue = 0;
291. pLayerCfg.Backcolor.Green = 0;
292. pLayerCfg.Backcolor.Red = 0;
293.
294. /* 配置圖層混合因數 */
295. pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
296. pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
297.
298. /* 配置行列大小 */
299. pLayerCfg.ImageWidth = Width;
300. pLayerCfg.ImageHeight = Height;
301.
302. /* 配置LTDC */
303. if (HAL_LTDC_Init(&hLTDC) != HAL_OK)
304. {
305. /* 初始化錯誤 */
306. Error_Handler(__FILE__, __LINE__);
307. }
308.
309. /* 配置圖層1 */
310. if (HAL_LTDC_ConfigLayer(&hLTDC, &pLayerCfg, LTDC_LAYER_1) != HAL_OK)
311. {
312. /* 初始化錯誤 */
313. Error_Handler(__FILE__, __LINE__);
314. }
315. }
316.
317. }
- 第89到123行,主要是GPIO配置,注意DMA2D時鐘和LTDC時鐘别忘使能。
- 第133到263行,這部分程式的實作在本教程第4章的4.4.4小節裡面有詳細說明。
- 第133行,六種面闆的識别是在bsp_ts_touch.c檔案中實作的。大家自己配置時用不到這個,僅需提供一組時序參數和輸出時鐘即可,除非項目中需要切換不同顯示屏。
- 第246到247行,全局變量g_LcdWidth和g_LcdHeight在檔案bsp_tft_lcd.c檔案定義。如果大家自己移植時用不到檔案bsp_tft_lcd.c的話,需要自行定義這兩個全局變量(另外,此檔案裡面的背光設定函數也要自行實作),因為這兩個變量要被檔案gx_display_driver_stm32f4_565rgb.c和gx_display_driver_stm32f4_24xrgb.c所調用
- 第266到268行,STM32F429的圖層是由背景層,圖層1和圖層2組成,這裡配置的是背景層的顔色值,分别配置了R,G,B三原色的數值,範圍都是0-255。
- 第274到300行,主要是配置圖層。
- 第280行,如果使用RGG565顔色格式要配置為LTDC_PIXEL_FORMAT_RGB565。
如果使用ARGB8888顔色格式要配置為LTDC_PIXEL_FORMAT_ARGB8888。
7.7.2 如何驗證LTDC的時序配置是否正确
下面說一個最重要的問題,配置好時序了,怎麼檢查自己的配置是否成功了?使用者僅需在函數LCD_LL_Init裡面的如下代碼後面加上兩個函數:
/* 配置LTDC */
if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
{
/* 初始化錯誤 */
Error_Handler(__FILE__, __LINE__);
}
/* 下面是添加的 */
LCD_SetBackLight(BRIGHT_DEFAULT);
while(1);
加上這兩行代碼後,再将背景層設定為一個合适的顔色,建議設定成紅色,友善觀察:
/* 配置背景層顔色 */
hltdc_F.Init.Backcolor.Blue = 0;
hltdc_F.Init.Backcolor.Green = 0;
hltdc_F.Init.Backcolor.Red = 0xFF;
如果背景層可以正常顯示紅色,說明引腳和時序配置都是沒有問題的。如果不成功要從以下幾個方面着手檢查:
- 首先要清楚一點,目前的配置是否成功與SDRAM沒有任何關系,因為背景層還用不到SDRAM,圖層1和圖層2才需要SDRAM做顯存使用。
- 從硬體着手檢查,保證STM32F429晶片焊接沒問題,TFT接口一定要牢固,防止接觸不良,特别是使用FPC軟排線的時候,測試階段,軟排線越短越好。有時候也可能是顯示屏有問題,最好可以備兩個顯示屏測試。
- 從軟體配置着手檢查,檢視LTDC涉及到的所有引腳是否配置,引腳時鐘是否使能。有時候無法顯示也有可能是闆子硬體設計不規範導緻幹擾較大造成的,此時,可以降低LTDC所涉及到GPIO的速度等級。
如果顯示了,但是顯示的位置不正确,可以重新調整時序參數即可。
7.8 第6步:電阻屏和電容屏觸摸驅動的實作
本小節的實作基于本教程的第5章,目前驅動對電阻觸摸晶片STMPE811和電容觸摸晶片FT5X06、GT911和GT811的顯示屏都進行了支援。
實作比較簡單,因為GUIX的觸摸分按下,松手和移動三個事件,正好這幾款觸摸晶片的驅動也是分這三個事件,是以僅需修改下函數TOUCH_PutKey,所有顯示屏觸摸就都可以完美融合了。
7.8.1 添加GUIX的按下,松手和移動三個事件
檔案bsp_ts_touch.c裡的函數TOUCH_PutKey修改如下:
/*
*********************************************************************************************************
* 函 數 名: TOUCH_PutKey
* 功能說明: 将1個觸摸點坐标值壓入觸摸FIFO緩沖區。電阻觸摸屏形參是ADC值,電容觸摸屏形參是坐标值
* 形 參: _usX, _usY 坐标值
* 返 回 值: 無
*********************************************************************************************************
*/
#include "gx_api.h"
void TOUCH_PutKey(uint8_t _ucEvent, uint16_t _usX, uint16_t _usY)
{
#if 1
uint16_t xx, yy;
GX_EVENT event;
if (g_tTP.Enable == 1) /* 電阻屏。 形參是ADC值 */
{
xx = TOUCH_TransX(_usX, _usY);
yy = TOUCH_TransY(_usX, _usY);
}
else /* GT811,FTX06,GT911 電容觸摸走此分之 */
{
/* 無需轉換, 直接是坐标值 */
xx = _usX;
yy = _usY;
}
/* 按下, 移動和松手事件 */
switch (_ucEvent)
{
case TOUCH_DOWN:
event.gx_event_type = GX_EVENT_PEN_DOWN;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = 0xC0000000;
gx_system_event_send(&event);
break;
case TOUCH_MOVE:
event.gx_event_type = GX_EVENT_PEN_DRAG;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = 0xC0000000;
gx_system_event_fold(&event);
break;
case TOUCH_RELEASE:
event.gx_event_type = GX_EVENT_PEN_UP;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = 0xC0000000;
gx_system_event_send(&event);
break;
default:
break;
}
#else
省略未寫
#endif
}
特别注意0xC0000000,這個不是随便寫的一個值,要與本章7.8.1小節設定STM32_SCREEN_HANDLE數值一緻。
7.8.2 周期性調用觸摸掃描函數
電阻觸摸和電容觸摸的掃描函數是TOUCH_Scan和TOUCH_CapScan,為了實作使用了ThreadX和裸機時的一樣的調用方式,專門在啟動任務裡面周期性的調用函數bsp_ProPer1ms(SysTick_ISR),而SysTick_ISR裡面調用了bsp_RunPer1ms,然後bsp_RunPer1ms裡面調用掃描函數,即如下的調用關系:
代碼如下:
/*
*********************************************************************************************************
* 函 數 名: AppTaskStart
* 功能說明: 啟動任務。
* 形 參: thread_input 是在建立該任務時傳遞的形參
* 返 回 值: 無
優 先 級: 2
*********************************************************************************************************
*/
static void AppTaskStart (ULONG thread_input)
{
(void)thread_input;
/* 先挂起定時器組 */
#ifndef TX_NO_TIMER
tx_thread_suspend(&_tx_timer_thread);
#endif
/* 優先執行任務統計 */
OSStatInit();
/* 恢複定時器組 */
#ifndef TX_NO_TIMER
tx_thread_resume(&_tx_timer_thread);
#endif
/* 核心開啟後,恢複HAL裡的時間基準 */
HAL_ResumeTick();
/* 外設初始化 */
bsp_Init();
/* 建立任務 */
AppTaskCreate();
/* 建立任務間通信機制 */
AppObjCreate();
while (1)
{
/* 需要周期性處理的程式,對應裸機工程調用的SysTick_ISR */
bsp_ProPer1ms();
tx_thread_sleep(1);
}
}
7.8.3 如何将觸摸驅動移植到自己的闆子
通過前面的講解,移植觸摸驅動到自己的闆子上,最簡單的辦法是将開發闆與觸摸相關的檔案全部移植過來,然後在這些檔案的基礎上進行修改。下面分兩種情況進行說明:
- 電容屏觸摸的移植比較簡單,如果使用者用的觸摸IC跟開發闆一樣,直接拿來用即可,如果不一樣,需要先将觸摸IC的驅動實作,然後按照開發闆提供的GT911,GT811或者FT5X06的觸摸掃描函數,照葫蘆畫瓢實作一個即可。
- 電阻屏的移植稍麻煩些,如果使用者用的觸摸IC跟開發闆一樣,直接拿來用即可,如果不一樣,需要先将觸摸IC的驅動實作,然後仿照bsp_ts_stmpe811.c檔案提供觸摸按下狀态函數,X軸,Y軸的ADC數值讀取函數和清除觸摸中斷标志函數。最後用重新實作的這幾個函數替換bsp_ts_touch.c檔案中的原函數即可。另外要注意一點,這種方式實作後,雖然觸摸校準依然可以使用,但是開發闆的觸摸校準參數是儲存在EEPROM中的,使用者可以根據自己的實際情況選擇存儲媒體。另外,觸摸參數的儲存和讀取在bsp_ts_touch.c檔案末尾的函數TOUCH_SaveParam和TOUCH_LoadParam實作。
- 如果大家不想用開發闆實作的方案,想自己重新實作一個,也是沒問題的,注意跟GUIX關聯的方式。
7.9 第7步:GUIX底層接口函數和配置
GUI的底層接口函數和配置的實作在我們第1步中添加的底層驅動接口檔案中:
- gx_display_driver_stm32f4_565rgb.c 對應硬體RGB565接口。
- gx_display_driver_stm32f4_24xrgb.c 對應硬體RGB888接口。
這兩個檔案的實作套路是一樣的,我們這裡以gx_display_driver_stm32f4_565rgb檔案為例進行說明。
7.9.1 DMA2D使能宏定義GX_CHROMEART_ENABLE
檔案開頭的宏定義GX_CHROMEART_ENABLE用于使能DMA2D加速,測試階段可以将其關閉掉,關閉方法就是注釋掉宏定義#define GX_CHROMEART_ENABLE即可。
7.9.2 顯存位址宏定義FrameBufer
LCD的顯存位址設定是通過宏定義配置:
#define FrameBufer SDRAM_LCD_BUF1
其中SDRAM_LCD_BUF1的定義是在bsp_fmc_sdram.h,即:
#define FrameBufer SDRAM_LCD_BUF1
其中SDRAM_LCD_BUF1的定義是在bsp_fmc_sdram.h,即:
#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000)
#define EXT_SDRAM_SIZE (16 * 1024 * 1024)
/* LCD顯存,第1頁, 配置設定2M位元組 */
#define
宏定義FrameBufer是在函數LCD_LL_Init裡面配置顯存位址的時候被調用。
7.9.3 接口函數stm32f4_graphics_driver_setup_565rgb
這個函數是GUIX連接配接底層最直接的函數,也是配置的關鍵。實作代碼如下:
1. /*
2. ******************************************************************************************************
3. * 函 數 名: stm32f4_graphics_driver_setup_565rgb
4. * 功能說明: 驅動接口函數
5. * 形 參: ---
6. * 返 回 值: GX_SUCCESS
7. ******************************************************************************************************
8. */
9. UINT stm32f4_graphics_driver_setup_565rgb(GX_DISPLAY *display)
10. {
11. LCD_LL_Init();
12.
13. _gx_display_driver_565rgb_setup(display, (VOID*)STM32_SCREEN_HANDLE, stm32h7_565rgb_buffer_toggle);
14.
15. #if defined(GX_CHROMEART_ENABLE)
16. display -> gx_display_driver_pixelmap_blend = gx_chromeart_pixelmap_blend;
17. display -> gx_display_driver_pixelmap_draw = gx_chromeart_pixelmap_draw;
18. display -> gx_display_driver_canvas_copy = gx_chromeart_canvas_copy;
19.
20. display->gx_display_driver_horizontal_line_draw = gx_chromeart_horizontal_line_draw;
21. display -> gx_display_driver_vertical_line_draw = gx_chromeart_vertical_line_draw;
22. display -> gx_display_driver_8bit_glyph_draw = gx_chromeart_glyph_8bit_draw;
23. #endif
24.
25. return(GX_SUCCESS);
26. }
- 第11行,GPIO和LTDC初始化。
- 第13行,為GUIX和LTDC建立關聯。特别注意參數STM32_SCREEN_HANDLE,宏定義如下:
#define STM32_SCREEN_HANDLE 0xC0000000
本章7.8.1小節裡面的用到的0xC0000000就是從這裡來的,務必保證比對。
- 第16到22行,對一些底層函數做重定向,進而實作DMA2D加速。
7.9.4 通過DMA2D加速的幾個函數
通過DMA2D加速的幾個函數如下(這幾個函數,大家做移植的話,僅需注意變量g_LcdWidth和g_LcdHeight正确指派了):
- gx_chromeart_horizontal_line_draw
- gx_chromeart_vertical_line_draw
- gx_chromeart_canvas_copy
- gx_chromeart_pixelmap_draw
- gx_chromeart_pixelmap_blend
- gx_chromeart_glyph_8bit_draw
這裡我們以函數gx_chromeart_horizontal_line_draw為例進行說明:
static VOID gx_chromeart_horizontal_line_draw(GX_DRAW_CONTEXT *context, INT xstart, INT xend, INT ypos, INT width, GX_COLOR color)
{
uint32_t put;
int length;
GX_CANVAS *canvas = context->gx_draw_context_canvas;
put = (uint32_t) canvas->gx_canvas_memory;
put += (canvas->gx_canvas_x_resolution * 2 * ypos) + (xstart * 2);
length = xend - xstart + 1;
DMA2D->CR = DMA2D_R2M;
DMA2D->OCOLR = color;
/* 輸出層 */
DMA2D->OMAR = (uint32_t)put;
DMA2D->OOR = canvas->gx_canvas_x_resolution - length;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(length << 16) | (uint16_t)width;
DMA2D->CR |= DMA2D_CR_START;
while (DMA2D->CR & DMA2D_CR_START) {}
}
這裡主要實作了一個水準線的DMA2D加速,功能比較好了解。DMA2D的詳細介紹在本教程的第6章進行了非常詳細的說明,大家如下想了解每個配置語句的功能,可以深入學習第6章。
7.9.5 更新畫布canvas内容到LCD顯存
目前GUIX的顯示是采用的畫布機制,即GUIX先在畫布上把界面繪制好,然後将畫布中需要更新的區域繪制到LCD。這部分代碼是移植成功與否的關鍵(如果大家是用于F429平台,下面的代碼無需任何修改可以直接使用):
/*
*********************************************************************************************************
* 函 數 名: stm32h7_565rgb_buffer_toggle
* 功能說明: 更新canvas内容到LCD顯存
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
static void stm32h7_565rgb_buffer_toggle(GX_CANVAS *canvas, GX_RECTANGLE *dirty)
{
GX_RECTANGLE Limit;
GX_RECTANGLE Copy;
ULONG offset;
INT copy_width;
INT copy_height;
#if !defined(GX_CHROMEART_ENABLE)
INT row;
INT src_stride_ulongs;
INT dest_stride_ulongs;
#endif
ULONG *get;
ULONG *put;
gx_utility_rectangle_define(&Limit, 0, 0,
canvas->gx_canvas_x_resolution - 1,
canvas->gx_canvas_y_resolution - 1);
if (gx_utility_rectangle_overlap_detect(&Limit, &canvas->gx_canvas_dirty_area, &Copy))
{
Copy.gx_rectangle_left &= 0xfffe;
Copy.gx_rectangle_right |= 0x01;
copy_width = Copy.gx_rectangle_right - Copy.gx_rectangle_left + 1;
copy_height = Copy.gx_rectangle_bottom - Copy.gx_rectangle_top + 1;
/* 從canvas讀取更新區 */
offset = Copy.gx_rectangle_top * canvas->gx_canvas_x_resolution;
offset += Copy.gx_rectangle_left;
offset /= 2;
get = canvas ->gx_canvas_memory;
get += offset;
/* 從LCD顯存讀取要更新的區域,将canvas更新的資料複制進來 */
put = (ULONG *) FrameBufer;
offset = (canvas->gx_canvas_display_offset_y + Copy.gx_rectangle_top)* g_LcdWidth;
offset += canvas->gx_canvas_display_offset_x + Copy.gx_rectangle_left;
offset /= 2;
put += offset;
#if !defined(GX_CHROMEART_ENABLE)
src_stride_ulongs = canvas ->gx_canvas_x_resolution / 2;
dest_stride_ulongs = g_LcdWidth / 2;
copy_width /= 2;
for(row = 0; row < copy_height; row++)
{
memcpy(put, get, copy_width * 4);
put += dest_stride_ulongs;
get += src_stride_ulongs;
}
#else
DMA2D->CR = 0x00000000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)get;
DMA2D->OMAR = (uint32_t)put;
DMA2D->FGOR = canvas->gx_canvas_x_resolution - copy_width;
DMA2D->OOR = g_LcdWidth - copy_width;
/* 前景層和輸出區域都采用RGB565顔色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(copy_width << 16) | (uint16_t)copy_height;
DMA2D->CR |= DMA2D_CR_START;
while (DMA2D->CR & DMA2D_CR_START) {}
#endif
}
}
7.10 第8步:添加GUIX應用進行測試
介紹完了前面幾步,剩下就是添加應用代碼了。為了友善起見,大家直接使用本章教程配套例子裡面整理好的即可
7.10.1 添加應用檔案MainTask.C和MainTask.h
MainTask.h是GUIX相關的頭檔案和函數聲明,MainTask.c檔案裡面主要是GUIX的初始化:
1. /*
2. ******************************************************************************************************
3. * 變量
4. ******************************************************************************************************
5. */
6. GX_WINDOW *pScreen;
7. GX_WINDOW_ROOT *root;
8.
9. /*
10. ******************************************************************************************************
11. * 函 數 名: MainTask
12. * 功能說明: GUI主函數
13. * 形 參: 無
14. * 返 回 值: 無
15. ******************************************************************************************************
16. */
17. void MainTask(void)
18. {
19. /* 避免上電後瞬間的撕裂感 */
20. LCD_SetBackLight(0);
21.
22. /*
23. 觸摸校準函數預設是注釋掉的,電阻屏需要校準,電容屏無需校準。如果使用者需要校準電阻屏的話,執行
24. 此函數即可,會将觸摸校準參數儲存到EEPROM裡面,以後系統上電會自動從EEPROM裡面加載。
25. */
26. #if 0
27. LCD_SetBackLight(255);
28. LCD_InitHard();
29. TOUCH_Calibration(2);
30. #endif
31.
32. /*初始化配置 */
33. gx_initconfig();
34.
35. /* 配置顯示屏 */
36. gx_studio_display_configure(DISPLAY_1, stm32f4_graphics_driver_setup_565rgb,
37. LANGUAGE_CHINESE, DISPLAY_1_THEME_1, &root);
38.
39. /* 建立視窗 */
40. gx_studio_named_widget_create("window", (GX_WIDGET *)root, (GX_WIDGET **)&pScreen);
41.
42. /* 顯示根視窗 */
43. gx_widget_show(root);
44.
45. /* 啟動GUIX */
46. gx_system_start();
47.
48. tx_thread_sleep(100);
49. LCD_SetBackLight(255);
50.
51. while(1)
52. {
53. tx_thread_sleep(20);
54. }
55. }
- 第20行,為了避免上電時瞬間的撕裂感,這裡先關閉LCD背光,等首界面繪制完畢後再打開。
- 第26到30行,觸摸校準函數預設是注釋掉的,電阻屏需要校準,電容屏無需校準。如果使用者需要校準電阻屏的話,執行此函數即可,會将觸摸校準參數儲存到EEPROM裡面,以後系統上電會自動從EEPROM裡面加載。
- 第36到第49行,主要是GUIX的初始化,初始化完畢後打開背光。
7.10.2 添加動态記憶體配置和Canvas畫布配置檔案
動态記憶體和Canvas畫布配置都放在了檔案App_SysFunctions.c檔案裡面實作。
1. /*
2. ******************************************************************************************************
3. * 動态記憶體配置設定
4. ******************************************************************************************************
5. */
6. #define GUI_NUMBYTES 1024*1024*8 /* 設定動态記憶體大小 */
7. #define Canvas_Memory 0xC0400000 /* 設定Canvas位址 */
8. TX_BYTE_POOL memory_pool;
9. uint8_t *MemoryBlock = (uint8_t *)(0xC0000000 + 1024*1024*8); /* 動态記憶體位址 */
10.
11.
12. /*
13. ******************************************************************************************************
14. * 變量
15. ******************************************************************************************************
16. */
17. extern GX_STUDIO_DISPLAY_INFO guiapp_display_table[1];
18.
19.
20. /*
21. ******************************************************************************************************
22. * 動态記憶體函數
23. ******************************************************************************************************
24. */
25. VOID *memory_allocate(ULONG size)
26. {
27. VOID *memptr;
28.
29. if (tx_byte_allocate(&memory_pool, &memptr, size, TX_NO_WAIT) == TX_SUCCESS)
30. {
31. return memptr;
32. }
33. return NULL;
34. }
35.
36. void memory_free(VOID *mem)
37. {
38. tx_byte_release(mem);
39. }
40.
41. /*
42. ******************************************************************************************************
43. * 函 數 名: gx_initconfig
44. * 功能說明: GUIX
45. * 形 參: 無
46. * 返 回 值: 無
47. ******************************************************************************************************
48. */
49. void gx_initconfig(void)
50. {
51. /* 初始化記憶體池 */
52. tx_byte_pool_create(&memory_pool, "MemoryBlock", MemoryBlock, GUI_NUMBYTES);
53.
54. /* 初始化GUIX */
55. gx_system_initialize();
56.
57. /* 注冊動态記憶體申請和釋放函數 */
58. gx_system_memory_allocator_set(memory_allocate, memory_free);
59.
60. /* 自适應不同分辨率顯示屏 */
61. switch (g_LcdType)
62. {
63.
64. case LCD_43_480X272: /* 4.3寸 480 * 272 */
65. case LCD_50_480X272: /* 5.0寸 480 * 272 */
66. guiapp_display_table[0].x_resolution = 480;
67. guiapp_display_table[0].y_resolution = 272;
68. break;
69.
70. case LCD_50_800X480: /* 5.0寸 800 * 480 */
71. case LCD_70_800X480: /* 7.0寸 800 * 480 */
72. guiapp_display_table[0].x_resolution = 800;
73. guiapp_display_table[0].y_resolution = 480;
74. break;
75.
76. default:
77. break;
78. }
79.
80. guiapp_display_table[0].canvas_memory = (GX_COLOR *)Canvas_Memory;
81. }
- 第6到第9行,設定動态記憶體位址和畫布的位址。
教程配套的STM32F429闆子有16MB的SDRAM空間。
- 前4MB空間用于顯存,位址0xC0000000。
- 中間4MB空間用于Canvas畫布,位址0xC0000000 + 1024*1024*4 = 0xC0400000。
- 最後8MB空間用動态記憶體,位址0xC0000000 + 1024*1024*8 = 0xC0800000。
- 第17行,這個是在GUIX Studio生成的xxxxx_resources.c檔案裡面。注意要比對。
- 第25到39行,動态記憶體的申請和釋放函數。然後通過gx_system_memory_allocator_set進行注冊。
- 第61到78行,實作了個簡單的不同顯示屏自适應。
- 第80行,設定Canvas畫布位址。
7.10.3 添加GUIX Studio生成的檔案
使用GUIX Studio生成了4個檔案:
- guiapp_resources.h
- guiapp_resources.c
- guiapp_specifications.h
- guiapp_specifications.c
後面章節再為大家介紹GUIX Studio生成的這幾個檔案。作為移植,大家進行将其加到移植工程裡面驗證是否正常即可。
7.10.4 BSP初始化并建立GUIX任務
BSP初始化主要涉及到SDRAM初始化,觸摸初始化,LTDC初始化(放到了GUIX底層驅動接口檔案裡面了),背光開啟和EEPROM初始化(用于存儲電阻觸摸屏校準參數):
/*
*********************************************************************************************************
* 函 數 名: bsp_Init
* 功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
bsp_InitDWT(); /* 初始化DWT時鐘周期計數器 */
bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
bsp_InitUart(); /* 初始化序列槽 */
bsp_InitExtIO(); /* 初始化FMC總線74HC574擴充IO. 必須在 bsp_InitLed()前執行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitTimer(); /* 初始化滴答定時器 */
bsp_InitExtSDRAM(); /* 初始化SDRAM */
bsp_InitI2C(); /* 初始化I2C總線,用于EEPROM */
TOUCH_InitHard();
/* 延遲200ms再點亮背光,避免瞬間高亮 */
bsp_DelayMS(200);
LCD_SetBackLight(255);
}
GUIX任務的建立在main.c檔案裡面實作,配置如下:
#define APP_CFG_TASK_GUI_PRIO 5u
#define APP_CFG_TASK_GUI_STK_SIZE 4096u
static TX_THREAD AppTaskGUITCB;
static uint64_t AppTaskGUIStk[APP_CFG_TASK_GUI_STK_SIZE/8];
/**************建立GUIX任務*********************/
tx_thread_create(&AppTaskGUITCB, /* 任務控制塊位址 */
"App Task GUI", /* 任務名 */
AppTaskGUI, /* 啟動任務函數位址 */
0, /* 傳遞給任務的參數 */
&AppTaskGUIStk[0], /* 堆棧基位址 */
APP_CFG_TASK_GUI_STK_SIZE, /* 堆棧空間大小 */
APP_CFG_TASK_GUI_PRIO, /* 任務優先級*/
APP_CFG_TASK_GUI_PRIO, /* 任務搶占閥值 */
TX_NO_TIME_SLICE, /* 不開啟時間片 */
TX_AUTO_START); /* 建立後立即啟動 */
/*
*********************************************************************************************************
* 函 數 名: AppTaskGUI
* 功能說明: GUI應用任務
* 形 參: thread_input 建立該任務時傳遞的形參
* 返 回 值: 無
優 先 級: 5
*********************************************************************************************************
*/
static void AppTaskGUI(ULONG thread_input)
{
MainTask();
}
7.10.5 自動建立的GUIX系統任務。
初始化了GUIX後,會自動建立一個GUIX任務,對于這點,大家移植的使用要特别注意。此任務的優先級和任務堆棧大小是在gx_port.h檔案裡面定義的。
7.11 顯示屏閃爍問題解決方法
如果大家調試狀态下或者剛下載下傳GUIX的程式到STM32H7/STM32F429裡面時,出現螢幕會閃爍,或者說抖動,這個是正常現象。詳見此貼的說明:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=16892 。
如果顯示屏長時間處于抖動狀态,說明LTDC的時鐘配置高了或者低了(高的機率居多),可以将LTDC時鐘降低一半或者提高一倍進行測試。配置方法看本教程第4章的4.4.3小節。
7.12 避免顯示屏上電瞬間高亮和撕裂感
大家使用顯示屏的時候,這兩個問題很容易遇到,這裡為大家提供個解決辦法,提升使用者體驗。
7.12.1 上電瞬間高亮解決辦法
這個問題并不是軟體配置造成的,通過條件PWM背光也是無法解決的。解決辦法是闆子上後,先不要開啟PWM,延遲200ms後再打開LCD的背光即可,注意時間不可以太短,太短沒效果,大家可以根據實際情況做條件。本章配套程式是放在bsp.c檔案的函數bsp_Init裡面做了處理。
/*
*********************************************************************************************************
* 函 數 名: bsp_Init
* 功能說明: 初始化所有的硬體裝置。該函數配置CPU寄存器和外設的寄存器并初始化一些全局變量。隻需要調用一次
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 省略未寫 */
/* 延遲200ms再點亮背光,避免瞬間高亮 */
bsp_DelayMS(200);
LCD_SetBackLight(255);
}
7.12.2 上電瞬間撕裂感解決辦法
有時候界面設計比較複雜時,開機後不能保證所有的控件同時加載出來,界面會有種撕裂的感覺,這個時候有個比較好的解決思路,GUIX初始化配置前關閉背光,初始化完畢并且首界面繪制完畢後再打開背光,使用者體驗就會好很多。本章配套程式是放在MainTask.c檔案的函數MainTask裡面做了處理。
/*
*********************************************************************************************************
* 函 數 名: MainTask
* 功能說明: GUI主函數
* 形 參: 無
* 返 回 值: 無
*********************************************************************************************************
*/
void MainTask(void)
{
/* 避免上電後瞬間的撕裂感 */
LCD_SetBackLight(0); 0表示關閉LED
/* 省略未寫 */
tx_thread_sleep(100);
LCD_SetBackLight(255); 255表示最亮
while(1)
{
tx_thread_sleep(20);
}
}
7.13 實驗例程
(注,如果是電阻屏,需要做觸摸校準,校準方法看本教程附件章節A)
本章節配套了如下幾個例子供大家移植參考:
- V6-2004_Threadx Kernel Template
ThreadX核心模闆,用于大家移植GUIX的參考Demo,含有GCC,IAR,MDK AC5和AC6四個版本工程。
- V6-2005_GUIX Template(RGB565)
GUIX模闆例子,采用的硬體RGB565接口,含有GCC,IAR,MDK AC5和AC6四個版本工程。
- V6-2006_GUIX Template(ARGB8888)
GUIX模闆例子,采用的硬體ARGB8888接口,含有GCC,IAR,MDK AC5和AC6四個版本工程。
- V6-2007_GUIX Studio Template
GUIX Studio工程模闆,設計界面後,生成的檔案可直接添加到MDK,IAR和GCC軟體平台使用。
顯示效果如下,800*480分辨率:
IAR,MDK AC5和AC6工程可以序列槽列印任務執行情況:按開發闆的按鍵K1可以列印,波特率 115200,資料位 8,奇偶校驗位無,停止位 1:
Embedded Studio(GCC)平台的序列槽列印是通過其調試元件SEGGER RTT做的序列槽列印,速度也非常快,列印效果如下:
展示裡面有亂碼是因為Embedded Studio不支援中文。
7.14 總結
本章節為大家講解的内容涉及到的知識較多,資訊量較大,部分知識點沒有弄明白是沒有關系的,但是一定要掌握ThreadX核心和ThreadX GUIX工程的架構設計,掌握後再分析細節,事半功倍。
如果可以的話,最好移植一個與教程配套開發闆不一樣的顯示屏和觸摸IC,這樣對于本章的認識将更加全面。