AC695N系列手表UI开发指南
695 Watch UI开发入门
UI工具
UI工程获取方法
由于ui资源相对较大,所以需要从百度网盘中获取。百度网盘获取链接会跟随SDK的发布更新在《AC695N_watch_SDK_发布版本信息. pdf》文件中。
通过上面方法将会获取到三个文件,分别是IOS和安卓的APP表盘升级的开发工程和UI工程,其中UI工程就包括UI开发所需要的工具
UI工程使用方法
UI工程的分布大致如下图所示:
Step1:UI工程放置路径
将下载下来的UI工程放到SDK\cpu\br23\tools文件夹下面,选择对应需要的工程进行使用。例如本示例
使用的是ui_ 240X240_watch.
Step2:打开UI工程
点击进入对应分辨率工程,没有可以复制一份进行修改,并通过点击打开SDK\cpu\br23\tools\UI工程\ui_ 240x240_watch\模式界面\step1-打开UI绘图工具. bat 批处理打开对应的UI绘图工具,并得到以下界面。就可以在UI工具中制作自己的UI界面,制作完成的界面可以点击保存工程进行保存
Step3:生成UI资源
点击SDK\cpu\br23\tools\UI工程\ui_ 240x240_watch\模式界面\step2-打开UI资源生成工具. bat批处理可以打开UI资源生成工具界面,点击"生成资源文件"时会通过调用本界面下的project\copy_file. bat批处理将资源文件生成到对应的路径下面,详细可以查看copy_file. bat
Step4:下载更新固件
下载程序会调用SDK\cpu\br23\tools\download\watch下面"download.bat"将UI资源打包通过批处理下载到程序中。
SDK程序
屏幕驱动
1.驱动路径
屏幕驱动放置路径: SDK\cpu\br23\ui_driver\lcd_spi文件夹下面
2.支持格式
目前695系列手表项目只支持spi 8bit屏幕(4-wire),暂不支持的9bit屏幕(3-wire),支持格式入下图。 3-wire有SCL / SDl / SDO / CSX 四根线, 4-wire有SCL / SDl / SDO / CSX / DCX五根线, 3-wire, 4-wire并不是指信号线的总数目,而是指有没有用DCX线来区分传输的数据是Data还是Cmd。
3.屏幕初始化
屏幕驱动中提供了两种初始化屏幕寄存器的方式,如下图所示。只需要将屏厂提供或屏幕手册的初始化相关寄存器信息填入其中即可。
屏幕初始化过程中若出现只亮背光不亮屏幕数据的情况,就需要用示波器或者逻辑分析仪测量屏幕的 reset脚是否达到屏幕所需要的的reset条件。而且留意屏幕中是否正确配置reset脚
TP驱动
1.驱动路径
屏幕驱动放置路径: SDK\apps\common\device\touch_panel文件夹下面
2.驱动程序
TP的使用会分两种情况,一种是直接可以通过IIC初始化使用,有一种是第一次使用时需要对TP芯片进行一个固件升级后才可以正常使用。公版SDK也提供两种TP的使用方法,分别在
SDK\apps\common\device\touch_panel\IT7259E.c和
SDK\apps\common\device\touch_panel\cst816s.c文件中。两个分别是两种TP的驱动文件,包括读取 TP的报点值、处理触摸事件,往UI线程发送触摸事件。下图以IT7259E示例。
使用示例
控件接口
各个控件调用接口路径在SDK\include_lib\system\ui\ui文件下,命名规则是ui_xxx. h,其中xxx代表控件名称。例如图片控件: ui_pic. h 、文本控件: ui_text. h、列表控件: ui_grid. h 等。
示例实现内容
示例将展现的内容包括:列表的创建与使用、多张图片切换显示、文字控件strpic和text格式的显示和使用、数字控件string格式和numb格式的使用、不同方式的页面切换和数据源的使用。
| //页面id管理#define | USR_TE_GRID_PAGE | PAGE_ 39 | //测试列表页面 |
|---|---|---|---|
| #define | USR_TE_PIC_PAGE | PAGE_40 | //测试图片页面 |
| #define | USR TE TEXT PAGE | PAGE_41 | //测试文本页面 |
| #define | USR TE NUMB PAGE | PAGE_42 | //测试数字页面 |
| //垂直列表选中的布局id #define USR_TE_SWITCH_PIC | TE_SWITCH_PIC_LAYOUT | ||
| #define USR_TE_SWITCH_TEXT | TE_SWITCH_TEXT_LAYOUT | ||
| #define USR_TE_SWITCH_NUMB | TE_SWITCH_NUMB_LAYOUT | ||
| //控件id管理 #define USR_TE_VERTLIST | TE_VERTLIST | //垂直列表id | |
| #define USR_TE_PIC | TE_PIC | //图片控件id | |
| #define USR_TE_TEXT_NAME | TE TEXT NAME | //文本控件蓝牙名id | |
| #define USR_TE_TEXT_ADDR | TE TEXT ADDR | //文本控件蓝牙地址id | |
| #define USR_TE_TEXT_VER | TE TEXT VER | //文本控件版本号id | |
| #define USR_TE_NUMB_NUM | TE NUMB NUM | //数字控件TYPE_NUM格式 | |
| #define USR_TE_NUMB_STR | TE NUMB STR | //数字控件TYPE_STRING格式 |
1.新建列表控件
首先每新建一个页面后需要添加图层和布局(带背景颜色),因为所有控件都需要在布局中使用。列表控件有垂直列表和水平列表,列表是带有滑动效果的,所以不需要编写滑动逻辑。但客户软件可以配置滑动方向。添加列表后如下图一样右键添加行、设置行高和间隔即可。设置好列表格式后会得到与添加行对应数量布局,只需要像下图一样添加想要显示的内容即可。下面通过添加strpic格式的文字控件作为示例,实现点击不同的项会进入不同界面的功能

typedef struct usr_menu_callback {
int sel_id ;
int (*exit_check)() ;
int window;
int (*enter_check)(int window) ;} USR_MENU_CB ;
const static USR_MENU_CB usr_menu_cb [] = {
//{被选中的id , 退出检查,,进入的页面id ,,进入检查} ,
{USR_TE_SWITCH_PIC , NULL , USR_TE_PIC_PAGE , NULL} ,
{USR_TE_SWITCH_TEXT , NULL , USR_TE_TEXT_PAGE , NULL} ,
{USR_TE_SWITCH_NUMB , NULL , USR_TE_NUMB_PAGE , NULL} ,} ;
//此处初始化的时候只需要限制一下滑动的方向
static int usr_test_grid_onchane(void *ctr , enum element_change_event e , void *arg)
{
struct ui_grid *grid = (struct ui_grid *)ctr ;
switch (e) {
case ON_CHANGE_INIT :
ui_grid_set_slide_direction(grid , SCROLL_DIRECTION_UD) ;
break;
case ON_CHANGE_RELEASE :
break;
case ON_CHANGE_FIRST_SHOW :
break;
default:
return false ;}
return false ;}
//选择处理
static int usr_menu_in_sel(struct ui_grid *grid)
{
u32 sel_item = ui_grid_cur_item(grid) ;
if (sel_item >= grid->avail_item_num) {
return false ;}
int id = grid->item [sel_item] .elm .id ;
for (int i = 0 ; i < (sizeof(usr_menu_cb) / sizeof(usr_menu_cb [0])) ; i++) { if (usr_menu_cb [i] .sel_id == id) {
printf( "menu layout id = %x \n " , id) ;
if (usr_menu_cb [i] .window) {
if (usr_menu_cb [i] .enter_check &&
(!usr_menu_cb [i] .enter_check(usr_menu_cb [i] .window))) {
return false ;
}
UI_HIDE_CURR_WINDOW() ;
UI_SHOW_WINDOW(usr_menu_cb [i] .window) ;
}
return true ;
break;
}
}
return false ;}
//列表控件滑动功能底层以及做好,所以只需要做点击选择切页面功能
static int usr_test_grid_ontouch(void *ctr , struct element_touch_event *e){
struct ui_grid *grid = (struct ui_grid *)ctr ;
int sel_item ;
static u8 move_flag = 0 ;
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE :
break;
case ELM_EVENT_TOUCH_L_MOVE :
break;
case ELM_EVENT_TOUCH_MOVE :
move_flag = 1;
return false;//不接管消息
break;
case ELM_EVENT_TOUCH_DOWN :
move_flag = 0 ;
return false;//不接管消息
break;
case ELM_EVENT_TOUCH_UP :
if (move_flag) {
move_flag = 0 ;
return false;//不接管消息
}
return usr_menu_in_sel(grid) ;
break;
default:
return false ;
break;}
return true;//接管消息}
//通过这种方式注册布局或者控件,实现软件控制UI各种动作
REGISTER_UI_EVENT_HANDLER(USR_TE_VERTLIST)
.onchange = usr_test_grid_onchane ,
.onkey = NULL ,
.ontouch = usr_test_grid_ontouch ,} ;
2.新建图片控件
同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后在需要的位置添加上图片控件,本示例将通过图片列表的方式添加多张图片,然后通过定时器不断的刷新切换不同的图片。
#define MAX_PIC_NUMBS 7
static void usr_pic_reflash(void)
{
static u8 CurrPicIndex = 0 ;
if(++CurrPicIndex >= MAX_PIC_NUMBS){
CurrPicIndex = 0 ;}
ui_pic_show_image_by_id(USR_TE_PIC ,CurrPicIndex) ;}
static int usr_test_pic_onchange(void *ctr , enum element_change_event e , void *arg)
{
struct ui_pic *pic = (struct ui_pic *)ctr ;
static u16 pic_timer_id = 0 ;
switch (e) {
case ON_CHANGE_INIT :
if(!pic_timer_id){
pic_timer_id = sys_timer_add(NULL , usr_pic_reflash , 1000) ;}
//默认进入显示第几张图片
ui_pic_set_image_index(pic ,2) ;
break;
case ON_CHANGE_FIRST_SHOW :
break;
case ON_CHANGE_RELEASE :
if(pic_timer_id){
sys_timer_del(pic_timer_id) ;
pic_timer_id = 0 ;
}
break;
default:
return false ;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_PIC)
.onchange = usr_test_pic_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
static int usr_change_page_ontouch(void *ctr , struct element_touch_event *e){
struct window *window = (struct window *)ctr ;
printf( "%s:e->event=%d " , func , e->event) ;
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE :
ui_send_event(KEY_CHANGE_PAGE , USR_TE_GRID_PAGE|BIT(31)) ;
return false ;
case ELM_EVENT_TOUCH_L_MOVE :
ui_send_event(KEY_CHANGE_PAGE , USR_TE_GRID_PAGE|BIT(31)) ;
return false ;
default:
break;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_PIC_PAGE)
.onchange = NULL ,
.onkey = NULL ,
.ontouch = usr_change_page_ontouch ,} ;
3.新建文本控件
文本控件格式分为ascii、strpic、text三种格式, strpic格式使用的是多国语言表中设置好的文本,一般会使用在菜单、标题等固定显示的页面; text格式是使用字库进行显示,一般使用在消息通知、歌词显示等不固定内容显示。同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后添加对应的文字控件。本示例将使用三个text格式的文本控件,通过数据源作为区分,分别显示蓝牙名、 mac地址和版本号内容。
//text 格式显示
extern const u8 *bt_get_mac_addr() ;
extern const char *bt_get_local_name() ;
static u8 VStr []= "v1 .0 .0 " ;
static int usr_test_text_onchange(void *ctr , enum element_change_event e , void *arg)
{
struct ui_text *text = (struct ui_text *)ctr ;
u8 * buf;
u8 * addr_buf = NULL ;
u8 mac_addr [6] = {0} ;
switch (e) {
case ON_CHANGE_INIT :
if (!strcmp(text->source , "name ")) {
buf = bt_get_local_name() ;
ui_text_set_text_attrs(text , buf , strlen(buf) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT) ;
}
else if (!strcmp(text->source , "addr ")){
//log_info( "set bt addr ") ;
memcpy(mac_addr , bt_get_mac_addr() , 6) ;
put_buf(mac_addr ,6) ;
addr_buf = malloc (20) ;
if(addr_buf == NULL){
ASSERT(0 , "addr_buf malloc fail ") ;
}
for(u8 i = 0 ; i < 6 ; i++){
if(i == 0 ) {
sprintf(addr_buf , "%X%c " ,mac_addr [i], ' : ') ;
}
else if (i < 5){
sprintf(addr_buf , "%s%X%c " ,addr_buf ,mac_addr [i] , ' : ') ;}
else{
sprintf(addr_buf , "%s%X " ,addr_buf ,mac_addr [i]) ;
}
}
ui_text_set_text_attrs(text , addr_buf , strlen(addr_buf) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT) ;
}
else if (!strcmp(text->source , "ver ")){
ui_text_set_text_attrs(text , VStr , strlen(VStr) , FONT_ENCODE_UTF8 , FONT_ENDIAN_SMALL , FONT_DEFAULT) ;
}
break;
case ON_CHANGE_RELEASE :
if(addr_buf){
free(addr_buf) ;
addr_buf = NULL ;
}
break;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_NAME)
.onchange = usr_test_text_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_ADDR)
.onchange = usr_test_text_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_VER)
.onchange = usr_test_text_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
static int usr_change_page_ontouch(void *ctr , struct element_touch_event *e){
struct window *window = (struct window *)ctr ;
printf( "%s:e->event=%d " , func , e->event) ;
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE :
UI_HIDE_CURR_WINDOW() ;
UI_SHOW_WINDOW(-1) ;
return false ;
case ELM_EVENT_TOUCH_L_MOVE :
UI_HIDE_CURR_WINDOW() ;
UI_SHOW_WINDOW(-1) ;
return false ; default:
break;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_TEXT_PAGE)
.onchange = NULL ,
.onkey = NULL ,
.ontouch = usr_change_page_ontouch ,} ;
4.新建数字控件
数字控件有TYPE_STRING和TYPE_NUM两种方式进行显示,两者区别在于TYPE_NUM格式最多只能显示xx/xx这样格式长度的内容,对于xx/xx/xx...这种多位数字格式的内容无法显示,这时候就可以 TYPE_STRING进行显示。
同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后添加对应的数字控件。本示例将使用两个数字控件,通过获取RTC的时间,然后使用TYPE_STRING和TYPE_NUM两种方式显示不同长度内容信息。同样使用数据源的格式区分同一个页面中两个数字控件

static u8 stopwatch_num_buf [ 10] = {0} ;
static void get_sys_time(struct sys_time *time)
{
void *fd = dev_open( " rtc " , NULL) ;
if (!fd) {
memset(time , 0 , sizeof(time)) ;
return ;}
dev_ioctl(fd , IOCTL_GET_SYS_TIME , (u32)time) ;
/ printf( "get_sys_time : %d-%d-%d ,%d:%d:%d\n " , time->year , time->month , time->day , time->hour , time->min , time->sec) ; */
dev_close(fd) ;}
void usr_string_reflash(void)
{
struct sys_time time ;
struct unumber str_unumb ;
struct unumber num_unumb ;
get_sys_time(&time) ;
sprintf(stopwatch_num_buf , "%2d%c%2d%c%2d " , time .hour , ' : ' , time .min , ' . ' , time .sec) ;
str_unumb .type = TYPE_STRING ;
str_unumb .num_str = &stopwatch_num_buf;
num_unumb .type = TYPE_NUM ;
num_unumb .numbs = 2 ;
num_unumb .number [0] = time .min ;
num_unumb .number [ 1] = time .sec ;
ui_number_update_by_id(USR_TE_NUMB_STR , &str_unumb) ;
ui_number_update_by_id(USR_TE_NUMB_NUM , &num_unumb) ;}
static int usr_test_num_onchange(void *ctrl , enum element_change_event e , void *arge)
{
struct ui_number *num = (struct ui_number *) ctrl ;
static u16 numb_timer_id = 0 ;
struct unumber unumb ;
struct sys_time time ;
switch (e) {
case ON_CHANGE_INIT :
if (!strcmp(num->source , "string ")) {
get_sys_time(&time) ;
sprintf(stopwatch_num_buf , "%2d%c%2d%c%2d " , time .hour , ' : ' , time .min , ' . ' , time .sec) ;
unumb .type = TYPE_STRING ;
unumb .num_str = &stopwatch_num_buf;
ui_number_update(num , &unumb) ;
}
else if (!strcmp(num->source , "munb ")) {
get_sys_time(&time) ;
unumb .type = TYPE_NUM ;
unumb .numbs = 2 ;
unumb .number [0] = time .min ;
unumb .number [ 1] = time .sec ;
ui_number_update(num , &unumb) ;
}
if (!numb_timer_id) {
numb_timer_id = sys_timer_add(NULL , usr_string_reflash , 500) ;}
break;
case ON_CHANGE_RELEASE :
if(numb_timer_id){
sys_timer_del(numb_timer_id) ;
numb_timer_id = 0 ;
}
break;
default:
break;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_NUM)
.onchange = usr_test_num_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_STR)
.onchange = usr_test_num_onchange ,
.onkey = NULL ,
.ontouch = NULL ,} ;
static int usr_change_page_ontouch(void *ctr , struct element_touch_event *e){
struct window *window = (struct window *)ctr ;
printf( "%s:e->event=%d " , func , e->event) ;
switch (e->event) {
case ELM_EVENT_TOUCH_R_MOVE :
ui_send_event(KEY_CHANGE_PAGE , 1) ;
return false ;
case ELM_EVENT_TOUCH_L_MOVE :
ui_send_event(KEY_CHANGE_PAGE , 1) ;
return false ;
default:
break;}
return false ;}
REGISTER_UI_EVENT_HANDLER(USR_TE_NUMB_PAGE)
.onchange = NULL ,
.onkey = NULL ,
.ontouch = usr_change_page_ontouch ,} ;
5.更多示例
以上只是拿部分控件的部分简单使用作为一个示例,多参考案例在
SDK\apps\watch\ui\lcd\STYLE_WATCH路径下面,可自行查看。除了使用UI工具中控件进行UI显示外, SDK还提供自定义绘图功能,可以自定义控制显示位置和显示内容,具体可查看自定义绘图功能可以参考SDK下的SDK\apps\watch\ui\lcd\STYLE_WATCH\ui_draw_demo.c文件。更