我们知道BRO-DBG-LINK V2.1开发板是带有USB接口的,这使得我们可以在没有调试器的情况下,通过它来下载和运行固件。也就是说我们可以写一个bootloader, 通过这个bootloader来下载最新的固件,同时通过它来加载运行新的固件。
同时,之前也有提到过如何在ST-LINK/V2-1调试器上运行maple bootloader,当然这个方法也适用于BRO-DBG-LINK:
当然,之前提供的bootloader是存在一些小小的问题- 有时候这个USB外设不能被主机很好的识别出来:如在bootloader阶段,电脑会将该USB外设识别成一个DFU设备,这样我们就可以通过dfu-util来下载固件,下载后运行固件的时候,USB外设又会作为一个虚拟串口与主机进行通信。显然,在运行固件时USB权举出现了问题。这时候,我想到的是OpenPilot.org/TauLabs.org系列产品使用的也是ST公司的STM32系列MCU, 就可以很好地解决这个问题: 在bootloader阶段被识别成一个HID外设,在固件运行阶段可以被识别成一个HID+VCOM外设。
这里我们来看看CC3D是如何实现的-使用的是完全相同的MCU: STM32F103CBT6。
- USB协议相关
TODO
- CC3D的做法
我们先来看看CC3D是如何实现USB设备的重新枚举的:
1. 相关的电路如下图:
可以看到USB接口只使用到了两根的信号线PA11-USB-DM与PA12-USB-DP,并且PA12-USB-DP信号线通过R14 1.5K电阻上拉至VCC。
完整的电路图请看这里:
https://github.com/TauLabs/TauLabs/blob/next/flight/targets/coptercontrol/hw/CC3D/CopterControl%203D%20Schematic.pdf
2. 相关的代码如下(CC3D使用的MCU为STM32F103RBT6):
https://github.com/TauLabs/TauLabs/blob/next/flight/PiOS/STM32F10x/pios_usb.c
// ...
/**
* Initialises USB COM layer
* \return < 0 if initialisation failed
* \note Applications shouldn't call this function directly, instead please use \ref PIOS_COM layer functions
*/
static uintptr_t pios_usb_com_id;
int32_t PIOS_USB_Init(uintptr_t * usb_id, const struct pios_usb_cfg * cfg)
{
PIOS_Assert(usb_id);
PIOS_Assert(cfg);
struct pios_usb_dev * usb_dev;
usb_dev = (struct pios_usb_dev *) PIOS_USB_alloc();
if (!usb_dev) goto out_fail;
/* Bind the configuration to the device instance */
usb_dev->cfg = cfg;
PIOS_USB_Reenumerate();
/*
* This is a horrible hack to make this available to
* the interrupt callbacks. This should go away ASAP.
*/
pios_usb_com_id = (uintptr_t) usb_dev;
/* Enable the USB Interrupts */
NVIC_Init((NVIC_InitTypeDef*)&usb_dev->cfg->irq.init);
/* Select USBCLK source */
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
/* Enable the USB clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
USB_Init();
USB_SIL_Init();
*usb_id = (uintptr_t) usb_dev;
return 0; /* No error */
out_fail:
return(-1);
}
// ...
int32_t PIOS_USB_Reenumerate()
{
/* Force USB reset and power-down (this will also release the USB pins for direct GPIO control) */
_SetCNTR(CNTR_FRES | CNTR_PDWN);
/* Using a "dirty" method to force a re-enumeration: */
/* Force DPM (Pin PA12) low for ca. 10 mS before USB Tranceiver will be enabled */
/* This overrules the external Pull-Up at PA12, and at least Windows & MacOS will enumerate again */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PIOS_DELAY_WaitmS(50);
/* Release power-down, still hold reset */
_SetCNTR(CNTR_PDWN);
PIOS_DELAY_WaituS(5);
/* CNTR_FRES = 0 */
_SetCNTR(0);
/* Clear pending interrupts */
_SetISTR(0);
/* Configure USB clock */
/* USBCLK = PLLCLK / 1.5 */
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
/* Enable USB clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
return 0;
}
// ...
从代码可以看到这里的做法是将USB的DP信号线强制拉低10ms, 使得Windows/Mac OS重新去枚举USB外设。
- BRO-DBG-LINK V2.1开发板的做法
从原理图中可以看到有一个IO信号控制着一个NPN的三极管来上拉USB_DP的信号,由于三极管状态的改变并不会使得Windows或者是Mac OS去重新枚举这个设备,所以我们可以将这个NPN三极管一直设为导通状态。其余的做法就与CC3D的相同。
1. maple-bootloader中相关代码的修改:
void setupUSB(void) {
u32 rwmVal; /* read-write-modify place holder var */
/* Setup the USB DISC Pin: GPIOA & AFIO clock enable */
rwmVal = GET_REG(RCC_APB2ENR);
rwmVal |= 0x00000005;
SET_REG(RCC_APB2ENR, rwmVal);
// JTAG-DP Disabled and SW-DP Enabled: Init GPIOA pin15
rwmVal = GET_REG(AFIO_MAPR);
rwmVal &= ~AFIO_MAPR_SWJ_CFG;
rwmVal |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE;
SET_REG(AFIO_MAPR, rwmVal);
rwmVal = GET_REG(GPIO_ODR(GPIOA));
rwmVal &= ~0x00008000;
SET_REG(GPIO_ODR(GPIOA), rwmVal); // disconnect
/* Setup GPIOA Pin 15 as Open Drain output */
rwmVal = GET_REG(GPIO_CRH(GPIOA));
rwmVal &= 0x0FFFFFFF;
rwmVal |= 0x60000000;
SET_REG(GPIO_CRH(GPIOA), rwmVal);
// Wait ~190ms to indicate USB disconnection
for (rwmVal = 0; rwmVal < 0x100000; rwmVal++) {
asm volatile("nop");
}
/* USB clock enable */
pRCC->APB1ENR |= 0x00800000;
/* initialize the usb application */
setPin(GPIOA, 15); /* present ourselves to the host */
usbAppInit();
}
NOTE: TODO
2. ChibiOS/RT中相关代码的修改
int __attribute__((noreturn)) main(void) {
// ...
/*
* Activates the USB driver and then the USB bus pull-up on D+.
* Note, a delay is inserted in order to not have to disconnect the cable
* after a reset.
*/
usbDisconnectBus(serusbcfg.usbp);
chThdSleepMilliseconds(1500);
usbConnectBus(serusbcfg.usbp);
/* Force USB reset and power-down (this will also release the USB pins for direct GPIO control) */
STM32_USB->CNTR = CNTR_FRES | CNTR_PDWN;
/* Using a "dirty" method to force a re-enumeration: */
/* Force DPM (Pin PA12) low for ca. 10 mS before USB Tranceiver will be enabled */
/* This overrules the external Pull-Up at PA12, and at least Windows & MacOS will enumerate again */
palSetPadMode(GPIOA, GPIOA_USBDP, PAL_MODE_OUTPUT_PUSHPULL);
chThdSleepMilliseconds(50);
/* Release power-down, still hold reset */
STM32_USB->CNTR = CNTR_PDWN;
chThdSleepMilliseconds(1);
/* CNTR_FRES = 0 */
STM32_USB->CNTR = 0;
/* Clear pending interrupts */
STM32_USB->ISTR = 0;
usbStart(serusbcfg.usbp, &usbcfg);
// ...
}
- 相关的参考文档
- https://github.com/openpilot/OpenPilot
- https://github.com/TauLabs/TauLabs
- https://github.com/TauLabs/TauLabs/blob/next/flight/targets/coptercontrol/hw/CC3D/CopterControl%203D%20Schematic.pdf

