STM32 USB学习笔记6

发布者:RadiantRiver最新更新时间:2018-09-14 来源: eefocus关键字:STM32  USB学习 手机看文章 扫描二维码
随时随地手机看文章

主机环境: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学习 引用地址:STM32 USB学习笔记6

上一篇:STM32 USB学习笔记2
下一篇:STM3的TIM4定时器功能设置

推荐阅读最新更新时间:2024-03-16 16:14

stm32的can总线理解及应用
CAN 是Controller Area Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。 它的通信速度较快,通信距离远,最高1Mbps(距离小于40米),最远可达10千里(速率低于5Kbps)。在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(Identifier 以下称为 ID)决定优先级。ID 并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时开始发送消息时,对各消息ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。 CAN协议经过ISO标准化后有
[单片机]
STM32的SPI外设片选只有一个,怎么破?
之前用STM32的SPI需要控制很多外部芯片,可是一个SPI的外设只有一个片选,要实现独立片选一主多从,怎么实现呢? SPI总线拓扑 一般地,SPI总线按照下图方式进行连接,一主多从。 如上图: 每个从设备都有独立的片选引脚,主机同一时间段内,与一个从设备进行通信,也即选中一个从设备。 MOSI/MISO/SCLK并联在一起 MISO须是三态门,当从设备未选中时,该脚须设置为高阻态,而不能是输出态,否则会影响总线 ! 对于MOSI/SCLK,虽然并联在一起,但是由于仅一个输出,多输入。 但是你看STM32的SPI外设,一个SPI仅有一个NSS信号,以STM32F407的SPI2为例: 那么要实现前面说的一主多从
[单片机]
如何理解STM32系统时钟和分频
  首先来手册里的一段话。   三种不同的时钟源可被用来驱动系统时钟 (SYSCLK)   · HSI振荡器时钟   · HSE振荡器时钟   · PLL时钟   一般用的是PLL时钟,后面有证据。   我们可以通过库函数获取各时钟值   void RCC_GetClocksFreq(RCC_ClocksTypeDef* RCC_Clocks)   在我的系统里,把时钟值打印信息如下:   SYSCLK:0x44aa200 //72000000, 72MHz   HCLK:0x44aa200 //72000000, 72MHz   PCLK1:0x2255100 //36000000, 36MHz   PCLK2:0x44aa20
[单片机]
如何理解<font color='red'>STM32</font>系统时钟和分频
STM32库函数USART波特率计算的问题
STM32的串口波特率计算本来没多大个事,只不过ST的StdPeriph以及后继者STM32Cube计算波特率那块弄得很复杂。写此文的目的是避免新手在这一块被函数库误导了。 STM32F1波特率计算只有一个公式: F2之后的系列有两公式,增加了8倍采样的模式,将16换成8就行。 先说常用的16倍采样。STM32的USART波特率生成支持小数分频,BRR寄存器高12位是整数部分,低4位是小数部分。刚好有4位小数部分,于是: BRR=(PCLK/(16*Baud)) 4=PCLK/Baud,so easy!你看硬件设计师都设计好了,波特率计算就这么简单。这样分频系数截尾误差最大1个bit,4位小数也就是1/16=0.0
[单片机]
<font color='red'>STM32</font>库函数USART波特率计算的问题
STM32 常用GPIO操作函数记录
STM32读具体GPIOx的某一位是1还是0 1 /** 2 * @brief Reads the specified input port pin. 3 * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. 4 * @param GPIO_Pin: specifies the port bit to read. 5 * This parameter can be GPIO_Pin_x where x can be (0..15). 6 * @retval The input port pin value. 7 */
[单片机]
STM32 USART使用奇偶校验位
无校验位时,数据位常用8位 当使用就校验位时,数据位应设置为9位. 奇偶校验位也包含在数据位中。 详见参考手册:
[单片机]
<font color='red'>STM32</font> USART使用奇偶校验位
STM32中单独设置GPIO端口高8位/低8位的方法
stm32的IO端口都是16位的, 如果要单独操作某高8位或低8位, 则不是那么简单, 先看两张BSRR/BRR寄存器的图: 据官方数据手册上面说, 这两个寄存器用于专门对ODR进行原子操作的位操作, 都是在置1的时候对某位有影响. 举例说下怎么对IO端口赋值: 1.对高8位/低8位/全部清零 很明显, 这个只需要操作BRR寄存器即可: 对高8位清零:GPIOA- BRR = 0xFF00 对低8位清零:GPIOA- BRR = 0x00FF 全部清零: GPIOA- BRR = 0xFFFF 或 GPIOA- ODR = 0x0000 当然了, 使用下面2,3的两个宏也可以完全该清零操作~ stm
[单片机]
<font color='red'>STM32</font>中单独设置GPIO端口高8位/低8位的方法
简述stm32的usart的功能特点
关于STM32的启动流程,网上有的资料在讨论几种boot模式,有的在回答启动文件的内容,在查阅了很多资料后,本文给出一个比较全面的总结和回答。 1. 根据boot引脚决定三种启动模式 复位后,在 SYSCLK 的第四个上升沿锁存 BOOT 引脚的值。BOOT0 为专用引脚,而 BOOT1 则与 GPIO 引脚共用。一旦完成对 BOOT1 的采样,相应 GPIO 引脚即进入空闲状态,可用于其它用途。BOOT0与BOOT1引脚的不同值指向了三种启动方式: 从主Flash启动。主Flash指的是STM32的内置Flash。选择该启动模式后,内置Flash的起始地址将被重映射到0x00000000地址,代码将在该处开始执行。一般我
[单片机]
简述<font color='red'>stm32</font>的usart的功能特点
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved