您的位置:首页 > 其它

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

2017-04-03 17:02 302 查看

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

1、初始化过程叙述

从main()函数开始

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

   由于这些代码都是采用库代码,所以主要分析每个代码具体做了什么工作。有些常用、类似的代码这里不列出了。
//*FunctionName:Set_System
//*Description:ConfiguresMainsystemclocks&power.*FunctionName:Set_SYSTEM
*Description:ConfiguresMainsystemclocks&power.
*Input:None.
*Return:None.
*******************************************************************************/
voidSet_SYSTEM()
{
/*Setupthemicrocontrollersystem.InitializetheEmbeddedFlashInterface,
initializethePLLandupdatetheSystemFrequencyvariable.*/
/*!<RCCsystemreset(fordebugpurpose)*/
/*!<SetHSIONbit*/
RCC->CR|=(uint32_t)0x00000001;
/*!<ResetSW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0]andMCO[2:0]bits*/
RCC->CFGR&=(uint32_t)0xF8FF0000;
/*!<ResetHSEON,CSSONandPLLONbits*/
RCC->CR&=(uint32_t)0xFEF6FFFF;
/*!<ResetHSEBYPbit*/
RCC->CR&=(uint32_t)0xFFFBFFFF;
/*!<ResetPLLSRC,PLLXTPRE,PLLMUL[3:0]andUSBPREbits*/
RCC->CFGR&=(uint32_t)0xFF80FFFF;
/*!<Disableallinterrupts*/
RCC->CIR=0x00000000;
/*!<ConfiguretheSystemclockfrequency,HCLK,PCLK2andPCLK1prescalers*/
/*!<ConfiguretheFlashLatencycyclesandenableprefetchbuffer*/
SetSysClock();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
}
   先将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)的工作过程;

//*FunctionName:USB_Interrupts_Config.
//*Description:ConfigurestheUSBinterrupts.
voidUSB_Interrupts_Config(void)
{
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
   设置向量表位置在FLASH起始位置:NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x00);  设置优先级分组,1位用于抢占优先级,其余用于子优先级:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  接下来配置、使能了三个中断,包括USB低优先级中断、USB唤醒中断、按键控制中断。

3)Set_USBClock()的工作过程;

*FunctionName:Set_USBClock
*Description:ConfiguresUSBClockinput(48MHz).
*Input:None.
*Output:None.
*Return:None.
*******************************************************************************/
voidSet_USBClock(void)
{
/*SelectUSBCLKsource*/
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
/*EnableUSBclock*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB,ENABLE);
}
  设置并使能USB时钟,从RCC输出可以看到,USB时钟是48MHz。

4)USB_Init()的工作过程;

//*FunctionName:USB_Init
//*Description:USBsysteminitialization
voidUSB_Init(void)
{
pInformation=&Device_Info;
pInformation->ControlState=2;
pProperty=&Device_Property;                  //设备本身支持的属性和方法
pUser_Standard_Requests=&User_Standard_Requests;  //主机请求的实现方法
/*Initializedevicesonebyone*/
pProperty->Init();                          //回调设备的初始化例程
}
//*FunctionName:Joystick_init.
//*Description:Joystick_initinitroutine.
voidJoystick_init(void)
{
/*UpdatetheserialnumberstringdescriptorwiththedatafromtheuniqueID*/
Get_SerialNum();
pInformation->Current_Configuration=0;
/*Connectthedevice*/
PowerOn();
/*USBinterruptsinitialization*/
_SetISTR(0);/*clearpendinginterrupts*/
wInterrupt_Mask=IMR_MSK;
_SetCNTR(wInterrupt_Mask);/*setinterruptsmask*/
bDeviceState=UNCONNECTED;
}
  这里主要初始化了三个全局结构体指针:      pInformation表明当前连续的状态和信息;      pProerity表明设备支持的方法;     pUser_Standard_Requests主机请求实现的函数指针数组。  最后调用pProperty->Init(),实质就是调用Joystick_init(void);  这个函数首先获取设备版本号,并转换为Unicode存入版本号字符串——Get_SerialNum();  设备当前配置为0;  然后调用PowerOn(),将D+上拉,此时USB设备就能被集线器检测到了,因此进入下一流程。

2、进入设备检测状态

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

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

2)复位中断的处理

//*FunctionName:USB_LP_CAN1_RX0_IRQHandler
//*Description:ThisfunctionhandlesUSBLowPriorityorCANRX0interrupts
requests.
voidUSB_LP_CAN1_RX0_IRQHandler(void)
{
USB_Istr();
}
//*FunctionName:USB_Istr
//*Description:STReventsinterruptserviceroutine
voidUSB_Istr(void)
{
wIstr=_GetISTR();
.........
if(wIstr&ISTR_RESET&wInterrupt_Mask)     //对中断位进行判断
{
_SetISTR((uint16_t)CLR_RESET);            //先判断复位中断位
Device_Property.Reset();                //进入设备定义的复位过程。
//实际上是调用JoyStick_Reset()函数进行处理
.........
}
.........
}/*USB_Istr*/

   当复位中断允许、且总线被集线器复位的时候,固件程序进入USB_LP中断。中断程序直接调用USB_Istr(void)程序。3)JoyStick_Reset()函数的处理
//*FunctionName:Joystick_Reset.
//*Description:JoystickMouseresetroutine.
voidJoystick_Reset(void)
{
/*SetJoystick_DEVICEasnotconfigured*/
pInformation->Current_Configuration=0;      //当前配置为0
/*thedefaultInterface*/
pInformation->Current_Interface=0;         //当前接口为0
/*CurrentFeatureinitialization*/
pInformation->Current_Feature=Joystick_ConfigDescriptor[7];   //需要总线供电
SetBTABLE(BTABLE_ADDRESS);                //设置包缓冲区地址
/*InitializeEndpoint0*/ 
              
SetEPType(ENDP0,EP_CONTROL);
//端点0为控制端点
SetEPTxStatus(ENDP0,EP_TX_STALL);//端点状态为发送无效,即主机IN令牌包来的时候,会送一个STALL
//
设置端点0描述符表,包括接收缓冲区地址、最大允许接收的字节数、发送缓冲区地址三个变量
SetEPRxAddr(ENDP0,ENDP0_RXADDR);
SetEPTxAddr(ENDP0,ENDP0_TXADDR);           //这是发送缓冲区地址
//清除EP_KIND的STATUS_OUT位,如果该位被设置,在控制模式下只对0字节数据包响应。其它的都返回STALL。主要用于控制传输的状态过程。
Clear_Status_Out(ENDP0);
SetEPRxCount(ENDP0,Device_Property.MaxPacketSize);   //接收缓冲区支持64个字节
SetEPRxValid(ENDP0);
/*InitializeEndpoint1*/
SetEPType(ENDP1,EP_INTERRUPT);//端点1为中断端点
SetEPTxAddr(ENDP1,ENDP1_TXADDR);//设置发送缓冲区地址
SetEPTxCount(ENDP1,4);                   //每次发送四个字节
SetEPRxStatus(ENDP1,EP_RX_DIS);            //接收禁止,只发送Mouse信息,不从主机接收
SetEPTxStatus(ENDP1,EP_TX_NAK);//现在发送端点还不允许发送数据
/*Setthisdevicetoresponseondefaultaddress*/
SetDeviceAddress(0);//连接状态改为已经连接,默认地址状态
bDeviceState=ATTACHED;//地址默认为0
}
  复位中断执行完后,开发板的USB接口能够以默认的地址对主机来的数据包进行响应了。这个阶段的分析结束,下面正式分析代码实现的枚举过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  usb joystickmouse 鼠标