USB的“JoyStickMouse”例程分析——学习笔记(3)__初始化过程

来源:互联网 发布:如何她推做淘宝客 编辑:程序博客网 时间:2024/05/17 22:52

三、USB的“JoyStickMouse”工作过程详细分析(1)

1、初始化过程叙述

从main()函数开始

1)Set_System(void)的工作过程;

    由于这些代码都是采用库代码,所以主要分析每个代码具体做了什么工作。有些常用、类似的代码这里不列出了。
  1. //* Function Name : Set_System
  2. //* Description : Configures Main system clocks & power.* Function Name : Set_SYSTEM
  3. * Description : Configures Main system clocks & power.
  4. * Input : None.
  5. * Return : None.
  6. *******************************************************************************/
  7. void Set_SYSTEM()
  8. {
  9. /* Setup the microcontroller system. Initialize the Embedded Flash Interface,
  10. initialize the PLL and update the SystemFrequency variable. */
  11. /*!< RCC system reset(for debug purpose) */
  12. /*!< Set HSION bit */
  13. RCC->CR |= (uint32_t)0x00000001;
  14. /*!< Reset SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], ADCPRE[1:0] and MCO[2:0] bits */
  15. RCC->CFGR &= (uint32_t)0xF8FF0000;
  16. /*!< Reset HSEON, CSSON and PLLON bits */
  17. RCC->CR &= (uint32_t)0xFEF6FFFF;
  18. /*!< Reset HSEBYP bit */
  19. RCC->CR &= (uint32_t)0xFFFBFFFF;
  20. /*!< Reset PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE bits */
  21. RCC->CFGR &= (uint32_t)0xFF80FFFF;
  22. /*!< Disable all interrupts */
  23. RCC->CIR = 0x00000000;
  24. /*!< Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  25. /*!< Configure the Flash Latency cycles and enable prefetch buffer */
  26. SetSysClock();
  27. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  28. }
    先将RCC部分复位,系统使用内部振荡HSI,8MHz-RCC_DeInit();
    使能HSE——RCC_HESConfig(RCC_HSE_ON);
    设置HCLK = SYSCLK———RCC_HCLKConfig(RCC_SYSCLK_Div1);
    设置PCLK2,PCLK1——RCC_PCLK2Config(RCC_HCLK_Div1);
    设置PLL,使能PLL——PLL采用HSE,输出=HSEx9;
    RCC_PLLConfig(RCC_PLL_Source_HSE_Div1, RCC_PLLMu1_9);
    系统时钟采用PLL输出——RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    使能PWR控制,目的是为了控制CPU的低功耗模式;
    将所有输入口初始化为模拟输入——GPIO_AINConfig();
    使能USB上拉控制GPIO端口的时钟,这个端口设置为低电平时,USB外设会被集线器检测到,并报告给主机,这也是设备枚举的开始;
    将这个端口的模式设置为开漏输出;
    初始化上下左右四个按键为上下拉输入;
    配置GPIOG8为EXTI8中断输入引脚,这个是在外部按键输入引起中断。
    配置EXTI18中断。这个是发生在USB唤醒事件时用。

2)USB_Interrupt_Config(void)的工作过程;

  1. //* Function Name : USB_Interrupts_Config.
  2. //* Description : Configures the USB interrupts.
  3. void USB_Interrupts_Config(void)
  4. {
  5. NVIC_InitTypeDef NVIC_InitStructure;
  6. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  7. NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
  8. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  9. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  10. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  11. NVIC_Init(&NVIC_InitStructure);
  12. }
    设置向量表位置在FLASH起始位置:NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x00);
    设置优先级分组,1位用于抢占优先级,其余用于子优先级:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    接下来配置、使能了三个中断,包括USB低优先级中断、USB唤醒中断、按键控制中断。

3)Set_USBClock()的工作过程;

  1. * Function Name : Set_USBClock
  2. * Description : Configures USB Clock input (48MHz).
  3. * Input : None.
  4. * Output : None.
  5. * Return : None.
  6. *******************************************************************************/
  7. void Set_USBClock(void)
  8. {
  9. /* Select USBCLK source */
  10. RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
  11. /* Enable USB clock */
  12. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
  13. }
    设置并使能USB时钟,从RCC输出可以看到,USB时钟是48MHz。

4)USB_Init()的工作过程;

  1. //* Function Name : USB_Init
  2. //* Description : USB system initialization
  3. void USB_Init(void)
  4. {
  5. pInformation = &Device_Info;
  6. pInformation->ControlState = 2;
  7. pProperty = &Device_Property;                        //设备本身支持的属性和方法
  8. pUser_Standard_Requests = &User_Standard_Requests;   //主机请求的实现方法
  9. /* Initialize devices one by one */
  10. pProperty->Init();                                   //回调设备的初始化例程
  11. }
    1. //* Function Name : Joystick_init.
    2. //* Description : Joystick_init init routine.
    3. void Joystick_init(void)
    4. {
    5. /* Update the serial number string descriptor with the data from the unique ID*/
    6. Get_SerialNum();
    7. pInformation->Current_Configuration = 0;
    8. /* Connect the device */
    9. PowerOn();
    10. /* USB interrupts initialization */
    11. _SetISTR(0); /* clear pending interrupts */
    12. wInterrupt_Mask = IMR_MSK;
    13. _SetCNTR(wInterrupt_Mask); /* set interrupts mask */
    14. bDeviceState = UNCONNECTED;
    15. }
    这里主要初始化了三个全局结构体指针:
        pInformation表明当前连续的状态和信息;
        pProerity表明设备支持的方法;
        pUser_Standard_Requests主机请求实现的函数指针数组。
    最后调用pProperty->Init(),实质就是调用Joystick_init(void);
    这个函数首先获取设备版本号,并转换为Unicode存入版本号字符串——Get_SerialNum();
    设备当前配置为0;
    然后调用PowerOn(),将D+上拉,此时USB设备就能被集线器检测到了,因此进入下一流程。

2、进入设备检测状态

1)在PowerOn()中执行的情况

  1. * Function Name : PowerOn
  2. * Description :
  3. * Input : None.
  4. * Output : None.
  5. * Return : USB_SUCCESS.
  6. *******************************************************************************/
  7. RESULT PowerOn(void)
  8. {
  9. uint16_t wRegVal;
  10. /*** cable plugged-in ? ***/
  11. USB_Cable_Config(ENABLE);
  12. /*** CNTR_PWDN = 0 ***/
  13. wRegVal = CNTR_FRES;
  14. _SetCNTR(wRegVal);
  15. /*** CNTR_FRES = 0 ***/
  16. wInterrupt_Mask = 0;
  17. _SetCNTR(wInterrupt_Mask);
  18. /*** Clear pending interrupts ***/
  19. _SetISTR(0);
  20. /*** Set interrupt mask ***/
  21. wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM;
  22. _SetCNTR(wInterrupt_Mask);
  23. return USB_SUCCESS;
  24. }
  在上面的USB_init()中调用PowerOn(),而它先调用  USB_Cable_Config(ENABLE),这个函数实质上
将USB连接控制线设置为低电平,然后主机就可以检测到设备了。
    当集线器报告设备连接状态,并收到指令后,会复位USB总线,这需要一定的时间(这段时间内设备应该
准备好处理复位指令)。但是现在设备初始化程序将继续向下进行,因为它还没有使能复位中断。
  1. wRegVal = CNTR_FRES;
  2. _SetCNTR(wRegVal); //这句话实际上使能了USB模块的电源,因为上电复位时,CNTR寄存器的中断电控制位:PDWN1,模块是断电的。
    这句话虽然将强制复位USB模块,但由于复位中断允许位没有使能,不会引起复位中断,而间接上又使
PDWN = 0,模块开始工作。
    _SetCNTR是一个宏,将wRegVal赋值给CNTR寄存器,此时所有的中断被屏蔽。
    再接下来两句指令又将清除复位信号:
  1. wInterrupt_Mask = 0;
  2. _SetCNTR(wInterrupt_Mask);
    然后清除所有的状态位:_SetISTR(0);
    接下来是很关键的两句:
  1. wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM;
  2. _SetCNTR(wInterrupt_Mask);
  后面的一个语句执行后,复位中断已经被允许,而此时集线器多半已经开始复位端口了。或者说稍微有限
延迟,设备固件还能继续初始化一些部件,但已经不会影响整个工作流程了。
    所以,接下来直接进入复位中断。

2)复位中断的处理

  1. //* Function Name : USB_LP_CAN1_RX0_IRQHandler
  2. //* Description : This function handles USB Low Priority or CAN RX0 interrupts requests.
  3. void USB_LP_CAN1_RX0_IRQHandler(void)
  4. {
  5. USB_Istr();
  6. }
    1. //* Function Name : USB_Istr
    2. //* Description : STR events interrupt service routine
    3. void USB_Istr(void)
    4. {
    5. wIstr = _GetISTR();
    6. .........
    7. if (wIstr & ISTR_RESET & wInterrupt_Mask)       //对中断位进行判断
    8. {
    9. _SetISTR((uint16_t)CLR_RESET);                //先判断复位中断位
    10. Device_Property.Reset();                      //进入设备定义的复位过程
    11. //实际上是调用JoyStick_Reset()函数进行处理
    12. .........
    13. }
    14. .........
    15. } /* USB_Istr */

    当复位中断允许、且总线被集线器复位的时候,固件程序进入USB_LP中断。中断程序直接调用USB_Istr(void)程序。
3)JoyStick_Reset()函数的处理

  1. //* Function Name : Joystick_Reset.
  2. //* Description : Joystick Mouse reset routine.
  3. void Joystick_Reset(void)
  4. {
  5. /* Set Joystick_DEVICE as not configured */
  6. pInformation->Current_Configuration = 0;        //当前配置为0
  7. /*the default Interface*/
  8. pInformation->Current_Interface = 0;            //当前接口为0
  9. /* Current Feature initialization */
  10. pInformation->Current_Feature = Joystick_ConfigDescriptor[7];    //需要总线供电
  11. SetBTABLE(BTABLE_ADDRESS);                      //设置包缓冲区地址
  12. /* Initialize Endpoint 0 */                    
  13. SetEPType(ENDP0, EP_CONTROL); //端点0为控制端点
  14. SetEPTxStatus(ENDP0, EP_TX_STALL); //端点状态为发送无效,即主机IN令牌包来的时候,会送一个STALL
  15. //设置端点0描述符表,包括接收缓冲区地址、最大允许接收的字节数、发送缓冲区地址三个变量
  16. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
  17. SetEPTxAddr(ENDP0, ENDP0_TXADDR);               //这是发送缓冲区地址
  18. //清除EP_KIND的STATUS_OUT位,如果该位被设置,在控制模式下只对0字节数据包响应。其它的都返回STALL。主要用于控制传输的状态过程。
  19. Clear_Status_Out(ENDP0);
  20. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);    //接收缓冲区支持64个字节
  21. SetEPRxValid(ENDP0);
  22. /* Initialize Endpoint 1 */
  23. SetEPType(ENDP1, EP_INTERRUPT); //端点1为中断端点
  24. SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送缓冲区地址
  25. SetEPTxCount(ENDP1, 4);                          //每次发送四个字节
  26. SetEPRxStatus(ENDP1, EP_RX_DIS);                 //接收禁止,只发送Mouse信息,不从主机接收
  27. SetEPTxStatus(ENDP1, EP_TX_NAK); //现在发送端点还不允许发送数据
  28. /* Set this device to response on default address */
  29. SetDeviceAddress(0); //连接状态改为已经连接,默认地址状态
  30. bDeviceState = ATTACHED; //地址默认为0
  31. }
    复位中断执行完后,开发板的USB接口能够以默认的地址对主机来的数据包进行响应了。这个阶段的分析结束,下面正式分析代码实现的枚举过程。

0 0
原创粉丝点击