主机环境:Windows 7 SP1
开发环境:MDK5.14
目标板:STM32F103C8T6
开发库:STM32F1Cube库和STM32_USB_Device_Library
现在来分析哈USB器件库代码,先来看usbd_core文件,其头文件只有一些函数声明,没啥可说的,只有一点,之前分析usbd_conf.c文件时里面USB中断回调函数中调用的底层接口都是在usbd_core.h文件中声明的,同样由用户实现的底层接口也是在该文件中声明的,在usbd_core.c文件中实现,该文件是很重要的一个文件,因为所有上层操作最终都会调用该文件中的API来实现。在器件库文档中提到了核心库的作用,如下:
第一个分析的函数是USB栈的初始化以及重新初始化,如下:
/**
* @brief USBD_Init
* Initializes the device stack and load the class driver
* @param pdev: device instance
* @param pdesc: Descriptor structure address
* @param id: Low level core index
* @retval None
*/
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id)
{
/* Check whether the USB Host handle is valid */
if(pdev == NULL)
{
USBD_ErrLog("Invalid Device handle");
return USBD_FAIL;
}
/* Unlink previous class*/
if(pdev->pClass != NULL)
{
pdev->pClass = NULL;
}
/* Assign USBD Descriptors */
if(pdesc != NULL)
{
pdev->pDesc = pdesc;
}
/* Set Device initial State */
pdev->dev_state = USBD_STATE_DEFAULT;
pdev->id = id;
/* Initialize low level driver */
USBD_LL_Init(pdev);
return USBD_OK;
}
/**
* @brief USBD_DeInit
* Re-Initialize th device library
* @param pdev: device instance
* @retval status: status
*/
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
{
/* Set Default State */
pdev->dev_state = USBD_STATE_DEFAULT;
/* Free Class Resources */
pdev->pClass->DeInit(pdev, pdev->dev_config);
/* Stop the low level driver */
USBD_LL_Stop(pdev);
/* Initialize low level driver */
USBD_LL_DeInit(pdev);
return USBD_OK;
}
USB初始化函数很简单,将USB句柄的设备类指针置NULL,同时将USB的描述符加载上去,将USB设备状态置为默认状态,该函数里面的id目前不清楚是作何用,最后调用USB_LL_Init()函数来初始化底层驱动。USB器件库中USB设备有四种状态,定义在usbd_def.h文件中,如下:
/* Device Status */
#define USBD_STATE_DEFAULT 1
#define USBD_STATE_ADDRESSED 2
#define USBD_STATE_CONFIGURED 3
#define USBD_STATE_SUSPENDED 4
默认状态、地址状态、配置状态、挂起状态。在USB2.0协议文档的第9章节中规定了USB设备的6种状态:连接状态、上电状态、默认状态、地址状态、配置状态、挂起状态,六者之间的关系图如下所示:
在USB库中是省略了连接和上电两个状态,剩下四种状态的说明可以在USB2.0协议的第九章节找到,由此可以看出USB2.0协议中第九章节有多重要了。在USB分配地址之前其使用默认地址,处在默认状态下的USB设备不能响应正常的请求,当USB设备分配了唯一的地址后即进入地址状态,响应正常请求,USB设备配置完成后进入配置状态,USB设备在指定时间长度内没有检测到总线通信时会进入挂起状态,但会保持任何内部状态,包括地址和配置。在USB重新初始化函数中,需要释放类资源,且停止USB底层驱动,重新初始化底层驱动。接着是注册类函数:
/**
* @brief USBD_RegisterClass
* Link class driver to Device Core.
* @param pDevice : Device Handle
* @param pclass: Class handle
* @retval USBD Status
*/
USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass)
{
USBD_StatusTypeDef status = USBD_OK;
if(pclass != 0)
{
/* link the class to the USB Device handle */
pdev->pClass = pclass;
status = USBD_OK;
}
else
{
USBD_ErrLog("Invalid Class handle");
status = USBD_FAIL;
}
return status;
}
注册设备类函数也很简单,把设备类指针传递给USB设备句柄即可,通过指针USB句柄包含了我们所用的所有资源,接着来看USB的一些基本操作,如下:
/**
* @brief USBD_Start
* Start the USB Device Core.
* @param pdev: Device Handle
* @retval USBD Status
*/
USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev)
{
/* Start the low level driver */
USBD_LL_Start(pdev);
return USBD_OK;
}
/**
* @brief USBD_Stop
* Stop the USB Device Core.
* @param pdev: Device Handle
* @retval USBD Status
*/
USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev)
{
/* Free Class Resources */
pdev->pClass->DeInit(pdev, pdev->dev_config);
/* Stop the low level driver */
USBD_LL_Stop(pdev);
return USBD_OK;
}
/**
* @brief USBD_RunTestMode
* Launch test mode process
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_RunTestMode (USBD_HandleTypeDef *pdev)
{
return USBD_OK;
}
/**
* @brief USBD_SetClassConfig
* Configure device and start the interface
* @param pdev: device instance
* @param cfgidx: configuration index
* @retval status
*/
USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
USBD_StatusTypeDef ret = USBD_FAIL;
if(pdev->pClass != NULL)
{
/* Set configuration and Start the Class*/
if(pdev->pClass->Init(pdev, cfgidx) == 0)
{
ret = USBD_OK;
}
}
return ret;
}
/**
* @brief USBD_ClrClassConfig
* Clear current configuration
* @param pdev: device instance
* @param cfgidx: configuration index
* @retval status: USBD_StatusTypeDef
*/
USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
/* Clear configuration and De-initialize the Class process*/
pdev->pClass->DeInit(pdev, cfgidx);
return USBD_OK;
}
/**
* @brief USBD_LL_Reset
* Handle Reset event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed)
{
pdev->dev_speed = speed;
return USBD_OK;
}
其中USBD_Start和USBD_Stop跟USBD的初始化类似都是调用usbd_conf中的底层基本操作,USBD_RunTestMode()函数为空,表明不支持测试模式,测试模式在USB2.0协议文档有提及,既然这里不支持就么有去细研究该功能。另外两个函数USBD_SetClassConfig()、USBD_ClrClassConfig()函数则是跟USB设备类相关,这里我们还没有分析到USB设备类中,所以也略过,知道其功能即可。最后有一个USBD_LL_SetSpeed()函数,USB通信有三种通信速度:低速、全速、高速,STM32F103C8T6支持全速模式,USB速度的定义如下:
/* Following USB Device Speed */
typedef enum
{
USBD_SPEED_HIGH = 0,
USBD_SPEED_FULL = 1,
USBD_SPEED_LOW = 2,
}USBD_SpeedTypeDef;
usbd_core.c中剩下的一些函数体则是在usbd_conf.cUSB中断回调函数中调用的USB通信处理的真正实现者,如下:
/**
* @brief USBD_SetupStage
* Handle the setup stage
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup)
{
USBD_ParseSetupRequest(&pdev->request, psetup);
pdev->ep0_state = USBD_EP0_SETUP;
pdev->ep0_data_len = pdev->request.wLength;
switch (pdev->request.bmRequest & 0x1F)
{
case USB_REQ_RECIPIENT_DEVICE:
USBD_StdDevReq (pdev, &pdev->request);
break;
case USB_REQ_RECIPIENT_INTERFACE:
USBD_StdItfReq(pdev, &pdev->request);
break;
case USB_REQ_RECIPIENT_ENDPOINT:
USBD_StdEPReq(pdev, &pdev->request);
break;
default:
USBD_LL_StallEP(pdev , pdev->request.bmRequest & 0x80);
break;
}
return USBD_OK;
}
/**
* @brief USBD_DataOutStage
* Handle data OUT stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata)
{
USBD_EndpointTypeDef *pep;
if(epnum == 0)
{
pep = &pdev->ep_out[0];
if ( pdev->ep0_state == USBD_EP0_DATA_OUT)
{
if(pep->rem_length > pep->maxpacket)
{
pep->rem_length -= pep->maxpacket;
USBD_CtlContinueRx (pdev,
pdata,
MIN(pep->rem_length ,pep->maxpacket));
}
else
{
if((pdev->pClass->EP0_RxReady != NULL)&&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->EP0_RxReady(pdev);
}
USBD_CtlSendStatus(pdev);
}
}
}
else if((pdev->pClass->DataOut != NULL)&&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->DataOut(pdev, epnum);
}
return USBD_OK;
}
/**
* @brief USBD_DataInStage
* Handle data in stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev ,uint8_t epnum, uint8_t *pdata)
{
USBD_EndpointTypeDef *pep;
if(epnum == 0)
{
pep = &pdev->ep_in[0];
if ( pdev->ep0_state == USBD_EP0_DATA_IN)
{
if(pep->rem_length > pep->maxpacket)
{
pep->rem_length -= pep->maxpacket;
USBD_CtlContinueSendData (pdev,
pdata,
pep->rem_length);
/* Prepare endpoint for premature end of transfer */
USBD_LL_PrepareReceive (pdev,
0,
NULL,
0);
}
else
{ /* last packet is MPS multiple, so send ZLP packet */
if((pep->total_length % pep->maxpacket == 0) &&
(pep->total_length >= pep->maxpacket) &&
(pep->total_length < pdev->ep0_data_len ))
{
USBD_CtlContinueSendData(pdev , NULL, 0);
pdev->ep0_data_len = 0;
/* Prepare endpoint for premature end of transfer */
USBD_LL_PrepareReceive (pdev,
0,
NULL,
0);
}
else
{
if((pdev->pClass->EP0_TxSent != NULL)&&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->EP0_TxSent(pdev);
}
USBD_CtlReceiveStatus(pdev);
}
}
}
if (pdev->dev_test_mode == 1)
{
USBD_RunTestMode(pdev);
pdev->dev_test_mode = 0;
}
}
else if((pdev->pClass->DataIn != NULL)&&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->DataIn(pdev, epnum);
}
return USBD_OK;
}
/**
* @brief USBD_LL_Reset
* Handle Reset event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev)
{
/* Open EP0 OUT */
USBD_LL_OpenEP(pdev,
0x00,
USBD_EP_TYPE_CTRL,
USB_MAX_EP0_SIZE);
pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE;
/* Open EP0 IN */
USBD_LL_OpenEP(pdev,
0x80,
USBD_EP_TYPE_CTRL,
USB_MAX_EP0_SIZE);
pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;
/* Upon Reset call user call back */
pdev->dev_state = USBD_STATE_DEFAULT;
if (pdev->pClassData)
pdev->pClass->DeInit(pdev, pdev->dev_config);
return USBD_OK;
}
/**
* @brief USBD_Suspend
* Handle Suspend event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev)
{
pdev->dev_old_state = pdev->dev_state;
pdev->dev_state = USBD_STATE_SUSPENDED;
return USBD_OK;
}
/**
* @brief USBD_Resume
* Handle Resume event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev)
{
pdev->dev_state = pdev->dev_old_state;
return USBD_OK;
}
/**
* @brief USBD_SOF
* Handle SOF event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev)
{
if(pdev->dev_state == USBD_STATE_CONFIGURED)
{
if(pdev->pClass->SOF != NULL)
{
pdev->pClass->SOF(pdev);
}
}
return USBD_OK;
}
/**
* @brief USBD_IsoINIncomplete
* Handle iso in incomplete event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
return USBD_OK;
}
/**
* @brief USBD_IsoOUTIncomplete
* Handle iso out incomplete event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
return USBD_OK;
}
/**
* @brief USBD_DevConnected
* Handle device connection event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev)
{
return USBD_OK;
}
/**
* @brief USBD_DevDisconnected
* Handle device disconnection event
* @param pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev)
{
/* Free Class Resources */
pdev->dev_state = USBD_STATE_DEFAULT;
pdev->pClass->DeInit(pdev, pdev->dev_config);
return USBD_OK;
}
虽然函数有些多,但看具体函数的代码量就可以知道哪些函数是重要的,第一个函数是USBD_LL_SetupStage(),USB请求分为三个阶段:Setup阶段、可选的数据阶段、状态阶段。该函数用于处理Setup阶段,解析USB主机发来的请求,调用USBD_ParseSetupRequest()函数来获取setup请求,并赋给pdev->request变量,该函数实现如下:
/**
* @brief USBD_ParseSetupRequest
* Copy buffer into setup structure
* @param pdev: device instance
* @param req: usb request
* @retval None
*/
void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata)
{
req->bmRequest = *(uint8_t *) (pdata);
req->bRequest = *(uint8_t *) (pdata + 1);
req->wValue = SWAPBYTE (pdata + 2);
req->wIndex = SWAPBYTE (pdata + 4);
req->wLength = SWAPBYTE (pdata + 6);
}
#define SWAPBYTE(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \
(((uint16_t)(*(((uint8_t *)(addr)) + 1))) << 8))
函数实现很简单即获取Setup的8个成员变量值,至于Setup包中8个数据来源在usbd_conf.c文件中传递的是hpcd->Setup,其数据来源是在Cube库中USB的实现的,因此在这里并没有关心。在获取完Setup包数据后将端点0状态置为USBD_EP0_SETUP,端点0比较重要是因为其是USB默认的控制端点用于接收USBSetup请求数据,在USB器件库中规定了端点0的几种状态,如下:
/* EP0 State */
#define USBD_EP0_IDLE 0
#define USBD_EP0_SETUP 1
#define USBD_EP0_DATA_IN 2
#define USBD_EP0_DATA_OUT 3
#define USBD_EP0_STATUS_IN 4
#define USBD_EP0_STATUS_OUT 5
#define USBD_EP0_STALL 6
可以看出端点0的状态跟USB请求息息相关。接着使用ep0_data_len来存储该Setup请求的数据长度,并根据bmRequest数据值来检测该请求的接收者,在USB2.0协议中规定了请求的接收者有三个:设备、接口、端点。根据接收者的不同调用不同的函数实现体,这些实现体在另一个文件usbd_ctlreq.c中实现,所以这里不细说,等分析usbd_ctlreq.c文件时再细说,如果接收者不是以上三种则调用USBD_LL_StallEP()来将端点设置一个停止条件。USBD_LL_DataOutStage()和USBD_LL_DataInStage()是最重要的两个函数且代码行数较多,放到最后分析,先分析另外几个比较简单的函数,USBD_LL_Reset()函数是重新初始化,这里是重新设置了端点0的属性,并把设备状态置为默认状态,并调用相应类的DeInit()函数来重新初始化设备类。USB的挂起和唤醒函数更简单,只是设置设备状态即可。USBD_LL_SOF()函数是发出起始帧信号,SOF是一个数据包,而EOF是一种电平状态。后面有一个设备断开连接的实现,跟复位有些类似,将设备状态置为默认状态,并重新初始化设备类。现在来分析两个重要的函数,首先是USBD_LL_DataOutStage(),需要注意的是Setup阶段是只跟端点0相关,而数据阶段是可以跟每一个端点相关的,因为任何端点都可以传输数据,所以该函数的参数中有epnum来传递传输数据的端点号,在函数实现中可以看到如果传递的epnum不是0,则表明是设备类中的端点传递数据,如果设备处于配置状态且设备类的DataOut指针非空则执行设备类中的DataOut函数,如果epnum为0则是端点0上的数据,DataOutStage上的数据是USB模块接收USB主机发来的,要明白其数据传输的方向。每个端点都有设置其最大包大小即maxpacket,端点接收的数据大小一定是小于等于maxpacket的,当需要接收远多于maxpacket的数据时是需要分包发送/接收的,一个很形象的例子如下:
该图是STM32论坛中培训资料中得到的,根据该图可以方便于我们分析DataOutStage以及DataInStage,在DataOutStage的处理中是获取该0号端点,且该端点处于DATA_OUT状态,端点的rem_length变量存储的是当次接收的数据总长度即在Setup函数中request.wLength,而maxpacket的值是在打开该端点时传递进来的,当我们接收到的数据长度大于自身的最大包大小时,表明我们还有数据需要继续接收,这里要注意的是该函数实际是USB中断函数中的DataStage的回调函数,即该函数执行时已经接收到了一包数据,因此这里才调用USBD_CtlContinueRx()即继续接收,这个跟串口的接收中断较类似,也由此,调用USBD_CtlContinueRx()中的参数是rem_length和maxpacket中的小值,当最后数据接收完毕时rem_length的值是小于等于maxpacket,就看传输的数据量是否是maxpacket的整数倍了。数据接收完成时这里调用了设备类的EP0_RxReady回调函数即交由对应的设备类对所收到的数据进行处理,对于本例的VCP类说即交由CDC类的接口文件中的CDC_Itf_Control()函数来处理(设置设备串口属性),最后调用USBD_CtlSendStatus()函数来执行请求的第三阶段:状态阶段。该函数我们在后面另分析。
有了分析DataOutStage的基础,DataInStage就容易分析多了,两者是相反的过程,该函数是发送数据到USB主机,首先当传输数据的端点是非0端点时调用相应设备类的DataIn函数进行处理,当传输数据的端点是0端点时且端点0处于DATA_IN状态,同理此处的rem_length同样和Setup阶段的request.wLength相等,且该函数执行时已经有一包数据发送出去,因此这里更新完rem_length后继续发送数据且使用USBD_LL_PrepareReceive()接收USB主机发来的应答信息。当rem_length小于等于maxpacket时表明数据已经发送完毕,如果所需要发送的数据量是maxpacket的整数倍这里需要发送一个0字节数据包来通知USB主机数据发送完毕。这里一直不理解的是下面这条语句
(pep->total_length < pdev->ep0_data_len )
因为按个人理解这两个值应该是相等的,通过检索可知ep0_data_len只有一处赋值即Setup阶段的request.wLength,而total_length的值在最开始的发送时与rem_length值相等也即为本次发送的数据长度值,无论怎么想二者都应该是相等才对。ep0_data_len的值只有在这里清零,下次进入该函数时total_length值即大于ep0_data_len(因此个人觉得这里应该是ep0_data_len和0的判断而不是和total_length判断),数据发送完毕后,如果EP0_TxSent回调函数不为空则执行该回调,最后接收USB主机发来的状态信息。当端点0的状态不为DATA_IN时这里有个测试模式的调用,由于没有使能测试模式,所以这里不关心,如果有兴趣的话可以研究哈。至此,usbd_core文件分析完毕,USB器件库的核心文件还剩下两个usbd_ioreq和usbd_ctlreq。
上一篇:STM32 USB学习笔记2
下一篇:STM3的TIM4定时器功能设置
推荐阅读
史海拾趣
在市场拓展方面,奇力公司采取了多元化的销售策略。除了与大型知名企业合作外,奇力公司还积极开拓国内外市场,参加各类展会和论坛,提升品牌知名度和影响力。同时,奇力公司还注重品牌建设,通过提升产品质量和服务水平,树立了良好的企业形象。
进入20世纪60年代,ERNI开始关注连接器技术的发展。他们意识到,随着电子设备的日益复杂和多样化,对连接器技术的需求也将不断增长。因此,ERNI投入大量资源进行研发,终于在1967年成功开发出第一个背板连接器,这一创新技术迅速成为公司的核心产品,也为ERNI在连接器市场赢得了声誉。
作为一家有社会责任感的企业,Capar Components Corp积极参与公益事业,回馈社会。公司定期组织员工参与社区服务活动,帮助困难群体解决实际问题。此外,公司还设立了奖学金和助学金,支持教育事业的发展。这些举措不仅彰显了公司的社会责任感,也为公司树立了良好的企业形象。
这些故事是基于一个虚构的电子行业公司背景编写的,您可以根据Capar Components Corp的实际情况进行调整和修改,以使其更符合该公司的历史和发展轨迹。请注意,由于我无法获取Capar Components Corp的具体信息,这些故事仅为示例性质,并非真实事件。
Advanced Illumination Inc公司成立于XXXX年,初创时期,公司便专注于LED照明光源的研发与生产。面对当时市场上众多的竞争对手,Ai公司凭借其对技术的深刻理解和对市场需求的敏锐洞察,选择了一条专注于机器视觉工业应用的道路。这一决策为公司后续的发展奠定了坚实的基础。
在成功开发出优质产品后,Ai公司开始积极拓展市场。公司通过参加各种行业展会、举办技术研讨会等方式,与潜在客户进行深入交流,成功将产品推广至全球范围。同时,公司还注重品牌建设,通过广告宣传、媒体报道等手段提升品牌知名度和美誉度。这些举措使得Ai公司的产品在市场上获得了广泛的认可。
自1994年(部分资料显示为1993年,但此处采用最新信息)成立以来,港源公司始终将技术创新视为企业发展的核心驱动力。公司专注于轻触开关、微动开关、AC/DC插座、变压器等开关零组件产品的研发,通过引进国内外先进技术,并自主成功研发了自动化生产设备和检测设备,实现了生产自动化,大幅提升了产品质量和生产效率。港源公司还积极参与国际技术交流与合作,与众多行业标杆客户长期紧密合作,能够尽早了解市场需求,快速响应并推出符合行业标准的新产品,从而引领了电子开关行业的发展方向。
Microvision笔记本PCMCIA彩色黑白图像采集卡 特别适于:笔记本图像采集处理 医学图像采集、显微成像、工业检测、野外图像采集存储、野外监控录像系统 便携移动图像采集处理、移动智能交通、移动电子警察、移动车辆稽查系统 【 ...… 查看全部问答∨ |
请问大虾们一个问题,由于芯片更换,以前接喇叭的音频信号从差分变成单端的了,原来用来放大差分音频信号的运放,负极怎么办,直接接地,还是运放也要换成单端的运放?… 查看全部问答∨ |
美资公司LSI上海研发中心诚聘 Product Engineer 美资公司LSI上海研发中心诚聘 Product Engineer 内部推荐,有意者请将中英文简历发送至:asic_tapeout@hotmail.com Product Engineer DESCRIPTION OF DUTIES IN ADDITION TO THOSE IN JOB DESCRIPTION: - To support prototype and production r ...… 查看全部问答∨ |
在设计一个9b92的板子,请问之前有人用过别的型号的usb 接口的功能么. 但作为usb device用时,是否一定要把vbus上拉。 我的板不从usb去电,是否可以通过软件在上电后启动usb口进行枚举? 因为我想省下vbus那个脚做io :-),LM的脚的复用的也太 ...… 查看全部问答∨ |
锅炉房的plc300系列有个触摸屏,现在出现个现象,有三个高压泵,2#一直运行中,1#3#备用,现在启动1#泵触摸屏会显示气压极高,导致锅炉停炉,但是起3#就没这种现象,高手们都来分析下,存在干扰?… 查看全部问答∨ |
我在CC2430上实现了MODBUS,在CC1110上实现了UART透传。 UART应用程序中超时,串口中断的时候数据往环形队列里面放,队列就是一个组数,有两个标记表示头和尾,这个方法我也是模仿一些国外开发系统中集成的程序。 主程序中用getch函数,读队 ...… 查看全部问答∨ |
我现在写的程序中想用goto语句来解决它(这样做的话,可读性很强,而且也非常容易解决),但是记得原来看过一些C语言的书说不推荐用goto语句,我现在应该怎么选择呢?goto语句对流水线的破坏到底有多大呢?… 查看全部问答∨ |