• Home
Name Date Size #Lines LOC

..--

AppScope/12-May-2024-3532

entry/12-May-2024-3,2232,732

hvigor/12-May-2024-2222

README.mdD12-May-20248.1 KiB236189

build-profile.json5D12-May-20241.1 KiB4342

hvigorfile.tsD12-May-2024159 21

hvigorwD12-May-20242 KiB6355

hvigorw.batD12-May-20242 KiB7356

oh-package.json5D12-May-2024862 2726

README.md

1# i2c结合OpenGL绘制渐变温度计
2
3## 实现介绍
4根据i2c读取温度,使用OpenGL动态显示室温,中间的温度线具有由蓝变红的渐变效果;达到某个值后呈现红色
5
6## 环境要求
7- 本示例仅支持在标准系统上运行。
8
9- SDK:Ohos_sdk_public 4.0.9.6 (API Version 10 )
10
11## 实现步骤
121. 实现OpenGL ES的温度计图示
132. 实现I2C——NAPI
143. 关于eTS的编写
15
16## 实现OpenGL ES的温度计图示
17OpenGL学习参考链接:https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/01%20OpenGL/
18### 实现步骤
19本实现主要对外实现四个接口,包括
201. OpenGL ES初始化
212. 温度值更新
223. 温度计图示随温度值更新
234. 回收退出OpenGL ES资源
24
25#### OpenGL ES初始化
26```C
27int32_t i2cOpengl::Init(void *window, int32_t width, int32_t height) {
28    LOGI("Init window = %{public}p, w = %{public}d, h = %{public}d.", window, width, height);
29    mEglWindow = reinterpret_cast<EGLNativeWindowType>(window);
30
31    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
32    if (mEGLDisplay == EGL_NO_DISPLAY) {
33        LOGE("unable to get EGL display.");
34        return -1;
35    }
36
37    EGLint eglMajVers, eglMinVers;
38    if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
39        mEGLDisplay = EGL_NO_DISPLAY;
40        LOGE("unable to initialize display");
41        return -1;
42    }
43
44    int version = 3;
45    mEGLConfig = getConfig(version, mEGLDisplay);
46    if (mEGLConfig == nullptr) {
47        LOGE("GLContextInit config ERROR");
48        return -1;
49    }
50
51    EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
52    if (mEglWindow) {
53        mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, winAttribs);
54        if (mEGLSurface == nullptr) {
55            LOGE("eglCreateContext eglSurface is null");
56            return -1;
57        }
58    }
59
60    /* Create EGLContext from */
61    int attrib3_list[] = {
62        EGL_CONTEXT_CLIENT_VERSION, 3,
63        EGL_NONE};
64
65    mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list);
66    if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
67        LOGE("eglMakeCurrent error = %{public}d", eglGetError());
68    }
69    CreateProgram();
70    LOGI("Init success.");
71
72    return 0;
73}
74```
75
76初始化通过EGL接口实现,其中,`CreateProgram()`则为封装创建着色器程序,本实现使用了两类着色器程序,一类绑定温度值刻度显示,另一类绑定温度计纹理图片,初始化大致流程如下:
77
78![Alt text](../figures/I2C_OpenGL/device/image.png)
79
80#### 更新温度值
81接受来自ETS端的温度值,并用此温度值改变OpenGLES图示的刻度值
82```C
83void i2cTemp::SetTemp(double tempe) {
84    temp = (float)tempe;
85}
86```
87#### 温度计图示随温度值更新
88实现如下:
89```C
90void i2cOpengl::Update(void) {
91    glClearColor(1.0f, 1.0, 1.0f, 1.0f);
92    glClear(GL_COLOR_BUFFER_BIT);
93    temperatureLocation = glGetUniformLocation(shaderProgram, "currentTemperature");
94    a_positionLocation = glGetAttribLocation(shaderProgram, "a_position");
95    float currentTemperature = generateNormalizedTemperature(temp);
96    SetAndEnable();
97    LoadTexture();
98
99    glEnable(GL_DEPTH_TEST);
100    glUseProgram(shaderProgram);
101    glUniform1f(temperatureLocation, currentTemperature);
102
103    glBindVertexArray(vao[0]);
104    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
105    if ((err = glGetError()) != GL_NO_ERROR) {
106        LOGE("%{public}u  %{public}d", err, __LINE__);
107    } else {
108        LOGI("success to draw %{public}u", err);
109    }
110
111    glUseProgram(shaderProgram1);
112    LOGI("Using shaderProgram1");
113    glBindVertexArray(vao[1]);
114    glActiveTexture(GL_TEXTURE0);
115    glBindTexture(GL_TEXTURE_2D, texture);
116    glUniform1i(glGetUniformLocation(shaderProgram1, "texture1"), 0);
117
118    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
119    if ((err = glGetError()) != GL_NO_ERROR) {
120        LOGE("%{public}u  %{public}d", err, __LINE__);
121    }
122    LOGE("Finish Drawing");
123    eglSwapBuffers(mEGLDisplay, mEGLSurface);
124}
125```
126其中, `SetAndEnable()`函数链接相应VBO所需数组以及给着色器相应参数赋值并使能,`LoadTexture()`函数则设置了相应纹理属性以及载入纹理.然后通过两个着色器程序的切换画图,再进行交换缓冲.
127
128- 本纹理实现需要单头文件图像加载库stb_image.h,导库地址如下:
129https://github.com/nothings/stb/blob/master/stb_image.h
130
131- 另外,由于OpenGL的调用关系,纹理图片须得放在板载上且为需要为绝对路径,本实现按函数`LoadTexture()`的实现情况而言:
132
133![Alt text](../figures/I2C_OpenGL/device/image-1.png)
134
135本实现的温度计图片放在`entry/src/main/resourses/base/media`,开发者须将图片放置于有效路径并进行读取路径的更改
136完整的目录须包含:
137
138![Alt text](../figures/I2C_OpenGL/device/image-4.png)
139
140### 实现I2C温湿度的读取
141本实现的I2C部分使用`vendor\unionman\unionpi_tiger\sample\napi\napisubsys\i2cnapipart\i2cnapidemo`里提供的I2C NAPI接口
142
143参考:
144https://gitee.com/openharmony/vendor_unionman/tree/master/unionpi_tiger/sample/napi/napisubsys/i2cnapipart/i2cnapidemo
145
146另外:通过HDC工具查询I2C设备挂载在`/dev/i2c-5 `总线上,且设备地址为0x44;
147![Alt text](../figures/I2C_OpenGL/device/image-1-1.png)
148
149如若与程序:
150
151![Alt text](../figures/I2C_OpenGL/device/image-2.png)
152
153不一致,应适当修改程序至相应正确总线的相应设备地址
154
155正确编译成动态库后需将其送至板载`/system/lib/module`目录下
156```shell
157hdc_std shell mount / -o remount,rw
158hdc_std file send ./libi2cnapidemo.z.so /system/lib/module
159```
160另外:由于APP要调用I2C设备需要权限,则以下提供两种方法:
1611.  修改系统权限,使APP能够访问i2c驱动
162    修改`device\board\unionman\unionpi_tiger\config\init\arm\init.A311D.cfg `文件,在cmds中添加
163    ```json
164    "chmod 777 /dev/i2c-5",
165    ```
166    ![Alt text](../figures/I2C_OpenGL/device/image-1-2.png)
167    更改后再烧录以及导库,这样,每次在开发板开机时,则会自动打开权限
1682. 每次开发板开机都手动更改权限
169    ```shell
170    chmod 777 /dev/i2c-5
171    ```
172
173
174至此,则完成了I2C库的导入.
175
176### 关于eTS的编写
177```TS
178 async aboutToAppear() {
179    Logger.info('aboutToAppear');
180    let resourceManager = getContext(this).resourceManager;
181    this.tempString = await resourceManager.getStringValue($r('app.string.mode_label').id);
182    i2cnapidemo.SoftReset();
183    i2cnapidemo.ModeSet(1,1);
184  }
185
186  getValue() {
187    i2cnapidemo.ReadData();
188    this.tempC = i2cnapidemo.ReadTemperatureC();
189    this.tempF = i2cnapidemo.ReadTemperatureF();
190    this.Hum = i2cnapidemo.ReadHumidity();
191  }
192
193  startWork() {
194    this.intervalID = setInterval(() => {
195      this.getValue();
196    }, 500)
197  }
198
199  Update() {
200    this.updateintervalID = setInterval(() => {
201      i2cOpengl.updateTemp(this.tempC);
202    }, 500)
203  }
204```
205本实现主要通过两个定时器,`startWork()`主要用于更新I2C读取的温湿度值,其中用到的接口来源于以上导入的I2C动态库,`Update()`则主要用于更新OpenGL的图示
206
207本OpenGLES图示主要显示在XComponent组件上,组件提供`onLoad()`与`onDestroy()`接口用于回调,本实现在`onLoad()`时调用`Update()`定时器进行更新图示,其中的`UpdateTemp()` NAPI接口主要调用`SetTemp(temp1)`更新数据以及调用`Update()`更新图示画面:
208```C++
209napi_value AppNapi::UpdateTemp(napi_env env, napi_callback_info info)
210{
211    LOGE("Update");
212    size_t argc = 1;
213    napi_value args[1] = {nullptr};
214
215    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
216
217    napi_valuetype valuetype0;
218    napi_typeof(env, args[0], &valuetype0);
219
220    double temp1;
221    napi_get_value_double(env, args[0], &temp1);
222
223    i2cOpengl_->SetTemp(temp1);
224
225    i2cOpengl_->Update();
226    i2cOpengl_->Update();
227
228    napi_value ret;
229    napi_create_double(env, temp1, &ret);
230
231    return ret;
232}
233```
234
235## 实现效果
236![Alt text](../figures/I2C_OpenGL/device/image-3.png)