【ThreadX全家桶】GuiX移植
前言
本篇文章的知识基本全部来源于《安富莱_STM32-V6开发板ThreadX GUIX教程》,有兴趣的同学可以前往学习。
此次GUIX移植未使用DMA2D做加速处理,后期深入了解后再实现DMA2D部分。
一、添加GUIX文件
因为STM32CubeMX没有GuiX包,所以需要从GitHUB下载GuiX源代码,手动将源文件添加到工程。
二、添加GUIX Studio生成的4个文件
GUIX Studio的操作方法参考《安富莱_STM32-V6开发板ThreadX GUIX教程》,将生成的4个文件添加到工程并添加路径。
三、显示驱动分析
LCD驱动的核心工作就是将画布中脏区域的数据更新到显存,该过程可分为两步实现:
①分析比较出画布脏的区域
②将脏区域拷贝到显存上相应的地方
代码如下(示例):
static void stm32f4_24xrgb_buffer_toggle(GX_CANVAS *canvas, GX_RECTANGLE *dirty)
{
GX_RECTANGLE Limit;
GX_RECTANGLE Copy;
ULONG offset;
INT copy_width;
INT copy_height;
INT row;
INT src_stride_ulongs;
INT dest_stride_ulongs;
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)) //此服务将检测提供的矩形是否有重叠。 如果发现重叠,服务将返回 GX_TRUE 和重叠矩形。
{
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 + Copy.gx_rectangle_left;
get = canvas ->gx_canvas_memory + 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;
put += offset; //计算脏区域 映射到 显存 上的起始地址
src_stride_ulongs = canvas ->gx_canvas_x_resolution;
dest_stride_ulongs = g_LcdWidth;
for(row = 0; row < copy_height; row++)
{
memcpy(put, get, copy_width * 4);
put += dest_stride_ulongs;
get += src_stride_ulongs;
}
}
}
GUIX 为所有支持颜色深度和格式的每个低级别绘图函数都提供默认软件实现。所以需要将新的驱动进行注册,用于覆盖GUIX的默认驱动。根据自己LCD像素格式选择合适的setup函数,LAYERADDR为显存地址。
代码如下(示例):
UINT stm32f4_graphics_driver_setup_24xrgb(GX_DISPLAY *display)
{
_gx_display_driver_32argb_setup(display, (VOID*)LAYERADDR, stm32f4_24xrgb_buffer_toggle);
return(GX_SUCCESS);
}
四、触摸驱动分析
GuiX是事件驱动模式,所以当发生屏幕触摸时,需要发送相应的事件到GuiX协议栈。触摸事件共分为三类:按压、释放和移动。
代码如下(示例):
void TouchDrive(void)
{
static uint8_t s_tp_down = 0;
static GX_EVENT event;
Touch_Scan(); //循环检测,坐标保存在touchInfo结构体
event.gx_event_payload.gx_event_pointdata.gx_point_x = touchInfo.x[0];
event.gx_event_payload.gx_event_pointdata.gx_point_y = touchInfo.y[0];
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = LAYERADDR;
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15) == GPIO_PIN_SET) //松手
{
if (s_tp_down == 1)
{
s_tp_down = 0;
event.gx_event_type = GX_EVENT_PEN_UP;
gx_system_event_send(&event);
}
}
else
{
if (s_tp_down == 0) //按下
{
s_tp_down = 1;
event.gx_event_type = GX_EVENT_PEN_DOWN;
gx_system_event_send(&event);
}
else //移动
{
event.gx_event_type = GX_EVENT_PEN_DRAG;
gx_system_event_send(&event);
}
}
}
五、GUIX测试函数
1.内存分配说明
GUIX项目中,内存需要分为四部分
①显存,LAYER0ADDR(0xD0000000)
②画布,Canvas_Memory 0xD0200000
③GUIX组件等活动所需的动态内存区域,地址0xD0300000,大小8M
④剩余内存,保留
2.动态内存申请与释放函数
1、GUIX的有关活动需要申请与释放内存,使用ThreadX提供的内存分配相关API封装两个内存分配与释放函数,供GUIX调用。
代码如下(示例):
TX_BYTE_POOL memory_pool;
VOID *memory_allocate(ULONG size)
{
VOID *memptr;
if (tx_byte_allocate(&memory_pool, &memptr, size, TX_NO_WAIT) == TX_SUCCESS)
{
return memptr;
}
return NULL;
}
void memory_free(VOID *mem)
{
tx_byte_release(mem);
}
3、应用程序
下面提供一个DEMO程序,显示配置与组件创建两个函数由GUIX Studio生成的文件定义,用户只需填好参数即可使用,函数gx_studio_display_configure的参数在xxx_resources.h中定义,函数gx_studio_named_widget_create的第一个参数与结构体xxx_widget_table下结构体window_define的成员保持一致。
代码如下(示例):
uint8_t *MemoryBlock = (uint8_t *)(0xD0300000); /* 动态内存地址 */
#define GUI_NUMBYTES 1024*1024*8 /* 设置动态内存大小 */
#define Canvas_Memory 0xD0200000 /* 设置Canvas地址 */
GX_WINDOW *pScreen;
GX_WINDOW_ROOT *root;
void GuixDemo(void)
{
Touch_Init(); //触摸IO初始化
tx_byte_pool_create(&memory_pool, "MemoryBlock", MemoryBlock, GUI_NUMBYTES); /* 初始化内存池 */
gx_system_memory_allocator_set(memory_allocate, memory_free); /* 注册动态内存申请和释放函数 */
gx_system_initialize(); /* 初始化GUIX */
guiapp_display_table[0].canvas_memory = (GX_COLOR *)Canvas_Memory; /* 设置画布地址 */
gx_studio_display_configure(0, stm32f4_graphics_driver_setup_24xrgb, LANGUAGE_ENGLISH, DISPLAY_1_THEME_1, &root);
gx_studio_named_widget_create("window", (GX_WIDGET *)root, (GX_WIDGET **)&pScreen);
gx_widget_show(root);
gx_system_start();
while(1)
{
TouchDrive(); //GuiX触摸驱动
tx_thread_sleep(20);
}
}
注:
1、LTDC只使用一个层,使用两个层时,画面刷新时会抽搐
2、触摸芯片的检测函数一般使用IO模拟IIC,其延时函数可能会被Keil优化掉,所以需要在延时函数中使用volatile修饰局部变量。