历史上的今天
返回首页

历史上的今天

今天是:2024年09月14日(星期六)

正在发生

2018年09月14日 | STM32 USB学习笔记8

2018-09-14 来源:eefocus

主机环境:Windows 7 SP1

开发环境:MDK5.14

目标板:STM32F103C8T6

开发库:STM32F1Cube库和STM32_USB_Device_Library

现在分析USB器件库核心文件的最后一个文件usbd_ctlreq,该文件提供了标准请求的处理,跟USB2.0协议的第九章节紧密关联。在Setup阶段根据bmRequest字段的内容分为:设备请求、接口请求、端点请求。并根据不同的请求调用不同的函数体,USB2.0协议中定义的标清请求有以下几种

通过第一个竖栏可以看出同一个请求代码可以对应多个接收者,例如CLEAR_FEATURE请求可以是设备请求也可以是接口请求,同样也可以是端点请求。对应的各个请求代码,如下

在usbd_def.h文件中可以找到与之对应的定义,如下:



#define  USB_REQ_GET_STATUS                             0x00

#define  USB_REQ_CLEAR_FEATURE                          0x01

#define  USB_REQ_SET_FEATURE                            0x03

#define  USB_REQ_SET_ADDRESS                            0x05

#define  USB_REQ_GET_DESCRIPTOR                         0x06

#define  USB_REQ_SET_DESCRIPTOR                         0x07

#define  USB_REQ_GET_CONFIGURATION                      0x08

#define  USB_REQ_SET_CONFIGURATION                      0x09

#define  USB_REQ_GET_INTERFACE                          0x0A

#define  USB_REQ_SET_INTERFACE                          0x0B

#define  USB_REQ_SYNCH_FRAME                            0x0C

首先看一下标准设备请求的处理,

/**

* @brief  USBD_StdDevReq

*         Handle standard usb device requests

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

USBD_StatusTypeDef  USBD_StdDevReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef  *req)

{

  USBD_StatusTypeDef ret = USBD_OK;  

  

  switch (req->bRequest) 

  {

  case USB_REQ_GET_DESCRIPTOR: 

    

    USBD_GetDescriptor (pdev, req) ;

    break;

    

  case USB_REQ_SET_ADDRESS:                      

    USBD_SetAddress(pdev, req);

    break;

    

  case USB_REQ_SET_CONFIGURATION:                    

    USBD_SetConfig (pdev , req);

    break;

    

  case USB_REQ_GET_CONFIGURATION:                 

    USBD_GetConfig (pdev , req);

    break;

    

  case USB_REQ_GET_STATUS:                                  

    USBD_GetStatus (pdev , req);

    break;

    

    

  case USB_REQ_SET_FEATURE:   

    USBD_SetFeature (pdev , req);    

    break;

    

  case USB_REQ_CLEAR_FEATURE:                                   

    USBD_ClrFeature (pdev , req);

    break;

    

  default:  

    USBD_CtlError(pdev , req);

    break;

  }

  

  return ret;

}

符合标准的设备请求通过Table 9-3可以看出一共有8种,这里只处理了7种,SET_DESCRIPTOR是没有处理的,当请求不是这7种请求的任何一种时通过USBD_CtlError()函数回应给USB主机一个STALL表明请求错误发生。首先看第一个请求获取描述符请求的处理,如下:


/**

* @brief  USBD_GetDescriptor

*         Handle Get Descriptor requests

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , 

                               USBD_SetupReqTypedef *req)

{

  uint16_t len;

  uint8_t *pbuf;

  

    

  switch (req->wValue >> 8)

  { 

#if (USBD_LPM_ENABLED == 1)

  case USB_DESC_TYPE_BOS:

    pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len);

    break;

#endif    

  case USB_DESC_TYPE_DEVICE:

    pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len);

    break;

    

  case USB_DESC_TYPE_CONFIGURATION:     

    if(pdev->dev_speed == USBD_SPEED_HIGH )   

    {

      pbuf   = (uint8_t *)pdev->pClass->GetHSConfigDescriptor(&len);

      pbuf[1] = USB_DESC_TYPE_CONFIGURATION;

    }

    else

    {

      pbuf   = (uint8_t *)pdev->pClass->GetFSConfigDescriptor(&len);

      pbuf[1] = USB_DESC_TYPE_CONFIGURATION;

    }

    break;

    

  case USB_DESC_TYPE_STRING:

    switch ((uint8_t)(req->wValue))

    {

    case USBD_IDX_LANGID_STR:

     pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len);        

      break;

      

    case USBD_IDX_MFC_STR:

      pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len);

      break;

      

    case USBD_IDX_PRODUCT_STR:

      pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len);

      break;

      

    case USBD_IDX_SERIAL_STR:

      pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len);

      break;

      

    case USBD_IDX_CONFIG_STR:

      pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len);

      break;

      

    case USBD_IDX_INTERFACE_STR:

      pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len);

      break;

      

    default:

#if (USBD_SUPPORT_USER_STRING == 1)

      pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue) , &len);

      break;

#else      

       USBD_CtlError(pdev , req);

      return;

#endif   

    }

    break;

  case USB_DESC_TYPE_DEVICE_QUALIFIER:                   

 

    if(pdev->dev_speed == USBD_SPEED_HIGH  )   

    {

      pbuf   = (uint8_t *)pdev->pClass->GetDeviceQualifierDescriptor(&len);

      break;

    }

    else

    {

      USBD_CtlError(pdev , req);

      return;

    } 

 

  case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION:

    if(pdev->dev_speed == USBD_SPEED_HIGH  )   

    {

      pbuf   = (uint8_t *)pdev->pClass->GetOtherSpeedConfigDescriptor(&len);

      pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION;

      break; 

    }

    else

    {

      USBD_CtlError(pdev , req);

      return;

    }

 

  default: 

     USBD_CtlError(pdev , req);

    return;

  }

  

  if((len != 0)&& (req->wLength != 0))

  {

    

    len = MIN(len , req->wLength);

    

    USBD_CtlSendData (pdev, 

                      pbuf,

                      len);

  }

  

}

也是一大串switch-case语句,这样的代码虽然多但容易分析,在GET_DESCRIPTOR请求中,根据Table 9-3可知wValue字段的含义是描述符类型和描述符索引,在USB2.0协议的9.4.3章节中可以看到wValue的高字节标记了描述符的类型,wValue的低字节标记了描述符的索引。在USBD_GetDescriptor()函数中首先是检测描述符类型,第一个分支是设备描述符,通过pdev句柄的pDesc指针下的GetDeviceDescriptor指针调用所需要的函数即我们之前分析过的usbd_desc.c文件中的对应函数。第二个分支是获取配置描述符,这个是通过设备类指针获取的,放在后面分析,且这里分出了高速和全速模式。接着一个大分支是获取字符串描述符,这里就需要用到wValue的低字节描述符索引来细分各个字符串描述符,获取字符串描述符的处理跟获取设备描述符类似,不再赘述,索引号错误返回请求错误状态。DEVICE_QUALIFIER和OTHER_SPEED_CONFIGURATION描述符的获取都是在高速模式下才会有,由于STM32F103C8T6只支持全速模式,因此这里不再分析,直接返回请求错误。在最后发送这些数据到USB主机时有判断len和wLenth的大小,且返回的数据长度是两者的最小值,USB2.0协议的9.3.5章节中提到在一个输入请求中设备返回的数据不可以大于wLength字段指示的值,在一个输出请求中wLength字段的值一定是USB主机发出的数据长度值。如下:



至此,描述符的获取分析完毕,返回到标准设备请求函数USBD_StdDevReq()中查看第二个请求设置地址,如下:



/**

* @brief  USBD_SetAddress

*         Set device address

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_SetAddress(USBD_HandleTypeDef *pdev , 

                            USBD_SetupReqTypedef *req)

{

  uint8_t  dev_addr; 

  

  if ((req->wIndex == 0) && (req->wLength == 0)) 

  {

    dev_addr = (uint8_t)(req->wValue) & 0x7F;     

    

    if (pdev->dev_state == USBD_STATE_CONFIGURED) 

    {

      USBD_CtlError(pdev , req);

    } 

    else 

    {

      pdev->dev_address = dev_addr;

      USBD_LL_SetUSBAddress(pdev, dev_addr);               

      USBD_CtlSendStatus(pdev);                         

      

      if (dev_addr != 0) 

      {

        pdev->dev_state  = USBD_STATE_ADDRESSED;

      } 

      else 

      {

        pdev->dev_state  = USBD_STATE_DEFAULT; 

      }

    }

  } 

  else 

  {

     USBD_CtlError(pdev , req);                        

  } 

}

一个USB总线上可以支持128个地址,地址0作为默认地址,且每个设备地址是唯一的,当USB主机为USB设备分配完地址后,设备即进入地址状态,等待配置完成,由Table 9-3可以看出SET_ADDRESS请求中wIndex和wLength字段值为0,wValue字段存储USB设备地址值,当设备处于配置状态时表明其已经分配了设备地址,因此返回请求错误,其他状态下通过USBD_LL_SetUSBAddress()函数使能该地址,并发送状态信息给USB主机(这里是没有数据阶段,只有状态阶段),当地址不为0时,标记设备进入地址状态。返回到USBD_StdDevReq()函数中查看SET_CONFIGURATION请求,如下:


/**

* @brief  USBD_SetConfig

*         Handle Set device configuration request

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_SetConfig(USBD_HandleTypeDef *pdev , 

                           USBD_SetupReqTypedef *req)

{

  

  static uint8_t  cfgidx;

  

  cfgidx = (uint8_t)(req->wValue);                 

  

  if (cfgidx > USBD_MAX_NUM_CONFIGURATION ) 

  {            

     USBD_CtlError(pdev , req);                              

  } 

  else 

  {

    switch (pdev->dev_state) 

    {

    case USBD_STATE_ADDRESSED:

      if (cfgidx) 

      {                                      

        pdev->dev_config = cfgidx;

        pdev->dev_state = USBD_STATE_CONFIGURED;

        if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL)

        {

          USBD_CtlError(pdev , req);  

          return;

        }

        USBD_CtlSendStatus(pdev);

      }

      else 

      {

         USBD_CtlSendStatus(pdev);

      }

      break;

      

    case USBD_STATE_CONFIGURED:

      if (cfgidx == 0) 

      {                           

        pdev->dev_state = USBD_STATE_ADDRESSED;

        pdev->dev_config = cfgidx;          

        USBD_ClrClassConfig(pdev , cfgidx);

        USBD_CtlSendStatus(pdev);

        

      } 

      else  if (cfgidx != pdev->dev_config) 

      {

        /* Clear old configuration */

        USBD_ClrClassConfig(pdev , pdev->dev_config);

        

        /* set new configuration */

        pdev->dev_config = cfgidx;

        if(USBD_SetClassConfig(pdev , cfgidx) == USBD_FAIL)

        {

          USBD_CtlError(pdev , req);  

          return;

        }

        USBD_CtlSendStatus(pdev);

      }

      else

      {

        USBD_CtlSendStatus(pdev);

      }

      break;

      

    default:

       USBD_CtlError(pdev , req);                     

      break;

    }

  }

}

代码稍微长了一些,该请求下wIndex和wLength字段的值同样为0,wValue字段值为配置ID,USB2.0协议中有关该请求的描述如下:



本例在usbd_conf.h文件中限制了最大配置数为1,该段代码中没有对wIndex和wLength进行限制,不科学那。。。当设备处于地址状态时可以对设备进行配置,记录此时的配置id号,切换设备状态,调用usbd_core.c文件中的USBD_SetClassConfig()函数执行真正的配置工作,该函数会链接到设备类的处理函数中。配置成功后发送状态信息到USB主机。如果设备处于配置状态时,发送0号配置ID设备进入地址状态,发送非0号配置ID且跟当前配置ID不同时重新加载该配置并发送状态信息到USB主机。设置配置流程比较简单,最重要的是它设备类中配置的实现。返回到标准设备请求函数中查看GET_CONFIGURATION请求,该请求返回设备的配置值,其中wValue、wIndex均为0,wLength为1,获取配置的实现如下:



/**

* @brief  USBD_GetConfig

*         Handle Get device configuration request

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_GetConfig(USBD_HandleTypeDef *pdev , 

                           USBD_SetupReqTypedef *req)

{

 

  if (req->wLength != 1) 

  {                   

     USBD_CtlError(pdev , req);

  }

  else 

  {

    switch (pdev->dev_state )  

    {

    case USBD_STATE_ADDRESSED:                     

      pdev->dev_default_config = 0;

      USBD_CtlSendData (pdev, 

                        (uint8_t *)&pdev->dev_default_config,

                        1);

      break;

      

    case USBD_STATE_CONFIGURED:   

      

      USBD_CtlSendData (pdev, 

                        (uint8_t *)&pdev->dev_config,

                        1);

      break;

      

    default:

       USBD_CtlError(pdev , req);

      break;

    }

  }

}

这里同样没有对wIndex和wIndex进行检测,只对wLength进行了检测,配置ID只占用了一个字节,因此返回的数据长度为1,设备处于地址状态时说明没有配置生效,因此返回默认的配置号0,而处于配置状态时返回当前设备的配置ID,该函数也比较简单。查看下一个标准设备请求GET_STATUS,如下:


/**

* @brief  USBD_GetStatus

*         Handle Get Status request

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_GetStatus(USBD_HandleTypeDef *pdev , 

                           USBD_SetupReqTypedef *req)

{

  

    

  switch (pdev->dev_state) 

  {

  case USBD_STATE_ADDRESSED:

  case USBD_STATE_CONFIGURED:

    

#if ( USBD_SELF_POWERED == 1)

    pdev->dev_config_status = USB_CONFIG_SELF_POWERED;                                  

#else

    pdev->dev_config_status = 0;                                   

#endif

                      

    if (pdev->dev_remote_wakeup) 

    {

       pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP;                                

    }

    

    USBD_CtlSendData (pdev, 

                      (uint8_t *)& pdev->dev_config_status,

                      2);

    break;

    

  default :

    USBD_CtlError(pdev , req);                        

    break;

  }

}

GET_STATUS请求的结构如下:

wValue和wLength分别为0和2,该请求支持三个接收者,这里我们只看对设备的请求,其返回的信息如下:

设备返回的状态只有两个有效位:是否支持自供电,是否支持远程唤醒,其中Self Powered特性是不能够通过SetFeature()或ClearFeature()请求修改的。在本例中设备支持自供电模式,因此该字段为1,如果支持远程唤醒则相应字段也为1,最后把设备状态信息发送到USB主机上。

接着查看SET_FEATURE请求的处理,如下:

/**

* @brief  USBD_SetFeature

*         Handle Set device feature request

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_SetFeature(USBD_HandleTypeDef *pdev , 

                            USBD_SetupReqTypedef *req)

{

 

  if (req->wValue == USB_FEATURE_REMOTE_WAKEUP)

  {

    pdev->dev_remote_wakeup = 1;  

    pdev->pClass->Setup (pdev, req);   

    USBD_CtlSendStatus(pdev);

  }

 

}

该请求结构如下:

同样支持三个接收者,其中特性选择器的取值如下:

对于设备请求来说,有1和2可选,其中TEST_MODE中还有更多测试可选,如下:

但本例是不支持测试模式的,因此这里只关心远程唤醒特性,该段代码没有对参数进行检测也没有对设备状态进行检测,感觉不够严谨了,当使能远程唤醒时调用设备类的相应函数进行处理,并发送状态信息到USB主机。最后看一下CLEAR_FEATURE请求的处理,


/**

* @brief  USBD_ClrFeature

*         Handle clear device feature request

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , 

                            USBD_SetupReqTypedef *req)

{

  switch (pdev->dev_state)

  {

  case USBD_STATE_ADDRESSED:

  case USBD_STATE_CONFIGURED:

    if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) 

    {

      pdev->dev_remote_wakeup = 0; 

      pdev->pClass->Setup (pdev, req);   

      USBD_CtlSendStatus(pdev);

    }

    break;

    

  default :

     USBD_CtlError(pdev , req);

    break;

  }

}

CLEAR_FEATURE请求跟SET_FEATURE请求相反,处理流程相同,但这里比之SET_FEATURE请求处理要严谨多了,对设备状态进行了检测。至此,标准设备请求的处理就分析完毕了,接着产看标准接口请求的处理,


/**

* @brief  USBD_StdItfReq

*         Handle standard usb interface requests

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

USBD_StatusTypeDef  USBD_StdItfReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef  *req)

{

  USBD_StatusTypeDef ret = USBD_OK; 

  

  switch (pdev->dev_state) 

  {

  case USBD_STATE_CONFIGURED:

    

    if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) 

    {

      pdev->pClass->Setup (pdev, req); 

      

      if((req->wLength == 0)&& (ret == USBD_OK))

      {

         USBD_CtlSendStatus(pdev);

      }

    } 

    else 

    {                                               

       USBD_CtlError(pdev , req);

    }

    break;

    

  default:

     USBD_CtlError(pdev , req);

    break;

  }

  return USBD_OK;

}

标准接口请求一共有5个,这里处理比较简单,只对调用设备类的处理函数来处理某些请求,其他请求均返回请求错误状态。现在只剩下标准端点请求的处理了,也是本文件的最后一个需要分析的函数,如下:


/**

* @brief  USBD_StdEPReq

*         Handle standard usb endpoint requests

* @param  pdev: device instance

* @param  req: usb request

* @retval status

*/

USBD_StatusTypeDef  USBD_StdEPReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef  *req)

{

  

  uint8_t   ep_addr;

  USBD_StatusTypeDef ret = USBD_OK; 

  USBD_EndpointTypeDef   *pep;

  ep_addr  = LOBYTE(req->wIndex);   

  

  /* Check if it is a class request */

  if ((req->bmRequest & 0x60) == 0x20)

  {

    pdev->pClass->Setup (pdev, req);

    

    return USBD_OK;

  }

  

  switch (req->bRequest) 

  {

    

  case USB_REQ_SET_FEATURE :

    

    switch (pdev->dev_state) 

    {

    case USBD_STATE_ADDRESSED:          

      if ((ep_addr != 0x00) && (ep_addr != 0x80)) 

      {

        USBD_LL_StallEP(pdev , ep_addr);

      }

      break;

      

    case USBD_STATE_CONFIGURED:   

      if (req->wValue == USB_FEATURE_EP_HALT)

      {

        if ((ep_addr != 0x00) && (ep_addr != 0x80)) 

        { 

          USBD_LL_StallEP(pdev , ep_addr);

          

        }

      }

      pdev->pClass->Setup (pdev, req);   

      USBD_CtlSendStatus(pdev);

      

      break;

      

    default:                         

      USBD_CtlError(pdev , req);

      break;    

    }

    break;

    

  case USB_REQ_CLEAR_FEATURE :

    

    switch (pdev->dev_state) 

    {

    case USBD_STATE_ADDRESSED:          

      if ((ep_addr != 0x00) && (ep_addr != 0x80)) 

      {

        USBD_LL_StallEP(pdev , ep_addr);

      }

      break;

      

    case USBD_STATE_CONFIGURED:   

      if (req->wValue == USB_FEATURE_EP_HALT)

      {

        if ((ep_addr & 0x7F) != 0x00) 

        {        

          USBD_LL_ClearStallEP(pdev , ep_addr);

          pdev->pClass->Setup (pdev, req);

        }

        USBD_CtlSendStatus(pdev);

      }

      break;

      

    default:                         

      USBD_CtlError(pdev , req);

      break;    

    }

    break;

    

  case USB_REQ_GET_STATUS:                  

    switch (pdev->dev_state) 

    {

    case USBD_STATE_ADDRESSED:          

      if ((ep_addr & 0x7F) != 0x00) 

      {

        USBD_LL_StallEP(pdev , ep_addr);

      }

      break;

      

    case USBD_STATE_CONFIGURED:

      pep = ((ep_addr & 0x80) == 0x80) ? &pdev->ep_in[ep_addr & 0x7F]:\

                                         &pdev->ep_out[ep_addr & 0x7F];

      if(USBD_LL_IsStallEP(pdev, ep_addr))

      {

        pep->status = 0x0001;     

      }

      else

      {

        pep->status = 0x0000;  

      }

      

      USBD_CtlSendData (pdev,

                        (uint8_t *)&pep->status,

                        2);

      break;

      

    default:                         

      USBD_CtlError(pdev , req);

      break;

    }

    break;

    

  default:

    break;

  }

  return ret;

}

代码比较长,标准端点请求一共有4个:CLEAR_FEATURE、GET_STATUS、SET_FEATURE、SYNCH_FRAME。这几个请求中wIndex字段的值为端点号,因此首先通过该字段获取端点号,根据bmRequest来检测是否是类请求,如果是类请求则调用设备类的相应函数进行处理,如果不是类请求则正常处理,这里只处理了前三个标准请求,首先看SET_FEATURE请求,在之前的特性选择器中端点对应的只有EP_HALT,在地址状态下如果指定的端点号非0,则设备回应请求错误,在配置状态下该请求有效,并调用设备类的处理函数实现特性的设置,CLEAR_FEATURE请求的处理跟SET_FEATURE类似,而GET_STATUS请求返回的信息如下:

有一个有效位,检测所请求端点的状态发送给USB主机,至此,usbd_ctlreq文件分析完毕,该文件需对照USB2.0协议的第9章节来分析,现在就只剩下USB的设备类文件没有分析了,下次分析usbd_cdc文件。



推荐阅读

史海拾趣

Eurotechnique公司的发展小趣事

在XXXX年,Eurotechnique迎来了一次重要的技术突破。公司成功研发出了一种新型的微控制器,具有更高的性能和更低的能耗。这一技术的突破不仅为公司带来了大量的订单,也进一步巩固了公司在微控制器领域的领先地位。随着技术的不断进步,Eurotechnique开始逐步扩展产品线,将业务范围拓展到数字信号处理、无线通信等多个领域。

Dynawave Incorporated公司的发展小趣事

随着技术的成熟,Dynawave开始寻求市场拓展。公司高层决定,将目标市场锁定在智能家居和物联网领域。他们与多家知名厂商合作,将自己的无线传输技术应用到各类智能设备中。通过不断的技术优化和市场推广,Dynawave的产品逐渐得到了市场的认可,销售额稳步增长。

GE Oil & Gas Digital Solutions公司的发展小趣事
使用高精度的电阻和电容元件可以减少元件误差对测量结果的影响。
Amveco Toroidal Power Products公司的发展小趣事

为了进一步扩大市场份额,Amveco公司积极寻求与行业内外的合作伙伴建立战略合作关系。公司与多家知名的电子设备制造商建立了长期稳定的合作关系,为其提供定制化的环形变压器解决方案。此外,公司还通过参加国际电子展会、建立海外销售网络等方式,积极开拓国际市场,实现了业务的全球化布局。

DSP Group Inc公司的发展小趣事

在智能语音领域,DSP Group凭借其深厚的技术积累和创新能力,取得了显著的突破。公司推出了一系列智能语音处理技术和产品,如SmartVoice系列语音处理芯片和算法套件等。这些技术和产品不仅为用户提供了更加自然、智能的语音交互体验,还为智能家居、汽车电子等领域的发展提供了有力支持。

芯源半导体(CW)公司的发展小趣事

随着公司产品的逐步成熟,芯源半导体(CW)公司开始寻求与行业内其他企业的合作。经过深入的市场调研和谈判,公司成功与多家知名电子企业建立了战略合作伙伴关系。这些合作伙伴为芯源半导体(CW)公司提供了更多的市场机会和技术支持,使公司得以迅速扩大市场份额,提升品牌影响力。

问答坊 | AI 解惑

OP放大器的设计与应用

如题所说,一份关于OP放大器的资料!~…

查看全部问答>

目前TIDSP缺货吗?

最近在网上溜达常能看到有人求购TI DSP,价格高的惊人,涨了10倍不止。 是TI的产能不足,还是目前市场需求太大?…

查看全部问答>

2440WINCE下做用中断做了个按键驱动,却不知道在哪里加延时去抖.

2440WINCE下做用中断做了个按键驱动,却不知道在哪里加延时去抖. 在流驱动里开了一个线程 DWORD UserKeyProcessThread(void) {                 while(1)         {   &n ...…

查看全部问答>

关于wince驱动的测试问题

我是用platform builder  用wince自带的CETK,来测试刚编程完的驱动程序,然后创建主干Tux模块,最后在编译工程的时候出现了错误: :cannot open program database:  c:/wince500\\pbworkspaces\\HID3\\WINCE500\\maistonei ...…

查看全部问答>

有关8253工作模式的问题

8253处于方式5硬件触发选通方式下,对于Gate而言是要每次上升沿到来才会计数?还是说Gate从低电平调到高电平后,只要维持在高电平就会计数? 请高人指点一下,谢谢!…

查看全部问答>

如果获取Pin码的剩余的输入次数?

我在试着在PPC做一个改变PIN2码的东东 可是pin2码的输入次数是有限制的,我应该可以通过API获得我所剩余的输入次数.请问这个API是哪个,我需要怎么做才能获得这个次数呢?? PS: 我截获了SIMCAPS这个结构体的值,好像没多大用 3Q …

查看全部问答>

如何成为驱动程序开发高手?

小弟想学习驱动程序开发,但不知道如何入手?做人要踏实,做事也要踏实,小弟我想循序渐进的学习驱动开发,不求取巧,但求步步为营。那位高手给小弟指一条循序渐进学习驱动开发的明路,或者推荐一些教材,小弟不胜感激………

查看全部问答>

自已做的ULink一链就通了,非常开心与大家共享一下

                                 买了个壳装上,看着也像那么回事…

查看全部问答>

SDIO多扇区写入遇到的问题!

调试环境:万利STM32-E板 4G卡,使用 um0427文档的参考代码:sdcard.c   V2.0.3   09/22/2008  MCD Application TeamSDIO 模式是 4bits DMA模式;1. 连续多 ...…

查看全部问答>

TI信号链基础知识合辑

碰到放大很多同学都有些头痛,也是见仁见智,上传点自己找到的TI信号链基础知识合辑,供大家分享,共同讨论………

查看全部问答>