植物生长补光:U盘实现流程跟踪分析01

来源:百度文库 编辑:中财网 时间:2024/05/05 05:14:04
一、追踪USB大容量设备的实现流程 1、从main.c开始(1)main函数的执行流程  Set_System();  //设置时钟、端口等。  Set_USBClock();  //设置usb的时钟  USB_Interrupts_Config();  //设置中断  Led_Config();  //设置所使用的到的灯。  MSD_Init();  //SD卡初始化  Get_Medium_Characteristics();  //获取SD块总数、每块字节数。  USB_Init();  //USB_init.c提供的初始化函数。从这里开始USB设备被主机检测到。  while (1)  {  //USB的工作都是在中断中完成的,主执行流程什么也没做。  }(2)与鼠标例程不同的地方在中断配置中,使能了USB高优先级中断。  NVIC_InitStructure.NVIC_IRQChannel= USB_HP_CAN_TX_IRQChannel;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure); 用到了几个灯指示,这个我的开发板上用不到,就不详细看了。 MSD_Init(),是对SD卡进行初始化。该函数在msd.c中,我看了一下它的SD卡实现的代码,比我的SD函数代码齐整多了。以后有时间要把我的USB驱动好好的整理一下。不过现在就先不管了。 接下来这个函数获取SD卡的容量,这样的函数我在SD卡驱动中也实现了,改变一下调用方式就行了。 USB_Init()函数在usb_init.c库函数中,但它最终会调用user_prop.c宏的用户初始化例程。下面就追踪进去看一看。 (3)大容量存储设备的初始化void MASS_init(){  pInformation->Current_Configuration = 0;  PowerOn();  连接电缆主机很快发总线复位。  _SetISTR(0);  wInterrupt_Mask = IMR_MSK;  _SetCNTR(wInterrupt_Mask);  开启复位和传输中断。  pInformation->Current_Feature = MASS_ConfigDescriptor[7];  while (pInformation->Current_Configuration == 0)  {    NOP_Process();  }  bDeviceState = CONFIGURED;  //这句执行完成后,设备处于已配置状态。我先在这里加一句调试语句。#if usb_debugUart_PutString(“设备已配置”);#endif} 2、进入复位中断(1)先列出中断处理代码发生总线复位中断以后,处理是在usb_prop.c的Mass_Reset()函数中完成的。void MASS_Reset(){  Device_Info.Current_Configuration = 0;  SetBTABLE(BTABLE_ADDRESS);   SetEPType(ENDP0, EP_CONTROL);  //端点0控制端点  SetEPTxStatus(ENDP0, EP_TX_NAK); //不响应IN  SetEPRxAddr(ENDP0, ENDP0_RXADDR); //设置接收缓冲区(OUT)  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); 接收长度。  SetEPTxAddr(ENDP0, ENDP0_TXADDR); //发送缓冲区(IN)  Clear_Status_Out(ENDP0);   SetEPRxValid(ENDP0);  //使能端点0的接收。    SetEPType(ENDP1, EP_BULK); //端点1批量模式  SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送缓冲区(IN)  SetEPTxStatus(ENDP1, EP_TX_NAK); 发送不响应。  SetEPRxStatus(ENDP1, EP_RX_DIS); //接收无效。对OUT无效   SetEPType(ENDP2, EP_BULK); //端点2批量模式  SetEPRxAddr(ENDP2, ENDP2_RXADDR); //设置接收缓冲区OUT  SetEPRxCount(ENDP2, Device_Property.MaxPacketSize);  SetEPRxStatus(ENDP2, EP_RX_VALID);  SetEPTxStatus(ENDP2, EP_TX_DIS); //发送无效,对IN无效   SetDeviceAddress(0);  //使能USB接口模块。  CBW.dSignature = BOT_CBW_SIGNATURE;  Bot_State = BOT_IDLE; //命令状态机初始化为空闲状态}在这里没有我没有看到将批量端点设置为双缓冲模式的迹象,难道这个例程没有用它? 3、进入枚举过程因为在鼠标例程中已经详细分析过枚举过程,这里主要是大容量设备枚举过程中不同的地方做一下分析。(1)获取设备描述符、设置地址。(2)获取配置描述符(3)获取配置描述符集合,这里主要讲接口描述符分析一下。    0x09,   /* bLength: Interface Descriptor size */    0x04,   /* bDescriptorType: */    0x00,   /* bInterfaceNumber: Number of Interface */    0x00,   /* bAlternateSetting: Alternate setting */    0x02,   /* bNumEndpoints*/  使用两个端点    0x08,   /* bInterfaceClass: MASS STORAGE Class,大容量存储类 */    0x06,   /* bInterfaceSubClass : SCSI transparent,SCSI传输*/    0x50,   /* nInterfaceProtocol ,仅批量传输*/    4,          /* iInterface: */ (4)获取字符串描述符(5)类请求实现:类获取逻辑盘:一般返回0类请求复位:    ClearDTOG_TX(ENDP1);    ClearDTOG_RX(ENDP2);    CBW.dSignature = BOT_CBW_SIGNATURE;    Bot_State = BOT_IDLE; (6)设置配置在用户设置的回调函数中,又调用void Mass_Storage_SetConfiguration(void){  if (pInformation->Current_Configuration)  {    ClearDTOG_TX(ENDP1);    ClearDTOG_RX(ENDP2);    Bot_State = BOT_IDLE;  }}这个工作前面已经做过了。 4、主机发命令INQUIRY(1)首先进入批量输出中断该中断的回调函数调用Mass_Storage_Out()进行处理。 (2)追踪进入Mass_Storage_Out()void Mass_Storage_Out (void){  u8 CMD;  CMD = CBW.CB[0]; //  Data_Len = GetEPRxCount(ENDP2);  PMAToUserBufferCopy(Bulk_Data_Buff,ENDP2_RXADDR, Data_Len);   switch (Bot_State)  {    case BOT_IDLE:      CBW_Decode();  //第一次收到命令肯定调用这个解码函数。      break;   //它的作用应该是填充CBW命令块封包结构    case BOT_DATA_OUT:      if (CMD == SCSI_WRITE10)      {        SCSI_Write10_Cmd();        break;      }} (3)追踪进入CBW_Decode()这个函数的代码较长,我就不列举在这里了,我就分析一下本次的主要工作。首先将用户缓冲区的数据复制到命令封包结构里面。然后准备好状态封包结构:  CSW.dTag = CBW.dTag;  //这个标志由主机生成,可以用于检查设备是否正确收到该命令。  CSW.dDataResidue = CBW.dDataLength;  然后主要是根据命令操作码,调用相应的SCSI命令处理函数。      switch (CBW.CB[0])      {        case SCSI_REQUEST_SENSE:          SCSI_RequestSense_Cmd ();          break;        case SCSI_INQUIRY:          SCSI_Inquiry_Cmd();          break;我这里就列出了两项,实际的命令是很多的。本次主要是查询处理。 (4)追踪进入SCSI_Inquiry_Cmd()以上函数是在usb_bot.c里面,现在跳转到usb_scsi.c里面。 这个函数的主要工作是调用Transfer_Data_Request(Inquiry_Data, Inquiry_Data_Length)来完成。这个Inquiry_Data = Standard_Inquiry_Data,后面这个Standard_Inquiry_Data是scsi_data.c里面定义的一个数据结构,专门用于inquiry命令的返回。u8 Standard_Inquiry_Data[] =  {    0x00,          /* Direct Access Device */    0x80,          /* RMB = 1: Removable Medium */    0x02,          /* Version: No conformance claim to standard */    0x02,           //这里圈圈的书上说应该为 0x01    36 - 4,         //这里圈圈的书上说应该为 31    0x00, 0x00, 0x00,     /* SCCS = 1: Storage Controller Component */      'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ',//厂商信息    'S', 'T', 'R', ' ', ' ', 'F', 'l', 'a', 's', 'h', ' ', 'D', 'i', 's', 'k', ' ',//产品信息    '1', '.', '0', ' '                //版本信息。  };(5)追踪进入Transfer_Data_Request () void Transfer_Data_Request(u8* Data_Pointer, u16 Data_Len){  UserToPMABufferCopy(Data_Pointer, ENDP1_TXADDR, Data_Len);   SetEPTxCount(ENDP1, Data_Len);  SetEPTxStatus(ENDP1, EP_TX_VALID);  Bot_State = BOT_DATA_IN_LAST;  CSW.dDataResidue -= Data_Len;  CSW.bStatus = CSW_CMD_PASSED; //设置好命令状态封包信息。} (6)接下来,主机会发IN,取走查询信息。并进入批量输入中断。在该中断中,将调用函数Mass_Storage_In (void) void Mass_Storage_In (void){  switch (Bot_State)  {    case BOT_CSW_Send:    case BOT_ERROR:      Bot_State = BOT_IDLE;      SetEPRxStatus(ENDP2, EP_RX_VALID);/* enable the Endpoint to recive the next cmd*/      break;    case BOT_DATA_IN_LAST:      Set_CSW (CSW_CMD_PASSED, SEND_CSW_ENABLE);      SetEPRxStatus(ENDP2, EP_RX_VALID);      break;    default:      break;  }}然后设备的命令状态机状态变为Set_CSW()这个函数所设置的状态,一般为BOT_CSW_Send。 (7)追踪进入Set_CSW()在该函数中:void Set_CSW (u8 CSW_Status, u8 Send_Permission){  CSW.dSignature = BOT_CSW_SIGNATURE;  CSW.bStatus = CSW_Status;  //命令状态封包数据已经准备好   UserToPMABufferCopy((&CSW),ENDP1_TXADDR, DATA_LENGTH);  SetEPTxCount(ENDP1, CSW_DATA_LENGTH);   Bot_State = BOT_ERROR;  if (Send_Permission)  {    Bot_State = BOT_CSW_Send;    SetEPTxStatus(ENDP1, EP_TX_VALID);  } }然后,主机再次发IN令牌包,取走命令状态封包。case BOT_ERROR:      Bot_State = BOT_IDLE;      SetEPRxStatus(ENDP2, EP_RX_VALID);端点2的接收又被使能,重新进入接收命令状态。
本文来源于电气自动化技术网 转载注明出处http://www.dqjsw.com.cn/dianqi/danpianji/13143.html