本帖最后由 Simon21ic 于 2015-8-23 16:12 编辑
之前说过,在一些特定的使用情况下,目标进程无法立即处理事件的时候,作为事件的缓冲。
那么在事件驱动下,如果使用信号量呢?
这里举例说明,如果要实现一个USB Hub的USB设备端驱动,端口上的变化,需要通过中断传输,发送notification给USB主机。发送notification的时候,会先写数据到相应的USB端点,然后等到数据发送完成,再看是否有其他notification需要发送。那么在等待USB发送完成的时候,就无法处理notification事件,所以就需要用到信号来缓冲。
第一种比较简单的方式,PT线程:vsf_err_t vsfusbd_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
struct vsfusbd_HUB_param_t *param =
(struct vsfusbd_HUB_param_t *)sm->user_data;
struct vsfusbd_device_t *device = param->device;
uint8_t notification;
vsfsm_pt_begin(pt);
vsfsm_sem_init(param->sem, 0, VSFUSBD_HUB_EVT_CHANGE);
while (1)
{
if (vsfsm_sem_pend(param->sem, sm))
{
vsfsm_pt_wfe(pt, VSFUSBD_HUB_EVT_CHANGE);
}
notification = 1;
device->drv->ep.write_IN_buffer(param->ep_in, notification, 1);
device->drv->ep.set_IN_count(param->ep_in, 1);
vsfsm_pt_wfe(pt, VSFUSBD_HUB_EVT_INOK);
}
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
这里,device->drv->ep是控制USB端点的接口,写入数据后,当发送完成后,会发送VSFUSBD_HUB_EVT_INOK事件。所以,写端口后,就可以直接简单等待这个事件就行。
vsfsm_sem_init用于初始化信号量,并且设置事件为VSFUSBD_HUB_EVT_CHANGE,主循环里,就试图获取信号量(vsfsm_sem_pend),如果得到的话返回0,就发送notification,否则返回非0,等待VSFUSBD_HUB_EVT_CHANGE事件。
其他代码发送了信号后,信号量发现有任务在等待,就发送相应的事件。
PT的代码简单明了,类似阻塞的代码方式,一看就明白。
用事件驱动的方式的话,就会相对麻烦一些,不过也很有意思:
static struct vsfsm_state_t *
vsfusbd_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
struct vsfusbd_HUB_param_t *param =
(struct vsfusbd_HUB_param_t *)sm->user_data;
struct vsfusbd_device_t *device = param->device;
uint8_t notification;
switch (evt)
{
case VSFSM_EVT_INIT:
vsfsm_sem_init(param->sem, 0, VSFUSBD_HUB_EVT_CHANGE);
case VSFUSBD_HUB_EVT_INOK:
if (vsfsm_sem_pend(param->sem, sm))
{
break;
}
case VSFUSBD_HUB_EVT_CHANGE:
notification = 1;
device->drv->ep.write_IN_buffer(param->ep_in, notification, 1);
device->drv->ep.set_IN_count(param->ep_in, 1);
break;
}
return NULL;
}
INIT为初始化事件,初始化信号,然后马上等待信号,这里一定等待不到,所以之后有信号后,系统会发送VSFUSBD_HUB_EVT_CHANGE事件。
收到VSFUSBD_HUB_EVT_CHANGE事件后(信号量会把本线程从等待列表中去掉,除非再次调用sem_pend,否则不会收到CHANGE事件),写USB端口,之后USB模块会发送VSFUSBD_HUB_EVT_INOK事件
在VSFUSBD_HUB_EVT_INOK事件里,继续等待信号量,是不是很简单?
|