KDocs 研发资料库
  • 手表穿戴技术应用文档入口汇总
  • JL707N穿戴客述问题FAQ
  • 彩屏仓 硬件开发指南 与 量产作业流程规范
  • 彩屏仓 AC98N和JL701烧录升级注意事项
  • JL707 硬件开发资料
  • JL707 手表硬件设计注意事项 总结
  • 穿戴生产烧录OTA和key文件添加使用说明
  • JL707N-入门到进阶开发指导文档
  • 彩屏仓982相关
  • 彩屏仓耳机介绍
  • 杰理701_707双屏demo介绍
  • 707电子吧唧开发资料
  • GPIO相关
  • UART串口相关
  • SPI相关
  • nandflash相关
  • norflash相关
  • IIC相关
  • PWM相关
  • SD卡相关
  • 定时器TIMER
  • 文件系统接口设计说明文档
  • 音视频录像开发流程
  • rdec编码器
  • 穿戴功耗数据标准
  • 手表蓝牙开发文档
  • 701蓝牙PAN联网demo
  • BLE开发使用说明
  • 701手表2.0.0版本SDK EQ在线调试和离线调试方法 V1.1
  • JL707 手表喇叭输出声音大小评估方法 V1.0
  • JL707 手表音频EQ调试方法 V1.0
  • AC707N 手表通话写卡 软件配置方法
  • 录音功能
  • 701手表2.X.X系列SDK调试EQ配置方法及音频调试技巧V1.1
  • 701手表104版本SDK EQ在线调试和离线调试方法 V1.1
  • mic2dac示例
  • P11 SensorHub相关
  • 手表升级操作文档
  • AC707NGPU
  • DBI模块介绍
  • GPU模块介绍
  • 701N屏幕驱动配置说明
  • 701N_UI编辑工具用户手册
  • 支付宝资料
  • 支付宝硬件接入资料
  • 支付宝乘车码接入资料
  • CAT1 接入文档
  • 701N_WATCH_CAT1开发说明
  • Findmy功能
  • music模式相关
  • 707电池曲线
  • 707N内置FLASH挂载FAT文件系统251112_v1.2
  • AC707N_本地视频播放demo(AVI-MJPEG-PCM)
  • 光波导屏幕调试
  • 耳机研发和生产流程 指南
  • 707-NandFlash 代码和UI资源独立烧录方案
  • 彩屏仓时间同步补丁
  • 彩屏仓抖音点赞移植
  • 707LVGLv8双屏开发
  • 707N蓝牙互传图传demo
  • AVIPCM数据流接入AIVOICE
  • Flash分区操作demo
  • 手表发射问题
  • 701_707 双备份升级DEMO
  • 707_乘车码刷码失败问题_共性

JL707N-入门到进阶开发指导文档

📎 原始文档:https://www.kdocs.cn/l/cpByOdCmDwIY JL707N-入门到进阶开发指导文档

温馨提示:707N系列空片贴片后,需先使用1T8升级一次,把晶振‘双脚模式’烧录写一次,后续就可以正常用USB强制工具升级。***注意:一旦烧过单脚振,芯片就回不去双脚振,‘特别’‘特别’‘特别’强调手表方案不要选单脚模式!
最新的芯片已经不需要此操作,如果还不能下载可以重复上面的操作,然后查看下晶振是否起振。

JL707n晶振两边负载电容物料必须焊接,电容取值与晶振参数相匹配。
本文档主要介绍从707N的工具安装,环境搭建

📌JL707N芯片型号规格:

image JL707N芯片,推屏没有分辨率限制!屏接口只能QSPI DSPI SPI;MCU8080-8bit(部分芯片支持)

📌JL707N穿戴客述问题FAQ

https://kdocs.cn/l/ciuBHkm9GOFK

📌手表穿戴技术应用文档入口汇总

手表穿戴技术应用文档入口汇总.otl
707N手表SDK快速入门.docx

📌JL707N硬件说明文档:

JL707 硬件开发资料.otl
JL707 手表喇叭输出声音大小评估方法 V1.0.otl

📌JL707N量产规范

手表量产作业流程规范.otl

📌杰理工具说明文档:

(程序开发工具、固件处理工具、量产相关工具)
欢迎使用杰理工具文档 — JL Project Documentation

杰理工具及开发板淘宝店

首页-杰理科技-淘宝网

一、开发板指导

以下配置过程中遇到问题可以参考 杰理工具文档,本文章旨在进行通俗指导。

1.1 硬件环境

1.1.1 强制升级工具

先把盖子撬开:日常使用拨码打开第5、7位就可以
imageimage

1.1.2 JL707N开发板

imageimage

1.1.3 USB供电调试:将跳线帽换到LDO3V7与VBAT短接

image

1.1.4 硬件设计指南

AC707N蓝牙智能手表硬件设计指南V1.2.pdf

1.2 软件环境

1.2.1 安装JL Studio可视化开发平台

请联系杰理获取安装包,暂不提供线上下载渠道!

1.2.2 安装JL Studio软件依赖

①安装杰理工具链
下载并安装 最新版本的杰理Windows版工具链(点击此处下载)
注意:请勿更改默认安装路径,按照默认路径安装

1.2.3 安装IDE集成开发环境

注意:Code::Blocks 或 vscode (IDE根据喜好二选其一,推荐vscode)
①安装VS Code
用于代码编辑 https://code.visualstudio.com/Download (支持配置工具编译错误跳转,推荐使用)
下载并安装vscode,vscode内安装必要的插件

  • Task Explorer
  • C/C++ imageimage

②安装Code::Blocks
在浏览器中打开链接:Code::Blocks Windows版本(点击此处下载)
双击下载后的 codeblocks-latest.exe ,按照提示进行安装即可。
注:打开安装好的 Code::Blocks 后,关闭 Code::Blocks (这是为了让Code::Blocks生成一些基础设置信息)
杰理工程环境的快速安装介绍.doc

1.3 SDK编译

1.3.1 编译准备

根据以上步骤准备好硬件和软件环境
板子USB和烧录器连接,烧录器接USB线到电脑上,能成功能识别
注意:没识别可能是你电脑没有CH340串口驱动,自行网络下载或联系杰理获取。
按下强制烧录工具“update”按键,烧录器绿灯灭。红灯常亮,表示进入烧录模式:
imageimageimage

1.3.2 JL Studio编译

image

1.3.3 VS Code编译

使用 VS Code 打开 SDK 目录,注意必须在SDK文件夹下打开VS Code,否则编译出错。
imageimageimage 编译成功页面:
image 编译报错页面:没有进入烧录模式,请按下烧录器按钮后(绿灯灭红灯常亮)重新编译烧录
image 编译并下载完成后,需要对开发板断上电重启,然后绿灯闪烁,系统运行。
image

1.3.4 Code::Blocks编译

直接打开 SDK 目录下的 cbp 工程(cbp是Code::Blocks的工程文件类型)7789
image 编译
image 编译成功页面:
image 编译并下载完成后,需要对开发板断上电重启,然后绿灯闪烁,系统运行。
image

1.4 文件新增/移除

1.4.1 VScode

新增/移除文件:新增源文件/库文件时,需要手动修改makefile,否则不会编译进去。
imageimageimageimage

1.4.2 Code::Blocks

新增源文件/库文件时,需要手动导入工程,否则不会编译进去。
①新增/移除文件(.h/.c):
image

②新增/移除库文件(.a):
鼠标选中工程,然后右键菜单,再点击编译选项。
imageimage 添加好第三方库之后,点击“OK”。
以上就可以成功导入第三方库到当前工程中去。

1.5 串口部署/配置

本节介绍用USB强制烧录工具,进行串口日志输出,无需外接其他USB串口,日志机制参考2.7.1

1.5.1 杰理串口驱动

安装杰理虚拟串口工具:强制升级工具4.0串口驱动(点击此处下载)
安装驱动后,需要重启电脑
打开杰理虚拟串口工具,进行端口绑定,步骤参考:虚拟串口绑定
该驱动主要是驱动烧录器上的串口,可以直接监听烧录器上的串口,充当串口调试器
完成以上步骤后,有两种串口连接方式

  • 利用烧录器的RX串口引脚
  • 利用烧录器的USB-DP引脚 ①利用烧录器的RX串口引脚
    image 设置串口通讯参数
    image 对应代码中的UART板级配置
    image ②利用烧录器的USB-DP引脚
    设置串口通讯参数
    image 对应代码中的UART板级配置
    image

1.5.2 串口监听软件

①XShell 非开源,自行网络下载安装包
参阅《Xshell带颜色打印说明文档》对xshell进行配置:
Xshell带颜色打印说明文档[已封装宏]

②Windterm
windterm是一款风格独特、开源、且完全免费的SSH/Telnet/Serial/Shell/Sftp 客户端
下载地址:https://github.com/kingToolbox/WindTerm/releases

③MobaXterm
MobaXterm 分免费开源版和收费专业版: 下载官网免费开源版
下载地址:https://mobaxterm.mobatek.net/download-home-edition.html

1.6 穿戴生产烧录OTA和key文件添加使用说明

此文档主要介绍穿戴系列芯片的代码添加key 文件的demo操作,客户根据实际添加更换key 文件接口,key 文件属于客户的秘钥文件,配合烧录器使用的,key 文件请找代理商拿取。
穿戴生产烧录OTA和key文件添加使用说明

二、JL Studio使用指导

2.1 JL Studio使用手册

imageimage

2.2 手表SDK使用手册

本文档由杰理官方推出,适用AC707N手表SDK
imageimage

三、SDK框架入门

3.1 SDK 文件结构

3.1.1 根目录

.\SDK
.\SensorHub
.\output
.\src
目录介绍:
SDK:存放SDK 模版
SensorHub:存放SensorHub 开发工程
output:可视化工具导出的配置文件
src:可视化工具配置文件

3.1.2 SDK重点目录

SDK\apps\watch\board:板卡操作目录
SDK\apps\watch\message\adapter:SDK模式、消息切换函数
SDK\apps\watch\mode:SDK 下的模式应用
SDK\apps\watch\ui:杰理UI 应用
SDK\apps\watch\app_main.c:应用入口函数
SDK\apps\watch\data_storage:杰理运动、信息存储模块

SDK\apps\common:SDK 通用模块、设备、文件系统配置文件夹
SDK\apps\common\third_party_profile:杰理app 协议应用对接

SDK\cpu\br35\ui_driver\lcd_drive:杰理屏幕驱动
SDK\cpu\br35\ui_driver\interface:杰理GPU 接口部分
SDK\cpu\components:SDK 通用外设接口(iic、rdec)
SDK\cpu\periph_demo:杰理通用外设demo
SDK\cpu\br35\power:杰理低功耗、sensorhub 交互消息

SDK\interface:杰理静态库头文件目录
SDK\cpu\br35\liba:杰理静态库目录

3.2 SDK启动流程

SDK启动流程为:
start->setup_arch->app_main->os_start->cpu1_start->os_sta

3.2.1 setup_arch

setup_arch是发布SDK能看到的第一个函数,主要有以下功能:

  1. 初始化内存管理
  2. 初始化看门狗
  3. 初始化efuse
  4. 初始化时钟
  5. 初始化操作系统
  6. 初始化tick_timer和sys_timer
  7. 初始化串口打印
  8. 初步初始化电源

3.2.2 app_main

app_main里面会新建一个app_core任务(任务会等到os_start后才开始执行)。在app_core 任务的入口位置的app_task_init函数里面包含了SDK的常见初始化,UI 初始化就是在其中。board_init函数就是我们面对用户使用的推荐函数,app_main里面是使用弱定义的方式注册了一个弱的board_init函数,实体部分依然是board 板卡函数里面实现。

3.2.3 os_start

os_start会开始操作系统,新建一个最低优先级的idle任务。如果多核,会初始化其他内核。

3.2.4 cpu1_start

cpu1_start是另一个内核的初始化流程,最后会调用os_start创建idle1任务。

3.3 SDK链接脚本

SDK的链接脚本是sdk.ld,由sdk_ld.c预编译生成。
image

3.3.1 map详细映射

显示每个编译单元在内存中的具体位置,包括代码段、数据段等。通过详细映射,开发者可以了解每个源文件对内存的贡献,从而优化代码和数据的布局。
imageimage 通过分析map文件,开发者可以了解程序的静态ram和flash使用情况,从而进行针对性的优化。此外,可以通过sdk.map可以分析每个段里面的函数先后顺序。

3.4 主任务app_core

3.4.1 整体框架

image 上层应用都是在app_core任务上去开发的,在app_core任务上搞出了一个模式的概念,btstack、btctrler等线程、以及按键、充电等中断会post关键事件给到app_core去处理。

3.4.2 模式

鉴于杰理音箱SDK开发历史原因,app_core引用了模式的概念,同一时刻有且只有一个模式在运行。模式的概念在手表的SDK是比较弱化的,因为我们的SDK设计是把UI操作放在了主导地位,SDK大部分场景是工作在蓝牙模式里面,在涉及本地音乐发射的场景,我们会切换音乐模式,在需要处理开关机界面、动画时候我们要切换开关机模式。因此,SDK里面的模式概念用户可以简单的理解和使用即可。

  1. 模式相关结构体 每个模式拥有自己的一个结构体变量,通过REGISTER_APP_MODE注册到模式的段中,结构体如下:
    image 操作函数结构体如下:
    image

② 模式切换的流程

image

③ 模式切换应用

模式切换时,要用app_send_message发送APP_MSG_GOTO_MODE或者APP_MSG_GOTO_NEXT_MODE事件去切换
比如切换到开机模式时,调用:
app_send_message(APP_MSG_GOTO_MODE, APP_MODE_POWERON);
切换到下一个模式,调用:
app_send_message(APP_MSG_GOTO_NEXT_MODE, 0);
为了方便在701手表SDK已经熟悉开发的客户,我们也封装支持了和701 SDK一样的接口进行模式切换,priv参数为用户需要自带的信息,默认为NULL:
app_task_switch_to(APP_MODE_POWERON, NULL);
模式返回:
app_task_switch_back();
获取当前模式:
app_get_curr_task();

3.4.3 事件处理流程

image app_core任务平时会一直pend着,直到有其他任务/中断post事件过来,才会执行。执行的时候会按照下面顺序依次执行:

  1. 使用APP_MSG_PROB_HANDLER注册的事件处理函数(app_get_messag 函数for_each_app_msg_prob_handler的遍历调用)
  2. 当前模式的事件处理函数(蓝牙模式是app_enter_bt_mode里面的注册处理)
  3. 使用APP_MSG_HANDLER注册的事件处理函数(在app_default_msg_handler函数里面被调用) 注意:仅仅APP_MSG_PROB_HANDLER注册的处理拥有拦截权,可以拦截消息不往下传输,实现参见app_get_messag函数注释。

3.5 事件分布处理

SDK中,有很多流程,都是采用链接脚本加入一个text段去实现分布处理。

3.5.1 原理

在链接脚本中定义一个段,然后将结构体变量放到对应段里面,形成一个结构体数组。使用时相当于操作一个结构体数组。
image

3.5.2 优点

可以在SDK任意文件中注册一个事件处理函数来实现想要的功能,方便将一个功能的相关代码集中到同一个文件中,有利于模块化管理。比如将播放提示音的流程都集中到了tone.c文件中。

3.5.3 缺点

当功能有耦合性和先后顺序时不好把控。比如要等ESCO解码关闭后再播放提示音,如果在BT_STATUS_SCO_STATUS_CHANGE事件就播提示音,有可能先播了提示音再停止解码。
解决办法:手表应用是UI高度接管的,UI拥有高度优先级,针对按键消息,可以直接接管。针对系统消息、蓝牙消息:UI进行监听即可、并且需要关注是否存在SDK关于系统、蓝牙消息直接处理,如果存在需要切断,由UI释放消息进一步处理。

3.6板级相关配置

3.6.1 DEBUG配置

  1. debug io配置 板卡的串口io和波特率配置是从杰理可视化工程生成的sdk_config.h里面配置生成。
    // ------------板级配置.json------------
#define TCFG_DEBUG_UART_ENABLE 1 // 调试串口
#if TCFG_DEBUG_UART_ENABLE
#define TCFG_DEBUG_UART_TX_PIN IO_PORTB_03 // 输出IO
#define TCFG_DEBUG_UART_BAUDRATE 0x1e8480 // 波特率
#define TCFG_EXCEPTION_LOG_ENABLE 0x1 // 打印异常信息
#define TCFG_EXCEPTION_RESET_ENABLE 0x0 // 异常自动复位
#define TCFG_CONFIG_DEBUG_RECORD_ENABLE 0x0 // 异常存flash
#endif // TCFG_DEBUG_UART_ENABLE

配置信息基本规则:

  1. sdk_config.h 是由杰理可视化工具在线配置生成,原则上不建议客户直接修改sdk_config.h的文件,如果客户希望可视化工具不覆盖sdk_config.h,可以配置可视化工具脱机模式
  2. board_config.h 是唯一包含有sdk_config.h和板卡配置的文件的,用户可以在自己的板卡对配置信息进行完善
  3. app_config.h 包含有board_config.h

因此如果用户如果配置不想耦合到杰理可视化工程,请在用户的board_config.h进行定义,如果已经定义在sdk_config.h的内容,客户不想使用工具配置,也可以重新进行undef。

  1. 异常打印配置 异常信息打印开关在log_config/lib_system_config.c
///打印是否时间打印信息
const int config_printf_time         = 1;
 
///异常中断,asser打印开启
#if CONFIG_DEBUG_ENABLE
const int config_asser         = TCFG_EXCEPTION_LOG_ENABLE;  // 1:使能异常打印; 2:追加额外调试信息(dump另外一个CPU寄存器信息)
const int config_exception_reset_enable = TCFG_EXCEPTION_RESET_ENABLE;
#else
const int config_asser         = 0;
const int config_exception_reset_enable = 1;
#endif
  1. 内存debug 该功能开启后malloc的赘余空间会增加,仅仅适用于SDK开发阶段对malloc的内存情况进行debug,不适用于量产使用和静态buf的越界。配置参见log_config/lib_system_config.c。单纯的内存泄漏问题也可以使用heap内存记录功能进行debug。
//================================================//
//          malloc 越界debug和记录功能            //
//const_malloc_list_check_debug_en             = 1;    //开启配置内存越界检查,注意,本功能和heap内存记录功能互斥,更注重debug性质和主动检查性质,开启后会申请额外信息,本功能仅仅适用开发后用于内存安全性检查和内存越界的检查
//const_malloc_list_check_add_extlen           = 0;    //用来做写穿检查,如果内存写穿比较厉害导致rets打印异常,可以增加长度
//const int const_malloc_list_check_for_each   = 1;    //每次申请内存都做全局性检查,效率低,但是可以快速回溯出异常地址
//内存泄漏debug:通过 malloc_list_status_printf_all      //可以打印最后没有释放的内存,cout 是需要打印是数量,存在内存泄漏一般看最后几个申请是否有rets重复即可
//void malloc_list_status_printf_all(int count);
//================================================//
const int const_malloc_list_check_debug_en   = 0;
const int const_malloc_list_check_add_extlen = 0;
const int const_malloc_list_check_for_each   = 0;

3.6.2 RTC相关结构体

用户可以初始化配置默认的系统时间和闹钟函数。

static struct sys_time def_sys_time = {  //初始化系统时间
    .year = 2020,
    .month = 1,
    .day = 1,
    .hour = 0,
    .min = 0,
    .sec = 0,
};
 
static struct sys_time def_alarm = {     //初始化闹钟时间
    .year = 2020,
    .month = 1,
    .day = 1,
    .hour = 0,
    .min = 5,
    .sec = 0,
};
 
extern void alm_wakeup_isr(void);
void rtc_alm_isr()    //闹钟回调函数测试
{
    printf("alarm_timeout!!!\n\n");
    /* alm_wakeup_isr(); */
}
 
struct rtc_config_init rtc_dev_config = {   //RTC初始化结构体
    .default_sys_time = &def_sys_time,   //配置默认系统时钟
    .default_alarm = &def_alarm,        //配置默认闹钟
    .rtc_clk = CLK_SEL_LRC,       // 配置时钟源
 
    .alm_en = 1,            //闹钟使能
    .cbfun = rtc_alm_isr,
    //rtc闹钟回调函数
};

SDK的RTC接口进行过重构,在701 SDK上我们是使用dev设备接口进行RTC读取,新SDK接口修改为:

void rtc_read_time(struct sys_time *time);
void rtc_write_time(const struct sys_time *time);
void rtc_read_alarm(struct sys_time *time);
void rtc_write_alarm(const struct sys_time *time);

3.6.3 LCD相关结构体

LCD结构体部分和701 SDK保持比较一致

#if TCFG_SPI_LCD_ENABLE
//推屏使用有专门硬件模块,不是普通spi模块,io固定,根据屏幕驱动类似输出时序
LCD_SPI_PLATFORM_DATA_BEGIN(lcd_spi_data) = {
    .pin_reset = IO_PORTC_03,
    .pin_en = IO_PORTC_01,
    .pin_en_ex = IO_PORTC_02,
    .pin_te = TCFG_LCD_TE_IO,
    .pin_bl = TCFG_LCD_BL_IO,
 
    LCD_SPI_PLATFORM_DATA_END()
};
 
const struct ui_devices_cfg ui_cfg_data = {
    .type = TFT_LCD,
    .private_data = (void *) &lcd_spi_data,
};
#endif /*TCFG_SPI_LCD_ENABLE*/
 

3.6.4 FLASH分区注册

SDK可以在flash上分配出内置分区,分配物理分区需要在路径 :br35/tools/isd_config_rule.c文件下载分配办法:

MODE_FILE = mode.bin;
MODE_ADR = TCFG_MODE_INSERT_FLASH_BASE;
MODE_LEN = TCFG_MODE_INSERT_FLASH_SIZE;
MODE_OPT = 1;
 
FATFSI_FILE = watch.bin;
FATFSI_ADR = TCFG_WATCH_INSERT_FLASH_BASE;
FATFSI_LEN = TCFG_WATCH_INSERT_FLASH_SIZE;
FATFSI_OPT = 1;

MODE和FATFSI是分区的名称,可以见板卡配置的路径,mode.bin 和watch.bin 是下载到分区的文件文件,分配的LEN长度必须大于文件长度
如果不知道如何分配地址大小,也可以先注册为以下形式:

/* MODE_FILE = mode.bin; */
MODE_ADR = AUTO;
MODE_LEN = TCFG_MODE_INSERT_FLASH_SIZE;
MODE_OPT = 1;

通过观察下载提醒的地址提示框分配地址:
image 板卡配置:

/注册只读文件系统分区
NORFLASH_SFC_DEV_PLATFORM_DATA_BEGIN(norflash_norfs_inside_dev_mode_data)
.path                 = (const u8 *)"mnt/sdfile/app/MODE",
.start_addr           = TCFG_MODE_INSERT_FLASH_BASE,
.size     = TCFG_MODE_INSERT_FLASH_SIZE,
NORFLASH_SFC_DEV_PLATFORM_DATA_END()


//注册杰理virfat分区,带读写文件系统.
NORFLASH_SFC_DEV_PLATFORM_DATA_BEGIN(norflash_norfs_inside_dev_data)
.path                 = (const u8 *)"mnt/sdfile/app/FATFSI",
.start_addr           = TCFG_WATCH_INSERT_FLASH_BASE,
.size     = TCFG_WATCH_INSERT_FLASH_SIZE,
NORFLASH_SFC_DEV_PLATFORM_DATA_END()


//使用内置flash  跑ui
//虚拟文件系统对接jl sdfile fat 文件系统设备入口,往下对接文件系统,往上对接物理设备
{ "virfat_flash",  &virfat_flash_dev_ops, (void *)"res_nor"},

//res_nor 是物理设备入口
{ "res_nor",   &inside_norflash_fs_dev_ops, (void *) &norflash_norfs_inside_dev_data},

//res_nor_mode 是文件系统物理设备入口
{ "res_nor_mode",   &inside_norflash_fs_dev_ops, (void *) &norflash_norfs_inside_dev_mode_data},

文件系统和设备的关联是在dev_manager/dev_reg.c 注册文件系统关联入口,设备名称就是关联的dev的设备,文件的跟目录也跟随关联的文件系统,即使是不同分区关联两个相同的文件系统,也是需要注册两次的(根路径不一样)

// dev_manager/dev_reg.c 注册文件系统入口
//flash 虚拟fat
{
    /*logo*/   "virfat_flash",
    /*name*/   "virfat_flash",
    /*storage_path*/ "storage/virfat_flash",
    /*root_path*/  "storage/virfat_flash/C/",
    /*fs_type*/   "sdfile_fat",
},

// 用于存储JL font sidebar
{
    /*logo*/   "res_nor_mode",
    /*name*/   "res_nor_mode",
    /*storage_path*/ "storage/res_nor_mode",
    /*root_path*/  "storage/res_nor_mode/C/",
    /*fs_type*/   "ex_sdfile",
},

3.7 低功耗管理

3.7.1 低功耗等级的区别

SDK的低功耗等级有两个重要配置(sdk_config.h配置),SLEEP和DSLEEP,即睡眠和深度睡眠。深度睡眠具备更优的功耗特性,但是对比SLEEP,深度睡眠场景下,芯片的寄存器、部分ram会进行掉电,因此对于退出深度睡眠后的场景,用户要注册外设模块的恢复,一般情况下SDK已经做了默认处理。
malloc的ram在进入低功耗前需要尽量释放内存,如果不满足可以掉电ram的颗粒度,芯片只能进入SLEEP,无法进入DSLEEP(这里和701 存在不同)。
SDK在进出低功耗过程会对malloc的内存进行自动整理,涉及硬件模块的内存建议使用静态内存分配或者使用dma_malloc接口申请内存(该接口的物理空间不会发生整理)。硬件访问的内存在使用过程发生整理会触发死机。

3.7.2 进出低功耗回调函数

用户可以在这里进行一些进低功耗前的特殊处理或者debug 低功耗进出时间

void sleep_enter_callback(u8 step)
{
    /* 此函数禁止添加打印 */
    putchar('<');
    gpio_portb_control(1);
}
 
void sleep_exit_callback(u32 usec)
{
    gpio_portb_control(0);
    putchar('>');
}

3.7.3 低功耗拦截

用户可以主动拦截低功耗行为,例如在亮屏操作时候不进低功耗

/* --------------------------------------------------------------------------*/
/**
 * @brief 按键模块是否可进低功耗查询函数
 *
 * @return 0:不可进
 *         1:可进
 */
/* ----------------------------------------------------------------------------*/
static u8 key_idle_query(void)
{
    if (g_key_idle_query_en) {
        return !g_is_key_active;
    } else {
        return 1;
    }
}
REGISTER_LP_TARGET(key_driver_target) = {
    .name = "key",
    .is_idle = key_idle_query,
};

3.7.4 低功耗等级拦截

用户可以主动拦截低功耗等级,例如可以强制把DSLEEP行为拦截为SLEEP行为,应用在客户实现灭屏表盘功能、一些特殊场景不能进深度睡眠的场景但是有基本功耗要求。

static u8 lowpower_level_target(void)
{
     return LOW_POWER_MODE_SLEEP;//返回轻度休眠
     return LOW_POWER_MODE_DEEP_SLEEP;//返回深度睡眠
 }
 
REGISTER_LP_TARGET(lowpower_level_target) = {
    .name = "lowpower_level_target",
    .level = lowpower_level_target,
}

3.7.5 低功耗注册拦截事件

本次拦截事件注册和拦截进低功耗有本质的区别,之前的拦截是通过idle查询方式的拦截,工作的场景是在低功耗外面的,本次拦截事件注册是在进低功耗过程中进行一次拦截,来做一些必须资源的释放然后下一个tick进入休眠,本应用在701 手表SDK我们用于外挂flash电源的释放。
注意:本次post消息处理是异步发送到第三方线程,这个时候就有一个异步问题,有使用这个场景时候建议联系杰理做进一步了解

static u8 extern_flash_handler(u32 timeout)
{
    int msg[3];
    ASSERT(extern_flash_status != EX_FLASH_POWER_OPENING);
 
    if (extern_flash_status == EX_FLASH_POWER_CLOSE) {
        return 0;//本次进低功耗
    }
 
    if (extern_flash_status == EX_FLASH_POWER_CLOSING) {
        return 1;//本次先不进低功耗
    }
 
    extern_flash_status = EX_FLASH_POWER_CLOSING;
    msg[0] = (int)norflash_flash_poweroff;
    msg[1] = 1;
    msg[2] = (int)NULL;
    last_id++;//产生时序上的唯一值,用于删消息池
    if (!last_id) {
        last_id = 1;
    }
    os_taskq_post_type("app_core", Q_CALLBACK | last_id | USER_MASK_TYPE, 3, msg);
 
    return 1;//本次先不进低功耗
}
 
static u8 extern_flash_handler1(u32 timeout)
{
    return 0;
}
 
//低功耗线程请求所有模块关闭,由对应线程处理
REGISTER_LP_REQUEST(power_flash_target) = {
    .name = "extern_flash",
    .request_enter = extern_flash_handler,
    .request_exit = extern_flash_handler1,
};

3.7.6 低功耗外设恢复

深度睡眠场景下,芯片的寄存器、部分ram会进行掉电,因此对于退出深度睡眠后的场景,用户要注册外设模块的恢复。如果有用户自行实现的外设驱动,需要注意进行低功耗的寄存器备份和恢复操作。

static void mcpwm_enter_deepsleep(void)
{
    mcpwm_con_en = 0;
    for (u8 id = 0; id < MCPWM_NUM_MAX; id++) {
        if (mcpwm_info[id] != NULL) {
            u32 ch = (u32)mcpwm_info[id]->cfg.ch;
            if (JL_MCPWM->MCPWM_CON0 & BIT(ch + MCPWM_CON_TMR_EN)) {
                mcpwm_con_en |= BIT(ch + MCPWM_CON_TMR_EN);
            }
            if (JL_MCPWM->MCPWM_CON0 & BIT(ch + MCPWM_CON_PWM_EN)) {
                mcpwm_con_en |= BIT(ch + MCPWM_CON_PWM_EN);
            }
        }
    }
}
 
static void mcpwm_exit_deepsleep(void)
{
    for (u8 id = 0; id < MCPWM_NUM_MAX; id++) {
        if (mcpwm_info[id] != NULL) {
            u32 ch = (u32)mcpwm_info[id]->cfg.ch;
            if ((mcpwm_con_en & BIT(ch + MCPWM_CON_TMR_EN)) && (mcpwm_con_en & BIT(ch + MCPWM_CON_PWM_EN))) {
                mcpwm_start(id);
            }
        }
    }
}
DEEPSLEEP_TARGET_REGISTER(mcpwm) = {
    .name   = "mcpwm",
    .enter  = (void *)mcpwm_enter_deepsleep,
    .exit   = (void *)mcpwm_exit_deepsleep,
};
 

3.8 GPIO

3.8.1 IO操作

SDK对GPIO的接口进行了重构,接口见gpio.h,常见的使用方式如:

 enum gpio_mode mode = val ? PORT_OUTPUT_HIGH : PORT_OUTPUT_LOW;
 gpio_set_mode(IO_PORT_SPILT(IO_PORTA_00), mode); //输出0、1
 gpio_set_mode(IO_PORT_SPILT(IO_PORTA_00), PORT_INPUT_PULLUP_100K); //上下拉配置
 gpio_set_mode(IO_PORT_SPILT(IO_PORTA_00), PORT_INPUT_FLOATING); //输入配置

特别注意:客户请不要直接操作GPIO寄存器、避免在多核系统造成原子操作问题。

3.8.2 IO中断操作

IO中断可以用于te、tp中断的捕捉,参见demo

struct gpio_irq_config_st tp_int_irq_config = {
    .pin = NO_CONFIG_PORT,
    .irq_edge = PORT_IRQ_EDGE_FALL,
    .callback = tp_irq_callback,
    .irq_priority = 3,
};
 
static void tp_irq_init(void)
{
    u8 port_x = TCFG_TP_INT_IO / IO_GROUP_NUM;
    u16 port_pin_x = BIT(TCFG_TP_INT_IO % IO_GROUP_NUM);
    tp_int_irq_config.pin =  port_pin_x;
    log_debug("[%s] port_x:%x, port_pin_x:%x", __func__, port_x, port_pin_x);
    gpio_irq_config(port_x, &tp_int_irq_config);
}

3.9按键注册和消息

3.9.1 按键初始化

707N 芯片的IO存在比较紧张的问题,因此SDK优先使用AD按键。下面以AD按键为例说明初始化:

int adkey_init(void)
{
    __this = get_adkey_platform_data();
     ...
    return 0;
}
//adkey_init函数在app mian 入口调用

adkey_init函数 在app mian入口调用,在SDK中get_adkey_platform_data()是获取可视化工具的cfg_tool.bin的配置,客户需要接管可视化工具的配置,需要对info和platform_data结构体重新赋值即可。

const struct adkey_platform_data *get_adkey_platform_data()
{
    struct adkey_info info;
    u16 key_numbers;
    if (platform_data.enable) {
        return &platform_data;
    }
    key_numbers = 2;
 
    info.cfg_key_list[0].res = 100;    // adkey1电阻值
    info.cfg_key_list[1].res = 330;    // adkey2电阻值
    info.max_ad_value = 4096;           // adc最大值
    info.pull_up_value = 1000;          // 上拉电阻值
 
    platform_data.adkey_pin = IO_PORTB_07;
    platform_data.extern_up_en = 0;
    platform_data.ad_channel = adc_io2ch(platform_data.adkey_pin);
    platform_data.long_press_enable = 1;
    platform_data.long_press_time = 8;
 
    platform_data.key_value[0] = KEY_AD_NUM0;
    platform_data.key_value[1] = KEY_AD_NUM1;
    platform_data.enable    = 1;
    
    ...
}

3.9.2 按键低功耗唤醒

区别于701 SDK的板卡注册,707 SDK注册按键唤醒在 br35/power/key_wakeup.c
芯片最多支持8通道注册,用户新增通道模仿以下操作即可,SDK内部会自动分配空闲通道的注册。对于AD按键来说,建议过滤一次唤醒时候SDK唤醒瞬态的ad 按键消息,避免出现部分情况按键唤醒有响应消息、部分场景不响应消息的问题。
key_active_set接口,在SDK设计为了重置按键驱动拦截低功耗的计数器,避免按键唤醒后立即进低功耗情况。

void key_active_set(u8 port);
static void key_wakeup_callback(P33_IO_WKUP_EDGE edge)
{
    key_active_set(0);
}
 
//只读配置,用户后续新增io唤醒、ad唤醒注意使用const修饰,节约静态ram
static const struct _p33_io_wakeup_config port0 = {
    .pullup_down_mode  = PORT_INPUT_PULLUP_10K,
    .filter        = PORT_FLT_DISABLE,
    .edge               = FALLING_EDGE,
    .gpio              = IO_PORTB_07,
    .callback          = key_wakeup_callback,
};
 
void key_wakeup_init()
{
#if (!TCFG_LP_TOUCH_KEY_ENABLE)
    p33_io_wakeup_port_init(&port0);
    p33_io_wakeup_enable(IO_PORTB_07, 1);
#endif
}

3.9.3 按键长按复位

进行长按复位支持是考虑客户样机发生未知异常情况可以强制复位,因为使用了AD按键,需要考虑IO电压能拉低到1v下。另外考虑到客户开关机情况长按按键比较久,需要对长按复位时间进行加长配置,时间最长配置16s。如果客户不能接受开关机按键16s长按复位,建议新增独立按键。

int adkey_init(void)
{
    ...
    if (__this->long_press_enable) {
        gpio_longpress_pin0_reset_config(__this->adkey_pin, 0, __this->long_press_time,  1,  PORT_INPUT_PULLUP_100K);
    }
    return 0;
}

3.9.4 按键消息

识别到按键消息后,在key/key_driver.c 里面调用key_event_handler 实现把消息发生到app core线程。app_send_message_from 为原生设计用于发送消息的接口。

 /* --------------------------------------------------------------------------*/
/**
 * @brief 按键事件过滤、检测和发送
 *
 * @param key:基础按键动作(mono_click、long、hold、up)和键值
 */
/* ----------------------------------------------------------------------------*/
void key_event_handler(struct key_event *key)
{
    ...
    //外部需要格外做的处理流程,请通过注册的形式在此处回调
    list_for_each_key_callback(p) {
        if (p->cb_deal == NULL) {
            continue;
        }
        if (p->cb_deal(key)) {
            return;
        }
    }
    app_send_message_from(MSG_FROM_KEY, sizeof(*key), (int *)key);
}

如果客户需要在消息发送前对按键消息进行拦截或者转发到用户自定义的线程,可以注册REGISTER_KEY_DET_CALLBACK的段,即可被list_for_each_key_callback 进行拦截。如下文,用户可以参考ui_key_remay.c的实现

static int ui_key_event_prob_handle(void *arg)
{
    struct key_event *key = (struct key_event *)arg;
    u16 key_event;
    log_debug("[%s] key->event:%d, key->value:%d", __func__, key->event, key->value);
    key_event = jlui_key_event_remap(key, jlui_key_watch_event_table);
 
    if (__this->send_event_cb) {
        __this->send_event_cb(key_event);
        return 1; /*接管事件*/
    }
    return 0; /*不接管事件*/
}
/*拦截key_driver产生的key_event*/
REGISTER_KEY_DET_CALLBACK(jlui_key) = {
    .name = NULL,
    .arg = NULL,
    .cb_deal = ui_key_event_prob_handle,
};

3.10 硬件IIC

SDK支持两组硬件iic,第一组iic的仅仅适用于主系统(可以支持普通io 映射),第二组iic用于主系统和sensorhub的共享iic(仅仅支持PORTB的IO映射)。sensorhub操作第二组iic接口要注意sensorhub 初始化io要和主系统保持一致。
板卡注册:

const struct iic_master_config hw_iic_cfg_const[MAX_HW_IIC_NUM] = {
    {
        .role = IIC_MASTER,
        .scl_io = TCFG_HW_I2C0_CLK_PORT,
        .sda_io = TCFG_HW_I2C0_DAT_PORT,
        .io_mode = PORT_INPUT_PULLUP_10K,      //上拉或浮空,如果外部电路没有焊接上拉电阻需要置上拉
        .hdrive = PORT_DRIVE_STRENGT_2p4mA,    //IO口强驱
        .master_frequency = TCFG_HW_I2C0_CLK,  //IIC通讯波特率
        .io_filter = 1,                        //是否打开滤波器(去纹波)
    },//iic0
    {
        .role = IIC_MASTER,
        .scl_io = TCFG_HW_I2C_P11_CLK_PORT,
        .sda_io = TCFG_HW_I2C_P11_DAT_PORT,
        .io_mode = PORT_INPUT_PULLUP_10K,      //上拉或浮空,如果外部电路没有焊接上拉电阻需要置上拉
        .hdrive = PORT_DRIVE_STRENGT_2p4mA,    //IO口强驱
        .master_frequency = TCFG_HW_I2C_P11_CLK,  //IIC通讯波特率
        .io_filter = 1,                        //是否打开滤波器(去纹波)
    },//iic_p11
};

iic接口参见iic_api.h:
建议客户使用system_iic 接口,所示接口iic地址为8bit地址,后续会调整会7bit地址,如果需要在中断使用iic,需要把iic的互斥量改成spin lock,但有限制cpu 性能影响,因此SDK 设计的tp 处理是在任务中进行,客户进行SDK 二次开发可以参考这个设计逻辑,避免中断运行iic。
注意:同一组iic不能交叉使用原生的硬件iic接口(没有共同的互斥锁)。主系统的iic1可以和sensorhub 共享使用,对比了701的大小核共享iic通讯,707和sensorhub支持硬件锁结构,用户可以无感直接调用iic接口即可。

int system_iic_init(hw_iic_dev iic, struct iic_master_config *i2c_config);
int system_iic_deinit(hw_iic_dev iic);
void system_iic_reset(hw_iic_dev iic);
int system_iic_suspend(hw_iic_dev iic);
int system_iic_resume(hw_iic_dev iic);
int system_iic_read_nbytes(hw_iic_dev iic,
                           unsigned char dev_addr, //设备地址
                           unsigned char *reg_addr, unsigned char reg_len,//设备寄存器地址,长度
                           unsigned char *read_buf, int read_len);//缓存buf,读取长度
int system_iic_write_nbytes(hw_iic_dev iic,
                            unsigned char dev_addr, //设备地址
                            unsigned char *reg_addr, unsigned char reg_len,//设备寄存器地址,长度
                            unsigned char *write_buf, int write_len);//数据buf, 写入长度
 

3.11 时钟管理

3.11.1 API简介

SDK针对时钟频率切换提供4个主要api:clock_alloc、clock_free、clock_lock、clock_unlock

clock_alloc:
函数介绍:用户主动增加时钟,name 为用户自定义名字,注意申请和释放使用统一名字即可。接口带防重入,即同一个名字自会增加一次时钟。
函数作用:用户调用clock_alloc后,系统时钟会自动设置到允许最大值,然后系统根据SDK 允许情况进行动态降频,直到最低运行频率降低为用户增加的时钟频率累计和。
clock_free:
函数介绍:用户主动释放时钟,name 为用户自定义名字,注意申请和释放使用统一名字即可。
函数作用:用户调用clock_free后,系统时钟会自动设置到允许最大值,然后系统根据SDK 允许情况进行动态降频,直到最低运行频率降低为当前用户释放时钟的频率。
clock_lock:
函数介绍:用户主动锁存时钟,锁存后,时钟即为用户设置时钟,系统不会进行变频。
clock_free:
函数介绍:用户主动解锁时钟,解锁存后,时钟恢复最大值,然后根据系统空闲情况,逐步降低为用户累加时钟的数值。

 /* --------------------------------------------------------------------------*/
/**
 * @brief clock_alloc,此函数会触发时钟频率设置
 *
 * @param name
 * @param clk
 *
 * @return 0:succ
 */
/* ----------------------------------------------------------------------------*/
int clock_alloc(const char *name, u32 clk)
 
 
/* --------------------------------------------------------------------------*/
/**
 * @brief clock_free,此函数会触发时钟频率设置
 *
 * @param name
 *
 * @return 0:succ
 */
/* ----------------------------------------------------------------------------*/
int clock_free(char *name)
 
 
/* --------------------------------------------------------------------------*/
/**
 * @brief clock_lock
 *
 * @param name: owner name
 * @param clk: lock_freq
 *
 * @return 0:succ, other:err code
 */
/* ----------------------------------------------------------------------------*/
int clock_lock(const char *name, u32 clk)
 
 
/* --------------------------------------------------------------------------*/
/**
 * @brief clock_unlock
 *
 * @param name: owner name
 *
 * @return 0:succ, other:err code
 */
/* ----------------------------------------------------------------------------*/
int clock_unlock(char *name)
         

3.11.2 API典型应用

屏幕亮屏时候对时钟进行锁存、灭屏时候进行时钟解锁

clock_lock("ui_show", clk_get_max_frequency());
clock_unlock("ui_show");

3.12 SensorHub交互

参考见:p11_cbuf.c,使用参见注释

3.13 时间管理

时间同步/更新API:rtc_update_time_api

3.14 功能模块

3.14.1 flash分区以及配置

image

四、杰理UI框架

4.1 UI 编辑工具

本章节意在简单介绍上手,工具的详细配置以及使用说明,请参阅以下文档:

参阅《UI布局工具使用说明》:
..\SDK\cpu\br35\tools\UI工程\doc
image

SDK的UI工程在以下路径:
..\SDK\cpu\br35\tools\UI工程
image

  • doc:存放工具的相关说明文档
  • UITools:存放相关的 UI 工具链
  • 字库文件:主要是负责UI中多语言文字编码
  • ui_320x386_watch:为 ui 工程主目录(新建 ui 工程需要参考该目录结构,也可以直接拷贝该文件夹,在该文件夹的基础上进行修改) image 运行 UI 编辑工具,直接双击下图的”step1-打开 UI 绘图工具.bat”,通过脚本调用打开:
    image UI 编辑工具主界面如下图:
    image UI 编辑完成后,直接双击”资源编译”,生成相关的资源文件
    imageimage

4.2 UI 控件

4.2.1 控件的映射

image 杰理 UI 基本流程:下图为UI编译工具链的工作流程,UI工程=Json文件+资源文件,Json文件上记录了UI参数信息(位置、尺寸等等)和资源索引地址,我们从UI编辑工具上改UI最后无非就是修改了Json文件内的内容而已,当然我们从脚本打开UI编辑工具时是自动读取读取Json文件参数来呈现。
imageimage

4.2.2 控件的响应

image 编译后,控件映射为SDK内的一个宏ID:
image 我们可以根据UI工具编译出的控件宏ID,给该控件注册一些响应逻辑,UI内核提供 3 种响应注册接口:
image 自己实现响应逻辑的接口,然后在代码里注册到UI内核,例如我在屏幕上触控某个UI控件,就会调用你给该控件注册的.ontouch接口。
image

注意:
在调用响应注册接口REGISTER_UI_EVENT_HANDLER 时,
必须在当前源文件定义#define STYLE_NAME JL,否则控件响应会注册失败且编译不会报错,
image 因为STYLE_NAME是REGISTER_UI_EVENT_HANDLER的隐含参数。
image

4.2.3 控件的属性

各类控件属性请参阅《UI控件结构体成员说明》:
UI控件结构体成员说明.pdf

每种控件都有自己的属性结构体(控件结构体 = 公共属性成员 + 私有属性成员)
比如文字控件的属性结构体定义在ui_text.h里面;
比如图片控件的属性结构体定义在ui_pic.h里面;
比如列表控件的属性结构体定义在ui_grid.h里面;
比如列表控件的属性结构体定义在ui_grid.h里面;
比如按键控件的属性结构体定义在ui_button.h里面;
...........其他控件 以此类推...........
举个例子🌰:按键控件
image

4.3 UI 消息类型

image

4.3.1 显示消息

image 注意:不要在onchange里面调用ui_XXX_by_id()、ui_show()、ui_hide(),这些接口会带有重绘(刷新)操作,因为onchange本身就会刷新,onchange里面调用重绘的接口会一直触发刷新(可能会导致卡顿死机)
建议:
1、想在onchange里面更新控件数据,用不带_by_id()后缀的接口,一般控件都会带有配套的接口;
2、想在onchange里面设置隐藏显示控件的话,编辑控件css属性里的invisible属性
(隐藏控件invisible = 1;显示控件invisible = 0;)

4.3.2 触摸消息

image 触控唤醒IO设置:
imageimage 收发响应接口:
image

4.3.3 按键消息

控件默认不响应硬件按键,如果硬件按键想要操作UI,需要在硬件按键的公共事件处理里面接管,然后把按键消息转发到UI线程.onkey处理
①被控控件,需要在初始化阶段设置接管按键消息
image ②硬件按键公共事件处理,会先检测有没有想接管的UI控件
image ③完成以上步骤,.onkey响应接口才会接收到事件消息

4.4 UI 消息传递机制

详情参阅《消息流程》:消息流程
image 注意:显示消息的ON_CHANGE_INIT传递顺序有特殊调整:子控件》控件》布局》图层,其他显示消息不变。

4.6.1 焦点控件

什么是焦点控件?焦点控件就是获取到焦点的控件,焦点可以灵活地在控件上转移,且有且只有一个焦点控件。控件成为焦点控件后,UI内核传递消息直接传达且只传达到焦点控件上,不会再传递到其他控件上。
设置焦点控件有两种方式:
①在控件初始化阶段锁定焦点在本控件
image ②暂时接管,通过return true设置焦点,return false释放焦点
image 注意:特殊调整

  • 显示类控件(文字、图片):默认不接收抬起事件,如要接收抬起事件,自行在down事件内return true;
  • 按键控件:会自动设置为焦点控件

4.5 UI工程工具

先把原来的UI工程复制一份,重命名
image 然后打开UI工程工具:
imageimage

4.5.1 UI工程缩放

两个流程:工程尺寸的缩放、图标资源的缩放:
①工程尺寸的缩放:
image

②图标资源的缩放:
image 工程下的每个文件夹,都走一遍这两个流程!!!!!
image 操作完上面步骤后,每个文件夹里面的ui工程都先重新资源编译一遍,再重新烧录,才能生效。
image

4.5.2 其他功能说明

参阅《UI工程源文件工具使用》:UI工程源文件工具使用

4.6 UI 进阶教程

4.6.1 自定义绘图

①自定义绘图接口

参考..\code\sdk\include_lib\system\ui_new\ui\ui.h

/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_remove_backcolor 删除控件的背景颜色
 *
 * @param elm 待删除颜色的控件句柄
 */
/* ------------------------------------------------------------------------------------*/
void ui_remove_backcolor(struct element *elm);

/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_remove_backimage 删除控件的背景图
 *
 * @param elm 待删除背景图的控件句柄
 */
/* ------------------------------------------------------------------------------------*/
void ui_remove_backimage(struct element *elm);

/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_remove_border 删除控件的边框
 *
 * @param elm 待删除边框的控件句柄
 */
/* ------------------------------------------------------------------------------------*/
void ui_remove_border(struct element *elm);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_fill_rect ui填充矩形
 *
 * @param dc UI框架句柄
 * @param left 矩形left坐标
 * @param top 矩形top坐标
 * @param width 矩形宽度
 * @param height 矩形高度
 * @param acolor 填充颜色(带透明度)
 *
 * @return
 */
/* ------------------------------------------------------------------------------------*/
int ui_fill_rect(struct draw_context *dc, int left, int top, int width, int height, u32 acolor);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_image 自定义绘图方式显示图片
 *
 * @param dc 待显示的上下文句柄
 * @param page 待显示图片所在页面
 * @param id 待显示图片所在控件ID
 * @param x 待显示图片将显示的X坐标
 * @param y 待显示图片将显示的Y坐标
 *
 * @return
 */
/* ------------------------------------------------------------------------------------*/
int ui_draw_image(struct draw_context *dc, int page, int id, int x, int y);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_image_large 大图片截取显示
 *
 * @param dc 显示上下文
 * @param page 页面号
 * @param id 图片索引
 * @param x 显示的X坐标(相对屏幕)
 * @param y 显示的Y坐标(相对屏幕)
 * @param width 显示宽度
 * @param height 显示高度
 * @param image_x 图片的X坐标(相对图片)
 * @param image_y 图片的Y坐标(相对图片)
 *
 * @return -1 失败,0 正常
 * @note 大图片显示具有尺寸限制,请留意工具或者代码提示信息
 */
/* ------------------------------------------------------------------------------------*/
int ui_draw_image_large(struct draw_context *dc, int page, int id, int x, int y, int width, int height, int image_x, int image_y);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_ascii 自定义合成ascii字符串显示
 *
 * @param dc 显示上下文
 * @param str 字符串指针
 * @param strlen 字符串长度
 * @param x 字符串X坐标
 * @param y 字符串Y坐标
 * @param color 字符串显示颜色
 *
 * @return 0 正常,-1 失败
 */
/* ------------------------------------------------------------------------------------*/
int ui_draw_ascii(struct draw_context *dc, char *str, int strlen, int x, int y, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_text 自定义显示字符串
 *
 * @param dc 显示上下文
 * @param encode 编码格式
 * @param endian 大小端
 * @param str 字符串指针
 * @param strlen 字符串长度
 * @param x 显示X坐标
 * @param y 显示Y坐标
 * @param color 显示颜色
 *
 * @return 0 正常,-1 失败
 */
/* ------------------------------------------------------------------------------------*/
int ui_draw_text(struct draw_context *dc, int encode, int endian, char *str, int strlen, int x, int y, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_strpic 显示多国语言excel表文本
 *
 * @param dc 显示上下文
 * @param id 文本控件ID
 * @param x 显示X坐标
 * @param y 显示Y坐标
 * @param color 文本颜色
 *
 * @return 0 正常,-1 失败
 */
/* ------------------------------------------------------------------------------------*/
int ui_draw_strpic(struct draw_context *dc, int id, int x, int y, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_line 绘制直线
 *
 * @param _dc 显示上下文
 * @param x0 直线起点X坐标
 * @param y0 直线起点Y坐标
 * @param x1 直线终点X坐标
 * @param y1 直线终点Y坐标
 * @param color 直线颜色
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_line(void *_dc, int x0, int y0, int x1, int y1, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_line_by_angle 绘制指定角度的直线
 *
 * @param _dc 显示上下文
 * @param x 直线起点X坐标
 * @param y 直线起点Y坐标
 * @param length 直线长度
 * @param angle 直线角度
 * @param color 直线颜色
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_line_by_angle(void *_dc, int x, int y, int length, int angle, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_rect 绘制矩形填充
 *
 * @param _dc 显示上下文
 * @param x 矩形左上角X坐标
 * @param y 矩形左上角Y坐标
 * @param width 矩形宽度
 * @param height 矩形高度
 * @param color 填充颜色
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_rect(void *_dc, int x, int y, int width, int height, int color);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_circle 绘制圆环
 *
 * @param dc 显示上下文
 * @param center_x 圆环中心X坐标
 * @param center_y 圆环中心Y坐标
 * @param radius_big 圆环外径
 * @param radius_small 圆环内径
 * @param angle_begin 起始角度
 * @param angle_end 结束角度
 * @param color 圆环颜色
 * @param percent 绘制百分比
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_circle(struct draw_context *dc, int center_x, int center_y,
                    int radius_big, int radius_small, int angle_begin,
                    int angle_end, int color, int percent);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_ring 绘制圆
 *
 * @param _dc 显示上下文
 * @param x 圆心X坐标
 * @param y 圆心Y坐标
 * @param radius_big 圆形外径
 * @param radius_small 圆形内径
 * @param angle_begin 起始角度
 * @param angle_end 结束角度
 * @param color 颜色
 * @param percent 绘制百分比
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_ring(void *_dc, int x, int y, int radius_big, int radius_small, int angle_begin, int angle_end, int color, int percent);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_bar 绘制水平进度条
 *
 * @param _dc 显示上下文句柄
 * @param x 绘制的X坐标(左上角)
 * @param y 绘制的Y坐标(右上角)
 * @param width 绘制宽度
 * @param height 绘制高度
 * @param color 高亮颜色
 * @param percent 高亮百分比
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_bar(void *_dc, int x, int y, int width, int height, int color, int percent);

/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_alpha_bar    透明度圆环
 *
 * @param _dc
 * @param x
 * @param y
 * @param width
 * @param height
 * @param color
 * @param percent (0-100)
 * @param alpha  (0-255)
 */
/* ------------------------------------------------------------------------------------*/
void ui_draw_alpha_bar(void *_dc, int x, int y, int width, int height, int color, int percent, int alpha);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_draw_get_mixed_pixel 颜色混合(ARGB565)
 *
 * @param backcolor 背景色
 * @param forecolor 前景色
 * @param alpha 透明度
 *
 * @return u16 混合后的颜色值
 */
/* ------------------------------------------------------------------------------------*/
u16 ui_draw_get_mixed_pixel(u16 backcolor, u16 forecolor, u8 alpha);


/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_custom_draw_clear 清空自定义绘图
 *
 * @param dc 显示上下文
 */
/* ------------------------------------------------------------------------------------*/
void ui_custom_draw_clear(struct draw_context *dc);

②自定义绘图Demo

参考..\apps\watch\ui\lcd\STYLE_WATCH_NEW\ui_action_watch.c下的Demo:

static int DRAW_DEMO_onchange(void *_ctrl, enum element_change_event event, void *arg)
{
    struct element *elm = (struct element *)_ctrl;
    struct draw_context *dc = (struct draw_context *)arg;
    struct rect rect = {0};
    struct rect r = {0};
    struct image_file f;
    struct rect r1;
    int angle;
    int id;
    int i;

    switch (event) {
    case ON_CHANGE_INIT:
        break;
    case ON_CHANGE_SHOW:
#if defined(TCFG_UI_ENABLE_DEMO) && TCFG_UI_ENABLE_DEMO
        /*根据需要调用以下三个接口:ui_remove_backcolor,ui_remove_backimage,ui_remove_border*/
        ui_remove_backcolor(elm);//移除控件背景颜色
        ui_remove_backimage(elm);//移除控件背景图像
        ui_remove_border(elm);//移除控件边界
#endif
        break;
    case ON_CHANGE_SHOW_POST:
#if defined(TCFG_UI_ENABLE_DEMO) && TCFG_UI_ENABLE_DEMO
        ui_custom_draw_clear(dc);

        ui_core_get_element_abs_rect(elm->dc->elm, &r); //跟随控件移动,注释掉这句则不跟随控件移动
        ui_core_get_element_abs_rect(elm, &rect); //跟随控件移动,注释掉这句则不跟随控件移动
        rect.left -= r.left;
        rect.top -= r.top;

        /*
         * 以下绘图函数只能在该控件的范围内显示,超出控件区域不显示
         * */

        /* 矩形填充 */
        ui_fill_rect(dc, 100, 100, 254, 254, 0xffff);

        /* 指定角度画直线  */
        for (angle = 0; angle <= 360; angle += 45) {
            ui_draw_line_by_angle(dc, 227, 227, 100, angle, 0xf800);
        }

        /* 画矩形 */
        ui_draw_rect(dc, 125, 125, 204, 204, 0xf800);

        /* 画线段 */
        ui_draw_line(dc, 120, 120, 333, 120, 0x07e0);
        ui_draw_line(dc, 333, 120, 333, 333, 0x07e0);
        ui_draw_line(dc, 333, 333, 120, 333, 0x07e0);
        ui_draw_line(dc, 120, 333, 120, 120, 0x07e0);

        /* 画圆 */
        ui_draw_ring(dc, 227, 227, 102, 90, 0, 360, 0x003f, 100);

        /* 画圆环 */
        ui_draw_ring(dc, 227, 227, 117, 105, 0 + 135, 270 + 135, 0xc618, 100);
        ui_draw_ring(dc, 227, 227, 117, 105, 0 + 135, 270 + 135, 0xf800, 75);

        /* 画进度条 */
        ui_draw_bar(dc, 120, 380, 200, 25, RGB565(58, 63, 72), 100);
        ui_draw_bar(dc, 120, 380, 200, 25, 0xf800, 50);

        /* 显示缓存叠加图片 */
        ui_draw_custom_rgb565(dc, rect.left + 100, rect.top + 100, 48, 49, icon_48x49_rgb565, sizeof(icon_48x49_rgb565));

        /* 显示缓存叠加图片(带alpha) */
        ui_draw_custom_argb8565(dc, rect.left + 220, rect.top + 100, 48, 49, icon_48x49_argb8565, sizeof(icon_48x49_argb8565));

        /* 显示图片id到指定坐标 */
        id = PAGE17_3ec1_01_MENU_ICON_TRAINING_SEL_S5;
        open_image_by_id(0, NULL, &f, id & 0xffff, id >> 16);
        ui_draw_image(dc, id >> 16, id & 0xffff, (r.width - f.width) / 2, (r.height - f.height) / 2); //屏幕居中显示

        /* 显示多国语言文本 */
        ui_draw_strpic(dc, M26, 200, 280, RGB565(0, 0, 0));

        /* 显示ascii文本 */
        char *ascii = "0123456789";
        ui_draw_ascii(dc, ascii, strlen(ascii), 200, 296, RGB565(255, 0, 0));

        /* 显示字库文本 */
        char *text = "字库文本测试";
        ui_draw_text(dc, FONT_ENCODE_UTF8, 0, text, strlen(text), 200, 322, RGB565(255, 0, 0));
#endif
        break;

    case ON_CHANGE_RELEASE:
        break;
    default:
        break;
    }
    return false;
}

问得比较多的案例:平角、圆角柱形
平角柱状用矩形填充
image

/* ------------------------------------------------------------------------------------*/
/**
 * @brief ui_fill_rect ui填充矩形
 *
 * @param dc UI框架句柄
 * @param left 矩形left坐标
 * @param top 矩形top坐标
 * @param width 矩形宽度
 * @param height 矩形高度
 * @param acolor 填充颜色(带透明度)
 *
 * @return
 */
/* ------------------------------------------------------------------------------------*/
int ui_fill_rect(struct draw_context *dc, int left, int top, int width, int height, u32 acolor);

圆角柱状用进度条模拟
image

•  * @param _dc 显示上下文句柄
•  * @param x 绘制的X坐标(左上角)
•  * @param y 绘制的Y坐标(右上角)
•  * @param width 绘制宽度
•  * @param height 绘制高度
•  * @param color 高亮颜色
•  * @param percent 高亮百分比

void ui_draw_bar(void *_dc, int x, int y, int width, int height, int color, int percent);

可以用画进度条的ui_draw_bar来实现,只要把进度条参数percent固定设置为100%(完全填充),width和height就是矩形的宽高。

4.6.2 表盘制作

①新增表盘工程

【金山文档】表盘添加步骤

②表盘新增控件

想做纯数字时钟表盘?表盘上新增控件显示运动参数?往下看:
表盘工程有点特殊,特殊在哪里?

现在的SDK对所有表盘工程,都是公用同一套响应代码逻辑,也就是说你现在可以看到每个表盘工程的ID都是公用的,比如下图:
imageimage 现在教你如何正确在表盘工程上DIY控件!
我们在自定义表盘的过程中,除了共用响应逻辑的控件可以同名(如),其他DIY的控件可能会有命名冲突,因此我们在表盘工程新增控件时,不能用控件ID,用控件数据源名称,来对不同表盘工程下的控件进行响应注册
举个例子🌰:如我想在一个表盘上单独添加一个控件,注册自己的onchange响应
imageimageimage 其他onkey、ontouch响应同理
image

五、功能模板

5.1 nandflash的烧录

5.1.1 707-NandFlash 代码和UI资源独立烧录方案

707-NandFlash 代码和UI资源独立烧录方案.otl

六、AC707N相关FAQ

JL707N穿戴客述问题FAQ.doc

本文档包含以下子页面

  • 707N手表SDK快速入门
  • AC707N蓝牙智能手表硬件设计指南V1.2
Prev
穿戴生产烧录OTA和key文件添加使用说明
Next
彩屏仓982相关