唯品会的华东仓在哪里:USB的“JoyStickMouse”工作过程详细分析 (一)

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 04:42:55

1、初始化过程叙述

从main()函数开始

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

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

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

使能HSE——RCC_HSEConfig(RCC_HSE_ON);

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

设置PCLK2,PCLK1——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外设会被集线器检测到,并报告给主机,这也是设备枚举的开始;

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

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

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

配置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);

(2)USB_Interrupts_Config(void)的工作过程

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

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x00);

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

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

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

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

(3)Set_USBClock()的工作过程

这个代码就两句话:
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);

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

(4)USB_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()函数进行处理。

}

(3)JoyStick_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_KIND的STATUS_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接口能够以默认地址对主机来的数据包进行响应了。这个阶段的分析到此结束,下一个阶段就是正式分析代码实现的枚举过程了。