信号与槽是qt的核心机制,是完成任意qt对象之间的通信机制,也降低了对象之间的耦合度。如果用过qt就会知道这是个好用的东东。

那么这个机制和单片机又有毛毛的关系。那是源于从一开始接触qt的时候就会不自觉的思考他这个信号与槽和win平台下的事件,和单片机下的中断到底有啥不同。实际上也可以把这种思路搬到单片机上来。

先看下单片机的中断服务函数执行的流程:

信号与槽的执行流程:(简化版)

如果这两幅图对比就会发现执行模型差别还是很大,并且看起来好像信号与槽的机制好像更复杂。实则是把事件和执行进行了解耦让思路更清晰了。如上图信号与槽整体被分为了三个部分:

  • 最上方的关联信号与槽部分
  • 信号发出部分
  • 槽执行部分

这里的槽函数B实际上就类似于事件的回调函数。只是事件的回调函数是在事件发生的时候就会立马得到执行。比如按键按下就会立马执行按键按下的回调函数。信号与槽的方式呢当按键按下时就只管发出了一个按键按下的信号,其他什么都不用管了。而要执行什么槽函数可以在应用层把按键信号和对应的槽函数(类比为回调函数)进行关联。

这样就轻松的把事件的产生和对应服务函数的执行进行了解耦。比如要实现按下按键控制led开关。在按键的驱动代码中不用包含任何led相关的代码。也并不需要实现一个按键事件回调接口暴露给更上层进行LED控制。

而上层的代码只需要把按键按下的信号和led开关的槽函数关联起来就可以了。

思想有了,那就可以用代码把该框架实现出来。实现思路比较简单,来一个信号就存入队列中去,然后再while循环中取出信号,检查是否有关联的槽函数,如果有就执行对应的槽。(一个信号可以关联多个槽)

分别建立三个文件:

  • sigslot.c (信号与槽核心框架代码)
  • sigslot.h (信号与槽框架API)
  • sigslot_user.h (用来定义用户的信号名称)

接口函数如下:

typedef void (*signal_handle)(void *params,uint16_t param_len);

void sigslot_init(void);
bool sigslot_emit(signal_name_t sig_name,void *params,uint16_t params_len);
int32_t sigslot_connect(signal_name_t sig_name,signal_handle handler);
bool sigslot_disconnect(uint32_t id);
void sigslot_exec(void);

void sigslot_init(void);

初始化信号槽框架

bool sigslot_emit(signal_name_t sig_name,void *params,uint16_t params_len);

发送信号

  • sig_name 信号名,在sigslot_user.h中定义
  • params,要传递的参数
  • params_len,传递的参数的长度

int32_t sigslot_connect(signal_name_t sig_name,signal_handle handler);

关联信号与槽

  • sig_name 信号名,在sigslot_user.h中定义
  • handler ,槽函数
  • 返回值,为槽id编号,取消关联时会使用

bool sigslot_disconnect(uint32_t id);

取消关联信号与槽

  • id,槽id,关联时候的返回值

void sigslot_exec(void);

该函数放在main的while循环中,取出信号,并遍历关联的槽函数,再执行槽函数。

具体的代码可以到此处访问获取:

https://github.com/Derrick45/sigslot

By DK45

发表评论

电子邮件地址不会被公开。 必填项已用*标注