KDocs 研发资料库
  • 屏融合应用方案入口汇总
  • JL701N穿戴客述问题FAQ
  • JL701N穿戴类补丁list
  • 手表软件调试
  • JL701手表硬件设计注意事项 总结
  • 手表烧录_升级工具说明
  • 手表量产相关
  • 生产烧录KEY及代码和升级代码
  • AC695N系列手表SDK开发指南
  • JL7012A手表-SDK入门指南V1.0
  • 穿戴手表JL701N-入门到进阶开发指导
  • 彩屏充电仓JL701N LVGL入门到进阶指导文档
  • AC695N手表软硬件问题整理
  • JL701N手表软硬件问题整理
  • 701n手表SDK共性问题汇总
  • UI效果参考demo文档
  • 屏幕触摸驱动相关参考
  • 穿戴类支付宝支付流程
  • AC701N_PMU_介绍
  • 经典蓝牙相关接口说明
  • music应用详细设计说明书
  • music_player接口设计说明文档
  • PO声测试以及处理方法
  • DAC性能测试指南
  • 杰理串口升级规范
  • AC696N串口升级说明
  • AC695N系列手表UI开发指南
  • AC695N添加升级UI说明文档.
  • UI布局工具使用说明
  • UI接口设计说明书
  • UI工具快速使用说明
  • 701N_UI编辑工具用户手册
  • UI工程源文件升级工具使用说明
  • 701N屏幕驱动配置说明
  • JL701N 2D图形硬件加速接口使用说明
  • JL701_watch常用例子参考
  • JL_WATCH_SDK UI相关工具使用文档
  • 智能手表音频 设计调试手册 V1.2
  • AC695X_AC696X_AD697X 常见通话问题处理参考资料
  • 701手表 PCBA产测工具使用说明书 V1.0
  • 杰理定频问题排查2021.1.26
  • BQB5.4 20231007
  • 各类概率性死机调试解决指南
  • 不开机问题处理汇总(仅供参考)
  • 蓝牙搜索不到问题处理汇总(仅供参考)
  • 样机发烫(仅供参考)
  • 蓝牙音乐卡顿问题处理汇总(仅供参考)
  • 充电问题处理汇总(仅供参考)
  • 触摸问题处理汇总(仅供参考)
  • 功耗调试指导
  • JL701N穿戴客述问题FAQ
  • 701N 开发板新屏幕驱动修改
  • AC701 nandflash A8图片显示问题
  • 支付宝乘车码接入资料
  • 多国语言及字库使用指南
  • 查看C程序Flash 和RAM 资源占用说明文档
  • 701N_SDK3.0.1_OPUS_OGG 编解码
  • 蓝牙在线调eq说明
  • BR EDR BLE 蓝牙性能测试配置方法_
  • BLE-HID
  • BLE-HID 调试文档
  • 【问题】701v301外置flash播放wts提示音
  • 本地录音开启及调试
  • 死机分析
  • 表盘传输APP层的压缩算法
  • 功能模版:JL701_V301_UI资源使用FAT文件系统demo
  • JL7013+4G CAT1 参考设计原理图
  • 吾爱4G模组+JL701接口说明
  • (电脑一拖多)手表升级dongle工具使用补充说明
  • 蓝牙接口使用说明
  • ble 开发流程
  • 彩屏仓功耗优化
  • 彩屏仓补丁list
  • 更新字库步骤
  • 支付宝硬件接入资料

AC695N系列手表UI开发指南

📎 原始文档:https://www.kdocs.cn/l/cfcg35OTvp17

695 Watch UI开发入门

UI工具

UI工程获取方法

由于ui资源相对较大,所以需要从百度网盘中获取。百度网盘获取链接会跟随SDK的发布更新在《AC695N_watch_SDK_发布版本信息. pdf》文件中。
imageimage 通过上面方法将会获取到三个文件,分别是IOS和安卓的APP表盘升级的开发工程和UI工程,其中UI工程就包括UI开发所需要的工具

UI工程使用方法

UI工程的分布大致如下图所示:
image

Step1:UI工程放置路径

将下载下来的UI工程放到SDK\cpu\br23\tools文件夹下面,选择对应需要的工程进行使用。例如本示例
使用的是ui_ 240X240_watch.
image

Step2:打开UI工程

点击进入对应分辨率工程,没有可以复制一份进行修改,并通过点击打开SDK\cpu\br23\tools\UI工程\ui_ 240x240_watch\模式界面\step1-打开UI绘图工具. bat 批处理打开对应的UI绘图工具,并得到以下界面。就可以在UI工具中制作自己的UI界面,制作完成的界面可以点击保存工程进行保存
image

Step3:生成UI资源

点击SDK\cpu\br23\tools\UI工程\ui_ 240x240_watch\模式界面\step2-打开UI资源生成工具. bat批处理可以打开UI资源生成工具界面,点击"生成资源文件"时会通过调用本界面下的project\copy_file. bat批处理将资源文件生成到对应的路径下面,详细可以查看copy_file. bat
image

Step4:下载更新固件

下载程序会调用SDK\cpu\br23\tools\download\watch下面"download.bat"将UI资源打包通过批处理下载到程序中。
image

SDK程序

屏幕驱动

1.驱动路径

屏幕驱动放置路径: SDK\cpu\br23\ui_driver\lcd_spi文件夹下面
image

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。
image

3.屏幕初始化

屏幕驱动中提供了两种初始化屏幕寄存器的方式,如下图所示。只需要将屏厂提供或屏幕手册的初始化相关寄存器信息填入其中即可。
image 屏幕初始化过程中若出现只亮背光不亮屏幕数据的情况,就需要用示波器或者逻辑分析仪测量屏幕的 reset脚是否达到屏幕所需要的的reset条件。而且留意屏幕中是否正确配置reset脚
image

TP驱动

1.驱动路径

屏幕驱动放置路径: SDK\apps\common\device\touch_panel文件夹下面
image

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示例。
image

使用示例

控件接口

各个控件调用接口路径在SDK\include_lib\system\ui\ui文件下,命名规则是ui_xxx. h,其中xxx代表控件名称。例如图片控件: ui_pic. h 、文本控件: ui_text. h、列表控件: ui_grid. h 等。
image

示例实现内容

示例将展现的内容包括:列表的创建与使用、多张图片切换显示、文字控件strpic和text格式的显示和使用、数字控件string格式和numb格式的使用、不同方式的页面切换和数据源的使用。
image

//页面id管理#defineUSR_TE_GRID_PAGEPAGE_ 39//测试列表页面
#defineUSR_TE_PIC_PAGEPAGE_40//测试图片页面
#defineUSR TE TEXT PAGEPAGE_41//测试文本页面
#defineUSR TE NUMB PAGEPAGE_42//测试数字页面
//垂直列表选中的布局id
#define USR_TE_SWITCH_PIC
TE_SWITCH_PIC_LAYOUT
#define USR_TE_SWITCH_TEXTTE_SWITCH_TEXT_LAYOUT
#define USR_TE_SWITCH_NUMBTE_SWITCH_NUMB_LAYOUT
//控件id管理
#define USR_TE_VERTLIST
TE_VERTLIST//垂直列表id
#define USR_TE_PICTE_PIC//图片控件id
#define USR_TE_TEXT_NAMETE TEXT NAME//文本控件蓝牙名id
#define USR_TE_TEXT_ADDRTE TEXT ADDR//文本控件蓝牙地址id
#define USR_TE_TEXT_VERTE TEXT VER//文本控件版本号id
#define USR_TE_NUMB_NUMTE NUMB NUM//数字控件TYPE_NUM格式
#define USR_TE_NUMB_STRTE NUMB STR//数字控件TYPE_STRING格式

1.新建列表控件

首先每新建一个页面后需要添加图层和布局(带背景颜色),因为所有控件都需要在布局中使用。列表控件有垂直列表和水平列表,列表是带有滑动效果的,所以不需要编写滑动逻辑。但客户软件可以配置滑动方向。添加列表后如下图一样右键添加行、设置行高和间隔即可。设置好列表格式后会得到与添加行对应数量布局,只需要像下图一样添加想要显示的内容即可。下面通过添加strpic格式的文字控件作为示例,实现点击不同的项会进入不同界面的功能
imageimageimage 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) ;
image 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各种动作
image REGISTER_UI_EVENT_HANDLER(USR_TE_VERTLIST)
.onchange = usr_test_grid_onchane ,
.onkey = NULL ,
.ontouch = usr_test_grid_ontouch ,​} ;

2.新建图片控件

同样往新建的页面中添加好图层和布局(需要填充背景颜色),然后在需要的位置添加上图片控件,本示例将通过图片列表的方式添加多张图片,然后通过定时器不断的刷新切换不同的图片。
imageimage #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;
image 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地址和版本号内容。
imageimage //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){​
image 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) ;
image 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两种方式显示不同长度内容信息。同样使用数据源的格式区分同一个页面中两个数字控件
imageimageimage 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) ;
​}
image 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文件。更

Prev
AC696N串口升级说明
Next
AC695N添加升级UI说明文档.