README.md
1# gpio app demo
2gpio的相关操作app demo
3## 开发环境
41. 搭载OpenHarmony-4.0-Release版本的Unionpi Tiger开发板
52. Microsoft VS Code 1.80.2
63. DevEco Studio 4.0 Beta2
74. 一堆led灯
8## gpio简介
9通用输入输出端口的简称。可以通过软件控制其输出和输入,通俗来说就是常用引脚,可以控制引脚的高低电平,对其进行读取或者写入。
10## 操作方式
11### Linux编程
12首先在/sys/class/gpio/export中写入具体的gpio号即可导出相应的设备文件
13不知道gpio号的可以执行以下命令查看
14> cat /sys/kernel/debug/gpio
15
16导出成功后,/sys/class/gpio/目录下会出现gpio[gpio号]文件夹,进入此文件夹,可操作以下设备文件用于操作gpio
171. direction 写入in, out, high, low。high/low会同时设置方向为输出,并将value设置为相应的1/0。
182. value文件是端口的数值,为1或0。1为高电平,0为低电平
19### hdf方式编程
20详见: [OpenHarmony hdf的gpio驱动文档](https://docs.openharmony.cn/pages/v3.2/zh-cn/device-dev/driver/driver-platform-gpio-des.md/)
21## NAPI导出
22众所周知,arkts支持面向对象编程,那么如何使用napi将c++类包装为一个arkts类?
23### 新建Napi类对应c++类的接口
24C++类
25```C++
26class GpioCtl
27{
28protected:
29 uint32_t gpio;
30
31public:
32 GpioCtl(uint32_t gpio,uint32_t direction);
33 uint16_t read();
34 bool write(uint16_t data);
35};
36```
37Napi类,napi不支持传入c++成员函数指针,因此只能用全静态函数
38```C++
39class GpioNapi
40{
41protected:
42 static napi_ref gpioClassRef;
43
44public:
45 static napi_value define(napi_env env);
46
47 static napi_value Constructor(napi_env env, napi_callback_info cb);
48
49 static void Destructor(napi_env env,void* nativeObject,void* finalize_hint);
50
51 static napi_value read(napi_env env, napi_callback_info cb);
52
53 static napi_value write(napi_env env, napi_callback_info cb);
54};
55napi_ref GpioNapi::gpioClassRef = nullptr;
56```
57### 定义arkts类
58在define函数中,调用napi_define_class定义一个arkts类,并传入构造时的回调函数,并将该类返回
59```C++
60static napi_value define(napi_env env)
61{
62 napi_value gpioClass;
63 napi_property_descriptor desc[]={
64 DECLARE_NAPI_FUNCTION("read",read),
65 DECLARE_NAPI_FUNCTION("write",write)
66 };
67 napi_define_class(env,"Gpio",NAPI_AUTO_LENGTH,Constructor,nullptr,sizeof(desc)/sizeof(desc[0]) ,desc,&gpioClass);
68 napi_create_reference(env,gpioClass,1,&gpioClassRef);
69 return gpioClass;
70}
71```
72### 绑定c++对象
73在构造时的回调函数中,接收参数,并将参数转换为c++变量用于构造c++对象,调用napi_warp将真正的c++对象指针绑定到arkts对象中,同时绑定析构时的回调函数,析构时的回调函数会删除c++对象以释放相关资源
74```C++
75static napi_value Constructor(napi_env env, napi_callback_info cb)
76{
77 napi_value this_var;
78 size_t argc=2;
79 napi_value argv[2];
80 napi_get_cb_info(env,cb,&argc,argv,&this_var,nullptr);
81 NAPI_ASSERT(env,argc==2,"requires 2 parameter");
82 napi_valuetype type;
83 napi_typeof(env,argv[0],&type);
84 NAPI_ASSERT(env,type==napi_number,"frist parameter type requires number");
85 napi_typeof(env,argv[1],&type);
86 NAPI_ASSERT(env,type==napi_number,"2nd parameter type requires number");
87
88 uint32_t gpioNum,dir;
89 napi_get_value_uint32(env,argv[0],&gpioNum);
90 napi_get_value_uint32(env,argv[1],&dir);
91
92 auto *gpio=new GpioCtl(gpioNum,dir);
93 napi_wrap(env,this_var,gpio,Destructor,nullptr,nullptr);
94 return this_var;
95}
96```
97### 编写arkts成员函数对应的NAPI函数
98首先通过napi_get_cb_info获取this对象以及参数,然后检查this对象是否是之前定义的arkts类的一个实例(如果是JavaScript调用,因为没有类型检查,可能存在被当成静态函数调用的情况,arkts则无此问题),然后调用napi_unwarp取出之前绑定的c++对象指针,然后操作此c++对象完成实际操作
99```C++
100static napi_value read(napi_env env, napi_callback_info cb)
101{
102 size_t argc=0;
103 napi_value this_var,gpioClass;
104 napi_get_cb_info(env,cb,&argc,nullptr,&this_var,nullptr);
105
106 napi_get_reference_value(env,gpioClassRef,&gpioClass);
107
108 bool isInstance;
109 napi_instanceof(env,this_var,gpioClass,&isInstance);
110 NAPI_ASSERT(env,isInstance,"this read function isn't static function!");
111
112 GpioCtl* gpio;
113 napi_unwrap(env,this_var,reinterpret_cast<void**>(&gpio));
114 /*使用C++对象完成相关操作...*/
115 return ret;
116}
117```
118### 定义枚举
119定义枚举可以有效约束接口参数的合法性以及使代码更加易读
120
121定义gpio名称枚举
122```C++
123static napi_value defineGpioName(napi_env env)
124{
125 napi_value gpioName,gpio1,gpio2,gpio3,gpio4,gpio5,gpio6,gpio7,gpio8,gpio9,gpio10,gpio11,gpio12,gpio13,gpio14,gpio15,gpio16;
126 napi_create_object(env,&gpioName);
127
128 napi_create_uint32(env,Gpio::GPIO_01,&gpio1);
129 ......//此处省略
130 napi_create_uint32(env,Gpio::GPIO_16,&gpio16);
131
132 napi_property_descriptor desc[]={
133 DECLARE_NAPI_STATIC_PROPERTY("GPIO_01",gpio1),
134 ......//此处省略
135 DECLARE_NAPI_STATIC_PROPERTY("GPIO_16",gpio16)
136 };
137 napi_define_properties(env,gpioName,sizeof(desc)/sizeof(desc[0]) ,desc);
138 return gpioName;
139}
140```
141定义gpio输入输出等方向枚举
142```C++
143static napi_value defineDir(napi_env env)
144{
145 napi_value dir,input,output,error;
146 napi_create_object(env,&dir);
147
148 napi_create_uint32(env,direction::input,&input);
149 napi_create_uint32(env,direction::output,&output);
150 napi_create_uint32(env,direction::error,&error);
151
152 napi_property_descriptor desc[]={
153 DECLARE_NAPI_STATIC_PROPERTY("input",input),
154 DECLARE_NAPI_STATIC_PROPERTY("output",output),
155 DECLARE_NAPI_STATIC_PROPERTY("error",error)
156 };
157 napi_define_properties(env,dir, sizeof(desc)/sizeof(desc[0]) ,desc);
158 return dir;
159}
160```
161定义高电平,低电平等枚举
162```C++
163static napi_value defineVal(napi_env env)
164{
165 napi_value val,low,height;
166 napi_create_object(env,&val);
167
168 napi_create_uint32(env,val::low,&low);
169 napi_create_uint32(env,val::height,&height);
170
171 napi_property_descriptor desc[]={
172 DECLARE_NAPI_STATIC_PROPERTY("low",low),
173 DECLARE_NAPI_STATIC_PROPERTY("height",height)
174 };
175 napi_define_properties(env,val, sizeof(desc)/sizeof(desc[0]) ,desc);
176 return val;
177}
178```
179### 编写arkts模块接口定义文件
180编写完napi接口后,还需要让方舟编译器知道模块里提供了哪些接口,有什么参数,各个参数的类型又是什么。这就需要模块接口定义文件(.d.ts)来说明,这里采用es6的模块声明方式来编写,将napi定义的枚举与类进行显式说明。
181```TypeScript
182export enum GpioName {
183 GPIO_01,
184 GPIO_02,
185 GPIO_03,
186 GPIO_04,
187 GPIO_05,
188 GPIO_06,
189 GPIO_07,
190 GPIO_08,
191 GPIO_09,
192 GPIO_10,
193 GPIO_11,
194 GPIO_12,
195 GPIO_13,
196 GPIO_14,
197 GPIO_15,
198 GPIO_16,
199}
200
201export enum Dir {
202 input,
203 output,
204 error
205}
206
207export enum Val {
208 low,
209 height
210}
211
212export class Gpio {
213 constructor(gpio: GpioName, direction: Dir);
214
215 read(): Val;
216
217 write(val: Val): void;
218}
219```
220### 编写app
221创建一个api10的app项目,创建一个选择框和开关,选择框用于选gpio针脚,开关控制gpio的高电平和低电平
222```TypeScript
223 Row(){
224 Text("选择pin: ")
225 Select([
226 { value: "GPIO_01" },
227 { value: "GPIO_02" },
228 { value: "GPIO_03" },
229 { value: "GPIO_04" },
230 { value: "GPIO_05" },
231 { value: "GPIO_06" },
232 { value: "GPIO_07" },
233 { value: "GPIO_08" },
234 { value: "GPIO_09" },
235 { value: "GPIO_10" },
236 { value: "GPIO_11" },
237 { value: "GPIO_12" },
238 { value: "GPIO_13" },
239 { value: "GPIO_14" },
240 { value: "GPIO_15" },
241 { value: "GPIO_16" },
242 ])
243 .selected(0)
244 .value(this.gpioSelectValue)
245 .onSelect((index: number, value: string) => {
246 this.gpioSelectValue = value;
247 this.gpio = new Gpio(GpioName[value], Dir.output);
248 let val = this.gpio.read();
249 this.gpioIsOpen = (val === Val.height);
250 })
251 }
252 Row() {
253 Text("开关: ")
254 Toggle({ type: ToggleType.Switch, isOn: this.gpioIsOpen })
255 .onChange((isOn: boolean) => {
256 this.gpioIsOpen = isOn;
257 if (isOn) {
258 this.gpio.write(Val.height);
259 }
260 else {
261 this.gpio.write(Val.low);
262 }
263 Prompt.showToast({message:isOn?"打开":"关闭"});
264 })
265 }
266```