CSh*_*ark 5 c embedded usb stm32 cmsis
更新\n对于任何感兴趣的人,这里有关于如何构建裸机 USB 堆栈、如何处理此类项目以及每个步骤需要了解的内容的分步说明和解释:STM32USB@GitHub
\nTLDR: \n我有一个STM32G441并且想要实现一个 USB 驱动程序,而不使用任何 HAL 库,只使用 CMSIS - 为了学习经验,为了空间,因为我想做的事情无论如何都需要更改 hal。
但我无法让这个东西接收任何东西。我在尝试获取设备地址时遇到了困难,该地址从未交给代码。hal 中间件工作得很好,所以这不是硬件问题。
\n我正在启用 USB 时钟(正如我所假设的那样,因为它可以使用我的逻辑分析仪发送 ACK 信号),按照数据表中的定义为 USB 外设通电,启用所有必要的中断并通过初始化BTable 和端点 0。现在我希望收到一个从未出现过的 CTR 中断。
\n\n\xce\xbcC 在 25MHz HSE 时钟上运行。USB 外设在 ~48MHz 的 PLL Q 时钟上运行,RCC 设置通过 CubeMX 时钟配置器进行验证。AHB 以半速运行,因为如果我尝试全速运行它,我会遇到总线错误硬故障,但这是另一个问题。系统时钟设置为 143.75MHz。
\nRCC->CR |= RCC_CR_HSEON | RCC_CR_HSION;\n\n// Configure PLL (R=143.75, Q=47.92)\nRCC->CR &= ~RCC_CR_PLLON;\nwhile (RCC->CR & RCC_CR_PLLRDY) {\n}\nRCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE | RCC_PLLCFGR_PLLM_0 | (23 << RCC_PLLCFGR_PLLN_Pos) | RCC_PLLCFGR_PLLQ_1;\nRCC->PLLCFGR |= RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLQEN;\nRCC->CR |= RCC_CR_PLLON;\n\n// Select PLL as main clock, AHB/2 > otherwise Bus Error Hard Fault\nRCC->CFGR |= RCC_CFGR_HPRE_3 | RCC_CFGR_SW_PLL;\n\n// Select & Enable IO Clocks (PLL > USB, ADC; HSI16 > UART)\nRCC->CCIPR = RCC_CCIPR_CLK48SEL_0 | RCC_CCIPR_ADC12SEL_1 | RCC_CCIPR_USART1SEL_1 | RCC_CCIPR_USART2SEL_1 | RCC_CCIPR_USART3SEL_1 | RCC_CCIPR_UART4SEL_1;\nRCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN | RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN;\nRCC->APB1ENR1 |= RCC_APB1ENR1_USBEN | RCC_APB1ENR1_UART4EN | RCC_APB1ENR1_USART3EN | RCC_APB1ENR1_USART2EN;\nRCC->APB2ENR |= RCC_APB2ENR_USART1EN;\n\n// Enable DMAMUX & DMA1 Clock\nRCC->AHB1ENR |= RCC_AHB1ENR_DMAMUX1EN | RCC_AHB1ENR_DMA1EN;\nRun Code Online (Sandbox Code Playgroud)\n据我所知,USB BTable 和端点缓冲区需要放置在 USB-SRAM 中,而不是放置在常规 SRAM 中。我添加了一些链接器指令来为此创建一个部分,根据内存分析器,它似乎工作得很好。Mem2Usb 只是重新计算相对于 USB-SRAM 偏移的绝对偏移和相对偏移。
\n#define __USB_MEM __attribute__((section(".usbbuf")))\n#define __USBBUF_BEGIN 0x40006000\n#define __MEM2USB(X) (((int)X - __USBBUF_BEGIN))\nRun Code Online (Sandbox Code Playgroud)\n第一个问题:访问宽度仅允许为 16 字节。但是,与 STM32F103 等相反,看起来不需要填充。内存工具在显示此区域时存在一些问题,因为它仅处理 WORD 访问,而该工具使用 DWORD 访问,但逐字复制 HAL 分配的内存也不会显示填充。那是对的吗?所以我应该能够使用所有 1024 个字节,而不仅仅是看到它们,而是只有 512 个。这也是 mem2usb 不将地址除以 2 的原因。
\n然后我为 BTable 和零端点创建一些结构。BTable0x40006000默认结束于。根据 USB 规范,端点 0 有一个接收和发送缓冲区,最大容量为 64 字节。对齐方式取自参考手册。内存不会自动清零。
typedef struct {\n unsigned short ADDR_TX;\n unsigned short COUNT_TX;\n unsigned short ADDR_RX;\n unsigned short COUNT_RX;\n} USB_BTABLE_ENTRY;\n\n__ALIGNED(8)\n__USB_MEM\nstatic USB_BTABLE_ENTRY BTable[8] = {0};\n\n__ALIGNED(2)\n__USB_MEM\nstatic char EP0_Buf[2][64] = {0};\nRun Code Online (Sandbox Code Playgroud)\n启用 NVIC,然后上电,等待 1\xce\xbcs 直到时钟按照数据表稳定,然后清除复位状态,清除挂起的中断,启用中断,最后启用内部上拉以开始枚举。
\nNVIC_SetPriority(USB_HP_IRQn, 0);\nNVIC_SetPriority(USB_LP_IRQn, 0);\nNVIC_SetPriority(USBWakeUp_IRQn, 0);\nNVIC_EnableIRQ(USB_HP_IRQn);\nNVIC_EnableIRQ(USB_LP_IRQn);\nNVIC_EnableIRQ(USBWakeUp_IRQn);\n\nUSB->CNTR &= ~USB_CNTR_PDWN;\n\n// Wait 1\xce\xbcs until clock is stable\nSysTick->LOAD = 100;\nSysTick->VAL = 0;\nSysTick->CTRL = 1;\nwhile ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0) {\n}\nSysTick->CTRL = 0;\n\nUSB->CNTR &= ~USB_CNTR_FRES;\nUSB->ISTR = 0;\n\nUSB->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ESOFM;\nUSB->BCDR |= USB_BCDR_DPPU;\nRun Code Online (Sandbox Code Playgroud)\n现在主机发送复位信号,该信号被正确触发。在复位信号期间,我初始化 BTable 和 EP0。我将 EP0 设置为 RX 上的 ACK 和 TX 请求上的 NACK,其他裸机 USB 示例和 HAL 也是如此(它们是切换,而不是写入,但寄存器处于已知状态 0x00,因为硬件会在重置时重置它们) 。最后,我将 USB 外设置于启用模式并将设备地址重置为 0。
\nif ((USB->ISTR & USB_ISTR_RESET) != 0) {\n USB->ISTR = ~USB_ISTR_RESET;\n\n // Enable EP0\n USB->BTABLE = __MEM2USB(BTable);\n\n BTable[0].ADDR_TX = __MEM2USB(EP0_Buf[0]);\n BTable[0].COUNT_TX = 0;\n BTable[0].ADDR_RX = __MEM2USB(EP0_Buf[1]);\n BTable[0].COUNT_RX = (1 << 15) | (1 << 10);\n\n USB->EP0R = USB_EP_CONTROL | (2 << 4) | (3 << 12);\n USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;\n\n USB->DADDR = USB_DADDR_EF;\n}\nRun Code Online (Sandbox Code Playgroud)\n调试表明 BTable 确实位于0x40006000并且缓冲区地址已正确写入(我假设)。将 EP0 寄存器与工作的 HAL 实现进行比较,此时它们是相同的。
我希望主机接下来发送设备地址(它没有,它发送一个睡眠和一个唤醒,然后首先发送另一个重置),这将触发 CRT 中断(被屏蔽)。重点是,它永远不会。我不知道为什么。主机很好地发送了请求,设备也很好地发送了对该请求的 ACK(逻辑分析仪),但 CRT 从未被触发。任何想法我还可以尝试什么或去哪里寻找?
\n更新
\n现在,我已将我的实现中的消息与 HAL 的消息进行了比较。现在,中断以完全相同的顺序处理完全相同的消息,并且 USB 寄存器还包含每个请求的完全相同的值。我已更改 BTable 和 USB-SRAM 布局,以包含与复位中断后的 HAL 完全相同的值。
\n我必须实现SUSP并WKUP才能使其发挥作用,这可能是缺少的东西之一。现在他们的行为完全一样。事实证明,问题是我从未收到过合适的 SOF 包。HALSOF在第二次重置后直接获得第一个 (HW-Reset > 2x ESOF > SUSP > WKUP > RESET > (Optional 1 ESOF) > SOF),而我的则获得一个ERR而不是SOF.
看起来该错误与 USB 寄存器或 USB-SRAM 无关。下一步将是比较我认为两个实现之间相关的所有寄存器。也许我忘记了一个时钟?
\n花费将近一周的时间。只是为了弄清楚我错误配置了 48MHz 时钟源......
RCC->CCIPR = RCC_CCIPR_CLK48SEL_0 | ...
Run Code Online (Sandbox Code Playgroud)
这会将 CLK48SEL 设置为Reserved(01),而不是 PLLQ 时钟 (10)...
RCC->CCIPR = RCC_CCIPR_CLK48SEL_1 | ...
Run Code Online (Sandbox Code Playgroud)
现在我收到SOF包裹了,一切CTR顺利。希望这个问题可以作为未来 USB 裸机的参考。