LPC1788—USB学习 第二章;让程序进入中断 第一篇;寄存器配置 上一次就说了让USB和PC机连接,但是USB的处理工作大部分都是在中断里完成。想要让LPC1788USB进入中断还有很多寄存器需要配置。这次我们继续来讲解寄存器那点儿事儿。现在我们不从main函数开始,从初始化函数USB_Init开始。 /********************************************************************** 函数功能;初始化USB函数 函 数 名;USB_Init 函数参数;无 函数返回;无 ***********************************************************************/ void USB_Init (void) { PINSEL_ConfigPin ( 0, 31, 1); //P0.31管脚设置为USB_D2+的功能 PINSEL_ConfigPin ( 0, 14, 3); //P0.14管脚设置为USB_CONNECT2功能 CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCUSB, ENABLE); //配置USB时钟/电源 LPC_USB->USBClkCtrl = 0x1A; /*USB时钟控制寄存器, 它控制了AHB、端口选 择寄存器、设备时钟, 这些时钟的使能和失能。*/ while ((LPC_USB->USBClkSt & 0x1A) != 0x1A); /*USBClkSt 寄存器是 时钟状态寄存器,检查 是否设置为0x1A。*/ LPC_USB->StCtrl = 0x3; //端口选择寄存器我们用的USB2所以是就是0x3 NVIC_EnableIRQ(USB_IRQn); //使能USB中断 USB_Reset(); //USB复位 } 现在的初始化函数就多了使能USB中断和USB复位,中断的一些寄存器都是在复位函数里配置的。在USB Device连接到电脑时,电脑会发命令让USB Device再次复位,让设备重新配置。这个目的是为了设备接收电脑发来的0地址数据,因为USB Device的地址是由电脑分配的,所以在没有分配地址的时候电脑会首先让USB Device复位以便接收来自电脑的0地址数据,这在USB的枚举时所需要的过程。这里要提一点在以后的学习中要注意;电脑发送给USB设备的数据叫OUT帧(输出帧),USB设备发送给电脑的数据叫IN帧(输入帧)。所有的数据方向都是以主机为主来决定方向。以后说OUT帧就知道是电脑到设备的数据,IN帧就知道是设备到电脑的数据。接下来开始分析USB_Reset();函数。 /********************************************************************** 函数功能;复位USB函数 函 数 名;USB_Reset 函数参数;无 函数返回;无 作 者: 旺宝电子科技有限公司 ***********************************************************************/ void USB_Reset (void) { /*EpInd寄存器和MaxPSize寄存器是一个 寄存器组,可以把它们两看成一个数组 EpInd就像数组的一个索引号, MaxPSize就像数组的元素*/ LPC_USB->EpInd = 0; //物理端点0 LPC_USB->MaxPSize = USB_MAX_PACKET0; //物理端点0的大小 LPC_USB->EpInd = 1; //物理端点1 LPC_USB->MaxPSize = USB_MAX_PACKET0; //物理端点1的大小 while ((LPC_USB->DevIntSt & 1<<8) == 0); /*DevIntSt是个设备中断状态寄存器, 在这里是检测有没有端点被使能*/ LPC_USB->EpIntClr = 0xFFFFFFFF; //端点中断寄存器清零 LPC_USB->EpIntEn = 0xFFFFFFFF; //使能所有端点中断 LPC_USB->DevIntClr = 0xFFFFFFFF; //设备中断寄存器清零 LPC_USB->DevIntEn = (1<<3)|(1<<2); /*设置设备中断使能寄存器。设置了, 总线复位usb挂起改变或链接改变时置位、 端点的慢速中断置位。*/ USB_SetAddress(0); //设置USB地址为0 #if PRINTF _DBG_("复位\r\n"); #endif } 从第一个EpInd寄存器开始介绍,在介绍之前我们先来看下1788的USB有哪些端点。 数据手册上说的端点分两类,一类是逻辑端点,一类是物理端点。逻辑端点是成对出现的一个是OUT一个是IN,在程序里就可以说,那个端点的输入和输出,但是在物理上是一个输入端点和一个输出端点。就像谈恋爱是一对,要一个男的和一个女的才叫谈恋爱。要是两个男的那就叫一对屌丝。从手册上看到端点很多有16个逻辑端点32个物理端点,端点的类型有中断传输、批量传输、同步传输和控制传输,唯有控制传输只有一个,而且这个控制传输端点是0这就是我们以后要讲到的枚举过程用到的端点0。电脑对设备复位后和设备进行枚举数据收发都是用的端点0,所以端点0很重要,而且多它一个没用少它一个不行。以后我们大部分的工作都是围绕着端点0开展。 程序里的EpInd 寄存器就是USB端点索引寄存器每个物理端点都是用它来找到的,而EpInd 寄存器和MaxPSize寄存器是一个寄存器组。就像程序里的注释你可以把他们理解成为一个数组,相信会点C语言的都知道数组,用索引号来找到后面的数据元素就可以对这个元素赋值。MaxPSize寄存器用来记录端点的最大包长度值。因此在写操作之前,要通过EpInd 寄存器“寻址”该寄存器。如果修改MaxPSize的值,在结束时DevIntSt 中的EP_RLZED 位也就是第8位将置位。while ((LPC_USB->DevIntSt & 1<<8) == 0);语句就是用来判断的。 EpIntClr端点中断寄存器清除 EpIntEn使能所有端点中断 DevIntClr设备中断寄存器清零 DevIntEn设置设备中断使能寄存器。设置了,总线复位usb挂起改变或链接改变时置位、 端点的慢速中断置位。 下面就是设置地址函数,设置地址的命令是D0。 #define CMD_SET_ADDR 0x00D00500 //D0设置地址,05命令。 #define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) //x为要写入的数据,01写。 /********************************************************************** 函数功能;设置USB地址函数 函 数 名;USB_SetAddress 函数参数;adr;要写入的地址 函数返回;无 作 者: 旺宝电子科技有限公司 ***********************************************************************/ void USB_SetAddress(uint32_t adr) { WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE((1<<7) | adr)); //设置地址 #if PRINTF _DBG_("地址"); _DBH(adr); _DBG_("\r\n"); #endif } WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE((1<<7) | adr)); 这条语句里的((1<<7)|adr)是在设置地址命令功能里面的第7位是设置地址使能位,0—6位才是地址位。程序里面的#if PRINTF预编译是调试输出信息所用,可以在USB_core.h文件里的#define PRINTF 1来控制。下面是WrCmdDat函数代码。 /********************************************************************** 函数功能;写命令数据函数 函 数 名;WrCmdDat 函数参数;cmd;命令 val: 数据 函数返回;无 作 者: 旺宝电子科技有限公司 ***********************************************************************/ void WrCmdDat(uint32_t cmd, uint32_t val) { LPC_USB->DevIntClr = 1<<4; /*DevIntClr寄存器是设备中断清除寄存器 写相应的位就会清除DevIntSt设备中断状态 寄存器相应位,应为我们要写命令,所以我 们要把命令代码寄存器为空中断标志位清除*/ LPC_USB->CmdCode = cmd; /*写入命令*/ while ((LPC_USB->DevIntSt & 1<<4) == 0);/*等待命令代码寄存器为空中断标志位置位*/ LPC_USB->DevIntClr = 1<<4; LPC_USB->CmdCode = val; /*写入数据*/ while ((LPC_USB->DevIntSt & 1<<4) == 0); } 第二篇;中断 把前面的工作都搞定了接下来就是中断,如果把中断函数加上能进入中断就说明我们之前的工作没有白费下面就是中断函数。 /********************************************************************** 函数功能;USB中断函数 函 数 名;USB_IRQHandler 函数参数;无 函数返回;无 作 者: 旺宝电子科技有限公司 ***********************************************************************/ void USB_IRQHandler (void) { uint32_t disr; disr = LPC_USB->DevIntSt; /*DevIntSt寄存器是设备中断状态寄 存器,这句就是读设备中断状态*/ #if PRINTF _DBG_("中断号"); _DBH32(disr); //打印中断号,可以在数据手册中查到中断的原因和功能。 _DBG_("\r\n"); #endif } 把程序下载到开发板中运行,在串口调试助手中显示如图(2—1—1)。 图(2—2—1) 可以看到显示的一大堆中断号为0x00000019。把它转换为二进制就是“11001”。现在在看数据手册上对这几位的中断解释。 从中断号中分别是第0位第3位和第4位产生了中断。他们的大概意思是: 第0位,每隔1ms产生一次帧中断。这一位在同步包的传输里。 第3位,这一位是在USB总线复位、USB挂起改变或者连接改变时会置位。 第4位,命令代码寄存器(USBCmdCode)为空(可以写入新的命令)。 在第3位的描述里还说了这一位的复位、挂起改变或连接改变在13.12.6的设置设备状态(命令:0xFE,写1字节)还有什么关系,但是我们现在不用管它。我们先把中断状态寄存器清了看会有什么情况。清中断就只需要把读到的中断状态信息给清中断寄存器。LPC_USB->DevIntClr = disr;程序运行的结果如图(2—2—2) 图(2—2—2) 现在是0x00000019没了,但是出来了一大堆的0x00000005。转换位二进制为“101”。前面的手册说,第2位是端点的慢速中断。如果端点中断在端点中断优先级寄存器USBEpIntPri中相应的位没有置位,则该端点中断与EP_SLOW 位相关。可以这样去理解这句话,就是端点中断优先级寄存器USBEpIntPri中没有把相应的端点位置位,它就会触发EP_SLOW中断。那说明现在端点缓冲区中有数据了。 好了到此第二章的东西讲完了。第三章会将读端点缓冲区的数据,分析数据。结合代码和数据手册可以更好的理解。不足之处还请多多指点。此帖会不断更新,以实现一个USB HID的整个过程。附件有实现本章的代码! 开发环境:集成开发环境µVision4 IDE版本4.60.0.0。 主机系统:Microsoft Windows XP。 开发平台:旺宝悍马1788开发板。
|