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:ThisfunctionhandlesUSBLowPriorityorCANRX0interruptsrequests.
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”例程分析——学习笔记(4)__枚举过程
- USB的“JoyStickMouse”例程分析——学习笔记(2)__例程结构分析+芯片的接口
- USB的“JoyStickMouse”例程分析——学习笔记(1)__目录
- USB上电过程 协议分析 初始化
- kernel 3.0.31 usb_init 子系统初始化过程分析
- USB上电过程 协议分析 初始化
- Linux内核初始化高端内存的过程(代码分析)
- EZ-USB FX2(CY7C68013)加电复位过程分析
- Red5源代码分析 - 关键类及其初始化过程
- LPC2294的uclinux启动过程分析--串口初始化过程
- Linux内核初始化过程的源码分析疑点记录+好书推荐(附下载)
- Nginx源码分析-启动初始化过程(一)
- Nginx源码分析-启动初始化过程(二)
- Arm linux 内核移植及系统初始化过程分析
- Red5源代码分析 – 关键类及其初始化过程
- USB协议深入分析_初始化
- Red5源代码分析 - 关键类及其初始化过程
- hostapd的radius/eap server代码分析(3)-初始化及一次认证过程
- 分析内核初始化时根内存盘的加载过程
- Red5源代码分析 - 关键类及其初始化过程