6 Platform API 参考
本章节介绍 platform API。
6.1 配置与信息
6.1.1 platform_config
配置一些 platform 功能。
6.1.1.2 参数
const platform_cfg_item_t item指定要配置的项目。可以是以下值之一:
PLATFORM_CFG_LOG_HCI:打印主机控制器接口消息。默认:禁用。仅在 ING918 上可用。HCI 日志仅用于对 BLE 行为进行快速检查。请考虑使用跟踪(参见 调试与跟踪)。
PLATFORM_CFG_POWER_SAVING:省电模式。默认:禁用。PLATFORM_CFG_TRACE_MASK:选定的跟踪项的位图。默认:0。typedef enum { PLATFORM_TRACE_ID_EVENT = 0, PLATFORM_TRACE_ID_HCI_CMD = 1, PLATFORM_TRACE_ID_HCI_EVENT = 2, PLATFORM_TRACE_ID_HCI_ACL = 3, PLATFORM_TRACE_ID_LLCP = 4, //.. } platform_trace_item_t;PLATFORM_CFG_RT_RC_EN:启用/禁用实时 RC 时钟。默认:启用。PLATFORM_CFG_RT_OSC_EN:启用/禁用实时晶体振荡器。默认:启用。PLATFORM_CFG_RT_CLK:实时时钟选择。标志为platform_rt_clk_src_t。默认:PLATFORM_RT_RCtypedef enum { PLATFORM_RT_OSC, // 外部实时晶体振荡器 PLATFORM_RT_RC // 内部实时 RC 时钟 } platform_rt_clk_src_t;对于 ING918,修改此配置时,RT_RC 和 RT_OSC 都应启用并运行:
* 对于 RT_OSC,等待直到 RT_OSC 的状态为 OK; * 对于 RT_RC,启用后等待 100μs。并在禁用未使用的时钟之前再等待 100μs。
PLATFORM_CFG_RT_CLK_ACC:配置实时时钟精度(ppm)。PLATFORM_CFG_RT_CALI_PERIOD:实时时钟自动校准周期(秒)。默认:3600 * 2(2小时)。PLATFORM_CFG_DEEP_SLEEP_TIME_REDUCTION:睡眠时间缩减(深度睡眠模式)(微秒)。ING918默认:~550μs。PLATFORM_CFG_SLEEP_TIME_REDUCTION:睡眠时间缩减(其他睡眠模式)(微秒)。ING918默认:~450μs。PLATFORM_CFG_LL_DBG_FLAGS:链路层标志。ll_cfg_flag_t中的位组合。详见 《低功耗蓝牙开发者手册》23。PLATFORM_CFG_LL_LEGACY_ADV_INTERVAL:链路层传统广告间隔,高占空比模式(高16位)和正常占空比模式(低16位)(微秒)。高占空比模式默认:1250;正常占空比模式默认:1500。PLATFORM_CFG_RTOS_ENH_TICK:启用 RTOS 的增强型滴答。默认:禁用。启用后,当外设频繁生成中断请求时,可以保持滴答的准确性。PLATFORM_CFG_LL_DELAY_COMPENSATION:链路层的延迟补偿。当系统以较低频率运行时,链路层需要更多时间(以 μs 为单位)来调度RF任务。例如,如果 ING916 以 24MHz 运行,则需要约 2500μs 的补偿。
PLATFORM_CFG_24M_OSC_TUNE:24M OSC 调谐。不适用于ING918。对于 ING916,调谐值可能在 0x16~0x2d 之间。
PLATFORM_CFG_ALWAYS_CALL_WAKEUP:无论深度睡眠过程是否完成或中止(失败),始终触发PLATFORM_CB_EVT_ON_DEEP_SLEEP_WAKEUP事件。ING918默认:禁用以保持向后兼容性。ING916默认:启用。PLATFORM_CFG_FAST_DEEP_SLEEP_TIME_REDUCTION:快速深度睡眠模式的睡眠时间减少(微秒)。不适用于ING918。此配置必须小于或等于
PLATFORM_CFG_DEEP_SLEEP_TIME_REDUCTION。当等于PLATFORM_CFG_DEEP_SLEEP_TIME_REDUCTION时,不使用快速深度睡眠模式。ING916 默认:~2000μs。
PLATFORM_CFG_AUTO_REDUCE_CLOCK_FREQ:在这些情况下自动降低 CPU 时钟频率:- 默认的 IDLE 过程,
- 进入睡眠模式时。
不适用于 ING918。ING916 默认:启用。
const uint32_t flag用于禁用或启用项目。可以是以下值之一:
PLATFORM_CFG_ENABLEPLATFORM_CFG_DISABLE
6.1.2 platform_get_version
获取 platform 的版本号。
6.1.3 platform_read_info
读取 platform 信息。
6.1.3.2 参数
const platform_info_item_t item信息项。
PLATFORM_INFO_RT_OSC_STATUS: 读取实时晶体振荡器的状态。值为 0:不正常;非 0:正常。
对于 ING916:此时钟在被选为实时时钟源之后才会开始运行。
PLATFORM_INFO_RT_CLK_CALI_VALUE: 读取当前实时时钟校准结果。PLATFOFM_INFO_IRQ_NUMBER: 获取 platform IRQ 的底层 IRQ 编号。例如,获取 UART0 的底层 IRQ 编号:
platform_read_info( PLATFOFM_INFO_IRQ_NUMBER + PLATFORM_CB_IRQ_UART0)
6.2 事件与中断
6.2.1 platform_set_evt_callback_table
为所有 platform 事件注册一个回调函数表。
6.2.1.4 备注
此函数应仅在 app_main 中调用。如果使用了 platform_set_evt_callback,则不应使用此函数。
与 platform_set_evt_callback 相比,使用此函数可以节省 sizeof(platform_evt_cb_table_t) 字节的 RAM 空间。
6.2.1.5 示例
static const platform_evt_cb_table_t evt_cb_table =
{
.callbacks = {
[PLATFORM_CB_EVT_HARD_FAULT] = {
.f = (f_platform_evt_cb)cb_hard_fault,
},
[PLATFORM_CB_EVT_PROFILE_INIT] = {
.f = setup_profile,
},
// ...
}
};
int app_main()
{
// ...
platform_set_evt_callback_table(&evt_cb_table);
// ...
}6.2.2 platform_set_irq_callback_table
为所有 platform 中断请求注册一个回调函数表。
6.2.3 platform_set_evt_callback
注册回调函数以处理 platform 事件。
6.2.3.1 函数原型
void platform_set_evt_callback(platform_evt_callback_type_t type,
f_platform_evt_cb f,
void *user_data);6.2.3.2 参数
platform_evt_callback_type_t type指定要注册回调函数的事件类型。可以是以下值之一:
PLATFORM_CB_EVT_PUTC:输出 ASCII 字符事件当 platform 想要输出 ASCII 字符用于日志记录时,会触发此事件。传递给回调函数的参数
void *data是从char *类型转换而来。如果在创建新项目时在
Common Function中勾选了Print to UART,Wizard可以自动生成将 platform 日志重定向到 UART 的代码。PLATFORM_CB_EVT_PROFILE_INIT:配置文件初始化事件当主机初始化时,会触发此事件以请求应用程序初始化GATT配置文件。
在创建新项目时,
Wizard可以自动为此事件生成代码。PLATFORM_CB_EVT_ON_DEEP_SLEEP_WAKEUP:从深度睡眠唤醒事件当从深度睡眠唤醒时,会触发此事件。在深度睡眠期间,外设接口(如UART、I2C等)都已关闭。因此,唤醒时可能需要重新初始化这些接口。
如果在创建新项目时在
Common Function中勾选了Deep Sleep,Wizard可以自动为此事件生成代码。传递给回调函数的参数
void *data是从platform_wakeup_call_info_t *类型转换而来。此时 RTOS 尚未恢复,某些 RTOS API 不可用;某些 platform API(如
platform_get_us_time)也可能不可用。PLATFORM_CB_EVT_ON_IDLE_TASK_RESUMED:操作系统从节能模式完全恢复。如果唤醒原因是
PLATFORM_WAKEUP_REASON_NORMAL,则在PLATFORM_CB_EVT_ON_DEEP_SLEEP_WAKEUP之后调用此回调。 对于 NoOS 变体,回调由platform_os_idle_resumed_hook()调用。 此事件与PLATFORM_CB_EVT_ON_DEEP_SLEEP_WAKEUP不同:- 所有操作系统功能都已恢复(对于NoOS变体,这取决于
platform_os_idle_resumed_hook()的正确使用) - 所有 platform API都可用
- 回调在空闲任务中调用。
参数
void *data始终为NULL。- 所有操作系统功能都已恢复(对于NoOS变体,这取决于
PLATFORM_CB_EVT_QUERY_DEEP_SLEEP_ALLOWED:查询是否允许深度睡眠事件当platform准备进入深度睡眠模式时,会触发此事件以查询应用程序当前是否允许深度睡眠。回调函数可以通过返回
0拒绝深度睡眠,或通过返回非0值允许深度睡眠。如果在创建新项目时在
Common Function中勾选了Deep Sleep,Wizard可以自动为此事件生成代码。PLATFORM_CB_EVT_HARD_FAULT:硬件故障发生当硬件故障发生时,会触发此事件。传递给回调函数的参数
void *data是从hard_fault_info_t *类型转换而来。 如果未定义此回调,当硬件故障发生时,CPU将进入死循环。PLATFORM_CB_EVT_ASSERTION:软件断言失败当软件断言失败时,会触发此事件。传递给回调函数的参数
void *data是从assertion_info_t *类型转换而来。 如果未定义此回调,当断言发生时,CPU将进入死循环。PLATFORM_CB_EVT_LLE_INIT:链路层引擎初始化。当链路层引擎初始化时,会触发此事件。
PLATFORM_CB_EVT_HEAP_OOM:内存不足。当堆分配失败(堆内存不足)时,会触发此事件。 如果触发此事件且未定义回调,CPU将进入死循环。 参数
void *data是从整数转换而来。这个整数代表内存不足的堆的编号:0: FreeRTOS 的堆;1: 链路层的堆;2: 链路层任务池。
PLATFORM_CB_EVT_TRACE:跟踪输出。当发出跟踪项时,会触发此事件。应用程序可以为此事件定义回调函数以保存或记录跟踪输出。回调的
param是从platform_trace_evt_t *类型转换而来(参见调试与跟踪)。typedef struct { const void *data1; const void *data2; uint16_t len1; uint16_t len2; } platform_evt_trace_t;跟踪项是
data1和data2的组合。注意:len1或len2可能为0,但不能同时为0;如果回调函数发现无法输出大小为
len1+len2的数据,则应丢弃data1和data2以避免跟踪项损坏。
PLATFORM_CB_EVT_EXCEPTION:硬件异常。参数
void *data是从platform_exception_id_t *类型转换而来。PLATFORM_CB_EVT_IDLE_PROC:自定义IDLE过程。参见“Programmer’s Guide - Power Saving”24。
PLATFORM_CB_EVT_HCI_RECV:接管 HCI 并完全隔离内置主机。当定义时:
- HCI事件和ACL数据传递给此回调;
PLATFORM_CB_EVT_PROFILE_INIT被忽略。
参数
void *data是从const platform_hci_recv_t *类型转换而来。另请参见platform_get_link_layer_interf。PLATFORM_CB_EVT_BEFORE_DEEP_SLEEP: Platform 即将进入深度睡眠当 platform 决定进入深度睡眠模式时,会触发此事件。应用程序可以借此机会配置外设。回调函数应保持简单并尽快返回。
参数
void *data是从platform_sleep_category_b_t转换而来,代表所要进入的深度睡眠的类型。
f_platform_evt_cb f注册到事件
type的回调函数。f_platform_evt_cb定义为:typedef uint32_t (*f_platform_evt_cb)(void *data, void *user_data);若非专门说明,Platform 忽略这个回调函数的返回值。
void *user_data此参数原封不动地传递给回调函数的
user_data。
6.2.4 platform_set_irq_callback
注册中断请求的回调函数。
开发者无需在应用中定义IRQ处理程序,而是使用回调函数代替。
6.2.4.1 函数原型
void platform_set_irq_callback(platform_irq_callback_type_t type,
f_platform_irq_cb f,
void *user_data);6.2.4.2 参数
platform_irq_callback_type_t type指定要注册回调函数的 IRQ 类型。不同芯片系列的值有所不同。以 ING918 为例:
PLATFORM_CB_IRQ_RTC, PLATFORM_CB_IRQ_TIMER0, PLATFORM_CB_IRQ_TIMER1, PLATFORM_CB_IRQ_TIMER2, PLATFORM_CB_IRQ_GPIO, PLATFORM_CB_IRQ_SPI0, PLATFORM_CB_IRQ_SPI1, PLATFORM_CB_IRQ_UART0, PLATFORM_CB_IRQ_UART1, PLATFORM_CB_IRQ_I2C0, PLATFORM_CB_IRQ_I2C1f_platform_irq_cb f注册到 IRQ
type的回调函数。f_platform_irq_cb定义为:typedef uint32_t (*f_platform_irq_cb)(void *user_data);void *user_data该参数将原封不动地传递给回调函数的
user_data。
6.2.4.4 备注
当回调函数注册到 IRQ 时,IRQ 会自动启用。另请参见 platform_enable_irq。
6.3 时钟
另请参阅“Programmer’s Guide - Power Saving”25中的“The Real-time Clock”26。
6.3.2 platform_rt_rc_auto_tune
自动调整内部实时RC时钟,并获取调整值。
对于ING918,此函数将内部实时RC时钟调整至50kHz27。 对于其他设备,它将内部实时RC时钟调整至32768Hz。
6.3.4 platform_rt_rc_tune
使用调谐值调整内部实时RC时钟。
6.3.4.2 参数
uint16_t value用于调谐时钟的值(由
platform_rt_rc_auto_tune或platform_rt_rc_auto_tune2返回)
6.4 RF
6.5 内存与实时操作系统(RTOS)
6.5.1 platform_call_on_stack
在独立的专用堆栈上调用函数。当需要偶尔调用一个使用大量堆栈的函数时,可以使用此接口。
6.5.1.1 函数原型
void platform_call_on_stack(
f_platform_function f,
void *user_data,
void *stack_start,
uint32_t stack_size);6.5.2 platform_get_current_task
获取调用此API的当前任务。
6.5.4 platform_get_heap_status
获取当前内置 RTOS 的堆的状态,例如可用大小等。
6.5.7 platform_install_task_stack
为特定 platform 任务安装一个新的RTOS堆栈。
当默认堆栈大小不足以满足内部任务需求时,使用此函数来扩大堆栈。例如,用户开发的 RTOS 定时器回调可能需要更大的堆栈空间。
开发者可以查阅 RTOS 文档以了解如何检查堆栈使用情况。例如,在FreeRTOS中,uxTaskGetStackHighWaterMark 用于查询任务接近溢出分配给它的堆栈空间的程度。
6.6 时间与定时器
读取当前时间(定时器计数器)的API:
platform_get_timer_counterplatform_get_us_time
使用625微秒分辨率的定时器的API:
platform_set_abs_timerplatform_set_timerplatform_delete_timer
使用1微秒分辨率的定时器的API:
platform_create_us_timerplatform_cancel_us_timerplatform_create_us_timer2
这两种类型的定时器都可以在节能模式下使用,即当节能模式启用时,它们会按预期工作。表6.1比较了这两种定时器。
表: (#tab:ch-api-compare-timers) 两种 platform 定时器的比较
| 类型 | 625微秒分辨率 | 1微秒分辨率 |
|---|---|---|
| 回调 | 从类似任务的上下文中调用 | 从中断服务例程(ISR)或调用者中调用 |
| 标识符 | 回调函数指针 | 定时器句柄 |
| 容量 | 受限于堆空间 | 少量几个 |
SDK v8.5 重新设计微秒分辨率定时器。
在旧版本中,
API 实际上是对一些 Controller 函数的封装,其中
platform_create_us_timer其实表示在时间点 T 调度一个持续时间为1微秒的操作。因此,如果 Controller 将要 在时间点 T 附近执行扫描或广告等操作,platform_create_us_timer将 失败。在callback中调用platform_create_us_timer也是禁止的。在v8.5及之后的版本中,
这些 API 不再是 Controller 函数的封装,
platform_create_us_timer在上面这种情况下也会成功。Controller 也使用此接口来管理所有 RF 操作。 可以在callback中调用platform_create_us_timer。
platform_create_us_timer 从专用内存池中分配数据,该内存池的大小是固定且有限的。
在 v8.5 及之后的版本中,最多可以创建 4 个定时器。注意:Controller
使用并且仅使用其中一个这样的定时器。
如果 Controller 创建定时器失败,将引发 PLATFORM_CB_EVT_HEAP_OOM 事件。
6.6.2 platform_create_us_timer
设置一个具有 1 微秒(μs)分辨率的单次触发 platform 定时器。
6.6.2.1 函数原型
platform_us_timer_handle_t platform_create_us_timer(
uint64_t abs_time,
f_platform_us_timer_callback callback,
void *param);6.6.2.2 参数
uint64_t abs_time当
platform_get_us_timer() == abs_time时,回调函数将被调用。f_platform_us_timer_callback callback回调函数。其签名为:
typedef void (* f_platform_us_timer_callback)( platform_us_timer_handle_t timer_handle, uint64_t time_us, void *param);其中,
timer_handle是platform_create_us_timer的返回值,time_us是调用回调时platform_get_us_timer的当前值,param是创建此定时器时的用户参数。void *param用户参数。
6.6.3 platform_create_us_timer2
设置一个具有 1 微秒(μs)分辨率的单次触发 platform 定时器。
6.6.3.1 函数原型
int platform_create_us_timer2(
uint64_t abs_time,
f_platform_us_timer_callback callback,
void *param,
platform_us_timer_handle_t *timer_handle);6.6.3.2 参数
uint64_t abs_time当
platform_get_us_timer() == abs_time时,回调函数将被调用。f_platform_us_timer_callback callback回调函数。
void *param用户参数。
platform_us_timer_handle_t *timer_handle当创建成功时,
timer_handle被设置为定时器的句柄。如果定时器失败,timer_handle被设置为 NULL。
6.6.3.4 备注
一种常见的用法是在回调函数清除句柄。当使用 platform_create_us_timer,这种做法可能存在问题。例如:
platform_us_timer_handle_t handle;
void callback(...)
{
handle = NULL;
}
// 回调函数可能在返回前就调用了,这时,
// handle 未能清除
handle = platform_create_us_timer(T,
callback);使用 platform_create_us_timer2 可以避免这种问题:
// 在调用 `callback` 之前,
// `handle` 已由 `platform_create_us_timer2`
// 填入了正确的句柄
platform_create_us_timer2(T,
callback,
&handle);6.6.5 platform_get_timer_counter
读取platform定时器的计数器,分辨率为625微秒。
6.6.6 platform_set_abs_timer
设置一个单次触发的 platform 定时器,在绝对时间触发,分辨率为 625 微秒。
6.6.6.2 参数
f_platform_timer_callback callback定时器到期时的回调函数,该函数在类似 RTOS 任务的上下文中调用,而不是在中断服务例程(ISR)中调用。
uint32_t abs_time当
platform_get_timer_counter() == abs_time时,callback被调用。 如果abs_time刚刚超过platform_get_timer_counter(),callback会立即被调用, 例如,abs_time为platform_get_timer_counter() - 1。
6.6.6.5 示例
使用此函数模拟一个周期性定时器。
#define PERIOD 100
static uint32_t last_timer = 0;
void platform_timer_callback(void)
{
last_timer += PERIOD;
platform_set_abs_timer(platform_timer_callback, last_timer);
// 执行周期性任务
// ...
}
last_timer = platform_get_timer_counter() + PERIOD;
platform_set_abs_timer(platform_timer_callback, last_timer);6.6.7 platform_set_timer
设置一个单次触发的 platform 定时器,从“现在”开始延迟,分辨率为 625 微秒。
6.6.7.2 参数
f_platform_timer_callback callback定时器到期时的回调函数,该函数在类似 RTOS 任务的上下文中调用,而非中断服务例程(ISR)。
uint32_t delay定时器到期前的延迟时间(单位:625 微秒)。
有效范围:0~0x7fffffff。当
delay为 0 时,定时器被清除。
6.6.7.4 备注
此函数总是成功,除非内存耗尽。
platform_set_timer(f, 100) 等同于:
platform_set_abs_timer(f,
platform_get_timer_counter() + 100);platform_set_timer(f, 0) 等同于:
platform_delete_timer(f);但不等同于:
platform_set_abs_timer(f,
platform_get_timer_counter() + 0);由于 callback 也是定时器的标识符,以下两行代码仅定义了一个在 200 个单位后到期的定时器,而非两个独立的定时器:
platform_set_timer(f, 100);
platform_set_timer(f, 200); // 更新定时器,而非创建新的如果 f 再次用于platform_set_abs_timer,则定时器再次被更新:
platform_set_abs_timer(f, ...);6.7 节能
本节提供了在 RTOS 中实现节能的 API。 参见 “Programmer’s Guide - Power Saving”28。
6.8 实用工具
6.8.3 platform_read_persistent_reg
从持久寄存器中读取值。另请参阅
platform_write_persistent_reg。
6.8.3.3 返回值
由
platform_write_persistent_reg
写入的值。
6.8.5 platform_shutdown
将整个系统置于关机状态,并在指定时间后重新启动。可选地,在关机期间可以保留一部分内存,重启后应用程序可以继续使用这部分内存。
请注意,除非关机过程无法启动,否则此函数不会返回。可能导致失败的原因包括:
- 外部唤醒信号被发出;
- 输入参数不正确;
- 内部组件繁忙。
6.8.5.1 函数原型
void platform_shutdown(const uint32_t duration_cycles,
const void *p_retention_data,
const uint32_t data_size);6.8.6 platform_write_persistent_reg
向持久寄存器写入一个值。该值在省电模式、关机模式或切换到另一个应用程序时仍会保留。
只有少量位被保存,如表 6.2 所示。
表: (#tab:ch-api-reg-bits) 持久寄存器位大小
| 芯片系列 | 寄存器大小 (位) |
|---|---|
| ING918 | 4 |
| ING916 | 5 |
6.9 调试与追踪
6.9.1 platform_printf
存储在 platform 二进制文件中的 printf 函数。
6.10 其他
6.10.2 sysSetPublicDeviceAddr
设置设备的公共地址。
BLE设备的公共地址是一个48位的扩展唯一标识符(EUI-48),按照IEEE 802-2014标准创建29。
https://ingchips.github.io/application-notes/pg_ble_stack_cn/index.html↩︎
https://ingchips.github.io/application-notes/pg_power_saving_en/↩︎
https://ingchips.github.io/application-notes/pg_power_saving_en/↩︎
https://ingchips.github.io/application-notes/pg_power_saving_en/ch-api.html#the-real-time-clock↩︎
从v8.4.6版本开始。对于更早的版本,使用的是32768Hz。↩︎
https://ingchips.github.io/application-notes/pg_power_saving_en/↩︎