STM32的USB例程JoyStickMouse代码分析(全)--转载网友"追风"帖子

2019-07-24 13:49发布

  本帖最后由 追风 于 2010-12-4 17:30 编辑

一、USB的“JoyStickMouse”例程结构分析 1、例程的结构 1)底层结构 包括5个文件:usb_core.cUSB总线数据处理的核心文件),usb_init.cusb_int.c(用于端点数据输入输入中断处理),usb_mem.c(用于缓冲区操作),usb_regs.c(用于寄存器操作)。它们都包含了头文件“usb_lib.h”。在这个头文件中,又有以下定义: #include "usb_type.h" #include "usb_regs.h" #include "usb_def.h" #include "usb_core.h" #include "usb_init.h" #include "usb_mem.h" #include "usb_int.h" usb_lib.h中又包含了七个头文件,其中usb_type.h中主要是用typedefstm32支持的数据类型取一些新的名称。usb_def.h中主要是定义一些相关的数据类型。 还有一个未包含在usb_lib.h中的头文件,usb_conf.h用于USB设备的配置 2)上层结构 上层结构总共5个文件:hw_config.c(用于USB硬件配置)、usb_pwr.c(用于USB连接、断开操作)、usb_istr.c(直接处理USB中断)、usb_prop.c(用于上层协议处理,比如HID协议,大容量存储设备协议)、usb_desc.c(具体设备的相关描述符定义和处理)。 可见,STUSB操作库结构十分清晰明了,我先不准备直接阅读源代码。而是先利用MDK的软件模拟器仿真执行,先了解一下设备初始化的流程。 2、设备初始化所做的工作 1Set_System(void) 这个是main函数中首先调用的函数,它位于hw_config.c文件中。它的主要功能是初始化时钟系统、使能相关的外围设备电源 配置了JoyStickMouse所用到的5个按键,并且配置了两个EXTI中断,一个是用于把USB从挂起模式唤醒,还有一个用途未知。 2USB_Interrupts_Config(); 这个是main函数中调用的第二个函数,它也位于hw_config.c文件中。主要功能是配置USB所用到的中断 跟踪到代码中,主要设配置了USB低优先级中断和唤醒中断,又有一个EXTI中断功能未知。 3Set_USBClock() 这个是main函数中调用的第三个函数,它也位于hw_config.c文件中。它的功能是配置和使能USB时钟 4USB_Init(void) 这个是main函数中调用的第四个函数,它也位于usb_init.c文件中。它初始化了三个全局指针,指向DEVICE_INFOUSER_STANDARD_REQUESTSDEVICE_PROP结构体。 后面两个是函数指针结构体,里面都是USB请求实现、功能实现的函数指针。 void USB_Init(void) {
pInformation = &Device_Info;

pInformation->ControlState = 2;

pProperty = &Device_Property;


pUser_Standard_Requests = &User_Standard_Requests;

/* Initialize devices one by one */


pProperty->Init();
} 这三个结构体都是与具体设备枚举和功能实现相关的,定义在usb_prop.cusb_desc.c文件中。 DEVICE_PROP Device_Property =
{

Joystick_init,
Joystick_Reset,

Joystick_Status_In,

Joystick_Status_Out,

Joystick_Data_Setup,

Joystick_NoData_Setup,

Joystick_Get_Interface_Setting,

Joystick_GetDeviceDescriptor,

Joystick_GetConfigDescriptor,

Joystick_GetStringDescriptor,

0,

0x40 /*MAX PACKET SIZE*/

};
USER_STANDARD_REQUESTS User_Standard_Requests =
{

Joystick_GetConfiguration,

Joystick_SetConfiguration,

Joystick_GetInterface,

Joystick_SetInterface,

Joystick_GetStatus,

Joystick_ClearFeature,

Joystick_SetEndPointFeature,

Joystick_SetDeviceFeature,

Joystick_SetDeviceAddress

};
Usb_init()函数调用pProperty->Init()实质上就是Joystick_init)完成设备的初始化。 上层程序调用下次函数是常规性的操作。而下层函数(usb_init相对于usb_prop是输入底层操作文件)调用上层文件函数我们称之为回调。 回调函数的意义在于同一种操作模式、提供不同的回调函数则可以实现不同的功能。Windows中处理消息,好像也用到了这种模式。 回调函数的实现方法是函数指针数组。这是指针的高级应用。 这是函数的代码: void Joystick_init(void) {/* Update the serial number string descriptor with the data from the unique
ID*/


Get_SerialNum();
//
获取设备序列号,转变为unicode字符串
pInformation->Current_Configuration = 0;

/* Connect the device */

PowerOn();
//连接USB设备,实质是能让主机检测到了。
/* USB interrupts initialization */

_SetISTR(0);
/* clear pending interrupts */

wInterrupt_Mask = IMR_MSK;

_SetCNTR(wInterrupt_Mask); /* set interrupts mask */

bDeviceState = UNCONNECTED;
} 实质上代码执行到这里开发板已经可以响应主机发来的数据了。但我还是先把main()函数的代码看完吧。 5SysTick_Config(); 这个函数调用主要是为程序中用到的精确延时作配置。 3、进入主循环 进入主循环的工作就两个: Joystick_Send(JoyState()) JoyState()用来获取按键的状态。 Joystick_Send(JoyState())用来把按键状态发到主机。当然这里真正的发送工作并不是由该代码完成的。它的工作只是将数据写入IN端点缓冲区,主机的IN令牌包来的时候,SIE负责把它返回给主机。 主要代码如下:
UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1), 4);
//
从用户复制四个字节到端点1缓冲区,控制端点的输入缓冲区。
SetEPTxValid(ENDP1); /* enable endpoint for transmission */ 4、中断处理过程大致理解 1usb_istr()函数中的中断处理简单分析 有用的代码大概以下几段,首先是处理复位的代码,调用设备结构中的复位处理函数。
wIstr = _GetISTR();

if (wIstr & ISTR_RESET & wInterrupt_Mask)

{

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

Device_Property.Reset();
}
处理唤醒的代码:
if (wIstr & ISTR_WKUP & wInterrupt_Mask)

{

_SetISTR((u16)CLR_WKUP);

Resume(RESUME_EXTERNAL);
}
处理总线挂起的代码:
if (wIstr & ISTR_SUSP & wInterrupt_Mask)

{

if (fSuspendEnabled) /* check if SUSPEND is possible */

{

Suspend();
}

else

{

/* if not possible then resume after xx ms */


Resume(RESUME_LATER);

}

/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */

_SetISTR((u16)CLR_SUSP);

}
处理端点传输完成的代码,这段是最重要的,它调用底层usb_int.c()文件中的CTR_LP()函数来处理端点数据传输完成中断。
if (wIstr & ISTR_CTR & wInterrupt_Mask)

{

CTR_LP(); /* servicing of the endpoint correct transfer interrupt */
}
二、STM32处理器的USB接口 1、接口模块的内部结构 在书上有一个很好的USB内部接口模块内部结构图,比较好的解释了各个模块之间的关系,我这里试着用我自己的理解阐述一下吧。 首先在总线端(与D+D-相连的那一端),通过模拟收发器与SIE连接SIE使用48MHz的专用时钟。 SIE相关的的有三大块:CPU内部控制、中断和端点控制寄存器挂起定时器(这个好像是USB协议的要求,总线在一定时间内没有活动,SIE模块能够进入SUSPEND状态以节约电能),还有包缓冲区接口模块 说到包缓冲区接口模块,这个对应的含义是,USB设备应该提供USB包缓冲区。这块缓冲区同时受到SIECPU核心的控制,用于CPUSIE共享达到数据传输的目的。 所以CPU通过APB1总线接口访问,SIE通过包缓冲区接口模块访问,中间通过Arbiter来协调访问 当然我们关注的中心点是控制、中断和端点控制寄存器。我们通过这些寄存器来获取总线传输的状态,控制各个端点的状态,并可以产生中断来让CPU处理当前的USB事件。 CPU可以通过APB1总线接口来访问这些寄存器。它们使用的都是PCLK1时钟。 2USB模块的寄存器认识 1
控制寄存器CNTR 传输完成中断允许位。CTRM1有效,如果SIE置位传输完成标志,则相应的数据传输完成中断发生。 15 包缓冲区溢出中断允许位 错误中断允许位 唤醒中断允许位WKUPM1有效,如果唤醒请求标志位置位,则产生唤醒中断。 挂起中断允许位SUSPM1有效,当总线挂起标志置位时,发生挂起中断。 复位中断允许位RESETM1有效,软件强制复位和总线复位信号,都能触发复位中断。 帧首中断允许位 期望帧首中断允许位。ESOFM它的含义是没有收到帧首信号,允许发生中断。 8 向主机发送的唤醒请求RESUME1有效,主机收到该信号,将唤醒设备这个由软件置位。 4 强制挂起控制FSUSP1有效。与由于总线无活动引起挂起的效果相同。 低功耗模式。前提是先进入挂起状态。由软件设置,一般又硬件复位(被唤醒后自动清零)。 断电模式控制位。PDWN此位为1时,USB模块关闭。 强制复位控制FRES与总线上的复位信号产生相同的效果。也能产生复位中断. 0位。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。