历史上的今天
返回首页

历史上的今天

今天是:2025年03月11日(星期二)

正在发生

2019年03月11日 | STM32 USB学习笔记8

2019-03-11 来源: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文件。



推荐阅读

史海拾趣

American Custom Components公司的发展小趣事

American Custom Components公司自创立之初,就致力于电子元器件的创新研发。在一次技术研讨会上,公司的研发团队发现了一种新型的半导体材料,具有出色的导电性和稳定性。经过数月的实验和验证,团队成功地将这种材料应用于新产品中,推出了一款性能卓越的集成电路。这一创新技术不仅提升了公司的产品质量,还赢得了市场的广泛认可,为公司的发展奠定了坚实基础。

FILTRONETICS Inc公司的发展小趣事

FILTRONETICS Inc成立于1970年代,初期专注于滤波器技术的研发与生产。在那个时代,随着通信技术的快速发展,对高质量滤波器的需求日益增长。公司研发团队通过不懈努力,成功研发出了一种新型LC滤波器,该滤波器在频率选择性和插入损耗方面表现出色,迅速获得了市场的认可。这一技术突破不仅巩固了FILTRONETICS在滤波器领域的地位,也为公司的后续发展奠定了坚实的基础。

Alpha & Omega Semiconductor(万国半导体)公司的发展小趣事

进入21世纪后,FILTRONETICS意识到全球化市场的重要性,开始积极开拓国际市场。公司加强了与国际客户的沟通与合作,参加了多个国际电子展会,展示了公司的技术和产品。同时,公司还在海外设立了分支机构,以便更好地服务当地客户。通过不懈的努力,FILTRONETICS成功打入多个国际市场,并赢得了众多国际客户的信赖和支持。

ELDECO公司的发展小趣事

在激烈的市场竞争中,ELDECO公司始终坚持质量至上的原则。公司建立了严格的质量管理体系和检测机制,确保每一台出厂的ELD产品都符合高质量标准。同时,公司还注重售后服务体系建设,为客户提供及时、专业的技术支持和解决方案。正是凭借过硬的产品质量和优质的售后服务,ELDECO公司的ELD产品赢得了客户的广泛认可和信赖。

Eurotechnique公司的发展小趣事

Eurotechnique公司成立于XXXX年,由几位具有远见卓识的电子工程师和企业家共同创立。他们看到了当时电子行业快速发展的趋势,尤其是数字电路和微控制器领域的巨大潜力。因此,公司决定专注于这两个领域的技术研发和产品生产。在创立初期,Eurotechnique通过不断的技术创新和产品优化,逐渐在市场上树立了良好的口碑。

台湾君耀(Brightking)公司的发展小趣事

君耀(Brightking)公司于1996年在台湾新竹高科技园区创立,初期专注于防雷元器件的研发和生产。凭借对技术的深入研究和市场需求的敏锐洞察,君耀逐步在台湾的电子器件市场上占得一席之地。随着产品质量和技术含量的提升,君耀的产品开始受到国内外客户的青睐,销售网络逐渐拓展至全球。

问答坊 | AI 解惑

wap gprs cmwap cmnet 联通wap之间的关系及不同

1.WAP和GPRS有什么区别啊? 严格的说wap和gprs并不能相提并论的机以比较,因为这是两个不同的技术概念。简单的说,wap是一种无线网络应用协议,而gprs是实现wap应用的一种网络传输技术方式。形象的说,wap如果是一封信,gprs就是送信到达的邮车 ...…

查看全部问答>

msgQReceive 接收不到消息?

请问:msgQReceive 接收不到消息? 有可能是哪些原因? -------------------------------- …

查看全部问答>

请教关于IPSM的问题

大侠好:    目前修改了FLASH升级到64M,现在已经可以正常进入BOOT的MAIN中,用以前的PSM也可以进入到CE中,但是分区还是跟以前一样,在CE下可用的只有3M。    现在我将IPSM中唯一可以修改的文件plat_api.c进行修改: 如下 ...…

查看全部问答>

弱弱的问一下:在wince5.0中如何实现对SD卡的驱动?

在wince5.0中如何实现对SD卡的驱动,是不是要以下步骤: 1.在WINCE 项目中添加SD 卡的相关的组建,例如: CATALOG->device drivers->SDIO->SDMemory CATALOG->device drivers->SDIO->SDIO Host->Samsung 2410 SDIO Host CATALOG->device drivers-> ...…

查看全部问答>

问一个arm中断的问题

我的环境:at91rm9200+28F128+dataflash 我运行仿真器时,程序执行起点是__ENTRY,地址是0x20000000,(1)请问这个地址是可变的吗? 1ffffff4        [0xffffffff]   dci      0xffffffff ; ...…

查看全部问答>

关于cpu指令集的问题?

指令集指什么?硬件上的还是软件上的? 如果我自己做一个cpu的话,需要和使用的操作系统之间 在指令集上有什么考虑?…

查看全部问答>

怎么在新话题里面插图片呀?麻烦高手指点一下,谢谢

我想把我的一些原理图弄到新话题里面,请教高手一些问题,但不知道怎么把图片弄进来?  …

查看全部问答>

stm32中延时程序

                                 现在处理器越用性能越强,突然想起一个问题,不知道大家通常在STM32中是怎样写比较准确点的延时程序,如延时 ...…

查看全部问答>

献给新手:解析STM32的库函数

replyreload += \',\' + 1099317; 意法半导体在推出STM32微控制器之初,也同时提供了一套完整细致的固件开发包,里面包含了在STM32开发过程中所涉及到的所有底层操作。通过在程序开发中引入这样的固件开发包,可以使开发人员从复杂冗余的底层寄 ...…

查看全部问答>