mirror of
https://gitee.com/zlgopen/awtk.git
synced 2024-11-29 18:48:09 +08:00
update docs
This commit is contained in:
parent
685ab4ac24
commit
e32c8b81f7
88
docs/lcd.md
88
docs/lcd.md
@ -1,42 +1,87 @@
|
||||
## LCD接口的三种实现方式
|
||||
## LCD 接口的四种实现方式
|
||||
|
||||
LCD是对显示设备的抽象,提供了基本的绘图函数。自己实现一个LCD虽然不难,但是需要花费不少功夫,所以AWTK提供了几种缺省的实现,利用这些缺省的实现,在移植到新的平台时,一般只需要很少的代码就行了。下面我们介绍一下几种常见的LCD实现方式:
|
||||
LCD 是对显示设备的抽象,提供了基本的绘图函数。自己去实现 LCD 接口虽然不难,但是需要花费不少功夫,AWTK 提供了几种缺省的实现,利用这些缺省的实现,在移植到新的平台时,一般只需要很少的代码就行了。
|
||||
|
||||
### 一、基于寄存器实现的LCD
|
||||
下面我们介绍一下几种常见的 LCD 实现方式:
|
||||
|
||||
在低端的嵌入式平台上,内存只有几十K,没有足够的内存使用framebuffer,通常直接向寄存器中写入坐标和颜色数据。lcd\_reg.inc提供了基于寄存器实现的LCD,用它实现不同平台的LCD时,只需要提供两个宏即可:
|
||||
### 一、基于寄存器实现的 LCD
|
||||
|
||||
在低端的嵌入式平台上,内存只有几十 KB,没有足够的内存使用 framebuffer,通常直接向寄存器中写入坐标和颜色数据。lcd\_reg.inc 提供了基于寄存器实现的 LCD,用它实现不同平台的 LCD 时,只需要提供两个宏即可:
|
||||
|
||||
* set\_window\_func 设置要写入颜色数据的区域,相对于每次设置坐标而言,可以极大提高工作效率。
|
||||
* write\_data\_func 写入颜色数据。
|
||||
|
||||
下面是STMF103ze上LCD的实现,这里把set\_window\_func定义为TFT\_SetWindow,把write\_data\_func定义为TFT\_WriteData:
|
||||
下面是 STMF103ze 上 LCD 的实现,这里把 set\_window\_func 定义为 TFT\_SetWindow,把 write\_data\_func 定义为 TFT\_WriteData:
|
||||
|
||||
```
|
||||
#include "gui.h"
|
||||
#include "lcd_driver.h"
|
||||
|
||||
#include "base/mem.h"
|
||||
#include "tftlcd.h"
|
||||
#include "tkc/mem.h"
|
||||
#include "lcd/lcd_reg.h"
|
||||
|
||||
typedef uint16_t pixel_t;
|
||||
#define set_window_func TFT_SetWindow
|
||||
#define write_data_func TFT_WriteData
|
||||
|
||||
#include "blend/rgb565.inc"
|
||||
#define LCD_FORMAT BITMAP_FMT_BGR565
|
||||
#define pixel_from_rgb(r, g, b) \
|
||||
((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
|
||||
#define pixel_to_rgba(p) \
|
||||
{ (0xff & ((p >> 11) << 3)), (0xff & ((p >> 5) << 2)), (0xff & (p << 3)) }
|
||||
|
||||
#define set_window_func LCD_Set_Window
|
||||
#define write_data_func LCD_WriteData_Color
|
||||
|
||||
#include "base/pixel.h"
|
||||
#include "blend/pixel_ops.inc"
|
||||
#include "lcd/lcd_reg.inc"
|
||||
```
|
||||
|
||||
基于寄存器实现的实现有几个限制:
|
||||
|
||||
* 由于内存和CPU性能的问题,不提供任何类型的动画。
|
||||
* 由于读取LCD当前内容速度很慢,所以需要与底色进行混合时,由GUI自己处理(APP无需关心)。
|
||||
* 由于内存和 CPU 性能的问题,不提供任何类型的动画。
|
||||
* 由于读取 LCD 当前内容速度很慢,所以需要与底色进行混合时,由 GUI 自己处理 (APP 无需关心)。
|
||||
* 屏幕大时会出现闪烁。
|
||||
|
||||
### 二、基于framebuffer实现的LCD
|
||||
> 在 AWTK 中,不再推荐此方法,基于片段帧缓冲实现的 LCD 是更好的选择。
|
||||
|
||||
这是在嵌入式平台上最常见的方式。一般有两个framebuffer,一个称为online framebuffer,一个称为offline framebuffer。online framebuffer是当前现实的内容,offline framebuffer是GUI当前正在绘制的内容。lcd\_mem\_rgb565提供了rgb565格式的LCD实现,lcd\_mem\_rgba8888提供了rgba8888格式的LCD实现,它们都是在lcd\_mem.inc基础上实现的,要增加新的格式也是很方便的。
|
||||
### 二、基于片段帧缓冲实现的 LCD
|
||||
|
||||
下面是STMF429上LCD的实现:
|
||||
在低端的嵌入式平台上,内存只有几十 KB,没有足够的内存创建一屏的帧缓冲,而使用基于寄存器的方式屏幕容易闪烁。
|
||||
|
||||
比较好的办法是,创建一小块帧缓冲,把屏幕分成很多小块,一次只绘制一小块。由于有脏矩形机制,除了打开新窗口时,在正常情况下,绘制速度仍然很快,可以有效的解决闪速问题。
|
||||
|
||||
lcd\_mem_fragment.inc 提供了基于片段帧缓冲实现的 LCD,用它实现不同平台的 LCD 时,只需要提供两个宏即可:
|
||||
|
||||
* set\_window\_func 设置要写入颜色数据的区域,相对于每次设置坐标而言,可以极大提高工作效率。
|
||||
* write\_data\_func 写入颜色数据。
|
||||
|
||||
下面是 STMF103ze 上 LCD 的实现,这里把 set\_window\_func 定义为 TFT\_SetWindow,把 write\_data\_func 定义为 TFT\_WriteData:
|
||||
|
||||
```
|
||||
#include "tftlcd.h"
|
||||
|
||||
#include "tkc/mem.h"
|
||||
#include "lcd/lcd_mem_fragment.h"
|
||||
|
||||
typedef uint16_t pixel_t;
|
||||
|
||||
#define LCD_FORMAT BITMAP_FMT_BGR565
|
||||
#define pixel_from_rgb(r, g, b) \
|
||||
((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
|
||||
#define pixel_to_rgba(p) \
|
||||
{ (0xff & ((p >> 11) << 3)), (0xff & ((p >> 5) << 2)), (0xff & (p << 3)) }
|
||||
|
||||
#define set_window_func LCD_Set_Window
|
||||
#define write_data_func LCD_WriteData_Color
|
||||
|
||||
#include "base/pixel.h"
|
||||
#include "blend/pixel_ops.inc"
|
||||
#include "lcd/lcd_mem_fragment.inc"
|
||||
```
|
||||
|
||||
### 三、基于 framebuffer 实现的 LCD
|
||||
|
||||
这是在嵌入式平台上最常见的方式。一般有两个 framebuffer,一个称为 online framebuffer,一个称为 offline framebuffer。online framebuffer 是当前现实的内容,offline framebuffer 是 GUI 当前正在绘制的内容。lcd\_mem\_rgb565 提供了 rgb565 格式的 LCD 实现,lcd\_mem\_rgba8888 提供了 rgba8888 格式的 LCD 实现,它们都是在 lcd\_mem.inc 基础上实现的,要增加新的格式也是很方便的。
|
||||
|
||||
下面是 STMF429 上 LCD 的实现:
|
||||
|
||||
```
|
||||
extern u32 *ltdc_framebuf[2];
|
||||
@ -48,15 +93,12 @@ lcd_t* platform_create_lcd(wh_t w, wh_t h) {
|
||||
}
|
||||
```
|
||||
|
||||
### 三、基于vgcanvas实现的LCD
|
||||
### 三、基于 vgcanvas 实现的 LCD
|
||||
|
||||
在支持OpenGL 3D硬件加速的平台上(如PC和手机),我们使用nanovg把OpenGL封装成vgcanvas的接口,在vgcanvas基础之上实现LCD。lcd\_vgcanvas.inc将vgcanvas封装成LCD的接口,这里出于可移植性考虑,并没有直接基于nanovg的函数,而是基于vgcanvas的接口,所以在没有GPU时,如果CPU够强大,也是可以基于agg/picasso去实现的LCD。
|
||||
在支持 OpenGL 3D 硬件加速的平台上(如 PC 和手机),我们使用 nanovg 把 OpenGL 封装成 vgcanvas 的接口,在 vgcanvas 基础之上实现 LCD。lcd\_vgcanvas.inc 将 vgcanvas 封装成 LCD 的接口,这里出于可移植性考虑,并没有直接基于 nanovg 的函数,而是基于 vgcanvas 的接口,所以在没有 GPU 时,如果 CPU 够强大,也是可以基于 agg/picasso 去实现的 LCD。
|
||||
|
||||
> 这种方式实现,一般不会在嵌入平台上使用,读者不需要关注它。
|
||||
|
||||
### 总结
|
||||
|
||||
以上几种实现方式,基本上涵盖了最常用的场景,所以在移植到新的平台时,并不需要在实现LCD接口上费多少功夫。
|
||||
|
||||
|
||||
|
||||
以上几种实现方式,基本上涵盖了最常用的场景,所以在移植 AWTK 到新的平台时,并不需要在实现 LCD 接口上费多少功夫。
|
||||
|
@ -11,20 +11,22 @@ AWTK 的可移植性很高,在移植时只需要实现平台初始化、lcd
|
||||
> 以下是初始化内存的代码。
|
||||
|
||||
```
|
||||
static uint32_t s_heam_mem[2 * 4096];
|
||||
|
||||
ret_t platform_prepare(void) {
|
||||
if(!s_inited) {
|
||||
s_inited = TRUE;
|
||||
static bool_t inited = FALSE;
|
||||
static uint32_t s_heam_mem[4000];
|
||||
|
||||
if (!inited) {
|
||||
inited = TRUE;
|
||||
tk_mem_init(s_heam_mem, sizeof(s_heam_mem));
|
||||
}
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
```
|
||||
|
||||
> 参考:awtk-port/platform.c
|
||||
|
||||
> 以下是初始化systick的代码。
|
||||
> 以下是初始化 systick 的代码(在板子提供代码上修改而来)。
|
||||
|
||||
```
|
||||
static u8 fac_us = 0;
|
||||
@ -39,33 +41,25 @@ void systick_enable_int(void) {
|
||||
void SysTick_Init(void) {
|
||||
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
|
||||
fac_us = SystemCoreClock / 8000000;
|
||||
|
||||
fac_ms = (u16)fac_us * 1000;
|
||||
|
||||
systick_enable_int();
|
||||
}
|
||||
|
||||
void delay_us(u32 nus) {
|
||||
u32 temp = 0;
|
||||
SysTick->LOAD = nus * fac_us;
|
||||
SysTick->VAL = 0x00;
|
||||
SysTick->LOAD = nus * fac_us;
|
||||
SysTick->VAL = 0x00;
|
||||
do {
|
||||
temp = SysTick->CTRL;
|
||||
} while ((temp & 0x01) && !(temp & (1 << 16)));
|
||||
|
||||
systick_enable_int();
|
||||
} while ((temp & 0x01) && !(temp & (1 << 16)));
|
||||
}
|
||||
|
||||
|
||||
void delay_ms(u16 nms) {
|
||||
u32 temp = 0;
|
||||
SysTick->LOAD = (u32)nms * fac_ms;
|
||||
SysTick->VAL = 0x00;
|
||||
SysTick->LOAD = (u32)nms * fac_ms;
|
||||
SysTick->VAL = 0x00;
|
||||
do {
|
||||
temp = SysTick->CTRL;
|
||||
} while ((temp & 0x01) && !(temp & (1 << 16)));
|
||||
|
||||
systick_enable_int();
|
||||
} while ((temp & 0x01) && !(temp & (1 << 16)));
|
||||
}
|
||||
```
|
||||
|
||||
@ -73,29 +67,31 @@ void delay_ms(u16 nms) {
|
||||
|
||||
### 二、实现 lcd
|
||||
|
||||
lcd\_t 接口提供基本的显示功能,AWTK 提供基于寄存器和基于 framebuffer 两种缺省实现,在此基础上实现自己的 lcd\_t 接口非常方便。
|
||||
lcd\_t 接口提供基本的显示功能,AWTK 提供基于*寄存器*、*帧缓冲*和*片段帧缓冲*三种缺省实现,在此基础上实现自己的 lcd\_t 接口非常方便。
|
||||
|
||||
stm32f103ze 使用基于寄存器的 lcd 的缺省实现,只需要提供 set\_window\_func 和 write\_data\_func 两个函数/宏即可。这里直接使用了 TFT\_SetWindow 和 TFT\_WriteData 两个函数。
|
||||
stm32f103ze 使用基于*片段帧缓冲*的 lcd 的缺省实现,只需要提供 set\_window\_func 和 write\_data\_func 两个函数/宏即可。这里直接使用了 TFT\_SetWindow 和 TFT\_WriteData 两个函数。
|
||||
|
||||
> 直接写寄存器的方式容易闪烁,而帧缓冲又需要大量内存,片段帧缓冲能有效解决低内存平台的闪烁问题。
|
||||
|
||||
```
|
||||
#include "gui.h"
|
||||
#include "lcd_driver.h"
|
||||
|
||||
#include "tftlcd.h"
|
||||
#include "tkc/mem.h"
|
||||
#include "lcd/lcd_reg.h"
|
||||
#include "lcd/lcd_mem_fragment.h"
|
||||
|
||||
typedef uint16_t pixel_t;
|
||||
|
||||
#define LCD_FORMAT BITMAP_FMT_BGR565
|
||||
#define pixel_from_rgb(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
|
||||
#define pixel_to_rgba(p) {(0xff & ((p >> 11) << 3)), (0xff & ((p >> 5) << 2)), (0xff & (p << 3))}
|
||||
#define pixel_from_rgb(r, g, b) \
|
||||
((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
|
||||
#define pixel_to_rgba(p) \
|
||||
{ (0xff & ((p >> 11) << 3)), (0xff & ((p >> 5) << 2)), (0xff & (p << 3)) }
|
||||
|
||||
#define set_window_func TFT_SetWindow
|
||||
#define write_data_func TFT_WriteData
|
||||
#define set_window_func LCD_Set_Window
|
||||
#define write_data_func LCD_WriteData_Color
|
||||
|
||||
#include "base/pixel.h"
|
||||
#include "blend/pixel_ops.inc"
|
||||
#include "lcd/lcd_reg.inc"
|
||||
#include "lcd/lcd_mem_fragment.inc"
|
||||
|
||||
```
|
||||
|
||||
@ -106,23 +102,74 @@ typedef uint16_t pixel_t;
|
||||
main\_loop 主要负责事件分发和绘制这个不断循环的过程。main\_loop\_raw.inc 里实现了裸系统 main\_loop 的基本功能,在移植时加上输入事件的的分发即可:
|
||||
|
||||
```
|
||||
ret_t platform_disaptch_input(main_loop_t* l) {
|
||||
return RET_OK;
|
||||
|
||||
#include "key.h"
|
||||
#include "led.h"
|
||||
#include "rtc.h"
|
||||
#include "stdlib.h"
|
||||
#include "tftlcd.h"
|
||||
#include "touch.h"
|
||||
#include "usart.h"
|
||||
|
||||
|
||||
#include "base/idle.h"
|
||||
#include "base/timer.h"
|
||||
#include "tkc/platform.h"
|
||||
#include "base/main_loop.h"
|
||||
#include "base/event_queue.h"
|
||||
#include "base/font_manager.h"
|
||||
#include "lcd/lcd_mem_fragment.h"
|
||||
#include "main_loop/main_loop_simple.h"
|
||||
|
||||
|
||||
ret_t platform_disaptch_input(main_loop_t *l) { return RET_OK; }
|
||||
|
||||
static lcd_t *platform_create_lcd(wh_t w, wh_t h) {
|
||||
return lcd_mem_fragment_create(w, h);
|
||||
}
|
||||
|
||||
static lcd_t* platform_create_lcd(wh_t w, wh_t h) {
|
||||
return lcd_reg_create(w, h);
|
||||
void dispatch_input_events(void) {
|
||||
int key = KEY_Scan(0);
|
||||
|
||||
switch (key) {
|
||||
case KEY_UP: {
|
||||
key = TK_KEY_UP;
|
||||
break;
|
||||
}
|
||||
case KEY_DOWN: {
|
||||
key = TK_KEY_DOWN;
|
||||
break;
|
||||
}
|
||||
case KEY_LEFT: {
|
||||
key = TK_KEY_RETURN;
|
||||
break;
|
||||
}
|
||||
case KEY_RIGHT: {
|
||||
key = TK_KEY_BACK;
|
||||
break;
|
||||
}
|
||||
default: { key = 0; }
|
||||
}
|
||||
|
||||
if (key) {
|
||||
main_loop_post_key_event(main_loop(), TRUE, key);
|
||||
} else {
|
||||
main_loop_post_key_event(main_loop(), FALSE, key);
|
||||
}
|
||||
|
||||
if (TOUCH_Scan() == 0) {
|
||||
main_loop_post_pointer_event(main_loop(), TRUE, TouchData.lcdx,
|
||||
TouchData.lcdy);
|
||||
} else {
|
||||
main_loop_post_pointer_event(main_loop(), FALSE, TouchData.lcdx,
|
||||
TouchData.lcdy);
|
||||
}
|
||||
}
|
||||
|
||||
void TIM3_IRQHandler(void) {
|
||||
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
|
||||
dispatch_input_events();
|
||||
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
|
||||
|
||||
if (TOUCH_Scan() == 0) {
|
||||
main_loop_post_pointer_event(main_loop(), TRUE, TouchData.lcdx, TouchData.lcdy);
|
||||
} else {
|
||||
main_loop_post_pointer_event(main_loop(), FALSE, TouchData.lcdx, TouchData.lcdy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,9 +185,9 @@ void TIM3_IRQHandler(void) {
|
||||
* 3. 增加如下头文件路径:
|
||||
|
||||
```
|
||||
..\awtk\src
|
||||
..\awtk\3rd
|
||||
..\awtk-port
|
||||
awtk\src
|
||||
awtk\3rd
|
||||
awtk-port
|
||||
```
|
||||
|
||||
> 请根据项目文件位置进行调整。
|
||||
@ -155,10 +202,10 @@ void TIM3_IRQHandler(void) {
|
||||
|
||||
六、常见问题
|
||||
|
||||
* 莫名其妙的崩溃,可能是栈溢出。请修Stack_Size的大小。
|
||||
* 1.莫名其妙的崩溃,可能是栈溢出。请修 Stack_Size 的大小。
|
||||
|
||||
```
|
||||
Stack_Size EQU 0x00001000
|
||||
```
|
||||
|
||||
* 如果出现wcsxxx之类的函数没有定义时,请在awtk-port/awtk\_config.h中定义WITH\_WCSXXX。
|
||||
* 2.如果出现 wcsxxx 之类的函数没有定义时,请在 awtk-port/awtk\_config.h 中定义 WITH\_WCSXXX。
|
||||
|
Loading…
Reference in New Issue
Block a user