资讯详情

SylixOS里的事件集实现原理详解

实现原理

  • 事件集不使用事件控制块,而是使用事件集控制块,因此与信号消息队列有很大的不同。
  • 与其他系统资源类似,该系统还通过单向链表定义了事件集控制块数组,并进行分配和回收。
  • 事件集是一个32位的集合,有多组匹配模式可供选择。
  • 只有在get只有在多个线程中才能阻塞;set时才能唤醒阻塞的线程,set叠加结果与任何地方匹配get唤醒目标。
  • 事件集只需要一个阻塞链表。
  • 由于线程删除、重启、唤醒等操作也会从相应的事件集中删除线程,但本文不涉及这些操作。
  • get如果目标匹配,则直接退出,否则进入阻塞状态,等待set时间遍历阻塞链表,如有匹配,则唤醒。
  • set如果有目标匹配,可以选择这个set或清楚所有事件。

实现源码

事件集控制块操作

事件集控制块结构:

typedef struct { 
                                                                /* 事件集 */     LW_LIST_MONO         EVENTSET_monoResrcList;                        /* 空闲资源表 */     UINT8                EVENTSET_ucType;                               /* 类型 */     PLW_LIST_LINE        EVENTSET_plineWaitList;                        /* 指向第一个等待线程 */     ULONG                EVENTSET_ulEventSets;                          /* 32 bit 事件位 */     ULONG                EVENTSET_ulOption;                             /* 事件集选项 */     UINT16               EVENTSET_usIndex;                              /* 数组中的索引 */     CHAR                 EVENTSET_cEventSetName[LW_CFG_OBJECT_NAME_SIZE];                                                                         /* 事件标志组名 */ } LW_CLASS_EVENTSET; typedef LW_CLASS_EVENTSET *PLW_CLASS_EVENTSET; 

事件集节点结构:

typedef struct { 
                                                                /* 事件集节点 */     LW_LIST_LINE         EVENTSETNODE_lineManage;                       /* 事件标志组管理表 */          PVOID                EVENTSETNODE_ptcbMe;                           /* 指向等待任务TCB */     PVOID                EVENTSETNODE_pesEventSet;                      /* 指向标志组 */     ULONG                EVENTSETNODE_ulEventSets;                      /* 标志组开始等待 */     UINT8                EVENTSETNODE_ucWaitType;                       /* 等待类型 */ } LW_CLASS_EVENTSETNODE; typedef LW_CLASS_EVENTSETNODE   *PLW_CLASS_EVENTSETNODE; typedef PLW_CLASS_EVENTSETNODE   PLW_EVENTSETNODE; 

全局变量定义,事件控制块资源定义

__KERNEL_EXT LW_CLASS_EVENT          _K_eventBuffer[LW_CFG_MAX_EVENTS]; /* 事件控制块缓冲区 */ __KERNEL_EXT LW_CLASS_OBJECT_RESRC   _K_resrcEvent;                     /* 管理事件对象的资源 */ 

事件集控制块资源初始化:

/********************************************************************************************************* ** 函数名称: _EventInit ** 功能描述: 初始化事件缓冲池 ** 输 入 : ** 输 出 : ********************************************************************************************************/
VOID  _EventInit (VOID)
{ 
        
    REGISTER ULONG                 ulI;
    REGISTER PLW_CLASS_EVENT       peventTemp1;
    REGISTER PLW_LIST_MONO         pmonoTemp1;

    REGISTER PLW_CLASS_EVENT    peventTemp2;
    REGISTER PLW_LIST_MONO      pmonoTemp2;
    
    _K_resrcEvent.RESRC_pmonoFreeHeader = &_K_eventBuffer[0].EVENT_monoResrcList;
                                                                            /* 设置资源表头 */
    peventTemp1 = &_K_eventBuffer[0];                                       /* 指向缓冲池首地址 */
    peventTemp2 = &_K_eventBuffer[1];                                       /* 指向缓冲池首地址 */
    
    for (ulI = 0; ulI < ((LW_CFG_MAX_EVENTS) - 1); ulI++) { 
        
        pmonoTemp1 = &peventTemp1->EVENT_monoResrcList;                     /* 获得资源表 */
        pmonoTemp2 = &peventTemp2->EVENT_monoResrcList;                     /* 获得资源表 */
        
        peventTemp1->EVENT_ucType  = LW_TYPE_EVENT_UNUSED;                  /* 事件类型 */
        peventTemp1->EVENT_usIndex = (UINT16)ulI;                           /* 事件缓冲区下标 */
   
        _LIST_MONO_LINK(pmonoTemp1, pmonoTemp2);                            /* 建立资源连接 */
        
        peventTemp1++;
        peventTemp2++;
    }
                                                                            /* 初始化最后一个节点 */
    pmonoTemp1 = &peventTemp1->EVENT_monoResrcList;                         /* 获得资源表 */
    
    peventTemp1->EVENT_ucType  = LW_TYPE_EVENT_UNUSED;                      /* 事件类型 */
    peventTemp1->EVENT_usIndex = (UINT16)ulI;                               /* 事件缓冲区下标 */

    _INIT_LIST_MONO_HEAD(pmonoTemp1);                                       /* 初始化最后节点 */
    
    _K_resrcEvent.RESRC_pmonoFreeTail = pmonoTemp1;                                                                     /* LW_CFG_MAX_EVENTS == 1 */
    _K_resrcEvent.RESRC_uiUsed    = 0;
    _K_resrcEvent.RESRC_uiMaxUsed = 0;
}

事件集控制块分配函数:

/********************************************************************************************************* ** 函数名称: _Allocate_EventSet_Object ** 功能描述: 从空闲EventSet控件池中取出一个空闲EventSet ** 输 入 : ** 输 出 : 获得的Object地址,失败返回 NULL *********************************************************************************************************/
PLW_CLASS_EVENTSET  _Allocate_EventSet_Object (VOID)
{ 
        
    REGISTER PLW_LIST_MONO       pmonoFree;
    REGISTER PLW_CLASS_EVENTSET  pesFree;
    
    if (_LIST_MONO_IS_EMPTY(_K_resrcEventSet.RESRC_pmonoFreeHeader)) { 
          /* 检查缓冲区是否为空 */
        return  (LW_NULL);
    }
    
    pmonoFree  = _list_mono_allocate_seq(&_K_resrcEventSet.RESRC_pmonoFreeHeader, 
                                         &_K_resrcEventSet.RESRC_pmonoFreeTail);
                                                                        /* 获得资源 */
    pesFree    = _LIST_ENTRY(pmonoFree, LW_CLASS_EVENTSET, 
                             EVENTSET_monoResrcList);                   /* 获得资源表容器地址 */
                             
    _K_resrcEventSet.RESRC_uiUsed++;
    if (_K_resrcEventSet.RESRC_uiUsed > _K_resrcEventSet.RESRC_uiMaxUsed) { 
        
        _K_resrcEventSet.RESRC_uiMaxUsed = _K_resrcEventSet.RESRC_uiUsed;
    }
    
    return  (pesFree);
}

事件集控制块回收函数:

/********************************************************************************************************* ** 函数名称: _Free_EventSet_Object ** 功能描述: 将EventSet控制块交还缓冲池 ** 输 入 : ** 输 出 : *********************************************************************************************************/
VOID  _Free_EventSet_Object (PLW_CLASS_EVENTSET  pesFree)
{ 
        
    REGISTER PLW_LIST_MONO    pmonoFree;
    
    pmonoFree = &pesFree->EVENTSET_monoResrcList;
    
    _list_mono_free_seq(&_K_resrcEventSet.RESRC_pmonoFreeHeader, 
                        &_K_resrcEventSet.RESRC_pmonoFreeTail, 
                        pmonoFree);
    
    _K_resrcEventSet.RESRC_uiUsed--;
}

事件集接口操作

创建事件集对象:

/********************************************************************************************************* ** 函数名称: API_EventSetCreate ** 功能描述: 建立事件集 ** 输 入 : ** pcName 事件集名缓冲区 ** ulInitEvent 初始化事件 ** ulOption 事件集选项 ** pulId 事件集ID指针 ** 输 出 : 事件句柄 *********************************************************************************************************/
LW_OBJECT_HANDLE  API_EventSetCreate (CPCHAR             pcName, 
                                      ULONG              ulInitEvent, 
                                      ULONG              ulOption,
                                      LW_OBJECT_ID      *pulId)
{ 
        
    REGISTER PLW_CLASS_EVENTSET    pes;
    REGISTER ULONG                 ulIdTemp;

    __KERNEL_MODE_PROC(
        pes = _Allocate_EventSet_Object();                              /* 获得一个事件控制块 */
    );
    
    if (pcName) { 
                                                               /* 拷贝名字 */
        lib_strcpy(pes->EVENTSET_cEventSetName, pcName);
    } else { 
        
        pes->EVENTSET_cEventSetName[0] = PX_EOS;                        /* 清空名字 */
    }
    
    pes->EVENTSET_ucType        = LW_TYPE_EVENT_EVENTSET;
    pes->EVENTSET_plineWaitList = LW_NULL;
    pes->EVENTSET_ulEventSets   = ulInitEvent;
    pes->EVENTSET_ulOption      = ulOption;                             /* 记录选项 */
    
    ulIdTemp = _MakeObjectId(_OBJECT_EVENT_SET, 
                             LW_CFG_PROCESSOR_NUMBER, 
                             pes->EVENTSET_usIndex);                    /* 构建对象 id */
    
    if (pulId) { 
        
        *pulId = ulIdTemp;
    }
    
    return  (ulIdTemp);
}

释放事件集对象:

/********************************************************************************************************* ** 函数名称: API_EventSetDelete ** 功能描述: 删除事件集 ** 输 入 : ** pulId 事件句柄指针 ** 输 出 : ERROR CODE *********************************************************************************************************/
ULONG  API_EventSetDelete (LW_OBJECT_HANDLE  *pulId)
{ 
        
             INTREG                    iregInterLevel;
    REGISTER UINT16                    usIndex;
    REGISTER PLW_CLASS_EVENTSET        pes;
    REGISTER PLW_CLASS_EVENTSETNODE    pesn;
             PLW_LIST_LINE             plineList;
    REGISTER LW_OBJECT_HANDLE          ulId;
    
    ulId = *pulId;
    
    usIndex = _ObjectGetIndex(ulId);
    
    pes = &_K_esBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /* 进入内核 */
    _ObjectCloseId(pulId);                                              /* 清除句柄 */
    
    for (plineList  = pes->EVENTSET_plineWaitList;                      /* 表头指针 */
         plineList != LW_NULL;
         plineList  = _list_line_get_next(plineList)) { 
                         /* 释放所有有效线程 */
                  
        pesn = _LIST_ENTRY(plineList, LW_CLASS_EVENTSETNODE, EVENTSETNODE_lineManage);
        _EventSetDeleteReady(pesn);
    }
    
    pes->EVENTSET_ucType        = LW_TYPE_EVENT_UNUSED;
    pes->EVENTSET_plineWaitList = LW_NULL;
    
    _Free_EventSet_Object(pes);                                         /* 交还缓冲区 */
    
    __KERNEL_EXIT_IRQ(iregInterLevel);                                  /* 退出内核 */

    return  (ERROR_NONE);
}

将线程插入等待事件集队列:

/********************************************************************************************************* ** 函数名称: _EventSetBlock ** 功能描述: 将线程插入等待事件集队列 (进入内核且关中断状态下被调用) ** 输 入 : pes 事件组控制块 ** pesn 事件组等待节点 ** ulEvents 事件组 ** ucWaitType 等待类型 ** ulWaitTime 等待时间 ** 输 出 : NONE *********************************************************************************************************/
VOID  _EventSetBlock (PLW_CLASS_EVENTSET        pes,
                      PLW_CLASS_EVENTSETNODE    pesn,
                      ULONG                     ulEvents,
                      UINT8                     ucWaitType,
                      ULONG                     ulWaitTime)
{ 
        
    REGISTER PLW_CLASS_PCB               ppcb;
             PLW_CLASS_TCB               ptcbCur;
             
    LW_TCB_GET_CUR(ptcbCur);                                            /* 当前任务控制块 */
    
    ptcbCur->TCB_usStatus       |= LW_THREAD_STATUS_EVENTSET;
    ptcbCur->TCB_ucWaitTimeout   = LW_WAIT_TIME_CLEAR;
    ptcbCur->TCB_ucIsEventDelete = LW_EVENT_EXIST;
    
    ptcbCur->TCB_ulDelay = ulWaitTime;
    if (ulWaitTime) { 
        
        __ADD_TO_WAKEUP_LINE(ptcbCur);                                  /* 加入等待扫描链 */
    }
    
    pesn->EVENTSETNODE_pesEventSet = (PVOID)pes;
    pesn->EVENTSETNODE_ulEventSets = ulEvents;
    pesn->EVENTSETNODE_ucWaitType  = ucWaitType;
    pesn->EVENTSETNODE_ptcbMe      = (PVOID)ptcbCur;
    
    _List_Line_Add_Ahead(&pesn->EVENTSETNODE_lineManage, &pes->EVENTSET_plineWaitList);
    
    ppcb = _GetPcb(ptcbCur);
    
    __DEL_FROM_READY_RING(ptcbCur, ppcb);                               /* 从就绪环中删除 */
}

将线程从等待事件集队列中解除:

/********************************************************************************************************* ** 函数名称: _EventSetUnQueue ** 功能描述: 将线程从等待事件集队列中解除 ** 输 入 : ** 输 出 : *********************************************************************************************************/
VOID  _EventSetUnQueue (PLW_CLASS_EVENTSETNODE   pesn)
{ 
        
    REGISTER PLW_CLASS_EVENTSET    pes;
    
    pes = (PLW_CLASS_EVENTSET)pesn->EVENTSETNODE_pesEventSet;
    
    _List_Line_Del(&pesn->EVENTSETNODE_lineManage, &pes->EVENTSET_plineWaitList);
}

由于事件集删除,激活等待事件集队列的线程:

/********************************************************************************************************* ** 函数名称: _EventSetDeleteReady ** 功能描述: 由于事件集删除,激活等待事件集队列的线程 (进入内核并关中断后被调用) ** 输 入 : pesn 事件组控制块 ** 输 出 : 是否调度 *********************************************************************************************************/
BOOL  _EventSetDeleteReady (PLW_CLASS_EVENTSETNODE    pesn)
{ 
        
    REGISTER PLW_CLASS_TCB    ptcb;
    REGISTER PLW_CLASS_PCB    ppcb;
    REGISTER BOOL             bIsSched = LW_FALSE;
    
    ptcb = (PLW_CLASS_TCB)pesn->EVENTSETNODE_ptcbMe;
    ptcb->TCB_ucIsEventDelete = LW_EVENT_DELETE;                        /* 事件被删除了 */
    
    if (ptcb->TCB_usStatus & LW_THREAD_STATUS_DELAY) { 
                          /* 存在于 wake up 表中 */
        __DEL_FROM_WAKEUP_LINE(ptcb);                                   /* 从等待链中删除 */
    }
    
    ptcb->TCB_ulDelay      = 0ul;
    ptcb->TCB_ulEventSets  = 0ul;
    ptcb->TCB_usStatus    &= (~LW_THREAD_STATUS_EVENTSET);
    
    if (__LW_THREAD_IS_READY(ptcb)) { 
                                           /* 是否就绪 */
        ptcb->TCB_ucSchedActivate = LW_SCHED_ACT_INTERRUPT;
        ppcb = _GetPcb(ptcb);
        __ADD_TO_READY_RING(ptcb, ppcb);                                /* 加入就绪表 */
        bIsSched = LW_TRUE;
    }
    
    _EventSetUnQueue(pesn);
    
    return  (bIsSched);
}

激活等待事件集队列的线程:

/********************************************************************************************************* ** 函数名称: _EventSetThreadReady ** 功能描述: 激活等待事件集队列的线程 (进入内核并关中断后被调用) ** 输 入 : pesn 事件组控制块 ** ulEventsReady 新的事件标志 ** 输 出 : 是否调度 *********************************************************************************************************/
BOOL  _EventSetThreadReady (PLW_CLASS_EVENTSETNODE    pesn,
                            ULONG                     ulEventsReady)
{ 
        
    REGISTER PLW_CLASS_TCB    ptcb;
    REGISTER PLW_CLASS_PCB    ppcb;
    REGISTER BOOL             bIsSched = LW_FALSE;
    
    ptcb = (PLW_CLASS_TCB)pesn->EVENTSETNODE_ptcbMe;
    if (ptcb->TCB_usStatus & LW_THREAD_STATUS_DELAY) { 
                          /* 存在于 wake up 表中 */
        __DEL_FROM_WAKEUP_LINE(ptcb);                                   /* 从等待链中删除 */
    }
    
    ptcb->TCB_ulDelay      = 0ul;
    ptcb->TCB_ulEventSets  = ulEventsReady;
    ptcb->TCB_usStatus    &= (~LW_THREAD_STATUS_EVENTSET);
    
    if (__LW_THREAD_IS_READY(ptcb)) { 
                                           /* 是否就绪 */
        ptcb->TCB_ucSchedActivate = LW_SCHED_ACT_INTERRUPT;
        ppcb = _GetPcb(ptcb);
        __ADD_TO_READY_RING(ptcb, ppcb);                                /* 加入就绪表 */
        bIsSched = LW_TRUE;
    }
    
    _EventSetUnQueue(pesn);
    
    return  (bIsSched);
}

等待事件集相关事件:

/********************************************************************************************************* MACRO *********************************************************************************************************/
#define __EVENTSET_NOT_READY() do { \ __KERNEL_EXIT_IRQ(iregInterLevel); \ _ErrorHandle(ERROR_THREAD_WAIT_TIMEOUT); \ return (ERROR_THREAD_WAIT_TIMEOUT); \ } while (0)
         
#define __EVENTSET_SAVE_RET() do { \ if (ulOption & LW_OPTION_EVENTSET_RETURN_ALL) { \ if (pulEvent) { \ *pulEvent = pes->EVENTSET_ulEventSets; \ } \ } else { \ if (pulEvent) { \ *pulEvent = ulEventRdy; \ } \ } \ } while (0)
/********************************************************************************************************* ** 函数名称: API_EventSetGetEx ** 功能描述: 等待事件集相关事件 ** 输 入 : ** ulId 事件集句柄 ** ulEvent 等待事件 ** ulOption 等待方法选项 ** ulTimeout 等待时间 ** pulEvent 接收到的事件 ** 输 出 : 事件句柄 *********************************************************************************************************/
ULONG  API_EventSetGetEx (LW_OBJECT_HANDLE  ulId, 
                          ULONG             ulEvent,
                          ULONG             ulOption,
                          ULONG             ulTimeout,
                          ULONG            *pulEvent)
{ 
        
    LW_CLASS_EVENTSETNODE          esnNode;
    
             INTREG                iregInterLevel;
             
             PLW_CLASS_TCB         ptcbCur;
    REGISTER UINT16                usIndex;
    REGISTER PLW_CLASS_EVENTSET    pes;
    REGISTER UINT8                 ucWaitType;
    REGISTER ULONG                 ulEventRdy;
    REGISTER ULONG                 ulWaitTime;
             ULONG                 ulTimeSave;                          /* 系统事件记录 */
             INT                   iSchedRet;
             
             ULONG                 ulEventSetOption;                    /* 事件创建选项 */
    
    usIndex = _ObjectGetIndex(ulId);
    
    
    LW_TCB_GET_CUR_SAFE(ptcbCur);                                       /* 当前任务控制块 */
    
    ucWaitType = (UINT8)(ulOption & 0x0F);                              /* 获得等待类型 */
    

    if (ulTimeout == LW_OPTION_WAIT_INFINITE) { 
        
        ulWaitTime = 0ul;
    } else { 
        
        ulWaitTime = ulTimeout;
    }
    pes = &_K_esBuffer[usIndex];
    
    iregInterLevel = __KERNEL_ENTER_IRQ();                              /* 进入内核 */

    switch (ucWaitType) { 
        
    
    case LW_OPTION_EVENTSET_WAIT_SET_ALL:                               /* 全部置位 */
        ulEventRdy = (ulEvent & pes->EVENTSET_ulEventSets);
        if (ulEvent == ulEventRdy) { 
                                            /* 事件都已经存在 */
            __EVENTSET_SAVE_RET();
            if (ulOption & LW_OPTION_EVENTSET_RESET) { 
        
                pes->EVENTSET_ulEventSets &= (~ulEventRdy);
            } else if (ulOption & LW_OPTION_EVENTSET_RESET_ALL) { 
        
                pes->EVENTSET_ulEventSets = 0ul;
            }
            ptcbCur->TCB_ulEventSets = ulEventRdy;
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /* 退出内核 */
            return  (ERROR_NONE);
            
        } else { 
        
            if (ulTimeout == LW_OPTION_NOT_WAIT) { 
                              /* 不等待 */
                __EVENTSET_NOT_READY();
            }                                                           /* 阻塞线程 */
            __KERNEL_TIME_GET_NO_SPINLOCK(ulTimeSave, ULONG);           /* 记录系统时间 */
            _EventSetBlock(pes, &esnNode, ulEvent, ucWaitType, ulWaitTime);
        }
        break;
    
    case LW_OPTION_EVENTSET_WAIT_SET_ANY:
        ulEventRdy = (ulEvent & pes->EVENTSET_ulEventSets);
        if (ulEventRdy) { 
        
            __EVENTSET_SAVE_RET();
            if (ulOption & LW_OPTION_EVENTSET_RESET) { 
        
                pes->EVENTSET_ulEventSets &= (~ulEventRdy);
            } else if (ulOption & LW_OPTION_EVENTSET_RESET_ALL) { 
        
                pes->EVENTSET_ulEventSets = 0ul;
            }
            ptcbCur->TCB_ulEventSets = ulEventRdy;
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /* 退出内核 */
            return  (ERROR_NONE);
        
        } else { 
        
            if (ulTimeout == LW_OPTION_NOT_WAIT) { 
                              /* 不等待 */
                __EVENTSET_NOT_READY();
            }                                                           /* 阻塞线程 */
            __KERNEL_TIME_GET_NO_SPINLOCK(ulTimeSave, ULONG);           /* 记录系统时间 */
            _EventSetBlock(pes, &esnNode, ulEvent, ucWaitType, ulWaitTime);
        }
        break;
        
    case LW_OPTION_EVENTSET_WAIT_CLR_ALL:
        ulEventRdy = (ulEvent & ~pes->EVENTSET_ulEventSets);
        if (ulEvent == ulEventRdy) { 
        
            __EVENTSET_SAVE_RET();
            if (ulOption & LW_OPTION_EVENTSET_RESET) { 
        
                pes->EVENTSET_ulEventSets |= ulEventRdy;
            } else if (ulOption & LW_OPTION_EVENTSET_RESET_ALL) { 
        
                pes->EVENTSET_ulEventSets  = __ARCH_ULONG_MAX;
            }
            ptcbCur->TCB_ulEventSets = ulEventRdy;
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /* 退出内核 */
            return  (ERROR_NONE);
        
        } else { 
        
            if (ulTimeout == LW_OPTION_NOT_WAIT) { 
                              /* 不等待 */
                __EVENTSET_NOT_READY();
            }                                                           /* 阻塞线程 */
            __KERNEL_TIME_GET_NO_SPINLOCK(ulTimeSave, ULONG);           /* 记录系统时间 */
            _EventSetBlock(pes, &esnNode, ulEvent, ucWaitType, ulWaitTime);
        }
        break;
    
    case LW_OPTION_EVENTSET_WAIT_CLR_ANY:
        ulEventRdy = (ulEvent & ~pes->EVENTSET_ulEventSets);
        if (ulEventRdy) { 
        
            __EVENTSET_SAVE_RET();
            if (ulOption & LW_OPTION_EVENTSET_RESET) { 
        
                pes->EVENTSET_ulEventSets |= ulEventRdy;
            } else if (ulOption & LW_OPTION_EVENTSET_RESET_ALL) { 
        
                pes->EVENTSET_ulEventSets  = __ARCH_ULONG_MAX;
            }
            ptcbCur->TCB_ulEventSets = ulEventRdy;
            __KERNEL_EXIT_IRQ(iregInterLevel);                          /* 退出内核 */
            return  (ERROR_NONE);
        
        } else { 
        
            if (ulTimeout == LW_OPTION_NOT_WAIT) { 
                              /* 不等待 */
                __EVENTSET_NOT_READY();
            }                                                           /* 阻塞线程 */
            __KERNEL_TIME_GET_NO_SPINLOCK(ulTimeSave, ULONG);     

标签: 24ppcb板公整套连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台