完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
硬件原理图
在开始枚举设备的一些初始化 void bsp_USBInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_USB_PULL_UP, ENABLE); USB_CABLE_DISABLE(); GPIO_InitStructure.GPIO_Pin = PIN_USB_PULL_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); USB_Init(); } 现在开始分析真正的初始化 第一步:初始化,总线复位及向默认地址 0发送 GET_DESCRIPTOR 指令包,请求设备描述 1)Index[4 - 5]:表示 USB插入总线复位; 2)Index[7 - 8]:表示主机向默认地址发送GET_DESCRIPTOR指令包,详细信 息也抓出来了,如(图二)所示 3)Index[15 - 17]:表示设备向主机发送设备描述数据 Index[16] 4)Index[18 - 19]:表示主机完成 GET_DESCRIPTOR指令后,给设备发送一个 空应答 现在具体的分析103的usb的执行过程 按顺序向下执行 ***************(1)************** DEVICE_INFO *pInformation; DEVICE_PROP *pProperty; 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 }; 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内核将主机发送过来的用于实现USB设备的设置包保存在设备信息结构表中 typedef struct _DEVICE_INFO { uint8_t USBbmRequestType; uint8_t USBbRequest; uint16_t_uint8_t USBwValues; uint16_t_uint8_t USBwIndexs; uint16_t_uint8_t USBwLengths; uint8_t ControlState; uint8_t Current_Feature; uint8_t Current_Configuration; uint8_t Current_Interface; uint8_t Current_AlternateSetting; ENDPOINT_INFO Ctrl_Info; }DEVICE_INFO; u***_init.c文件里面的 void USB_Init(void) { pInformation = &Device_Info; pInformation-》ControlState = 2; pProperty = &Device_Property; pUser_Standard_Requests = &User_Standard_Requests; pProperty-》Init(); } ***************(2)**************通过函数指针指向这个初始化函数pProperty 在u***_prop.c文件里面 void Joystick_init(void) { Get_SerialNum(); //得到串行号 pInformation-》Current_Configuration = 0; // PowerOn(); //将USB上电 连接设备 USB_SIL_Init(); //主要是CNTR寄存器的初始化 bDeviceState = UNCONNECTED; //设备状态标志 当前状态未连接 } hw_config.c文件里面 这个和标准的不一样有改动,获取设备版本号,将其存入到版本号字符串。 void Get_SerialNum(void) //得到串行号 { uint32_t Device_Serial0, Device_Serial1, Device_Serial2; Device_Serial0 = *(__IO uint32_t*)(0x1FFFF7E8); Device_Serial1 = *(__IO uint32_t*)(0x1FFFF7EC); Device_Serial2 = *(__IO uint32_t*)(0x1FFFF7F0); Device_Serial0 += Device_Serial2; if (Device_Serial0 != 0) { IntToUnicode (Device_Serial0, &Joystick_StringSerial[2] , 8); IntToUnicode (Device_Serial1, &Joystick_StringSerial[18], 4); } } u***_pwr.c文件里面 在这个文件里面只是使能了复位,挂起,唤醒中断,在PowerOn函数使能了复位中断以后,将进入到USB的复位中断里面去。 然后再执行函数USB_SIL_Init 将所有的USB中断都打开。在D+被接通上拉以后,设备就能被主机检测到。 RESULT PowerOn(void) { #ifndef STM32F10X_CL uint16_t wRegVal; USB_Cable_Config(ENABLE); //将USB上电连接 //对USB模块强制复位,类似于USB总线上的复位信号。USB模块将一直保持在复位状态下 //直到软件清除此位。如果USB复位中断被使能,将产生一个复位中断。 wRegVal = CNTR_FRES; //强制复位 _SetCNTR(wRegVal); wInterrupt_Mask = 0; _SetCNTR(wInterrupt_Mask); //清除复位信号 _SetISTR(0); //复位中断屏蔽位 挂起中断屏蔽位 唤醒中断屏蔽位使能 wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM; _SetCNTR(wInterrupt_Mask); #endif return USB_SUCCESS; } u***_istr.c文件里面,下面只写了进入到复位中断函数,进入到USB连接状态 void USB_Istr(void) { wIstr = _GetISTR(); #if (IMR_MSK & ISTR_RESET) //USB复位请求中断 if (wIstr & ISTR_RESET & wInterrupt_Mask) { _SetISTR((uint16_t)CLR_RESET); //清楚复位中断标志 Device_Property.Reset(); //进入到复位中断 #ifdef RESET_CALLBACK RESET_Callback(); #endif } #end } u***_prop.c文件里面,实现对端点的设置。 void Joystick_Reset(void) { pInformation-》Current_Configuration = 0; pInformation-》Current_Interface = 0; pInformation-》Current_Feature = Joystick_ConfigDescriptor[7]; //供电模式选择 #ifdef STM32F10X_CL OTG_DEV_EP_Init(EP1_IN, OTG_DEV_EP_TYPE_INT, 4); #else SetBTABLE(BTABLE_ADDRESS); //分组缓冲区描述表地址设置 SetEPType(ENDP0, EP_CONTROL); //初始化为控制端点类型 SetEPTxStatus(ENDP0, EP_TX_STALL); //端点以STALL分组响应所有的发送请求。 //也就是端点状态设置成发送无效,也就是主机的IN令牌包来的时候,回送一个STALL。 SetEPRxAddr(ENDP0, ENDP0_RXADDR); //设置端点0描述符的接受地址, SetEPTxAddr(ENDP0, ENDP0_TXADDR); //设置端点0描述符的发送地址 Clear_Status_Out(ENDP0); //仅用于控制端点 如果STATUS_OUT位被清除,OUT分组可以包含任意长度的数据 SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); //设置端点0的接受字节寄存器的最大值是64 SetEPRxValid(ENDP0); //设置接受端点有效 SetEPType(ENDP1, EP_INTERRUPT); //初始化为中断端点类型 SetEPTxAddr(ENDP1, ENDP1_TXADDR); //设置发送数据的地址 SetEPTxCount(ENDP1, 4); //设置发送的长度 SetEPRxStatus(ENDP1, EP_RX_DIS); //设置接受端点关闭 SetEPTxStatus(ENDP1, EP_TX_NAK); //设置发送端点端点非应答 SetDeviceAddress(0); //设置设备用缺省地址相应 #endif bDeviceState = ATTACHED; //当前状态连接 } u***_sil.c的文件里面,主要是使能了如下这些中断 CNTR_CTRM 正确传输(CTR)中断使能 CNTR_WKUPM 唤醒中断使能 CNTR_SUSPM 挂起(SUSP)中断使能 CNTR_ERRM 出错中断使能 CNTR_SOFM 帧首中断使能 CNTR_ESOFM 期望帧首中断使能CNTR_RESETM 设置此位将向PC主机发送唤醒请求。根据USB协议,如果此位在1ms到15ms内保持有效,主机将对USB模块实行唤醒操作。 uint32_t USB_SIL_Init(void) { #ifndef STM32F10X_CL _SetISTR(0); //清除中断标志 wInterrupt_Mask = IMR_MSK; //这组寄存器用于定义USB模块的工作模式,中断的处理,设备的地址和读取当前帧的编号 _SetCNTR(wInterrupt_Mask); //设置相应的控制寄存器 #else OTG_DEV_Init(); #endif return 0; } ***************(3)************** 1.获取设备描述符 u***_int.c的文件里面 低优先级中断 在控制 中断 批量传输下使用(在单缓冲模式下使用) 当一次正确的OUT,SETUP,IN数据传输完成后,硬件会自动设置此位为NAK状态,使应用程序有足够的时间处理完当前传输的数据后,响应下一个数据分组 void CTR_LP(void) { __IO uint16_t wEPVal = 0; while (((wIstr = _GetISTR()) & ISTR_CTR) != 0) { EPindex = (uint8_t)(wIstr & ISTR_EP_ID); //读出端点ID if (EPindex == 0) //如果是端点0 { SaveRState = _GetENDPOINT(ENDP0); //读取端点0寄存器USB_EP0R SaveTState = SaveRState & EPTX_STAT; //保存发送状态位 SaveRState &= EPRX_STAT; //保存接受状态位 _SetEPRxTxStatus(ENDP0,EP_RX_NAK,EP_TX_NAK); //端点以NAK分组响应所有的发送和接受请求(解释在上面) if ((wIstr & ISTR_DIR) == 0) //IN令牌,数据被取走 { _ClearEP_CTR_TX(ENDP0); //清除正确发送标志位 In0_Process(); //处理INT事件 _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState); return; } else { wEPVal = _GetENDPOINT(ENDP0); //得到端点0寄存器的数据 if ((wEPVal &EP_SETUP) != 0) //SETUP分组传输完成标志 { _ClearEP_CTR_RX(ENDP0); Setup0_Process(); //处理SETUP事件 //程序会进入到这个函数里面 _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState); return; } else if ((wEPVal & EP_CTR_RX) != 0) { _ClearEP_CTR_RX(ENDP0); Out0_Process(); //处理OUT事件 _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState); return; } } } else //如果是除端点0以外的端点 { wEPVal = _GetENDPOINT(EPindex); //得到相应端点寄存器值 if ((wEPVal & EP_CTR_RX) != 0) //检测正确接收标志 PC-USB OUT int { _ClearEP_CTR_RX(EPindex); //清除相应的标志 (*pEpInt_OUT[EPindex-1])(); //调用OUT int服务功能 } if ((wEPVal & EP_CTR_TX) != 0) //检测正确发送标志 USB-PC IN int { _ClearEP_CTR_TX(EPindex); //清除相应的标志 (*pEpInt_IN[EPindex-1])(); //调用IN int服务功能 } } } } u***_coer.c的文件里面,主要是得到主机发来的标准请求命令 uint8_t Setup0_Process(void) { union { uint8_t* b; uint16_t* w; } pBuf; #ifdef STM32F10X_CL USB_OTG_EP *ep; uint16_t offset = 0; ep = PCD_GetOutEP(ENDP0); pBuf.b = ep-》xfer_buff; #else uint16_t offset = 1; //得到接受缓冲区地址寄存器地址 pBuf.b = PMAAddr + (uint8_t *)(_GetEPRxAddr(ENDP0) * 2); #endif if (pInformation-》ControlState != PAUSE) { pInformation-》USBbmRequestType = *pBuf.b++; pInformation-》USBbRequest = *pBuf.b++; pBuf.w += offset; pInformation-》USBwValue = ByteSwap(*pBuf.w++); pBuf.w += offset; pInformation-》USBwIndex = ByteSwap(*pBuf.w++); pBuf.w += offset; pInformation-》USBwLength = *pBuf.w; } pInformation-》ControlState = SETTING_UP; if (pInformation-》USBwLength == 0) { NoData_Setup0(); } else { Data_Setup0(); //由于是有数据的传输,所有要进入到这个函数 } return Post0_Process(); } u***_core.c的文件里面,这里只是选取了GET DESCRIPTOR 的程序部分,其他的部分删除了 void Data_Setup0(void) { uint8_t *(*CopyRoutine)(uint16_t); RESULT Result; uint32_t Request_No = pInformation-》USBbRequest; uint32_t Related_Endpoint, Reserved; uint32_t wOffset, Status; CopyRoutine = NULL; wOffset = 0; //看标准请求码格式就知道了 if (Request_No == GET_DESCRIPTOR) { //pInformation-》USBbmRequestType是下面的两种 标准请求或设备请求 if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) { uint8_t wValue1 = pInformation-》USBwValue1; //高一字节得到描述表种类 一共有5种 if (wValue1 == DEVICE_DESCRIPTOR) //设备描述 { CopyRoutine = pProperty-》GetDeviceDescriptor; } else if (wValue1 == CONFIG_DESCRIPTOR) { CopyRoutine = pProperty-》GetConfigDescriptor; //配置描述 } else if (wValue1 == STRING_DESCRIPTOR) { CopyRoutine = pProperty-》GetStringDescriptor; //字符串描述 } } } if (CopyRoutine) { pInformation-》Ctrl_Info.U***_wOffset = wOffset; //本子程序的wOffset是0 pInformation-》Ctrl_Info.CopyData = CopyRoutine; //使指针pInformation-》Ctrl_Info.CopyData指向CopyRoutine (*CopyRoutine)(0); //第一次执行时Length=0 返回的是有效数据的长度 存储到pInformation-》Ctrl_Info.U***_wLength Result = USB_SUCCESS; } else { //如果标准请求不存在 看类 厂商请求中是否有 Result = (*pProperty-》Class_Data_Setup)(pInformation-》USBbRequest); if (Result == USB_NOT_READY) { pInformation-》ControlState = PAUSE; return; } } if (pInformation-》Ctrl_Info.U***_wLength == 0xFFFF) //如果字符的长度是0xffff { pInformation-》ControlState = PAUSE; return; } if ((Result == USB_UNSUPPORT) || (pInformation-》Ctrl_Info.U***_wLength == 0)) { pInformation-》ControlState = STALLED; return; } if (ValBit(pInformation-》USBbmRequestType, 7)) //D7表示数据传输方向 1:设备向主机 { __IO uint32_t wLength = pInformation-》USBwLength; //设置使其为USB主机设置的长度 本程序HID 鼠标 pProperty-》MaxPacketSize是0x40 if (pInformation-》Ctrl_Info.U***_wLength 》 wLength) //字符的长度大于主机要求的长度 { pInformation-》Ctrl_Info.U***_wLength = wLength; //将其设置为主机要求的 } else if (pInformation-》Ctrl_Info.U***_wLength 《 pInformation-》USBwLength) //字符的长度小于主机要求的 { if (pInformation-》Ctrl_Info.U***_wLength 《 pProperty-》MaxPacketSize) //如果字符的长度长度小于每包数据最大字节数 { Data_Mul_MaxPacketSize = FALSE; } else if ((pInformation-》Ctrl_Info.U***_wLength % pProperty-》MaxPacketSize) == 0) //如果是其整数倍 { Data_Mul_MaxPacketSize = TRUE; } } pInformation-》Ctrl_Info.PacketSize = pProperty-》MaxPacketSize; DataStageIn(); } else //主机向设备 { pInformation-》ControlState = OUT_DATA; vSetEPRxStatus(EP_RX_VALID); } return; } u***_coer.c的文件里面 void DataStageIn(void) { ENDPOINT_INFO *pEPinfo = &pInformation-》Ctrl_Info; //端点信息保存在指针变量中 uint32_t save_wLength = pEPinfo-》U***_wLength; //得到字符的长度 uint32_t ControlState = pInformation-》ControlState; //得到当前的状态 uint8_t *DataBuffer; uint32_t Length; if ((save_wLength == 0) && (ControlState == LAST_IN_DATA)) //如果字符长度为0 且控制状态是最后输入的数据 { if(Data_Mul_MaxPacketSize == TRUE) //如果字符的长度是数据包的整数倍 { Send0LengthData(); ControlState = LAST_IN_DATA; Data_Mul_MaxPacketSize = FALSE; //这一次发送0字节 状态转为最后输入阶段 } else //字符的长度比数据包要小 { //数据已经发送完 ControlState = WAIT_STATUS_OUT; #ifdef STM32F10X_CL PCD_EP_Read (ENDP0, 0, 0); #endif #ifndef STM32F10X_CL vSetEPTxStatus(EP_TX_STALL); //设置端点的发送状态停止 #endif } goto Expect_Status_Out; } Length = pEPinfo-》PacketSize; //得到数据包大小 64字节 ControlState = (save_wLength 《= Length) ? LAST_IN_DATA : IN_DATA;//比较大小得到是LAST_IN_DATA还是IN_DATA 18字节《64字节 ControlState = LAST_IN_DATA if (Length 》 save_wLength) { Length = save_wLength; } DataBuffer = (*pEPinfo-》CopyData)(Length); //DataBuffer指向要复制数据的地址 这个地址是随U***_wOffset变化的 #ifdef STM32F10X_CL PCD_EP_Write (ENDP0, DataBuffer, Length); #else //GetEPTxAddr(ENDP0) 得到发送缓冲区相应端点的地址 //将DataBuffer中的数据复制到相应的发送缓冲区中 UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length); #endif SetEPTxCount(ENDP0, Length); //设置相应的端点要发送的字节数 pEPinfo-》U***_wLength -= Length;//等于0 pEPinfo-》U***_wOffset += Length;//偏移到18 vSetEPTxStatus(EP_TX_VALID); //使能发送端点 只要主机的IN令牌包一来 SIE就会将描述符返回给主机 USB_StatusOut(); //设置接收端点有效 这个实际上使接受也有效, Expect_Status_Out: pInformation-》ControlState = ControlState; //保存控制状态 } ***************(4)************** uint8_t In0_Process(void) { uint32_t ControlState = pInformation-》ControlState; if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA))//进入到这里 { DataStageIn();//第一次取设备描述符只取一次 当前的状态变为WAIT_STATUS_IN表明设备等待状态过程 主机输出0字节 ControlState = pInformation-》ControlState; } else if (ControlState == WAIT_STATUS_IN) //设置地址状态阶段进入这个程序 { if ((pInformation-》USBbRequest == SET_ADDRESS) && (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))) { SetDeviceAddress(pInformation-》USBwValue0); //设置使用新的地址 pUser_Standard_Requests-》User_SetDeviceAddress(); } (*pProperty-》Process_Status_IN)(); ControlState = STALLED; //变为这个状态 } else { ControlState = STALLED; } pInformation-》ControlState = ControlState; return Post0_Process(); } uint8_t Out0_Process(void) { uint32_t ControlState = pInformation-》ControlState; if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA)) { //主机在完成传输前终止传输 ControlState = STALLED; } else if ((ControlState == OUT_DATA) || (ControlState == LAST_OUT_DATA)) { DataStageOut(); ControlState = pInformation-》ControlState; } else if (ControlState == WAIT_STATUS_OUT) //进入到这个里面 { (*pProperty-》Process_Status_OUT)(); //这个函数其实什么也没做 #ifndef STM32F10X_CL ControlState = STALLED; //状态变成了终止发送和接受 #endif } else { ControlState = STALLED; } pInformation-》ControlState = ControlState; return Post0_Process(); } ***************(5)************** 获取设备描述符以后,主机再一次的复位设备,设备又进入初始状态。开始枚举的第二步设置地址。 void NoData_Setup0(void) { RESULT Result = USB_UNSUPPORT; uint32_t RequestNo = pInformation-》USBbRequest; uint32_t ControlState; if (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT)) //设备请求 { else if (RequestNo == SET_ADDRESS) /设置地址 { if ((pInformation-》USBwValue0 》 127) || (pInformation-》USBwValue1 != 0) || (pInformation-》USBwIndex != 0) || (pInformation-》Current_Configuration != 0)) { ControlState = STALLED; goto exit_NoData_Setup0; } else { Result = USB_SUCCESS; #ifdef STM32F10X_CL SetDeviceAddress(pInformation-》USBwValue0); #endif } } ControlState = WAIT_STATUS_IN; USB_StatusIn();//准备好发送0字节的状态数据包 SetEPTxCount(ENDP0, 0); //vSetEPTxStatus(EP_TX_VALID);建立阶段后直接的进入状态阶段 exit_NoData_Setup0: pInformation-》ControlState = ControlState; return; } uint8_t In0_Process(void) { uint32_t ControlState = pInformation-》ControlState; if ((ControlState == IN_DATA) || (ControlState == LAST_IN_DATA)) //控制状态 { DataStageIn();//第一次取设备描述符只取一次 当前的状态变为WAIT_STATUS_IN 表明设备等待状态过程 主机输出0字节 ControlState = pInformation-》ControlState; } else if (ControlState == WAIT_STATUS_IN) //设置地址状态阶段进入这个程序 { if ((pInformation-》USBbRequest == SET_ADDRESS) && (Type_Recipient == (STANDARD_REQUEST | DEVICE_RECIPIENT))) { SetDeviceAddress(pInformation-》USBwValue0); //设置使用新的地址 pUser_Standard_Requests-》User_SetDeviceAddress(); } (*pProperty-》Process_Status_IN)(); ControlState = STALLED; //终止发送和接受 } else { ControlState = STALLED; } pInformation-》ControlState = ControlState; return Post0_Process(); } uint8_t Post0_Process(void) { #ifdef STM32F10X_CL USB_OTG_EP *ep; #endif SetEPRxCount(ENDP0, Device_Property.MaxPacketSize); //设置端点0 要接受的字节数 if (pInformation-》ControlState == STALLED) //这种状态下只接受SETUP命令包 { vSetEPRxStatus(EP_RX_STALL); //终止端点0接受 vSetEPTxStatus(EP_TX_STALL); //终止端点0发送 } return (pInformation-》ControlState == PAUSE); } ***************(6)************* 从新地址获取设备描述符 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1627 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1550 浏览 1 评论
984 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1601 浏览 2 评论
1867浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
650浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
518浏览 3评论
536浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
506浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 18:12 , Processed in 0.735474 second(s), Total 48, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号