查看: 4515|回复: 4

NXP LPC1788悍马开发板USB学习笔记

[复制链接]

该用户从未签到

发表于 2015-5-7 14:36:49 | 显示全部楼层 |阅读模式
分享到:
LPC1788USB学习

第一章;开发板和PC实现连接
开发环境:集成开发环境µVision4 IDE版本4.60.0.0
主机系统:Microsoft Windows XP
开发平台:旺宝悍马1788开发板

一:了解硬件

本开发板是基于NXPLPC1788FBD208基础上研发的一款开发板。LPC1788内部集成ARM Cortex-M3微控制器,NXP 半导体针对各种高级通讯,高质量图像显示等应用场合而设计的一款具有高集成度的SOC。居然是针对各种高级通讯,那么学习USB那就是可以的。
USB电路图如下图;
图片11.png
图(111
如果想要开发USB Device需要把J19J20两个拨码开关的“1”和“2”接通,使USBD2-D2+连接到J16USB Device接口。下面的J17USB主机的,有人就会问现在学USB DeviceUSB Host电路图贴出来干嘛啊!不急等下我们就会说到,现在先不用管它。
二:USB基础知识
当我们每次用到带有USB接口的设备感觉就是两个字“方便”。拿到USB数据线两头一插就OK。现在我们就从把数据线插入电脑的那一瞬间开始说起。那些什么同步啊拓扑结构啊啥的先抛开,嫩得我们费脑子胡思乱想,到后来想了半天不知道干嘛。
有细心的朋友可能都看到图(11)电路图,USB DeviceUSB Host两个电路的不同,在USB Device电路中D2+的线上连接了一个1.5K电阻经过了一个三级管Q13.3V(高电平),而在USB Host电路中D2-D2+上各经过了一个10K的电阻到地(低电平)。我们电脑上的USB接口就是USB Host,内部电路就和上图的USB Host类似。当我们把USB Device插入到电脑时,电脑上的USB接口就会有一个5V电压给我们的USB DeviceUSB Device内部硬件初始化,而内部的硬件就会从USB_CONNECT2输出一个信号给三极管Q1让三极管导通把USB DeviceD2+拉为高电平,这时电脑上USB HostD2+由原来的低电平也变为高电平。这时在电脑上就会检测到发现新硬件,而且是一个无法正常运行的设备,如图121。这设备管理器中的通用串行总线控制器会出现一个未知设备,如图122
图片10.png
图(121
图片9.png
图(122
这个过程当中主机检测到有设备插入就会向设备发送一些数据,而这些数据是设备刚插入主机时主机所要设备描述符的控制传输。现在我们的设备没有任何驱动程序来响应“组织的号召”,所以我们的“中央”就不知道如何来加载主机驱动来管理我们的设备,“中央”就会把它拉入黑名单说,硬件安装出现问题,设备不能正常运行,所以在设备管理器中就冒出了一个未知设备。在未知设备上点右键属性,在详细信息选项卡中会看到VIDPID都是0如图123a。图123b是枚举成功的大容量存储设备。
图片8.png
图(123a
图片7.png
图(123b
三:软件配置
我们来看一下程序在悍马1788是如何实现连接的。工程创建不讲述。
/**********************************************************************
函数功能;主函数
函 数 名;main
函数参数;无
函数返回;无
***********************************************************************/
int main (void)
{
        debug_frmwrk_init();                /*串口调试初始化,
用于打印调试信息
和主机发给开发板
的数据还有开发板
发给主机的数据。*/
        print_menu();                                        //打印字符串。
        USB_Init();                              // 初始化USB
        USB_SetDevCondition(DEV_CON);        // 链接USB                     
        while (1)
        {       
                ;
        }
}
看起来好像很简单,main函数里面关于USB的代码就两行。现在我们就来看下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
}
在初始化函数里面就只有USB_D2+USB_CONNECT2引脚被配置了,而USB_D2-VBUS没有配置。我们来看下LPC1788Data Sheet里对USB Device的管脚描述。
图片6.png
VBUS引脚描述的大概意思是,VBUS是一个输入状态,在没有IOCON寄存器,它的内部是上拉的。反正大概意思就是这样。如果是上拉那么它就可以触发内部的硬件。为了程序的简单我们就不对它进行配置。VBUS可以不管那USB_D2-咋不配置呢?现在来看一下原理图,如图24
图片5.png
图(124
这个管脚是默认为USB_D2-,可以不用配置。LED管脚也不用配置,应为我们没有用到LED灯。
接下来就是USB基本配置用库函数的CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCUSB, ENABLE);来使能时钟和电源,下一行是USBClkCtrl寄存器配置,这个寄存器是USB时钟控制寄存器,它控制了AHB、端口选择寄存器、设备时钟,这些时钟的使能和失能,下面就是USBClkSt寄存器,这个寄存器是时钟状态寄存器,只是用来检测USBClkCtrl寄存器是否成功配置,还有一个是StCtrl寄存器,它是用来选择是用那一个USB端口,我们用的USB2看下数据手册,从寄存器里看设置为3是对的。
图片4.png
实现简单的连接功能,初始化这些就可以搞定。现在分析USB_SetDevCondition(DEV_CON)函数;
/**********************************************************************
函数功能;设置USB设备状态
函 数 名;USB_SetDevCondition
函数参数;conDEV_CON      0x01连接位表示设备的当前连接状态。用于CONNECT输出
DEV_CON_CH  0x02连接发生改变
  DEV_SUS      0x04挂起位表示当前的挂起状态
                          DEV_SUS_CH  0x08挂起位(DEV_SUS)的变化指示,
                                                                  设备进入挂起状态、设备断开连接、                                                                                                  设备在其上行端口上接收到恢复信号,                                                                                                  会发生翻转
                           DEV_RST      0x10总线复位位
函数返回;无
***********************************************************************/
void USB_SetDevCondition(uint32_t con)
{
  WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(con));
}
我们看到USB_SetDevCondition(DEV_CON);函数里面还调用了WrCmdDat(CMD_SET_DEV_STAT, DAT_WR_BYTE(con));函数这个函数是一个写命令数据函数。
/**********************************************************************
函数功能;写命令数据函数
函 数 名;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);        /*清除中断*/
}
在写命令数据函数里DevIntClr 寄存器有很详细的注释,关键要看的就是CmdCode寄存器,下面就是CmdCode寄存器的描述。
图片3.png
/*命令格式
低八位保留。
8--15位是命令段,
0x02代表读、0x01代表写、0x05代表命令。
16--23位是多用途字段,
当命令阶段为0x05命令或0x02读时,该域包含着命令代码,
当命令阶段为0x01写状态时,该域包含着命令写数据。*/
现在来看第一次给CmdCode寄存器赋值,后面的注释是说,/*写入命令*/而我们的命令是这样定义的,如下代码。
#define CMD_SET_DEV_STAT  0x00FE0500        //FE设置设备状态,05命令
按照上面的命令格式分析。815位是命令段值是0x05说明代表的是命令,1623位是就是命令代码了值是0xFE。现在看手册上说FE命令代表的是啥。
图片2.png
图片1.png
再看描述的时候一定要注意,上面写的Command: 0xFE, Data: write 1 byte
意思是,命令0xFE,数据写入一个字节,而写入的一个字节是下一次向CmdCode寄存器执行写数据时就可以操作0xFE命令所提供的功能,注释/*写入数据*/的那一行代码。就是我们要实现的连接功能,现在来看下定义的数据是多少!
#define DAT_WR_BYTE(x)   (0x00000100 | ((x) << 16))        //x为要写入的数据,01写。
用前面说的命令格式分析就知道这条命令是写命令。在main函数中调用的USB_SetDevCondition(DEV_CON);函数的参数DEV_CON的定义是;
#define DEV_CON  0x01        //连接位表示设备的当前连接状态。用于CONNECT输出
这说明写入0xFE命令所提供功能的数据是0x01,对照前面0xFE提供的功能可以知道这就是让CONNECT输出低电平。这样就实现了D2+拉高了。



回复

使用道具 举报

该用户从未签到

发表于 2015-5-22 14:33:36 | 显示全部楼层
LPC1788USB学习
第二章;让程序进入中断

开发环境:集成开发环境µVision4 IDE版本4.60.0.0
主机系统:Microsoft Windows XP
开发平台:旺宝悍马1788开发板

第一篇;寄存器配置
上一次就说了让USBPC机连接,但是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寄存器开始介绍,在介绍之前我们先来看下1788USB有哪些端点。
图片6.png
图片5.png
数据手册上说的端点分两类,一类是逻辑端点,一类是物理端点。逻辑端点是成对出现的一个是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位是设置地址使能位,06位才是地址位。程序里面的#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)。
图片4.png
图(221
可以看到显示的一大堆中断号为0x00000019。把它转换为二进制就是“11001”。现在在看数据手册上对这几位的中断解释。
图片3.png
从中断号中分别是第0位第3位和第4位产生了中断。他们的大概意思是:
0位,每隔1ms产生一次帧中断。这一位在同步包的传输里。
3位,这一位是在USB总线复位、USB挂起改变或者连接改变时会置位。
4位,命令代码寄存器(USBCmdCode)为空(可以写入新的命令)。
在第3位的描述里还说了这一位的复位、挂起改变或连接改变在13.12.6的设置设备状态(命令:0xFE,写1字节)还有什么关系,但是我们现在不用管它。我们先把中断状态寄存器清了看会有什么情况。清中断就只需要把读到的中断状态信息给清中断寄存器。LPC_USB->DevIntClr = disr;程序运行的结果如图(222
图片2.png
图(222
现在是0x00000019没了,但是出来了一大堆的0x00000005。转换位二进制为“101”。前面的手册说,第2位是端点的慢速中断。如果端点中断在端点中断优先级寄存器USBEpIntPri中相应的位没有置位,则该端点中断与EP_SLOW 位相关。可以这样去理解这句话,就是端点中断优先级寄存器USBEpIntPri中没有把相应的端点位置位,它就会触发EP_SLOW中断。那说明现在端点缓冲区中有数据了。
好了到此第二章的东西讲完了。第三章会将读端点缓冲区的数据,分析数据。结合代码和数据手册可以更好的理解。不足之处还请多多指点。此帖会不断更新,以实现一个USB HID的整个过程。

回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2015-6-17 15:07:33 | 显示全部楼层
本帖最后由 一座城 于 2015-6-17 15:09 编辑

LPC1788USB学习
第三章;USB标准请求


开发环境:集成开发环境µVision4 IDE版本4.60.0.0
主机系统:Microsoft Windows XP
开发平台:旺宝悍马1788开发板

第一篇;在中断里复位USB设备
上次触发了EP_SLOW中断,说明端点接收到了数据。再讲端点的数据前,还要说一下在触发EP_SLOW中断前还有个设备中断号0x00000019转换成二进制“11001”,就像我们之前说的,第3位(按照人的逻辑是第4位),这一位是在USB总线复位、USB挂起改变或者连接改变时会置位。以前我们还说,当把设备插入主机时主机会对设备复位。中断号的,第4位,是命令代码寄存器(USBCmdCode)为空(可写入新的命令)。我们就写一个设备状态命令命令号为“FE”命令定义#define CMD_SET_DEV_STAT 0x00FE0500,“05”为命令,然后在读出命令数据命令号也为“FE”命令定义#define DAT_GET_DEV_STAT 0x00FE0200“02”为读,看到底要让我们干嘛,代码如下。
/**********************************************************************
函数功能;USB中断函数
函 数 名;USB_IRQHandler
函数参数;无
函数返回;无
作          者:        旺宝电子科技有限公司
***********************************************************************/
void USB_IRQHandler (void)
{
  uint32_t disr, val;
  uint32_t episr;
  disr = LPC_USB->DevIntSt;      //读设备中断状态
        #if PRINTF
                _DBG_("中断号");
                _DBH32(disr);
                _DBG_("\r\n");
        #endif        
  if (disr & DEV_STAT_INT) {                        /*判断是否有复位、连接发
生改变、挂起和恢复的中断*/

LPC_USB->DevIntClr = DEV_STAT_INT;        //清除复位、挂起、链接中断

WrCmd(CMD_SET_DEV_STAT);                        /*写一个设置设备状态,05
命令,准备获得设备状态*/

    val = RdCmdDat(DAT_GET_DEV_STAT);    //读命令数据,读取设备状态
                #if PRINTF
                        _DBG_("设备状态号");
                        _DBH32(val);
                        _DBG_("\r\n");
                #endif               
    if (val & DEV_RST){                //判断复位
      USB_Reset();
                        #if PRINTF
                        _DBG_("复位\r\n");
                        #endif        
    }
    if (val & DEV_SUS_CH) {            //判断挂起和恢复
      if (val & DEV_SUS) {             //挂起
        USB_Suspend();
                                #if PRINTF
                                _DBG_("挂起\r\n");
                                #endif        
      }
                        else {                     //恢复
        USB_Resume();
                                #if PRINTF
                                _DBG_("恢复\r\n");
                                #endif        
      }
    }
  }
上面的程序有两个函数一个是USB_Suspend();函数挂起,一个是USB_Resume();函数恢复这两个都是空函数不用管它,而复位函数也就是我们以前讲的复位函数也不用分析。现在把程序下载到开发板运行会在串口调试助手里看到以下内容。
*******************************************************************************
Hello NXP Semiconductors
USB2 Decice
         - MCU: LPC17xx
         - Core: ARM CORTEX-M3
         - UART Communication: 115200 bps
This example used to test USB2 Decice
*******************************************************************************
地址
0x00
中断号
0x00000019
设备状态号
0x0000000D
挂起
中断号
0x00000039
设备状态号
0x00000019
地址
0x00
复位
恢复
中断号
0x00000015
现在的中断号是0x00000015转换为二进制“10101”,产生的中断是:                        
1、每隔1ms 产生一次帧中断。
2、端点的慢速中断。
3、命令代码寄存器(USBCmdCode)为空。
和刚才的“11001”中断号少了一个让我们复位的过程,转而多一个端点慢速中断,在前面我们说了,有了端点慢速中断说明,端点里有数据。到底是哪一个端点,端点里又有什么数据!现在就要用到端点中断状态寄存器(USBEpintST)。
图片4.png
第二篇;读端点0数据
在读端点数据时先要判断主机发给端点的这一帧数据是要让设备干嘛的,先要向前面说的,读一下命令数据寄存器看要让设备干嘛。代码就在原来的中断函数里加入。定义的读端点命令数据为#define DAT_SEL_EP(x)  (0x00000200 | ((x) << 16))  //选择第x个端点读。02为读。
if (disr & EP_SLOW_INT) {                                 //端点的慢速中断
episr = LPC_USB->EpIntSt;                                //读端点中断状态
  LPC_USB->EpIntClr = episr;                        //清除读到的端点中断。
        #if PRINTF
                _DBG_("读端点中断");
                _DBH32(episr);
                _DBG_("\r\n");
        #endif
          val = RdCmdDat(DAT_SEL_EP(episr));//episr端点的命令数据。
#if PRINTF
        _DBG_("命令数据");
        _DBH32(val);
        _DBG_("\r\n");
#endif
if(val & EP_SEL_F){                        //如果命令位FE置位表示OUT端点
   if(1 == episr){
        if(val & EP_SEL_STP){                //判断是否为SETUP(控制传输)令牌包。
                USB_EndPoint0(USB_EVT_SETUP);        //让端点0处理SETUP(控制传输)包。        
                                }
       }
   else{

                }
}
else{                                        //如果命令位FE0表示IN端点

}
LPC_USB->DevIntClr = EP_SLOW_INT;        //清除端点慢速中断
}
实验现象如图(321)。
图片3.png
图321
现在我们读到的端点中断时0x00000001,转换二进制“0000 0000 0000 0000 0000 0000 0000 0001”说明是端点0中断。前面说,逻辑端点0是控制传输,产生中断的是逻辑端点里的物理端点0,也就是OUT(输出)端点0。读到的命令数据是0x00000025,现在转换为二进制我们就转换25为“100101”。

图片5.png
图片2.png
从数据手册上的说明,可以看出来端点缓冲区的这帧数据是要干嘛用的。中断的第0位大概意思是说,该位置位,对于OUT 端点,有一个端点读缓冲区是满的。第2位的大概意思是说,该位置位,所选端点接收到的包为SETUP 令牌包。SETUP令牌是通知设备将要开始一次控制传输。

在USB的协议中,规定了有8字节的标准设备请求,用来对设备枚举过程进行控制。这8个字节是一次性接收到的,所以端点的大小不能小于8字节的空间。前面我们设置端点0的大小为16字节,用来接收肯定是够了。接收到的这8个字节在枚举过程中会不断的出现,去控制设备返回给主机的信息也就以后所要说的描述符,这些描述符就会描述一个设备的属性让主机能够加载自身的驱动去驱动枚举好的设备。下面我们就来说一下标准设备请求的结构和作用如表(3—2—2)。


表(3—2—2)USB标准设备请求数据结构
偏移量(字节)
大小(字节)
取值
描述
0
bmRequestType
1
位图
D7:数据传输方向
   0—主机到设备
   1—设备到主机
D5~6:请求类型
   0—标准
   1—类
   2—厂商
   3—保留
D0~4:请求连接者
   0—设备
   1—接口
   2—端点
   3—其他
   4~31—保留
1
bRequest
1
数值
请求代码
2
wValue
2
数值
该域意义由具体请求决定
4
wIndex
2
索引或偏移量
该域意义由具体请求决定
6
wLength
2
字节数
数据过程所需要的数据长度

在表中我们看到有项是“域”的这个东西。在USB里数据是按包来传输数据的,而包的由域组成的。域就是USB传输中的最小单位。前面说了,端点0里有数据现在就把数据读出来看到的是些啥数据。我们先要定义一个结构体来接收这个标准请求。
/*USB标准设备请求结构体*/
typedef __packed struct _USB_SETUP_PACKET {        //这个结构体的“__packed
__packed        union {
                        uint8_t B;
                        __packed struct{
                                uint8_t Recipient  : 5;                //请求的接收者
                                uint8_t Type      : 2;        //请求类型
                                uint8_t Dir       : 1;                //数据传输方向
                        } BM;
} bmRequestType;                                                //请求的特性
        uint8_t  bRequest;                                        //请求代码        
__packed        union {
                uint16_t W;
                __packed struct {
                        uint8_t L;
                        uint8_t H;                                
                } WB;
} wValue;                                                        //该域意义由具体请求决定

__packed        union {
                uint16_t W;
                __packed struct {
                        uint8_t L;
                        uint8_t H;
                } WB;
} wIndex;                                        //该域意义由具体请求决定
uint16_t      wLength;                //需要传输的字节数
} USB_SETUP_PACKET;
利用这个结构体我们把就收到的数据传给它,以后在用的时候就方便多了。现在还要一个接收来自端点数据的函数,代码如下:
/**********************************************************************
函数功能;读USB端点数据函数
函 数 名;USB_ReadEP
函数参数;EPNum;要读的端点。
                *pData;指向缓冲区的指针。
函数返回;读到的字节数。
作         者:        旺宝电子科技有限公司
***********************************************************************/
#define PKT_LNGTH_MASK      0x000003FF
#define CTRL_RD_EN          0x00000001
#define CTRL_WR_EN          0x00000002
uint32_t USB_ReadEP (uint32_t EPNum, uint8_t *pData)
{
  uint32_t cnt, n;

  LPC_USB->Ctrl = ((EPNum & 0x0F) << 2) | CTRL_RD_EN;        /*Crtl寄存器是控制端点读写的寄存
                                                                                                器,端点号EPNum上移两位的原因是
                                                                                                低两位是读写控制位。*/
  do {
    cnt = LPC_USB->RxPLen;                                                        //得到接收长度。
  } while ((cnt & (1<<11)) == 0);                                                        //检查接收到的长度字段是否正确。
  cnt &= PKT_LNGTH_MASK;                                                        //提取接收到的字节长度信息。
  for (n = 0; n < (cnt + 3) / 4; n++) {                                                //开始循环读数据
    *((__packed uint32_t *)pData) = LPC_USB->RxData;
    pData += 4;
  }
  LPC_USB->Ctrl = 0;                                                                //禁止向端点读写

  return (cnt);
}
在端点0的数据都是控制传输,所以要做的工作比较多我们就再建一个函数来专对端点0的控制传输数据进行相应的操作,现在我们的目的是先把数据打印出来再说,所以现在不用过多的处理。



回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2015-6-17 15:08:04 | 显示全部楼层
本帖最后由 一座城 于 2015-6-17 15:09 编辑
一座城 发表于 2015-6-17 15:07
LPC1788—USB学习第三章;USB标准请求
开发环境:集成开发环境µVision4 IDE版本4.60.0.0主机系统:Micros ...

/**********************************************************************
函数功能;USB端点0函数
函 数 名;USB_EndPoint0
函数参数;Event; 需要端点0执行的事件。
函数返回;无
        者:        旺宝电子科技有限公司
***********************************************************************/
void USB_EndPoint0 (uint32_t Event)
{
  switch (Event) {
    case USB_EVT_SETUP:
USB_ReadEP (0, (uint8_t *)&SetupPacket);              //读端点0的数据,把数据传给SetupPacket结构体
#if PRINTF
_DBH(SetupPacket.bmRequestType.B);         //打印请求特性
_DBG_(" ");
_DBH(SetupPacket.bRequest);         //打印请求代码
_DBG_(" ");
_DBH(SetupPacket.wValue.WB.L);        //打印wValue域低字段
_DBG_(" ");
_DBH(SetupPacket.wValue.WB.H);        //打印wValue域高字段
_DBG_(" ");
_DBH(SetupPacket.wIndex.WB.L);        //打印wIndex域低字段
_DBG_(" ");
_DBH(SetupPacket.wIndex.WB.H);        //打印wIndex域高字段
_DBG_(" ");
_DBH16(SetupPacket.wLength);        //打印数据过程索要的长度
_DBG_(" ");
_DBG_("\r\n");
#endif
break;
case USB_EVT_OUT:
break;
case USB_EVT_IN :
break;   
}
}
这个USB_EndPoint0函数是在中断里调用的,所以还要在中断里添加代码。
/**********************************************************************
函数功能;USB中断函数
函 数 名;USB_IRQHandler
函数参数;无
函数返回;无
        者:        旺宝电子科技有限公司
***********************************************************************/
void USB_IRQHandler (void)
{
  uint32_t disr, val;
  uint32_t episr;
  disr = LPC_USB->DevIntSt;       //读设备中断状态
#if PRINTF
_DBG_("中断号");
_DBH32(disr);
_DBG_("\r\n");
#endif
  if (disr & DEV_STAT_INT) {        //判断是否有复位、连接发生改变、挂起和恢复的中断
    LPC_USB->DevIntClr = DEV_STAT_INT;        //清除复位、挂起、链接中断
    WrCmd(CMD_SET_DEV_STAT);        //写一个设置设备状态,05命令,准备获得设备状态
    val = RdCmdDat(DAT_GET_DEV_STAT);   //读命令数据,读取设备状态
#if PRINTF
_DBG_("设备状态号");
_DBH32(val);
_DBG_("\r\n");
#endif
    if (val & DEV_RST){                            //判断复位
      USB_Reset();
#if PRINTF
_DBG_("复位\r\n");
#endif
    }
    if (val & DEV_SUS_CH) {                //判断挂起和恢复
      if (val & DEV_SUS) {                  //挂起
        USB_Suspend();
#if PRINTF
_DBG_("挂起\r\n");
#endif
      }
else {                        //恢复
        USB_Resume();
#if PRINTF
_DBG_("恢复\r\n");
#endif
      }
    }
  }
  if (disr & EP_SLOW_INT) {         //端点的慢速中断
    episr = LPC_USB->EpIntSt;        //读端点中断状态
LPC_USB->EpIntClr = episr;        //清除读到的端点中断。
#if PRINTF
_DBG_("读端点中断");
_DBH32(episr);
_DBG_("\r\n");
#endif
val = RdCmdDat(DAT_SEL_EP(episr));        //episr端点的命令数据。
#if PRINTF
_DBG_("命令数据");
_DBH32(val);
_DBG_("\r\n");
#endif
if(val & EP_SEL_F){        //如果命令位FE置位表示OUT端点
if(1 & episr){        //判断是否有端点0的中断
if(val & EP_SEL_STP){        //判断是否为SETUP(控制传输)令牌包。
USB_EndPoint0(USB_EVT_SETUP);        //让端点0处理SETUP(控制传输)包。
}
}
else{
}
}
else{        //如果命令位FE0表示IN端点
}
    LPC_USB->DevIntClr = EP_SLOW_INT;        //清除端点慢速中断
  }
}
因为只是接收中断的数据,所以中断函数也不是太复杂。下载到开发板运行的结果如图323
图片1.png
图(323


回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2016-12-5 14:54:20 | 显示全部楼层
增加几个例程网络的
[1]悍马1788_HTTP(UCOS).rar (1.05 MB, 下载次数: 3)
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /1 下一条



手机版|小黑屋|与非网

GMT+8, 2024-4-24 16:55 , Processed in 0.156210 second(s), 24 queries , MemCache On.

ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

苏公网安备 32059002001037号

Powered by Discuz! X3.4

Copyright © 2001-2024, Tencent Cloud.