c-STM32裸金属USB实现



UPDATE对于任何感兴趣的人,这里有一个关于如何构建裸金属USB堆栈、如何处理这样的项目以及每一步需要了解的内容的分步说明和解释:STM32USB@GitHub

TLDR:我有一个STM32G441,我想在不使用任何HAL库的情况下实现一个USB驱动程序,只使用CMSIS——为了学习经验、空间,因为我想做的事情无论如何都需要更改HAL。

但我不能让这个东西收到任何东西。我在试图获取设备地址时遇到了麻烦,但从未将其交给代码。hal中间件工作得很好,所以它不是硬件问题。

我在做什么

我正在启用USB时钟(我认为是正确的,因为它可以使用我的逻辑分析器发送ACK信号),按照数据表中的定义打开USB外围设备,启用所有必要的中断,并通过初始化BTable和Endpoint 0来处理重置事件。现在我希望收到一个CTR中断,它永远不会出现。

参考手册

时钟

μC在25MHz HSE时钟上运行。USB外围设备在~48MHz的PLL Q时钟上运行,RCC设置通过CubeMX时钟配置器进行验证。AHB以半速运行,因为如果我试图以全速运行,我会遇到总线错误硬故障,但这是另一个问题。系统时钟设置为143.75MHz。

RCC->CR |= RCC_CR_HSEON | RCC_CR_HSION;
// Configure PLL (R=143.75, Q=47.92)
RCC->CR &= ~RCC_CR_PLLON;
while (RCC->CR & RCC_CR_PLLRDY) {
}
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE | RCC_PLLCFGR_PLLM_0 | (23 << RCC_PLLCFGR_PLLN_Pos) | RCC_PLLCFGR_PLLQ_1;
RCC->PLLCFGR |= RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLQEN;
RCC->CR |= RCC_CR_PLLON;
// Select PLL as main clock, AHB/2 > otherwise Bus Error Hard Fault
RCC->CFGR |= RCC_CFGR_HPRE_3 | RCC_CFGR_SW_PLL;
// Select & Enable IO Clocks (PLL > USB, ADC; HSI16 > UART)
RCC->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;
RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN | RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN;
RCC->APB1ENR1 |= RCC_APB1ENR1_USBEN | RCC_APB1ENR1_UART4EN | RCC_APB1ENR1_USART3EN | RCC_APB1ENR1_USART2EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// Enable DMAMUX & DMA1 Clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMAMUX1EN | RCC_AHB1ENR_DMA1EN;

USB存储器

据我所知,USB BTable和端点缓冲区需要放置在USB-SRAM中,而不是常规SRAM中。我添加了一些链接器指令来创建一个部分,根据内存分析器的说法,这个部分似乎工作得很好。Mem2Usb只是重新计算从绝对到相对USB-SRAM偏移量的偏移量。

#define __USB_MEM __attribute__((section(".usbbuf")))
#define __USBBUF_BEGIN 0x40006000
#define __MEM2USB(X) (((int)X - __USBBUF_BEGIN))

第一个问题:访问权限只能为16字节宽。但是,与例如STM32F103相反,似乎不需要填充。内存工具在显示该区域时存在一些问题,因为它只处理WORD访问,而该工具使用DWORD访问,但逐字复制HAL分配的内存也没有显示填充。这是正确的吗?所以我应该能够使用所有1024个字节,而不仅仅是看到它们,而且只有512个。这也是mem2usb不将地址除以2的原因。

然后我为BTable和零端点创建了一些结构。默认情况下,BTable以0x40006000结束。端点0有一个rx和一个tx缓冲区,根据USB规范,最大64字节。对齐方式取自参考手册。内存不会自动清零。

typedef struct {
unsigned short ADDR_TX;
unsigned short COUNT_TX;
unsigned short ADDR_RX;
unsigned short COUNT_RX;
} USB_BTABLE_ENTRY;
__ALIGNED(8)
__USB_MEM
static USB_BTABLE_ENTRY BTable[8] = {0};
__ALIGNED(2)
__USB_MEM
static char EP0_Buf[2][64] = {0};

初始化

启用NVIC,然后通电,等待1μs,直到时钟根据数据表稳定,然后清除重置状态,清除挂起的中断,启用中断,最后启用内部上拉以启动枚举。

NVIC_SetPriority(USB_HP_IRQn, 0);
NVIC_SetPriority(USB_LP_IRQn, 0);
NVIC_SetPriority(USBWakeUp_IRQn, 0);
NVIC_EnableIRQ(USB_HP_IRQn);
NVIC_EnableIRQ(USB_LP_IRQn);
NVIC_EnableIRQ(USBWakeUp_IRQn);
USB->CNTR &= ~USB_CNTR_PDWN;
// Wait 1μs until clock is stable
SysTick->LOAD = 100;
SysTick->VAL = 0;
SysTick->CTRL = 1;
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0) {
}
SysTick->CTRL = 0;
USB->CNTR &= ~USB_CNTR_FRES;
USB->ISTR = 0;
USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_WKUPM | USB_CNTR_SUSPM | USB_CNTR_ESOFM;
USB->BCDR |= USB_BCDR_DPPU;

USB重置

现在主机发送一个复位信号,该信号被正确触发。在复位信号期间,我初始化BTable和EP0。我在RX上将EP0设置为ACK,在TX请求上设置为NACK,其他裸机USB示例和HAL也是如此(它们是切换的,而不是写入的,但当硬件在重置时重置它们时,寄存器处于0x00的已知状态)。最后,我将USB外设置于启用模式,并将设备地址重置为0。

if ((USB->ISTR & USB_ISTR_RESET) != 0) {
USB->ISTR = ~USB_ISTR_RESET;
// Enable EP0
USB->BTABLE = __MEM2USB(BTable);
BTable[0].ADDR_TX = __MEM2USB(EP0_Buf[0]);
BTable[0].COUNT_TX = 0;
BTable[0].ADDR_RX = __MEM2USB(EP0_Buf[1]);
BTable[0].COUNT_RX = (1 << 15) | (1 << 10);
USB->EP0R = USB_EP_CONTROL | (2 << 4) | (3 << 12);
USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->DADDR = USB_DADDR_EF;
}

调试表明BTable确实位于0x40006000,并且Buffer地址写入(我认为)正确。将EP0寄存器与工作的HAL实现进行比较,并且它们在这一点上是相同的。

我被卡住了

我希望主机接下来发送设备地址(它没有,它先发送睡眠和唤醒,然后再发送另一个重置),这将触发CRT中断(被屏蔽)。重点是,它从来没有。我不知道为什么。主机很好地发送请求,设备也很好地对该请求发送ACK(逻辑分析器),但CRT从未被触发。有什么想法吗?我还可以尝试什么,或者去哪里看?

更新

我现在已经将来自我的实现的消息与HAL消息进行了比较。中断现在以完全相同的顺序处理完全相同的消息,USB寄存器也为每个请求包含完全相同的值。我已经更改了BTable和USB-SRAM布局,以包含与重置中断后的HAL完全相同的值。

我必须实现SUSPWKUP才能工作,这可能是缺少的东西之一。现在他们俩的行为完全一样。事实证明,问题是我从来没有收到一个合适的SOF包。HAL在第二次重置之后直接获得其第一个SOF(HW reset>2x ESOF>SUSP>WKUP>reset>(可选的1个ESOF)>);SOF),而我的得到ERR而不是SOF

该错误似乎与USB寄存器或USB-SRAM无关。下一步将比较我认为在这两个实现之间相关的所有寄存器。也许我忘了带时钟了?

花将近一周的时间。只是想弄清楚我的48MHz时钟源配置错误。。。

RCC->CCIPR = RCC_CCIPR_CLK48SEL_0 | ...

这将CLK48SEL设置为Reserved(01),而不是PLLQ时钟(10)。。。

RCC->CCIPR = RCC_CCIPR_CLK48SEL_1 | ...

现在我得到了SOF包和CTR。愿这个问题在未来成为USB裸机的参考。

相关内容

  • 没有找到相关文章

最新更新