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```