文档内容拷贝自:
codebase_host/yunhal/opendoc/zh/yunhal_doc/led.md
联系人:@陈杰
YunOS Light Hardware Abstract Layer,主要涉及下面几个方面:
依据系统“Light”形态不同,其控制能力主要包含:
下表展示了已知“LED灯”的形态:
| ID | 说明 |
|---|---|
| LED_ID_BACKLIGHT | 屏幕背光 |
| LED_ID_KEYBOARD | 键盘背光 |
| LED_ID_BUTTON | 导航区的触摸式按扭 |
| LED_ID_BATTERY | 充电指示灯 |
| LED_ID_NOTIFICATIONS | 新通知到达的指示灯 |
| LED_ID_ATTENTION | 提醒指示灯 |
| LED_ID_BLUETOOTH | 蓝牙传输指示灯 |
| LED_ID_WIFI | 无线传输指示灯 |
实现 Light HAL 的主要工作包括:实现 VendorModule 接口,声明模块入口;“继承” 和 实现 LEDDevice 接口。
| 域 | 类型 | 说明 |
|---|---|---|
| version | uint32_t | 模块版本 |
| id | const char* | 模块标识 |
| name | const char* | 模块名 |
| author | const char* | 作者 |
| CreateDevice() | int32_t (*CreateDevice) (const VendorModule* module, const char* deviceID, VendorDevice** device) | 工厂方法:“生产” VendorDevice |
一个 CreateDevice() 的实现示例如下:
int32_t createDevice(const VendorModule* module, const char* deviceID, VendorDevice** outDevice)
{
if (!strcmp(deviceID, LED_ID_BACKLIGHT)) {
outDevice[0] = (VendorDevice*) &sBacklightDevice;
return 0;
}
if (!strcmp(deviceID, LED_ID_BATTERY)) {
outDevice[0] = (VendorDevice*) &sBatteryDevice;
return 0;
}
if (...) {
...
return 0;
}
outDevice[0] = NULL;
return -ENODEV;
}
声明模块入口,是为了HAL of LED的实现模块,能被 libhal 所装载,示例代码如下:
static VendorModule sModule = {
.common.version = 1,
.common.id = "LED",
.common.name = "My LED",
.common.author = "My corp.",
.common.CreateDevice = createDevice
};
VENDOR_MODULE_ENTRY(sModule);
| 域 | 类型 | 说明 |
|---|---|---|
| version | uint32_t | 设备版本 |
| id | const char* | 设备标识 |
| module | const VendorModule* | 指向模块的指针 |
| Destroy() | int32_t (*Destroy) (VendorDevice* device) | 析构函数 |
| SetLED() | int (*SetLED)(LEDDevice* LEDDev, LEDState* state) | 设置“LED灯”显示状态 |
示例代码如下:
struct MyLEDDevice {
LEDDevice common; /* inherit, C style */
/* Other context data goes here */
};
static int setMyLED(struct LEDDevice* device, LEDState* state) {
struct MyLEDDevice *realDevice = (MyLEDDevice*) device;
/* apply the state specified by LEDState parameter */
return 0;
}
static int32_t destroyMyLEDDevice(VendorDevice* device) {
struct MyLEDDevice *realDevice = (MyLEDDevice*) device;
...
return 0; /* returns some error code if has error */
}
/* static instance of the Backlight LED Device */
static struct MyLEDDevice* sBacklightDevice = {
/* initialize field of VendorDevice */
.common.common.version = 1,
.common.common.id = LED_ID_BACKLIGHT,
.common.common.module = &sModule,
.common.common.Destroy = destroyMyLEDDevice,
/* initialize field of LEDDevice */
.common.SetLED = setMyLED,
/* initialize other extended fileds */
};
HAL 实现中,确保对 YunOS 主镜像具有最小的依赖,即除了 glibc、libstdc++ 以及 liblog 以外,不依赖其他 libraries。
一个示例的 yunos.mk 如下:
LOCAL_PATH=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my_led
LOCAL_MODULE_PATH := usr/lib/yunhal
LOCAL_SRC_FILES := my_led.c
LOCAL_C_INCLUDES := \
$(base-includes) \
$(yunhal-includes)
...
LOCAL_SHARED_LIBRARIES := libhal liblog
LOCAL_COMPILE_SHELL_CMD := \
mkdir -p $(XMAKE_ROOTFS)/usr/lib/yunhal/ && \
ln -sf my_led.so $(XMAKE_ROOTFS)/usr/lib/yunhal/libyunhal_LED.so
# May be changed to include $(BUILD_HAL) ?
include $(BUILD_SHARED_LIBRARY)
通过 led_yunhal_test 命令,检测所实现的 LED 模块是否兼容 YunOS,输出如下:
Running main() from gtest_main.cc [==========] Running 8 tests from 1 test case. [----------] Global test environment set-up. [----------] 8 tests from LEDTest [ RUN ] LEDTest.test_backlight SetUp export LED_HAL_VALIDATE_SET=1: The underlying HAL may write and then read to confirm a success write TestBacklight(): set brightness to 0(color=0xff000000) TestBacklight(): set brightness to 10(color=0xff0a0a0a) TestBacklight(): set brightness to 20(color=0xff141414) TestBacklight(): set brightness to 40(color=0xff282828) TestBacklight(): set brightness to 60(color=0xff3c3c3c) TestBacklight(): set brightness to 100(color=0xff646464) TestBacklight(): set brightness to 150(color=0xff969696) TestBacklight(): set brightness to 201(color=0xffc9c9c9) TestBacklight(): set brightness to 224(color=0xffe0e0e0) TestBacklight(): set brightness to 255(color=0xffffffff) TearDown [ OK ] LEDTest.test_backlight (10094 ms) [ RUN ] LEDTest.test_keyboard SetUp export LED_HAL_VALIDATE_SET=1: The underlying HAL may write and then read to confirm a success write TestFlash(): set 'keyboard' to color=0xff000000, hold for 3secs TestFlash(): set 'keyboard' to color=0xff000000, flash: 2secs ON, 1secs OFF TestFlash(): set 'keyboard' to color=0xffffffff, hold for 3secs TestFlash(): set 'keyboard' to color=0xffffffff, flash: 2secs ON, 1secs OFF TestFlash(): set 'keyboard' to color=0xffff0000, hold for 3secs TestFlash(): set 'keyboard' to color=0xffff0000, flash: 2secs ON, 1secs OFF TestFlash(): set 'keyboard' to color=0xff00ff00, hold for 3secs TestFlash(): set 'keyboard' to color=0xff00ff00, flash: 2secs ON, 1secs OFF TestFlash(): set 'keyboard' to color=0xff0000ff, hold for 3secs TestFlash(): set 'keyboard' to color=0xff0000ff, flash: 2secs ON, 1secs OFF TestFlash(): set 'keyboard' to color=0xffc4a000, hold for 3secs TestFlash(): set 'keyboard' to color=0xffc4a000, flash: 2secs ON, 1secs OFF TearDown
需要说明的是,测试程序特意设置了一个环境变量LED_HAL_VALIDATE_SET. LED 模块的实现,可以依据此环境变量,进入测试模式 —— 即 write 底层硬件之后 read 之,比较 write 是否成功,并将比较结果返回在 SetLED() 方法中。
在实际硬件实现中,编程逻辑上“LED灯”,往往对应同一个物理上的“LED灯”,例如大多数手机顶端,有一个“indicator LED”,__同时对应了__ LED_ID_BATTERY 和 LED_ID_NOTIFICATIONS,在实现中,往往采用基于优先级的复用 —— 通常 LED_ID_BATTERY 具备较高的优先级。
另一方面,通常手机正面有LED_ID_BATTERY/LED_ID_NOTIFICATIONS、LED_ID_BACKLIGHT和LED_ID_BUTTON,其亮度需保持观感一致。
比如,当开启环境背光功能时,屏幕亮度会随着环境光强来设定相应的屏幕背光(LED_ID_BACKLIGHT) 。但LED_ID_BUTTON,即手机下方导航触摸按扭,通常没有环境背光,故而可能存在过暗或过亮。从正面亮度一致性的角度,可以考虑让LED_ID_BUTTON亮度与LED_ID_BACKLIGHT一致,即: