前言
之前研究qemu的目的之一就是想用用qemu的stm32二次开发版本进行LCD显示实验。但是真的看了qemu stm32的源码后,发现并不支持LCD驱动的。所以我考虑是否由我自己来添加LCD驱动仿真,进行qemu二次开发。而步骤1就是我要先自己玩下基于stm32的LCD驱动应用编程。而我之前买oled屏幕后也买过一块stm32F407的开发板。oled能正常驱动,并且翻出了10年前买的2.4寸tft屏幕(80接口8bit的ili9325)也能正常驱动。
然后我发现就简单的显示内容没有动画效果,觉得不好玩,于是想起来2019年底下载过一个GUI开源软件GuiLite。然后2021年了我去看看它是否在不断的更新。所以我主要分析的是2019年底的版本,2021最新版也看了下,基础内容差距不大。
当然我也尝试了下将GuiLite移植到stm32F407开发板上,按Doc中help截图的操作步骤,还是很容易的。我现在看似在玩应用,其实我研究的还是底层库源码设计机制,所以我定义的一年视觉相关底层的的研究方向没有跑偏哈~

GuiLite源码研究
首先要明确目标,就是我分析GuiLite源码的目的是想了解GUI的设计原理。因为让我直接写个GUI引擎框架,我暂时不会。因为好奇,所以要去了解,毕竟他就5000行代码搞定的事情,我觉得很神奇。
我看了几个例子后,通过调试跟进源码,基本上已经了解了他的设计方法,让我自己设计的话我也有了方向。虽然源码还没有全看完,但是surface及widgets基本控件的原理都已经了解了,此次的目标已达成。另外的好处是,对这些控件的源码分析后,自己也可以比较灵活的去调用API做些小作品,蛮好玩的。我就喜欢这样小巧的代码,麻雀虽小五脏俱全。如下是我过程中分析源码的笔记,取其精华去其糟粕。我也发现了些bug,以及我觉得某些点,它还有继续完善的空间。
A.HelloStar工程
范围检查技巧
宽度和高度超范处理
x0 = (x0 < 0) ? 0 : x0;
y0 = (y0 < 0) ? 0 : y0;
x1 = (x1 > (m_width - 1)) ? (m_width - 1) : x1;
y1 = (y1 > (m_height - 1)) ? (m_height - 1) : y1;
随机数不超过范围的技巧,用取余数
m_x = m_start_x = rand() % UI_WIDTH;
m_y = m_start_y = rand() % UI_HEIGHT;
物体移动的技巧
先清除之前的绘制变成当前的背景,再重新绘制新的。至于只有一个图层为黑色底色的,就是直接画黑色,就是清除的意思,然后再重新绘制新的物体图像,看上去就是移动的效果。
B.HelloLayers工程
关于Layer的处理技巧
若有2层surface,则一开始需要申请Z_ORDER_LEVEL_1,然后就会进入如下的for循环,会为m_layers[i].fb分配内存空间。
若有3层surface,则一开始需要申请Z_ORDER_LEVEL_2。
void set_surface(Z_ORDER_LEVEL max_z_order, c_rect layer_rect)
{
m_max_zorder = max_z_order;
if (m_display && (m_display->m_surface_cnt > 1))
{
m_fb = calloc(m_width * m_height, m_color_bytes);
}
for (int i = Z_ORDER_LEVEL_0; i < m_max_zorder; i++)
{//Top layber fb always be 0
ASSERT(m_layers[i].fb = calloc(layer_rect.width() * layer_rect.height(), m_color_bytes));
m_layers[i].rect = layer_rect;
}
}
这个内存在fill_rect函数中进行赋值的,m_layers[z_order].fb。若surface只有1级的话,不会为fb赋值。首先要思考下为什么要为fb赋值,其实就是备份的意思。
if (z_order == m_top_zorder)
{
int x, y;
c_rect layer_rect = m_layers[z_order].rect;
unsigned int rgb_16 = GL_RGB_32_to_16(rgb);
for (y = y0; y <= y1; y++)
{
for (x = x0; x <= x1; x++)
{
if (layer_rect.pt_in_rect(x, y))
{
if (m_color_bytes == 4)
{
((unsigned int*)m_layers[z_order].fb)[(y - layer_rect.m_top) * layer_rect.width() + (x - layer_rect.m_left)] = rgb;
}
else
{
((unsigned short*)m_layers[z_order].fb)[(y - layer_rect.m_top) * layer_rect.width() + (x - layer_rect.m_left)] = rgb_16;
}
}
}
}
return fill_rect_on_fb(x0, y0, x1, y1, rgb);
}
在draw_pixel函数中也有对fb赋值。也就是说,所有绘制的地方,都会对fb进行更新值,记录最新的值。技巧就是判断if (z_order == m_max_zorder)则直接return绘制结果。若为单层,则此条件一定满足,就不会备份fb了,而对于多层则要在更新图像的时候备份fb。目的是为了将上一层级消除的时候对下一层级进行还原。
if (z_order == m_max_zorder)
{
return draw_pixel_on_fb(x, y, rgb);
}
if (z_order > (unsigned int)m_top_zorder)
{
m_top_zorder = (Z_ORDER_LEVEL)z_order;
}
if (m_layers[z_order].rect.pt_in_rect(x, y))
{
c_rect layer_rect = m_layers[z_order].rect;
if (m_color_bytes == 4)
{
((unsigned int*)(m_layers[z_order].fb))[(x - layer_rect.m_left) + (y - layer_rect.m_top) * layer_rect.width()] = rgb;
}
else
{
((unsigned short*)(m_layers[z_order].fb))[(x - layer_rect.m_left) + (y - layer_rect.m_top) * layer_rect.width()] = GL_RGB_32_to_16(rgb);
}
}
对于c_rect对象的还原方法是overlapped_rect,需要调用2句函数,一句是创建一个rect对象,目的是设置工作局域,另外一句是设置要还原的图层。
c_rect overlapped_rect(LAYER_1_X, LAYER_1_Y, LAYER_1_WIDTH, LAYER_1_HEIGHT);
s_surface->show_layer(overlapped_rect, Z_ORDER_LEVEL_0);
可以看到在show_layer中会取出m_layers[z_order].fb,重写到LCD上。其实就是还原。
int show_layer(c_rect& rect, unsigned int z_order)
{
ASSERT(z_order >= Z_ORDER_LEVEL_0 && z_order < Z_ORDER_LEVEL_MAX);
c_rect layer_rect = m_layers[z_order].rect;
ASSERT(rect.m_left >= layer_rect.m_left && rect.m_right <= layer_rect.m_right &&
rect.m_top >= layer_rect.m_top && rect.m_bottom <= layer_rect.m_bottom);
void* fb = m_layers[z_order].fb;
int width = layer_rect.width();
for (int y = rect.m_top; y <= rect.m_bottom; y++)
{
for (int x = rect.m_left; x <= rect.m_right; x++)
{
unsigned int rgb = (m_color_bytes == 4) ? ((unsigned int*)fb)[(x - layer_rect.m_left) + (y - layer_rect.m_top) * width] : GL_RGB_16_to_32(((unsigned short*)fb)[(x - layer_rect.m_left) + (y - layer_rect.m_top) * width]);
draw_pixel_on_fb(x, y, rgb);
}
}
return 0;
}
基于HelloLayers将Hellostar添加入UIcode.c,变成双图层,但是底层图层是动态的。修改后,遇到的问题是,star绘制的时候会擦除顶层的图片。
load_resource();
draw_on_layer_0();
while(1) {
stars[0].move();
thread_sleep(70);
cnt++;
if (cnt % 60 == 0)
{
draw_on_layer_1();
layer1 = 0;
}
if (cnt % 91 == 0)
{
clear_layer_1();
layer1 = 1;
}
if (cnt >= 32767)
{
cnt = 0;
}
见图

然后想到了办法临时解决下。方法就是star运动绘制后它不是会更新顶层区域的图像嘛,所有我的修改时,当顶层小窗口显示时,下一层star重绘后,我立即重绘下顶层图像。可以解决如上问题,但是感觉刷屏比较频繁,屏幕有闪烁感。这个问题要等我全部看完GuiLite看看还有哪些API可以用来更好的解决我遇到问题。
load_resource();
draw_on_layer_0();
while(1) {
stars[0].move();
if (layer1 == 0)
draw_on_layer_1();
thread_sleep(70);
cnt++;
if (cnt % 60 == 0)
{
layer1 = 0;
}
if (cnt % 91 == 0)
{
clear_layer_1();
layer1 = 1;
}
if (cnt >= 32767)
{
cnt = 0;
}
}
C.HelloWidgets学习窗口对象的原理
链表归递的技巧
UI窗口对象的处理相关函数中会看到链表归递,其实归递函数我平时都不太用的,常用的还是数组,依次扫描。
通过child->show_window()进行归递,退出条件为child==null,依次扫描的目的是为每个对象调用on_paint进行绘制。而双链表对象是通过add_child_2_tail函数添加到双链表末尾的。
void c_wnd::show_window(){
if (ATTR_VISIBLE == (m_attr & ATTR_VISIBLE))
{
on_paint();
c_wnd *child = m_top_child;
if ( 0 != child )
{
while ( child )
{
child->show_window();
child = child->m_next_sibling;
}
上一篇:01 STM32CubeMX 安装和配置
下一篇:上拉、下拉电阻的原理和作用
推荐阅读最新更新时间:2026-03-20 11:34
- 使用 ON Semiconductor 的 FAN2518S 的参考设计
- LTC1530S8、3.3V/3A 稳压器
- 使用 ON Semiconductor 的 ADP3167 的参考设计
- 使用 Analog Devices 的 LT3420EDD 的参考设计
- 基于Kinetis® M的低成本单相电表参考设计
- LTC3708、具有上升/下降轨跟踪功能的 2.5V/15A 和 1.2V/15A 稳压器
- NXQ1TXH5插件板
- 应变仪仪表放大器
- WRL-13287,基于 ESP8266 802.11 无线局域网的 SparkFun Wi-Fi Shield
- 4.1W、3-LED 通用 LED 照明驱动器
- 大声量 | 2023机器视觉技术与应用峰会议程新鲜出炉!
- 艾利特CSF系列力控协作机器人来了!
- MTBF突破4万小时!这家工业级3D相机品牌产品稳定性获权威机构认可
- 英特尔创始人、“摩尔定律”提出者戈登·摩尔去世
- 本周机器人行业大事件TOP5
- STM32待机模式Standby Mode与ADC DMA模式测试备忘
- STM32F429DISCO与STemWin开发备忘
- STM32与FreeRTOS学习备忘,xSemaphoreGiveFromISR
- STM32F429-DISCO上手,stm32cubeMX与IAR学习,中断及Printf
- Artilux GeSi 3D传感技术 大幅增强人眼安全保障



ffmpeg基础库编程开发_20140307
现代雷达系统的信号设计
BFR340T






京公网安备 11010802033920号