原创 USB的“JoyStickMouse”例程分析03

2010-4-17 11:36 3456 7 7 分类: MCU/ 嵌入式

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 


 


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


1、初始化过程叙述


main()函数开始


1Set_System(void)的工作过程


由于这些代码都是采用库代码,所以我主要分析每个代码具体做了什么工作。有些常用、类似的代码这里就不列出来了。


 


先将RCC部分复位,系统使用内部振荡HSI8MHz——RCC_DeInit();


使能HSE——RCC_HSEConfig(RCC_HSE_ON);


设置HCLK = SYSCLK——RCC_HCLKConfig(RCC_SYSCLK_Div1);


设置PCLK2PCLK1——RCC_PCLK2Config(RCC_HCLK_Div1);


 


设置PLL,使能PLL——PLL采用HSE,输出=HSE X 9


RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);


系统时钟采用PLL输出——


RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);


 


使能PWR控制,目的是为了控制CPU的低功耗模式;


将所有输入口初始化为模拟输入——GPIO_AINConfig();


使能USB上拉控制GPIO端口的时钟,这个端口设置为低电平时,USB外设会被集线器检测到,并报告给主机,这也是设备枚举的开始;


将这个端口的模式设置为开漏输出;


 


初始化 上下左右 四个按键为 上下拉输入;


配置GPIOG8EXTI8中断输入引脚,这个是在外部按键输入引起中断。


配置EXTI18中断。这个是发生USB唤醒事件时用。


  EXTI_InitStructure.EXTI_Line = EXTI_Line18; // USB resume from suspend mode


  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;


  EXTI_InitStructure.EXTI_LineCmd = ENABLE;


  EXTI_Init(&EXTI_InitStructure);


2USB_Interrupts_Config(void)的工作过程


设置向量表位置在FLASH起始位置——


NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x00);


设置优先级分组,1位用于抢占组级别。其余用于子优先级—


NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);


接下来配置、使能了三个中断,包括USB低优先级中断、USB唤醒中断(EXTI18)、和EXTI8(按键控制)中断。


它的优先级设置有些问题,明明只有一位用于抢占优先级。它把EXTI8的抢占优先级设为2。结果在调试时发现,它的抢占优先级仍然是0


 


3Set_USBClock()的工作过程


这个代码就两句话:


  RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);


  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);


作用是设置并使能USB时钟,从RCC输出可以看到,USB时钟是48MHz


 


4USB_Init()的工作过程


void USB_Init(void)


{


  pInformation = &Device_Info;


  pInformation->ControlState = 2; 


  pProperty = &Device_Property;  //这个是设备本身支持的属性和方法


  pUser_Standard_Requests = &User_Standard_Requests; //这个是主机请求的实现方法。


  pProperty->Init();  //回调设备的初始化例程。


}


这个主要是初始化了三个全局结构体指针,pInformation表明当前连接的状态和信息,pProperty表明设备支持的方法,pUser_Standard_Requests是主机请求实现的函数指针数组。


Device_Info是一个结构体包括11个成员变量。这里是将它的ControlState设为2,意义现在还不十分明了。


typedef struct _DEVICE_INFO


{


  u8 USBbmRequestType;       /* bmRequestType */


  u8 USBbRequest;            /* bRequest */


  u16_u8 USBwValues;         /* wValue */


  u16_u8 USBwIndexs;         /* wIndex */


  u16_u8 USBwLengths;        /* wLength */


 


  u8 ControlState;           /* of type CONTROL_STATE */


  u8 Current_Feature;


  u8 Current_Configuration;   /* Selected configuration */


  u8 Current_Interface;   /* Selected interface of current configuration */


  u8 Current_AlternateSetting;/* Selected Alternate Setting of current


                                     interface*/


  ENDPOINT_INFO Ctrl_Info;  //端点信息结构体


}DEVICE_INFO;


 


最后调用pProperty->Init(),实质就是调用Joystick_init(void)


在这个函数中,首先获取设备版本,并转换为Unicode存入版本号字符串。


                    ——Get_SerialNum();


设备当前配置置为0然后调用PowerOn(),这个函数实质上将D+上拉,此时USB设备就能被集线器检测到了。因此分析进入下一个流程。


 


 


2、进入设备检测状态


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


USB_init()中调用PowerOn(),而它先调用USB_Cable_Config(ENABLE),这个函数实质上将USB连接控制线设置为低电平,然后设备就可以检测到设备了。


当集线器报告设备连接状态,并收到主机指令后,会复位USB总线,这需要一定的时间(这段时间内设备应该准备好处理复位指令)。但是现在设备初始化程序将继续往下进行,因为它还没有使能复位中断。


 


  wRegVal = CNTR_FRES;


  _SetCNTR(wRegVal);  //这句话实际上使能了USB模块的电源,因为上电复位时,CNTR寄存器的断电控制为PDWN位是1,模块是断电的。


这句话虽然将强制复位USB模块,但由于复位中断允许位没有使能,不会引起复位中断,而间接上由使PDWN=0,模块开始工作。


_SetCNTR是一个宏,将wRegVal赋值给CNTR寄存器,此时所有的中断被屏蔽。


 


再接下来两句指令又将清除复位信号。


然后清除所有的状态位。——_SetISTR(0);


接下来是很关键的两句话:


  wInterrupt_Mask=CNTR_RESETM| CNTR_SUSPM | CNTR_WKUPM;


  _SetCNTR(wInterrupt_Mask);


后面一个语句执行后,复位中断已经被允许,而此时集线器多半已经开始复位端口了。或者说稍微有限延迟,设备固件还能继续初始化一些部件,但已经不会影响整个工作流程了。


所以接下来,分析直接进入复位中断。


2)复位中断的处理。


当复位中断允许、且总线被集线器复位的时候,固件程序进入USB_LP中断。


中断程序直接调用USB_Istr(void)程序。


接下来讲对中断位进行判断:


  if (wIstr & ISTR_RESET & wInterrupt_Mask)


  {


    _SetISTR((u16)CLR_RESET);  //先清除复位中断位


    Device_Property.Reset();  //进入设备定义的复位过程。实际上是调用JoyStick_Reset()函数进行处理。


  }


3JoyStick_Reset()函数的处理。


这里将一句句来分析:


void Joystick_Reset(void)


{


  pInformation->Current_Configuration = 0;  //当前配置为0


  pInformation->Current_Interface = 0;/当前接口为0


 pInformation->Current_Feature = Joystick_ConfigDescriptor[7];


                                                //需要总线供电


  SetBTABLE(BTABLE_ADDRESS); //设置包缓冲区地址。


 


  SetEPType(ENDP0, EP_CONTROL);  //端点0为控制端点


  SetEPTxStatus(ENDP0, EP_TX_STALL);  //端点状态为 发送无效,也就是主机IN令牌包来的时候,回送一个STALL


 


  SetEPRxAddr(ENDP0, ENDP0_RXADDR); //设置端点0描述符表,包括接收缓冲区地址、最大允许接收的字节数、发送缓冲区地址三个量。


  SetEPTxAddr(ENDP0, ENDP0_TXADDR); //这是发送缓冲区地址


  Clear_Status_Out(ENDP0);  //清除EP_KINDSTATUS_OUT位,如果改位被设置,在控制模式下只对0字节数据包相应。其它的都返回STALL。主要用于控制传输的状态过程。


 


  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); //接收缓冲区支持64个字节。


  SetEPRxValid(ENDP0);  //使能端点0的接收,因为很快就要接收SETUP令牌包后面跟着的数据包了。


 


  SetEPType(ENDP1, EP_INTERRUPT);  //端点1为中断端点


  SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送缓冲区地址


  SetEPTxCount(ENDP1, 4);   //每次发送四个字节


  SetEPRxStatus(ENDP1, EP_RX_DIS);  //接收禁止,只发送Mouse信息,而不从主机接收。


  SetEPTxStatus(ENDP1, EP_TX_NAK); //现在发送端点还不允许发送数据。


  bDeviceState = ATTACHED;   //连接状态改为已经连接,默认地址状态。


  SetDeviceAddress(0); //地址默认为0.


}


 


复位中断执行完成后,开发板的USB接口能够以默认地址对主机来的数据包进行响应了。这个阶段的分析到此结束,下一个阶段就是正式分析代码实现的枚举过程了。


 


 


 

文章评论0条评论)

登录后参与讨论
我要评论
0
7
关闭 站长推荐上一条 /2 下一条