在书上有一个很好的USB内部接口模块内部结构图,比较好的解释了各个模块之间的关系,我这里试着用我自己的理解阐述一下吧。 首先在总线端(与D+、D-相连的那一端),通过模拟收发器与SIE连接。SIE使用48MHz的专用时钟。
与SIE相关的的有三大块:CPU内部控制、中断和端点控制寄存器,挂起定时器(这个好像是USB协议的要求,总线在一定时间内没有活动,SIE模块能够进入SUSPEND状态以节约电能),还有包缓冲区接口模块。 说到包缓冲区接口模块,这个对应的含义是,USB设备应该提供USB包缓冲区。这块缓冲区同时受到SIE和CPU核心的控制,用于CPU与SIE共享达到数据传输的目的。
所以CPU通过APB1总线接口访问,SIE通过包缓冲区接口模块访问,中间通过Arbiter来协调访问。
当然我们关注的中心点是控制、中断和端点控制寄存器。我们通过这些寄存器来获取总线传输的状态,控制各个端点的状态,并可以产生中断来让CPU处理当前的USB事件。
CPU可以通过APB1总线接口来访问这些寄存器。它们使用的都是PCLK1时钟。
2、USB模块的寄存器认识 (1)
控制寄存器CNTR 传输完成中断允许位。CTRM,1有效,如果SIE置位传输完成标志,则相应的数据传输完成中断发生。 第15位
复位中
挂起中断允许断允许位。位。RESETSUSPMM。1有
,1有效,效,软件帧首中当总线强制复断允许挂起标位和总位 志置位线复位时,发生信号,都挂起中能触发断。 复位中
断。 强制挂低功耗起控制,模式。前FSUSP。提是先1有效。进入挂与由于起状态。总线无由软件活动引设置,一
断电模
式控制位。
PDWN。此位为1时,USB模块关
期望帧首中断允许位。ESOFM。它的含义是没有收到帧首信号,允许发生中断。
第8位 强制复位控制。FRES。与总线上的复位信号产生相
包缓冲区溢出中断允许位
错误中断允许位
唤醒中断允许位。WKUPM。1有效,如果唤醒请求标志位置位,则产生唤醒中断。
向主机发送的唤醒请求,RESUME。1有效,主机
收到该起挂起信号,将的效果唤醒设相同。 备。这个由软件置位。
第4位
般又硬件复位(被唤醒后自动清零)。
闭。
同的效
果。也能产生复位中断.
第0位。
(2)
中断状态寄存器ISTR
这个寄存器主要是反映USB模块当前的状态的。第15-8为与控制寄存器的中断允许是意义对应的。相应的标志位置位,且中断未屏蔽,则向CPU发出对应的中断。
WKUP请求,总
CTR标
线检测
志,数据SUSPRESET
PMAOVRERR标到主机
传输完请求标请求标
标志 志 唤醒请
成后硬志位。 志位。
求时由
件置1.
硬件置位。
ESOF,
SOF帧
期待帧
首标志
首标志。
DIR传输方向,此位由硬件控制。IN
发生数据传输的端点的地址。
时为0,OUT为1. 第4位。
(3)USB设备地址寄存器
第7位,EF,USB模块允许位。如果EF=0,则USB模块将停止工作。 第6-0位。USB当前使用的地址。复位时为0.
(4)
端点状态和配置寄存器,8个寄存器,支持8个双向端点和16个单向端点。 CTR_RX,正确接收标志位。
第15位。
DTOG_RX,用于检测的数据翻转位。一般由硬件自动设置,软件写1可使其手动翻转。
STAT_RX,占据两位。
00表示该端点不可用,无回应。 01表示响应STALL 10响应NAK
11表示端点有效,可接收数据。
EP_KIND,端点特殊类型。在
EP_TYPE=01时,
SETUP标志。收到EP_TYPE,两位,表示端点类型。 SETUP令牌包时00表示批量端点。 置位。用户收到数01表示控制端点
据后需检查次位。 10表示等时端点。 第11位。 11表示中断端点。
表示设备期望主机
的0字节状态包。
DTOG_TX,用于检STAT_TX,占据两位。
CTR_TX。
测的数据翻转位。00表示该端点不可用,无回应。
正确发送标志。主
一般由硬件自动设01表示响应STALL
机的IN包之后。
置,软件写1可使其10响应NAK
第7位。
手动翻转。 11表示端点有效,可发送数据。 端点地址:EA【3:0】,表明该寄存器对应的端点号码。比如1、2号寄存器都可
以对应端点1(在双缓冲情况下)。 第3-0位。
(5)
端点描述符表相关寄存器
首先有一个描述符表地址寄存器,指明了包缓冲区内端点描述符表的地址。 每一个端点都对应一个描述附表。描述符表也在包缓冲区内。每个端点寄存器对应的描述符表的地址可根据公式计算。
单缓冲、双向的端点描述符表有四项,每项占据两个字节:分别是端点n的发送缓冲区地址、发送字节数、接收缓冲区地址、接收字节数。
了解USB相关寄存器的知识以后,接下来就可以分析“JoyStickMouse”详细的工作过程了。
三、USB的“JoyStickMouse”工作过程详细分析 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;