README_zh.md
1# 小凌派-RK2206开发板E53模块开发——智能手势
2
3本示例将演示如何在小凌派-RK2206开发板上实现智能手势的应用案例。
4
5
6
7## 硬件资源
8
9硬件资源图如下所示:
10
11
12EEPROM 24C02的设备地址为:0x1010001* ;
13手势传感器 PAJ7620U2 的设备地址为:0x0111001*
14
15## 硬件接口说明
16
17引脚名称开发者可在硬件资源图中查看,也可在人体感应模块背面查看。
18
19| 引脚名称 | 功能描述 |
20| :-------- | :------------------------------ |
21| LED_UP | 向上,LED灯控制线,低电平有效 |
22| LED_DOWN | 向下,LED灯控制线,低电平有效 |
23| LED_LEFT | 向左,LED灯控制线,低电平有效 |
24| LED_RIGHT | 向右,LED灯控制线,低电平有效 |
25| INT_N | 中断信号,低电平有效 |
26| I2C_SCL | I2C时钟信号线 |
27| I2C-SDA | I2C数据信号线 |
28| LED_CW | 顺时针,LED灯控制线,低电平有效 |
29| LED_CCW | 逆时针,LED灯控制线,低电平有效 |
30| LED_WAVE | 摆动,LED灯控制线,低电平有效 |
31| GND | 电源地引脚 |
32| 3V3 | 3.3V电源输入引脚 |
33| GND | 电源地引脚 |
34| 5V | 5V电源输入引脚 |
35
36## 硬件设计
37
38硬件电路如下图所示:
39
40
41模块整体硬件电路如上图所示,电路中包含了E53接口连接器,EEPROM存储器、手势识别传感器电路,灯光指示电路。电路比较简单,本文不再过多说明。
42
43### 硬件连接
44
45小凌派开发板与模块均带有防呆设计,故很容易区分安装方向,直接将模块插入到开发板的E53母座接口上即可,安装图如下所示:
46
47
48## 程序设计
49
50### API分析
51
52#### GPIO接口
53
54[GPIO接口文档](/device/rockchip/hardware/docs/GPIO.md)
55
56#### I2C接口
57
58[I2C接口文档](/device/rockchip/hardware/docs/I2C.md)
59
60#### LiteOS任务管理
61
62[LiteOS任务管理接口文档](/vendor/lockzhiner/lingpi/samples/a1_kernel_task/task.md)
63
64### 主要代码分析
65
66#### 初始化代码分析
67
68**初始化GPIO**
69
70本模块使用以下引脚作为LED灯。具体如下所示:
71
72| 引脚 | 功能 |
73| :-------- | :----------- |
74| GPIO0_PB1 | LED_UP |
75| GPIO0_PB0 | LED_DOWN |
76| GPIO0_PA2 | LED_LEFT |
77| GPIO0_PC4 | LED_RIGHT |
78| GPIO0_PB4 | LED_FORWARD |
79| GPIO0_PB7 | LED_BACKWARD |
80| GPIO0_PB6 | LED_CW |
81| GPIO0_PB5 | LED_CCW |
82| GPIO0_PB2 | LED_WAVE |
83
84初始化引脚为GPIO。首先通过 `LzGpioInit()`初始化GPIO引脚,其次通过 `LzGpioSetDir()`设置GPIO为输出模式,最后通过 `LzGpioSetVal()`设置输出高电平/低电平。
85
86```c
87/***************************************************************
88* 函数名称: gesture_sensor_led_init
89* 说 明: 初始化LED的GPIO引脚
90* 参 数: 无
91* 返 回 值: 无
92***************************************************************/
93static void gesture_sensor_led_init()
94{
95 PinctrlSet(GPIO_LED_UP, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
96 LzGpioInit(GPIO_LED_UP);
97 LzGpioSetDir(GPIO_LED_UP, LZGPIO_DIR_OUT);
98 gesture_sensor_led_up(0);
99
100 PinctrlSet(GPIO_LED_DOWN, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
101 LzGpioInit(GPIO_LED_DOWN);
102 LzGpioSetDir(GPIO_LED_DOWN, LZGPIO_DIR_OUT);
103 gesture_sensor_led_down(0);
104
105 PinctrlSet(GPIO_LED_LEFT, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
106 LzGpioInit(GPIO_LED_LEFT);
107 LzGpioSetDir(GPIO_LED_LEFT, LZGPIO_DIR_OUT);
108 gesture_sensor_led_left(0);
109
110 PinctrlSet(GPIO_LED_RIGHT, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
111 LzGpioInit(GPIO_LED_RIGHT);
112 LzGpioSetDir(GPIO_LED_RIGHT, LZGPIO_DIR_OUT);
113 gesture_sensor_led_right(0);
114
115 PinctrlSet(GPIO_LED_FORWARD, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
116 LzGpioInit(GPIO_LED_FORWARD);
117 LzGpioSetDir(GPIO_LED_FORWARD, LZGPIO_DIR_OUT);
118 gesture_sensor_led_forward(0);
119
120 PinctrlSet(GPIO_LED_BACKWARD, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
121 LzGpioInit(GPIO_LED_BACKWARD);
122 LzGpioSetDir(GPIO_LED_BACKWARD, LZGPIO_DIR_OUT);
123 gesture_sensor_led_backward(0);
124
125 PinctrlSet(GPIO_LED_CW, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
126 LzGpioInit(GPIO_LED_CW);
127 LzGpioSetDir(GPIO_LED_CW, LZGPIO_DIR_OUT);
128 gesture_sensor_led_cw(0);
129
130 PinctrlSet(GPIO_LED_CCW, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
131 LzGpioInit(GPIO_LED_CCW);
132 LzGpioSetDir(GPIO_LED_CCW, LZGPIO_DIR_OUT);
133 gesture_sensor_led_ccw(0);
134
135 PinctrlSet(GPIO_LED_WAVE, MUX_FUNC0, PULL_KEEP, DRIVE_KEEP);
136 LzGpioInit(GPIO_LED_WAVE);
137 LzGpioSetDir(GPIO_LED_WAVE, LZGPIO_DIR_OUT);
138 gesture_sensor_led_wave(0);
139}
140```
141
142**初始化i2c**
143
144初始化GPIO0_PA1和GPIO0_PA0为I2C0。首先通过 `I2cIoInit()`初始化i2c通道,最后通过 `LzI2cInit()`设置i2c配置。具体代码如下所示:
145
146```c
147/***************************************************************
148* 函数名称: paj7620u2_i2c_init
149* 说 明: 初始化与PAJ7620U2通信的i2c
150* 参 数: 无
151* 返 回 值: 无
152***************************************************************/
153static void paj7620u2_i2c_init()
154{
155 if (I2cIoInit(m_i2cBus) != LZ_HARDWARE_SUCCESS)
156 {
157 printf("%s, %d: I2cIoInit failed!\n", __FILE__, __LINE__);
158 return;
159 }
160 if (LzI2cInit(E53_I2C_BUS, m_i2c_freq) != LZ_HARDWARE_SUCCESS)
161 {
162 printf("%s, %d: I2cIoInit failed!\n", __FILE__, __LINE__);
163 return;
164 }
165
166 PinctrlSet(GPIO0_PA1, MUX_FUNC3, PULL_KEEP, DRIVE_KEEP);
167 PinctrlSet(GPIO0_PA0, MUX_FUNC3, PULL_KEEP, DRIVE_KEEP);
168}
169```
170
171**配置PAJ7620U2**
172
173通过i2c通信协议,配置PAJ7620U2。具体代码如下所示:
174
175```c
176/***************************************************************
177* 函数名称: paj7620u2_init_config
178* 说 明: 配置PAJ7620U2为手势感应模式
179* 参 数: 无
180* 返 回 值: 无
181***************************************************************/
182static void paj7620u2_init_config()
183{
184 uint8_t ret = 0;
185 uint32_t size;
186
187 ret = paj7620u2_wake_up();
188 if (ret != 0)
189 {
190 printf("%s, %s, %d: paj7620u2_wake_up failed(%d)\n", __FILE__, __func__, __LINE__, ret);
191 }
192
193 /* 初始化PAJ7620U2 */
194 size = sizeof(m_Paj7620u2_InitRegisterConfig) / (sizeof(uint8_t) * 2);
195 for (uint32_t i = 0; i < size; i++)
196 {
197 paj7620u2_write_data(m_Paj7620u2_InitRegisterConfig[i][0], m_Paj7620u2_InitRegisterConfig[i][1]);
198 }
199
200 /* 设置为手势识别模式 */
201 size = sizeof(m_Paj7620u2_SetGestureModeConfig) / (sizeof(uint8_t) * 2);
202 for (uint32_t i = 0; i < size; i++)
203 {
204 paj7620u2_write_data(m_Paj7620u2_SetGestureModeConfig[i][0], m_Paj7620u2_SetGestureModeConfig[i][1]);
205 }
206
207 paj7620u2_select_bank(BANK0);
208}
209```
210
211**创建轮询任务**
212
213通过 `LOS_TaskCreate()`创建任务,每隔100msec轮询PAJ7620U2的手势寄存器。具体代码如下所示:
214
215```c
216/***************************************************************
217* 函数名称: paj7620u2_poll_task_init
218* 说 明: 初始化轮询PAJ7620U2手势的任务
219* 参 数: 无
220* 返 回 值: 无
221***************************************************************/
222static void paj7620u2_poll_task_init()
223{
224 TSK_INIT_PARAM_S task;
225 uint8_t int_flag1, int_flag2;
226 UINT32 ret;
227
228 /* 初始化先进先出缓冲区 */
229 m_fifo_intflags.offset_read = 0;
230 m_fifo_intflags.offset_write = 0;
231
232 /* 锁任务调度 */
233 LOS_TaskLock();
234
235 /* 创建中断之后的i2c读取中断标记寄存器的任务 */
236 (VOID)memset_s(&task, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
237 task.pfnTaskEntry = (TSK_ENTRY_FUNC)paj7620u2_poll_task;
238 task.pcName = "InterruptSemTask";
239 task.uwStackSize = TASK_STACK_SIZE;
240 task.usTaskPrio = 10;
241 ret = LOS_TaskCreate(&m_pollTaskId, &task);
242 if (ret != LOS_OK)
243 {
244 printf("%s, %d: LOS_TaskCreate failed(%d)\n", __func__, __LINE__, ret);
245 /* 解锁任务调度 */
246 LOS_TaskUnlock();
247 return;
248 }
249
250 /* 解锁任务调度 */
251 LOS_TaskUnlock();
252
253 /* 先清空PAJ7620U2的中断标记寄存器 */
254 paj7620u2_select_bank(BANK0);
255 paj7620u2_read_data(PAJ_REG_GET_INT_FLAG1, &int_flag1);
256 paj7620u2_read_data(PAJ_REG_GET_INT_FLAG2, &int_flag2);
257}
258```
259
260#### 查询手势感应代码分析
261
262首先,通过 `LOS_TaskCreate()`创建任务,每隔100msec轮询PAJ7620U2的手势寄存器。如果寄存器有数据,则存储到FIFO队列中。具体代码如下所示:
263
264```c
265/***************************************************************
266* 函数名称: paj7620u2_poll_task
267* 说 明: 轮询任务,每隔100msec访问PAJ7620U2的手势中断寄存器,
268* 如果有手势数据,则填写到m_fifo_intflags队列中。
269* 参 数: 无
270* 返 回 值: 无
271***************************************************************/
272static VOID paj7620u2_poll_task(VOID *args)
273{
274 uint8_t int_flag1 = 0;
275 uint8_t int_flag2 = 0;
276 uint16_t value = 0;
277
278 while (1)
279 {
280 /* 读取Paj7620U2的手势中断寄存器 */
281 paj7620u2_select_bank(BANK0);
282 paj7620u2_read_data(PAJ_REG_GET_INT_FLAG1, &int_flag1);
283 paj7620u2_read_data(PAJ_REG_GET_INT_FLAG2, &int_flag2);
284
285 value = 0;
286 if (int_flag1 != 0)
287 {
288 value |= (uint16_t)(int_flag1);
289 }
290 if (int_flag2 != 0)
291 {
292 value |= (uint16_t)(int_flag2 << 8);
293 }
294
295 if (value != 0)
296 FifoPut(&m_fifo_intflags, value);
297
298 LOS_Msleep(100);
299 }
300}
301```
302
303上层软件通过调用 `E53_GestureSensor_GetGestureState()`获知FIFO队列中的最新手势信息。具体代码如下:
304
305```c
306/***************************************************************
307 * 函数名称: gesture_sensor_get_gesture_state
308 * 说 明: 获取手势感应模块手势
309 * 参 数:
310 * @flag:获取当前手势
311 * 返 回 值: 1为成功,0为失败
312 ***************************************************************/
313unsigned int gesture_sensor_get_gesture_state(unsigned short *flag)
314{
315 *flag = 0;
316
317 if (FifoGet(&m_fifo_intflags, flag) != 0)
318 {
319 return 1;
320 }
321 else
322 {
323 return 0;
324 }
325}
326```
327
328## 5. 编译调试
329
330### 5.1 修改 BUILD.gn 文件
331
332修改 `vendor/lockzhiner/lingpi/sample` 路径下 BUILD.gn 文件,指定 `e53_gs_example` 参与编译。
333
334```r
335"e53_gesture_sensor",
336```
337
338在主目录下输入编译命令。
339
340```shell
341hb build -f
342```
343
344### 5.2 运行结果
345
346示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,并请使用带有LCD屏幕显示如下:
347
348```c
349========== E53 Gesture Sensor Example ==========
350Get Gesture Statu: 0x1
351 Down
352========== E53 Gesture Sensor Example ==========
353Get Gesture Statu: 0x5
354 Down
355 Left
356```
357