单片机的信号与槽
信号与槽是qt的核心机制,是完成任意qt对象之间的通信机制,也降低了对象之间的耦合度。如果用过qt就会知道这是个好用的东东。
那么这个机制和单片机又有毛毛的关系。那是源于从一开始接触qt的时候就会不自觉的思考他这个信号与槽和win平台下的事件,和单片机下的中断到底有啥不同。实际上也可以把这种思路搬到单片机上来。
先看下单片机的中断服务函数执行的流程:
信号与槽的执行流程:(简化版)
如果这两幅图对比就会发现执行模型差别还是很大,并且看起来好像信号与槽的机制好像更复杂。实则是把事件和执行进行了解耦让思路更清晰了。如上图信号与槽整体被分为了三个部分:
- 最上方的关联信号与槽部分
- 信号发出部分
- 槽执行部分
这里的槽函数B实际上就类似于事件的回调函数。只是事件的回调函数是在事件发生的时候就会立马得到执行。比如按键按下就会立马执行按键按下的回调函数。信号与槽的方式呢当按键按下时就只管发出了一个按键按下的信号,其他什么都不用管了。而要执行什么槽函数可以在应用层把按键信号和对应的槽函数(类比为回调函数)进行关联。
这样就轻松的把事件的产生和对应服务函数的执行进行了解耦。比如要实现按下按键控制led开关。在按键的驱动代码中不用包含任何led相关的代码。也并不需要实现一个按键事件回调接口暴露给更上层进行LED控制。
而上层的代码只需要把按键按下的信号和led开关的槽函数关联起来就可以了。
思想有了,那就可以用代码把该框架实现出来。实现思路比较简单,来一个信号就存入队列中去,然后再while循环中取出信号,检查是否有关联的槽函数,如果有就执行对应的槽。(一个信号可以关联多个槽)
分别建立三个文件:
- sigslot.c (信号与槽核心框架代码)
- sigslot.h (信号与槽框架API)
- sigslot_user.h (用来定义用户的信号名称)
接口函数如下:
1 2 3 4 5 6 7 |
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循环中,取出信号,并遍历关联的槽函数,再执行槽函数。
具体的代码可以到此处访问获取: