8.3. 事件回调机制

8.3.1. 概述

PikaScript 内核中提供了事件回调机制,可以支持在 C 的事件/中断中触发 Python 定义的回调函数。

注意:需要内核版本不低于: v1.8.7

8.3.2. 头文件

  1. #include "PikaObj.h"

8.3.3. 数据类型

  1. typedef PikaObj PikaEventListener;

事件回调机制主要依靠 PikaEventListener 事件监听器,事件监听器中记录了每个被注册的事件 ID,当有信号发送至事件监听器时,事件监听器将根据 事件 ID 将调用相应的 Python 回调函数,并传递信号量。

8.3.4. 事件模型

事件模型的核心是 PikaEventListener 事件监听器。

_images/image-20220619102931608.png

PikaEventListener 的模型如上图所示,向事件监听器中注册事件后,将会在 PikaEventListener 内部记录下一个事件项 Event Item,包括:

  • Event ID 事件的唯一 ID

  • Event Handler Object 事件对象,记录了事件项的全部信息

    • Event CallBack 事件回调函数 ( Python 函数 )

Event Signal 事件信号到来时,事件监听器将会匹配 Event ID 找到相应的事件项,然后再将信号代码Event Code传递给 Event CallBak 然后触发回调函数。

_images/image-20220619104053576.png

8.3.5. 事件回调机制流程

  1. 初始化事件监听器

  2. 在 Python 中注册回调函数

  3. 在 C 中向事件监听器发送信号 (通常在中断或 C 的回调中)

  4. Python 中注册的回调函数被执行

8.3.6. 通过 PikaStdDevice 支持事件回调

继承 PikaStdDevice 是支持事件回调的最简单方式,PikaStdDevice.BaseDev 设备基本类已经支持了事件注册方法 addEventCallBack

  1. class BaseDev:
  2. def addEventCallBack(self, eventCallback: any): ...
  3. # need override
  4. def platformGetEventId(self):...
  • PikaStdDevice 中的设备类(如 GPIO)都继承了 BaseDev,因此都获得了 addEventCallBack 的方法,能够注册回调。

/package/PikaStdDevice/PikaStdDevice.pyi

  1. class GPIO(BaseDev):
  2. ...

平台驱动从 PikaStdDevice.GPIO 继承后,也获得了 addEventCallBack 方法。

/package/TemplateDevice/TemplateDevice.pyi

  1. # TemplateDevice.pyi
  2. class GPIO(PikaStdDevice.GPIO):
  3. # overrid
  4. ···
  5. def platformGetEventId(self): ...
  6. ···

只需要重写 platformGetEventId 平台方法,就能够支持注册回调。

例如:

/package/TemplateDevice/TemplateDevice_GPIO.c

  1. const uint32_t GPIO_PA8_EVENT_ID = 0x08;
  2. void TemplateDevice_GPIO_platformGetEventId(PikaObj* self) {
  3. char* pin = obj_getStr(self, "pin");
  4. if (strEqu(pin, "PA8")) {
  5. obj_setInt(self, "eventId", GPIO_PA8_EVENT_ID);
  6. }
  7. }

8.3.7. 在 Python 注册回调函数

  • 定义一个回调函数 callBack1,接收一个输入参数 signalsignal能够接收传入的信号码。

/examples/TemplateDevice/gpio_cb.py

  1. import TemplateDevice
  2. io1 = TemplateDevice.GPIO()
  3. io1.setPin('PA8')
  4. io1.setMode('in')
  5. io1.enable()
  6. EVENT_SIGAL_IO_RISING_EDGE = 0x01
  7. EVENT_SIGAL_IO_FALLING_EDGE = 0x02
  8. def callBack1(signal):
  9. if signal == EVENT_SIGAL_IO_RISING_EDGE:
  10. print('get rising edge!')
  11. elif signal == EVENT_SIGAL_IO_FALLING_EDGE:
  12. print('get falling edge!')
  13. io1.addEventCallBack(callBack1)

8.3.8. 信号触发

在需要触发事件回调时向 PikaEventListener 发送信号。

例如:/port/linux/test/event-test.cpp

  • 通过 extern PikaEventListener* g_pika_device_event_listener 得到 PikaStdDevice 提供的事件监听器。

  • 通过 pks_eventLisener_sendSignal 发送 eventIDsignal code

  1. extern PikaEventListener* g_pika_device_event_listener;
  2. #define EVENT_SIGAL_IO_RISING_EDGE 0x01
  3. #define EVENT_SIGAL_IO_FALLING_EDGE 0x02
  4. #define GPIO_PA8_EVENT_ID 0x08
  5. TEST(event, gpio) {
  6. /* init */
  7. PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain);
  8. /* run */
  9. pikaVM_runFile(pikaMain, "../../examples/TemplateDevice/gpio_cb.py");
  10. /* simulate run in the call back */
  11. pks_eventLisener_sendSignal(g_pika_device_event_listener, GPIO_PA8_EVENT_ID,
  12. EVENT_SIGAL_IO_RISING_EDGE);
  13. pks_eventLisener_sendSignal(g_pika_device_event_listener, GPIO_PA8_EVENT_ID,
  14. EVENT_SIGAL_IO_FALLING_EDGE);
  15. ...
  16. }
  • 运行结果:
  1. get rising edge!
  2. get falling edge!

8.3.9. 进阶:自定义事件注册函数

  • 除了通过 PikaStdDevice 支持事件回调外,还可以自定义事件注册函数,这部分属于进阶内容。

  • 自定义事件注册需要比较了解 PikaScript 的 C 模块机制和对象机制。

  • 定义一个 C 模块的 Python 接口,接收传入的事件回调函数。

例如:

/package/PikaStdDevice/PikaStdDevice.pyi

  1. class BaseDev:
  2. def addEventCallBack(self, eventCallback: any): ...

事件回调函数的类型注解是 any

  • 在 C 模块的实现中注册事件

例如:/package/PikaStdDevice/PikaStdDevice_BaseDev.c

  1. PikaEventListener* g_pika_device_event_listener;
  2. void PikaStdDevice_BaseDev_addEventCallBack(PikaObj* self, Arg* eventCallBack) {
  3. obj_setArg(self, "eventCallBack", eventCallBack);
  4. /* init event_listener for the first time */
  5. if (NULL == g_pika_device_event_listener) {
  6. pks_eventLisener_init(&g_pika_device_event_listener);
  7. }
  8. if (PIKA_RES_OK != obj_runNativeMethod(self, "platformGetEventId", NULL)) {
  9. obj_setErrorCode(self, 1);
  10. __platform_printf("Error: Method %s no found.\r\n",
  11. "platformGetEventId");
  12. }
  13. uint32_t eventId = obj_getInt(self, "eventId");
  14. pks_eventLicener_registEvent(g_pika_device_event_listener, eventId, self);
  15. }
  • 创建一个全局的 PikaEventListenerg_pika_device_event_listener

  • self 作为 Event Handler Object,将 evnetCallBack 传入 self

  • 获取 evnetID

    • 这个例子中通过调用 platformGetEventId() 平台函数来获得 eventID,需要 BaseDev 继承,然后重写 platformGetEventId(),在重写后的 platformGetEventId() 中设置 self.eventId

    • 例如:/package/TemplateDevice/TemplateDevice_GPIO.c

  • 调用 pks_eventLicener_registEvent,将 eventIdself 注册进事件监听器。