Add L052/dfu bootloader.
This commit is contained in:
parent
f767e7d75b
commit
6f1916fc2a
|
@ -10,6 +10,9 @@ They aren't too specific for the bat boards, except the usual pinout considerati
|
|||
|
||||
The board presents itself as a serial port (CDC). It echoes back what it receives; toggling the LED after each received packet. Pressing the `PA1` button will print `hello, world` over this port.
|
||||
|
||||
### usb_dfu
|
||||
|
||||
L052 only. This is the DFU bootloader that I use on the bat boards with L052. More info about it on [the bat board] main page.
|
||||
|
||||
## General setup notes
|
||||
|
||||
|
|
|
@ -27,20 +27,20 @@
|
|||
#include <libopencm3/usb/cdc.h>
|
||||
|
||||
static const struct usb_device_descriptor dev = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = USB_CLASS_CDC,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.idVendor = 0x0483,
|
||||
.idProduct = 0x5740,
|
||||
.bcdDevice = 0x0200,
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1,
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = USB_CLASS_CDC,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.idVendor = 0x0483,
|
||||
.idProduct = 0x5740,
|
||||
.bcdDevice = 0x0200,
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -49,121 +49,121 @@ static const struct usb_device_descriptor dev = {
|
|||
* cdc_acm driver.
|
||||
*/
|
||||
static const struct usb_endpoint_descriptor comm_endp[] = {{
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x83,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||
.wMaxPacketSize = 16,
|
||||
.bInterval = 255,
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x83,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||
.wMaxPacketSize = 16,
|
||||
.bInterval = 255,
|
||||
}};
|
||||
|
||||
static const struct usb_endpoint_descriptor data_endp[] = {{
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
}, {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x82,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = 0x82,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
}};
|
||||
|
||||
static const struct {
|
||||
struct usb_cdc_header_descriptor header;
|
||||
struct usb_cdc_call_management_descriptor call_mgmt;
|
||||
struct usb_cdc_acm_descriptor acm;
|
||||
struct usb_cdc_union_descriptor cdc_union;
|
||||
struct usb_cdc_header_descriptor header;
|
||||
struct usb_cdc_call_management_descriptor call_mgmt;
|
||||
struct usb_cdc_acm_descriptor acm;
|
||||
struct usb_cdc_union_descriptor cdc_union;
|
||||
} __attribute__((packed)) cdcacm_functional_descriptors = {
|
||||
.header = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_HEADER,
|
||||
.bcdCDC = 0x0110,
|
||||
},
|
||||
.call_mgmt = {
|
||||
.bFunctionLength =
|
||||
sizeof(struct usb_cdc_call_management_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
|
||||
.bmCapabilities = 0,
|
||||
.bDataInterface = 1,
|
||||
},
|
||||
.acm = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_ACM,
|
||||
.bmCapabilities = 0,
|
||||
},
|
||||
.cdc_union = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_UNION,
|
||||
.bControlInterface = 0,
|
||||
.bSubordinateInterface0 = 1,
|
||||
}
|
||||
.header = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_header_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_HEADER,
|
||||
.bcdCDC = 0x0110,
|
||||
},
|
||||
.call_mgmt = {
|
||||
.bFunctionLength =
|
||||
sizeof(struct usb_cdc_call_management_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT,
|
||||
.bmCapabilities = 0,
|
||||
.bDataInterface = 1,
|
||||
},
|
||||
.acm = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_acm_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_ACM,
|
||||
.bmCapabilities = 0,
|
||||
},
|
||||
.cdc_union = {
|
||||
.bFunctionLength = sizeof(struct usb_cdc_union_descriptor),
|
||||
.bDescriptorType = CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_CDC_TYPE_UNION,
|
||||
.bControlInterface = 0,
|
||||
.bSubordinateInterface0 = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct usb_interface_descriptor comm_iface[] = {{
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_CDC,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
|
||||
.iInterface = 0,
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_CDC,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
|
||||
.iInterface = 0,
|
||||
|
||||
.endpoint = comm_endp,
|
||||
.endpoint = comm_endp,
|
||||
|
||||
.extra = &cdcacm_functional_descriptors,
|
||||
.extralen = sizeof(cdcacm_functional_descriptors)
|
||||
.extra = &cdcacm_functional_descriptors,
|
||||
.extralen = sizeof(cdcacm_functional_descriptors)
|
||||
}};
|
||||
|
||||
static const struct usb_interface_descriptor data_iface[] = {{
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 1,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_DATA,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.iInterface = 0,
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 1,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_DATA,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.iInterface = 0,
|
||||
|
||||
.endpoint = data_endp,
|
||||
.endpoint = data_endp,
|
||||
}};
|
||||
|
||||
static const struct usb_interface ifaces[] = {{
|
||||
.num_altsetting = 1,
|
||||
.altsetting = comm_iface,
|
||||
.num_altsetting = 1,
|
||||
.altsetting = comm_iface,
|
||||
}, {
|
||||
.num_altsetting = 1,
|
||||
.altsetting = data_iface,
|
||||
.num_altsetting = 1,
|
||||
.altsetting = data_iface,
|
||||
}};
|
||||
|
||||
static const struct usb_config_descriptor config = {
|
||||
.bLength = USB_DT_CONFIGURATION_SIZE,
|
||||
.bDescriptorType = USB_DT_CONFIGURATION,
|
||||
.wTotalLength = 0,
|
||||
.bNumInterfaces = 2,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 0x32,
|
||||
.bLength = USB_DT_CONFIGURATION_SIZE,
|
||||
.bDescriptorType = USB_DT_CONFIGURATION,
|
||||
.wTotalLength = 0,
|
||||
.bNumInterfaces = 2,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 0x32,
|
||||
|
||||
.interface = ifaces,
|
||||
.interface = ifaces,
|
||||
};
|
||||
|
||||
static const char *usb_strings[] = {
|
||||
"flabbergast",
|
||||
"CDC-ACM Test",
|
||||
"DeMo",
|
||||
"flabbergast",
|
||||
"CDC-ACM Test",
|
||||
"DeMo",
|
||||
};
|
||||
|
||||
/* Buffer to be used for control requests. */
|
||||
|
@ -172,163 +172,165 @@ uint8_t usbd_control_buffer[128];
|
|||
/* Aux functions */
|
||||
static void led_flip(void) {
|
||||
if(GPIOA_IDR & (1<<15)) {
|
||||
GPIOA_BRR = GPIO15;
|
||||
GPIOA_BRR = GPIO15;
|
||||
} else {
|
||||
GPIOA_BSRR = GPIO15;
|
||||
GPIOA_BSRR = GPIO15;
|
||||
}
|
||||
}
|
||||
|
||||
static int cdcacm_control_request(usbd_device *usbd_dev,
|
||||
struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
|
||||
void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
|
||||
struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
|
||||
void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
|
||||
{
|
||||
(void)complete;
|
||||
(void)buf;
|
||||
(void)usbd_dev;
|
||||
(void)complete;
|
||||
(void)buf;
|
||||
(void)usbd_dev;
|
||||
|
||||
switch(req->bRequest) {
|
||||
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: {
|
||||
/*
|
||||
* This Linux cdc_acm driver requires this to be implemented
|
||||
* even though it's optional in the CDC spec, and we don't
|
||||
* advertise it in the ACM functional descriptor.
|
||||
*/
|
||||
char local_buf[10];
|
||||
struct usb_cdc_notification *notif = (void *)local_buf;
|
||||
switch(req->bRequest) {
|
||||
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: {
|
||||
/*
|
||||
* This Linux cdc_acm driver requires this to be implemented
|
||||
* even though it's optional in the CDC spec, and we don't
|
||||
* advertise it in the ACM functional descriptor.
|
||||
*/
|
||||
char local_buf[10];
|
||||
struct usb_cdc_notification *notif = (void *)local_buf;
|
||||
|
||||
/* We echo signals back to host as notification. */
|
||||
notif->bmRequestType = 0xA1;
|
||||
notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
|
||||
notif->wValue = 0;
|
||||
notif->wIndex = 0;
|
||||
notif->wLength = 2;
|
||||
local_buf[8] = req->wValue & 3;
|
||||
local_buf[9] = 0;
|
||||
// usbd_ep_write_packet(0x83, buf, 10);
|
||||
return 1;
|
||||
}
|
||||
case USB_CDC_REQ_SET_LINE_CODING:
|
||||
if(*len < sizeof(struct usb_cdc_line_coding))
|
||||
return 0;
|
||||
/* We echo signals back to host as notification. */
|
||||
notif->bmRequestType = 0xA1;
|
||||
notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
|
||||
notif->wValue = 0;
|
||||
notif->wIndex = 0;
|
||||
notif->wLength = 2;
|
||||
local_buf[8] = req->wValue & 3;
|
||||
local_buf[9] = 0;
|
||||
// usbd_ep_write_packet(0x83, buf, 10);
|
||||
return 1;
|
||||
}
|
||||
case USB_CDC_REQ_SET_LINE_CODING:
|
||||
if(*len < sizeof(struct usb_cdc_line_coding))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
|
||||
{
|
||||
(void)ep;
|
||||
(void)ep;
|
||||
|
||||
char buf[64];
|
||||
int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);
|
||||
char buf[64];
|
||||
int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);
|
||||
|
||||
if (len) {
|
||||
usbd_ep_write_packet(usbd_dev, 0x82, buf, len);
|
||||
buf[len] = 0;
|
||||
led_flip();
|
||||
}
|
||||
if (len) {
|
||||
usbd_ep_write_packet(usbd_dev, 0x82, buf, len);
|
||||
buf[len] = 0;
|
||||
led_flip();
|
||||
}
|
||||
}
|
||||
|
||||
static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
||||
{
|
||||
(void)wValue;
|
||||
(void)wValue;
|
||||
|
||||
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
|
||||
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
|
||||
usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
|
||||
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, cdcacm_data_rx_cb);
|
||||
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
|
||||
usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
|
||||
|
||||
usbd_register_control_callback(
|
||||
usbd_dev,
|
||||
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
|
||||
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
|
||||
cdcacm_control_request);
|
||||
usbd_register_control_callback(
|
||||
usbd_dev,
|
||||
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
|
||||
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
|
||||
cdcacm_control_request);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
usbd_device *usbd_dev;
|
||||
usbd_device *usbd_dev;
|
||||
|
||||
#if defined(BOOTLOADER8K)
|
||||
# define SCB_VTOR MMIO32(0xE000ED08)
|
||||
SCB_VTOR = (uint32_t) 0x08002000;
|
||||
# define SCB_VTOR MMIO32(0xE000ED08)
|
||||
SCB_VTOR = (uint32_t) 0x08002000;
|
||||
#endif
|
||||
|
||||
#if defined(STM32F0)
|
||||
rcc_clock_setup_in_hsi_out_48mhz();
|
||||
crs_autotrim_usb_enable();
|
||||
rcc_set_usbclk_source(RCC_HSI48);
|
||||
rcc_clock_setup_in_hsi_out_48mhz();
|
||||
crs_autotrim_usb_enable();
|
||||
rcc_set_usbclk_source(RCC_HSI48);
|
||||
|
||||
// this is needed for F042 in TSSOP-20 package; USB is on shared pins
|
||||
//rcc_periph_clock_enable(RCC_SYSCFG_COMP);
|
||||
//SYSCFG_CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
|
||||
// this is needed for F042 in TSSOP-20 package; USB is on shared pins
|
||||
//rcc_periph_clock_enable(RCC_SYSCFG_COMP);
|
||||
//SYSCFG_CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
|
||||
|
||||
#elif defined(STM32L0)
|
||||
|
||||
// libopencm3 clock setup can be a bit ad-hoc, so let's configure manually
|
||||
// 32MHz, PLL supplied by the internal HSI16 oscillator
|
||||
/* Set the flash latency */
|
||||
FLASH_ACR |= FLASH_ACR_LATENCY_1WS;
|
||||
/* Turn on HSI16 */
|
||||
RCC_CR |= RCC_CR_HSI16ON;
|
||||
// libopencm3 clock setup can be a bit ad-hoc, so let's configure manually
|
||||
// 32MHz, PLL supplied by the internal HSI16 oscillator
|
||||
/* Set the flash latency */
|
||||
FLASH_ACR |= FLASH_ACR_LATENCY_1WS;
|
||||
/* Turn on HSI16 */
|
||||
RCC_CR |= RCC_CR_HSI16ON;
|
||||
while ((RCC_CR & RCC_CR_HSI16RDY) == 0);
|
||||
/* Make sure PLL is off (it should be after reset, but ...) */
|
||||
RCC_CR &= ~RCC_CR_PLLON;
|
||||
/* Make sure PLL is off (it should be after reset, but ...) */
|
||||
RCC_CR &= ~RCC_CR_PLLON;
|
||||
while (RCC_CR & RCC_CR_PLLRDY);
|
||||
/* Set the PLL source to HSI16 */
|
||||
RCC_CFGR &= ~(1<<16); // RCC_CFGR_PLLSRC
|
||||
/* Set up the PLL */
|
||||
uint32_t reg = RCC_CFGR
|
||||
& ~( (RCC_CFGR_PLLMUL_MASK << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_MASK << RCC_CFGR_PLLDIV_SHIFT));
|
||||
RCC_CFGR = reg | (RCC_CFGR_PLLMUL_MUL4 << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_DIV2 << RCC_CFGR_PLLDIV_SHIFT);
|
||||
/* Turn on PLL and switch to it */
|
||||
RCC_CR |= RCC_CR_PLLON;
|
||||
while ((RCC_CR & RCC_CR_PLLRDY) == 0);
|
||||
RCC_CFGR |= RCC_CFGR_SW_PLL;
|
||||
/* Set the peripheral clock frequencies used. */
|
||||
rcc_ahb_frequency = 32000000;
|
||||
rcc_apb1_frequency = 32000000;
|
||||
rcc_apb2_frequency = 32000000;
|
||||
// end of manual clock configuration
|
||||
/* Set the PLL source to HSI16 */
|
||||
RCC_CFGR &= ~(1<<16); // RCC_CFGR_PLLSRC
|
||||
/* Set up the PLL */
|
||||
uint32_t reg = RCC_CFGR
|
||||
& ~( (RCC_CFGR_PLLMUL_MASK << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_MASK << RCC_CFGR_PLLDIV_SHIFT));
|
||||
RCC_CFGR = reg | (RCC_CFGR_PLLMUL_MUL4 << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_DIV2 << RCC_CFGR_PLLDIV_SHIFT);
|
||||
/* Turn on PLL and switch to it */
|
||||
RCC_CR |= RCC_CR_PLLON;
|
||||
while ((RCC_CR & RCC_CR_PLLRDY) == 0);
|
||||
RCC_CFGR |= RCC_CFGR_SW_PLL;
|
||||
/* Set the peripheral clock frequencies used. */
|
||||
rcc_ahb_frequency = 32000000;
|
||||
rcc_apb1_frequency = 32000000;
|
||||
rcc_apb2_frequency = 32000000;
|
||||
// end of manual clock configuration
|
||||
|
||||
// Enable VREFINT reference for HSI48
|
||||
rcc_periph_clock_enable(RCC_SYSCFG);
|
||||
SYSCFG_CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
|
||||
// Enable VREFINT reference for HSI48
|
||||
rcc_periph_clock_enable(RCC_SYSCFG);
|
||||
SYSCFG_CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
|
||||
|
||||
rcc_set_hsi48_source_rc48();
|
||||
crs_autotrim_usb_enable();
|
||||
rcc_set_hsi48_source_rc48();
|
||||
crs_autotrim_usb_enable();
|
||||
|
||||
rcc_osc_on(RCC_HSI48);
|
||||
rcc_wait_for_osc_ready(RCC_HSI48);
|
||||
rcc_osc_on(RCC_HSI48);
|
||||
rcc_wait_for_osc_ready(RCC_HSI48);
|
||||
|
||||
/* MCO on PA9, to verify MCU speed with a scope */
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
|
||||
gpio_set_af(GPIOA, GPIO_AF0, GPIO9);
|
||||
RCC_CFGR |= (RCC_CFGR_MCO_SYSCLK << RCC_CFGR_MCO_SHIFT) | (RCC_CFGR_MCOPRE_DIV16 << 28);
|
||||
/* MCO on PA9, to verify MCU speed with a scope */
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
|
||||
gpio_set_af(GPIOA, GPIO_AF0, GPIO9);
|
||||
RCC_CFGR |= (RCC_CFGR_MCO_SYSCLK << RCC_CFGR_MCO_SHIFT) | (RCC_CFGR_MCOPRE_DIV16 << 28);
|
||||
|
||||
#else
|
||||
#error "Edit the sources and do the clock setup!"
|
||||
#error "Edit the sources and do the clock setup!"
|
||||
#endif
|
||||
|
||||
rcc_periph_clock_enable(RCC_GPIOA);
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15); // LED
|
||||
rcc_periph_clock_enable(RCC_GPIOA);
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15); // LED
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO1); // button, has external pull-down
|
||||
|
||||
usbd_dev = usbd_init(&st_usbfs_v2_usb_driver, &dev, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));
|
||||
usbd_dev = usbd_init(&st_usbfs_v2_usb_driver, &dev, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));
|
||||
|
||||
usbd_register_set_config_callback(usbd_dev, cdcacm_set_config);
|
||||
usbd_register_set_config_callback(usbd_dev, cdcacm_set_config);
|
||||
|
||||
char greeting[16] = "hello, world\r\n";
|
||||
char greeting[16] = "hello, world\r\n";
|
||||
|
||||
while (1) {
|
||||
usbd_poll(usbd_dev);
|
||||
while (1) {
|
||||
usbd_poll(usbd_dev);
|
||||
if (gpio_get(GPIOA, GPIO1)) {
|
||||
usbd_ep_write_packet(usbd_dev, 0x82, greeting, 15);
|
||||
usbd_ep_write_packet(usbd_dev, 0x82, greeting, 15);
|
||||
for (uint32_t i = 0; i < 2000000; i++) { /* Wait a bit, cheap "debounce" */
|
||||
__asm__("nop");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim: tabstop=4:shiftwidth=4:noexpandtab
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
##
|
||||
## This file is part of the libopencm3 project.
|
||||
##
|
||||
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
##
|
||||
## This library is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public License
|
||||
## along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
BINARY = usbdfu
|
||||
|
||||
# OBJS += hardfault.o
|
||||
|
||||
OPENCM3_DIR=../libopencm3
|
||||
LDSCRIPT = $(OPENCM3_DIR)/lib/stm32/l0/stm32l0xx8.ld
|
||||
CSTD = -std=gnu99
|
||||
|
||||
include ../common.l0.mk
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# README
|
||||
|
||||
This example implements a USB Device Firmware Upgrade (DFU) bootloader
|
||||
to demonstrate the use of the USB device stack.
|
||||
|
||||
It is intended to run on STM32L0 chips; this particular instance on STM32L052C8Tx.
|
||||
|
||||
## Usage notes
|
||||
|
||||
The bootloader has been tested on a STM32L052C8T7, it takes 8kB of the flash space. It has been tested with [dfu-util] 0.9 on Mac OS X. The usual command is something like
|
||||
|
||||
dfu-util -s 0x8002000 -D <binary_fw>
|
||||
|
||||
The "user firmware" needs to be compiled to start at 0x8002000 (i.e. the vector table should be there), and set `SCB_VTOR` to 0x8002000 immediately after starting.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
To modify for another chip, the particular places to check are:
|
||||
|
||||
* `MCU_PAGE_SIZE` define: check the reference manual for page size (usually in the section "NVM organization"). The code assumes that the page size is the same for the whole part where the bootloader will write (i.e. from 0x8002000 to the end of flash).
|
||||
* The dfuse string in `usb_strings`. This describes the various parts of the flash, and the DfuSe app or dfu-util will use this to determine e.g. the page size, or which parts of the flash are writable.
|
||||
* The clock setup in `main()`.
|
||||
* The easy bits in `main()`: which pin to be checked (and how) for forcing the bootloader, which pin to blink on and how fast to blink.
|
||||
|
||||
* The `PROGRAM_BY_HALF_PAGE` define decides whether to write to flash word-by-word, or a half-a-page at once.
|
||||
* `hardfault.c` hooks into the hard fault handler, and defines some variables to make it easy(er) to figure out what caused the fault, in e.g. gdb. To enable this, just uncomment it in `OBJS` in the `Makefile`.
|
||||
|
||||
## Beware
|
||||
|
||||
The programming speed is about 3.2Kb/sec (compared to 1440 bytes/sec over the ROM USART bootloader using 115200 baud; and to 3K-6K/sec via SWD). It is achieved by pretending (over DFU) that the page size is 1K (instead of the internal 128 bytes) - the bottleneck turned out to be the USB.
|
||||
|
||||
Also it uses writing-half-a-page-at-once from RAM, instead of the (slower) option of writing word-by-word. (With word-by-word, pretending 1K blocks, it's 950 bytes/sec, with word-by-word OR half-a-page, but native 128 byte blocks. it's 420 bytes/sec).
|
||||
|
||||
## Credits
|
||||
|
||||
[Original code]: Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
|
||||
|
||||
Modifications to run on STM32L0: (C) 2016 flabbergast <flabbergast@drak.xyz>
|
||||
|
||||
[Original code]: https://github.com/libopencm3/libopencm3-examples/tree/8924042d2acd8b0a9dd7c307a3ea925cca87d554/examples/stm32/f1/other/usb_dfu
|
||||
[dfu-util]: http://dfu-util.sourceforge.net/
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* HardFault_HandlerAsm:
|
||||
* Alternative Hard Fault handler to help debug the reason for a fault.
|
||||
* To use, edit the vector table to reference this function in the HardFault vector
|
||||
* This code is suitable for Cortex-M3 and Cortex-M0 cores
|
||||
*/
|
||||
|
||||
#pragma weak hard_fault_handler = HardFault_Handler
|
||||
|
||||
// Use the 'naked' attribute so that C stacking is not used.
|
||||
__attribute__((naked))
|
||||
void HardFault_Handler(void){
|
||||
/*
|
||||
* Get the appropriate stack pointer, depending on our mode,
|
||||
* and use it as the parameter to the C handler. This function
|
||||
* will never return
|
||||
*/
|
||||
// ".syntax unified\n"
|
||||
__asm(
|
||||
"MOVS R0, #4 \n"
|
||||
"MOV R1, LR \n"
|
||||
"TST R0, R1 \n"
|
||||
"BEQ _MSP \n"
|
||||
"MRS R0, PSP \n"
|
||||
"B HardFault_HandlerC \n"
|
||||
"_MSP: \n"
|
||||
"MRS R0, MSP \n"
|
||||
"B HardFault_HandlerC \n"
|
||||
);
|
||||
// ".syntax divided\n"
|
||||
}
|
||||
|
||||
/**
|
||||
* HardFaultHandler_C:
|
||||
* This is called from the HardFault_HandlerAsm with a pointer the Fault stack
|
||||
* as the parameter. We can then read the values from the stack and place them
|
||||
* into local variables for ease of reading.
|
||||
* We then read the various Fault Status and Address Registers to help decode
|
||||
* cause of the fault.
|
||||
* The function ends with a BKPT instruction to force control back into the debugger
|
||||
*/
|
||||
void HardFault_HandlerC(unsigned long *hardfault_args){
|
||||
volatile unsigned long stacked_r0 ;
|
||||
volatile unsigned long stacked_r1 ;
|
||||
volatile unsigned long stacked_r2 ;
|
||||
volatile unsigned long stacked_r3 ;
|
||||
volatile unsigned long stacked_r12 ;
|
||||
volatile unsigned long stacked_lr ;
|
||||
volatile unsigned long stacked_pc ;
|
||||
volatile unsigned long stacked_psr ;
|
||||
volatile unsigned long _CFSR ;
|
||||
volatile unsigned long _HFSR ;
|
||||
volatile unsigned long _DFSR ;
|
||||
volatile unsigned long _AFSR ;
|
||||
volatile unsigned long _BFAR ;
|
||||
volatile unsigned long _MMAR ;
|
||||
|
||||
stacked_r0 = ((unsigned long)hardfault_args[0]) ;
|
||||
stacked_r1 = ((unsigned long)hardfault_args[1]) ;
|
||||
stacked_r2 = ((unsigned long)hardfault_args[2]) ;
|
||||
stacked_r3 = ((unsigned long)hardfault_args[3]) ;
|
||||
stacked_r12 = ((unsigned long)hardfault_args[4]) ;
|
||||
stacked_lr = ((unsigned long)hardfault_args[5]) ;
|
||||
stacked_pc = ((unsigned long)hardfault_args[6]) ;
|
||||
stacked_psr = ((unsigned long)hardfault_args[7]) ;
|
||||
|
||||
// Configurable Fault Status Register
|
||||
// Consists of MMSR, BFSR and UFSR
|
||||
_CFSR = (*((volatile unsigned long *)(0xE000ED28))) ;
|
||||
|
||||
// Hard Fault Status Register
|
||||
_HFSR = (*((volatile unsigned long *)(0xE000ED2C))) ;
|
||||
|
||||
// Debug Fault Status Register
|
||||
_DFSR = (*((volatile unsigned long *)(0xE000ED30))) ;
|
||||
|
||||
// Auxiliary Fault Status Register
|
||||
_AFSR = (*((volatile unsigned long *)(0xE000ED3C))) ;
|
||||
|
||||
// Read the Fault Address Registers. These may not contain valid values.
|
||||
// Check BFARVALID/MMARVALID to see if they are valid values
|
||||
// MemManage Fault Address Register
|
||||
_MMAR = (*((volatile unsigned long *)(0xE000ED34))) ;
|
||||
// Bus Fault Address Register
|
||||
_BFAR = (*((volatile unsigned long *)(0xE000ED38))) ;
|
||||
|
||||
__asm("BKPT #0\n") ; // Break into the debugger
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
diff --git a/include/libopencm3/stm32/l0/flash.h b/include/libopencm3/stm32/l0/flash.h
|
||||
index 3716300..c2179c3 100644
|
||||
--- a/include/libopencm3/stm32/l0/flash.h
|
||||
+++ b/include/libopencm3/stm32/l0/flash.h
|
||||
@@ -70,6 +70,14 @@
|
||||
#define FLASH_OPTR_RDPRT_LEVEL_1 (0x00)
|
||||
#define FLASH_OPTR_RDPRT_LEVEL_2 (0xcc)
|
||||
|
||||
+/* --- RAM functions workaround -------------------------------------------- */
|
||||
+
|
||||
+#if defined(L0_FLASH_FNS_FROM_RAM) && L0_FLASH_FNS_FROM_RAM
|
||||
+#define __FROM_RAM __attribute__((__long_call__,section(".data")))
|
||||
+#else
|
||||
+#define __FROM_RAM
|
||||
+#endif
|
||||
+
|
||||
/* --- Function prototypes ------------------------------------------------- */
|
||||
|
||||
BEGIN_DECLS
|
||||
diff --git a/lib/stm32/l0/Makefile b/lib/stm32/l0/Makefile
|
||||
index 8ed6779..a29a5ad 100644
|
||||
--- a/lib/stm32/l0/Makefile
|
||||
+++ b/lib/stm32/l0/Makefile
|
||||
@@ -20,6 +20,8 @@
|
||||
LIBNAME = libopencm3_stm32l0
|
||||
SRCLIBDIR ?= ../..
|
||||
|
||||
+FLASH_FROM_RAM ?= 0
|
||||
+
|
||||
PREFIX ?= arm-none-eabi
|
||||
#PREFIX ?= arm-elf
|
||||
CC = $(PREFIX)-gcc
|
||||
@@ -30,7 +32,8 @@ TGT_CFLAGS = -Os \
|
||||
-Wundef -Wshadow \
|
||||
-I../../../include -fno-common \
|
||||
-mcpu=cortex-m0plus $(FP_FLAGS) -mthumb -Wstrict-prototypes \
|
||||
- -ffunction-sections -fdata-sections -MD -DSTM32L0
|
||||
+ -ffunction-sections -fdata-sections -MD -DSTM32L0 \
|
||||
+ -DL0_FLASH_FNS_FROM_RAM=$(FLASH_FROM_RAM)
|
||||
TGT_CFLAGS += $(DEBUG_FLAGS)
|
||||
|
||||
ARFLAGS = rcs
|
||||
diff --git a/lib/stm32/l0/flash.c b/lib/stm32/l0/flash.c
|
||||
index ec7280a..9e2b34f 100644
|
||||
--- a/lib/stm32/l0/flash.c
|
||||
+++ b/lib/stm32/l0/flash.c
|
||||
@@ -93,13 +93,15 @@ Warning: this function MUST be executed from RAM.
|
||||
@param[in] data Pointer data to write
|
||||
*/
|
||||
|
||||
-void flash_program_half_page(uint32_t address, uint32_t *data, uint16_t page_size)
|
||||
+__FROM_RAM void flash_program_half_page(uint32_t address, uint32_t *data, uint16_t page_size)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
+ while (FLASH_SR & FLASH_SR_BSY);
|
||||
+
|
||||
FLASH_PECR |= FLASH_PECR_PROG | FLASH_PECR_FPRG;
|
||||
|
||||
- for (i = 0; i < ((page_size/2) * 4); i+=4, data++) {
|
||||
+ for (i = 0; i < (page_size/2); i+=4, data++) {
|
||||
MMIO32(address + i) = *data;
|
||||
}
|
||||
|
||||
@@ -151,7 +153,7 @@ memory. The information block is unaffected.
|
||||
Warning: this function MUST be executed from RAM.
|
||||
*/
|
||||
|
||||
-void flash_erase_all_pages(void)
|
||||
+__FROM_RAM void flash_erase_all_pages(void)
|
||||
{
|
||||
|
||||
/* (1) Check if the read protection is not level 2 */
|
||||
@@ -173,7 +175,8 @@ void flash_erase_all_pages(void)
|
||||
/* Should check that the memory was erased here */
|
||||
|
||||
/* (6) Lock the NVM by setting the PELOCK bit */
|
||||
- flash_lock_pecr();
|
||||
+ while (FLASH_SR & FLASH_SR_BSY);
|
||||
+ FLASH_PECR |= FLASH_PECR_PELOCK;
|
||||
|
||||
/* Infinite loop */
|
||||
while (1);
|
||||
@@ -191,7 +194,7 @@ Warning: this function MUST be executed from RAM.
|
||||
@param[in] address Address of option byte from @ref flash_options.
|
||||
*/
|
||||
|
||||
-void flash_erase_option_byte(uint32_t address)
|
||||
+__FROM_RAM void flash_erase_option_byte(uint32_t address)
|
||||
{
|
||||
FLASH_PECR |= FLASH_PECR_ERASE;
|
||||
|
||||
@@ -221,7 +224,7 @@ Warning: this function MUST be executed from RAM.
|
||||
@param[in] data value to write
|
||||
*/
|
||||
|
||||
-void flash_program_option_byte(uint32_t address, uint16_t data)
|
||||
+__FROM_RAM void flash_program_option_byte(uint32_t address, uint16_t data)
|
||||
{
|
||||
MMIO32(address) = (uint32_t)((~data << 16) | data);
|
||||
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/syscfg.h>
|
||||
#include <libopencm3/stm32/crs.h>
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
#include <libopencm3/cm3/scb.h>
|
||||
#include <libopencm3/usb/usbd.h>
|
||||
#include <libopencm3/usb/dfu.h>
|
||||
|
||||
#define APP_ADDRESS 0x08002000
|
||||
|
||||
#define PROGRAM_BY_HALF_PAGE
|
||||
|
||||
#define DFU_PACKET_SIZE 1024
|
||||
#define MCU_PAGE_SIZE 128
|
||||
/* Note: DFU_PACKET_SIZE has to be an integer multiple of MCU_PAGE_SIZE */
|
||||
/* DFU_PACKET_SIZE needs to match the DfuSe string below */
|
||||
|
||||
/* This is supposed to be in libopencm3/lib/stm32/l0/flash.c but isn't */
|
||||
void flash_erase_page(uint32_t address);
|
||||
|
||||
#if defined(PROGRAM_BY_HALF_PAGE)
|
||||
|
||||
#define __FROM_RAM __attribute__((__long_call__, section(".data"),optimize("Os")))
|
||||
|
||||
void flash_half_page(uint32_t address, uint32_t *data);
|
||||
__FROM_RAM void flash_half_page(uint32_t address, uint32_t *data)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
__asm__ volatile ("CPSID I\n");
|
||||
|
||||
FLASH_PECR |= FLASH_PECR_PROG | FLASH_PECR_FPRG;
|
||||
|
||||
i = 0;
|
||||
while(i < (MCU_PAGE_SIZE/8)) { /* half a page, 4byte words */
|
||||
/* Address doesn't need to be increased ... */
|
||||
MMIO32(address) = *data;
|
||||
data++;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (FLASH_SR & FLASH_SR_BSY);
|
||||
|
||||
__asm__ volatile ("CPSIE I\n");
|
||||
|
||||
if ((FLASH_SR & FLASH_SR_EOP) != 0) {
|
||||
FLASH_SR = FLASH_SR_EOP;
|
||||
} /* else error */
|
||||
|
||||
FLASH_PECR &= ~(FLASH_PECR_PROG | FLASH_PECR_FPRG);
|
||||
}
|
||||
|
||||
#endif /* PROGRAM_BY_HALF_PAGE */
|
||||
|
||||
/* Commands sent with wBlockNum == 0 as per ST implementation. */
|
||||
#define CMD_SETADDR 0x21
|
||||
#define CMD_ERASE 0x41
|
||||
|
||||
uint8_t usbd_control_buffer[DFU_PACKET_SIZE];
|
||||
|
||||
static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
|
||||
|
||||
static struct {
|
||||
uint8_t buf[sizeof(usbd_control_buffer)];
|
||||
uint16_t len;
|
||||
uint32_t addr;
|
||||
uint16_t blocknum;
|
||||
} prog;
|
||||
|
||||
const struct usb_device_descriptor dev = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = 0,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.idVendor = 0x0483,
|
||||
.idProduct = 0xDF11,
|
||||
.bcdDevice = 0x0200,
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
const struct usb_dfu_descriptor dfu_function = {
|
||||
.bLength = sizeof(struct usb_dfu_descriptor),
|
||||
.bDescriptorType = DFU_FUNCTIONAL,
|
||||
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
|
||||
.wDetachTimeout = 255,
|
||||
.wTransferSize = DFU_PACKET_SIZE,
|
||||
.bcdDFUVersion = 0x011A,
|
||||
};
|
||||
|
||||
const struct usb_interface_descriptor iface = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 2,
|
||||
|
||||
/* The ST Microelectronics DfuSe application needs this string.
|
||||
* The format isn't documented... */
|
||||
.iInterface = 4,
|
||||
|
||||
.extra = &dfu_function,
|
||||
.extralen = sizeof(dfu_function),
|
||||
};
|
||||
|
||||
const struct usb_interface ifaces[] = {{
|
||||
.num_altsetting = 1,
|
||||
.altsetting = &iface,
|
||||
}};
|
||||
|
||||
const struct usb_config_descriptor config = {
|
||||
.bLength = USB_DT_CONFIGURATION_SIZE,
|
||||
.bDescriptorType = USB_DT_CONFIGURATION,
|
||||
.wTotalLength = 0,
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = 0xC0,
|
||||
.bMaxPower = 0x32,
|
||||
|
||||
.interface = ifaces,
|
||||
};
|
||||
|
||||
static const char *usb_strings[] = {
|
||||
"flabbergast",
|
||||
"L052 DFU Bootloader",
|
||||
"STM32L052C8Tx",
|
||||
/* This string is used by ST Microelectronics' DfuSe utility. */
|
||||
"@Internal Flash /0x08000000/8*001Ka,56*001Kg"
|
||||
};
|
||||
|
||||
/* Notes about the dfuse string above:
|
||||
* /<start_address>/<number>*<page_size><multiplier><memtype>
|
||||
* <number>: how many pages
|
||||
* <page_size>: self explanatory
|
||||
* <multiplier>: 'B'(ytes), 'K'(ilobytes), 'M'(egabytes)
|
||||
* <memtype>: the bottom three bits are significant:
|
||||
* writeable|erasable|readable
|
||||
* subsequent blocks separated by commas
|
||||
*
|
||||
* Using the internal page size: "@Internal Flash /0x08000000/64*128Ba,448*128Bg"
|
||||
* Using 1K blocks: "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
|
||||
*/
|
||||
|
||||
static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout)
|
||||
{
|
||||
switch (usbdfu_state) {
|
||||
case STATE_DFU_DNLOAD_SYNC:
|
||||
usbdfu_state = STATE_DFU_DNBUSY;
|
||||
*bwPollTimeout = 100;
|
||||
return DFU_STATUS_OK;
|
||||
case STATE_DFU_MANIFEST_SYNC:
|
||||
/* Device will reset when read is complete. */
|
||||
usbdfu_state = STATE_DFU_MANIFEST;
|
||||
return DFU_STATUS_OK;
|
||||
default:
|
||||
return DFU_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbdfu_getstatus_complete(usbd_device *usbd_dev, struct usb_setup_data *req)
|
||||
{
|
||||
int i;
|
||||
(void)req;
|
||||
(void)usbd_dev;
|
||||
|
||||
switch (usbdfu_state) {
|
||||
case STATE_DFU_DNBUSY:
|
||||
flash_unlock();
|
||||
if (prog.blocknum == 0) {
|
||||
switch (prog.buf[0]) {
|
||||
/* code to protect the bootloader? */
|
||||
case CMD_ERASE:
|
||||
{
|
||||
uint32_t *dat = (uint32_t *)(prog.buf + 1);
|
||||
i = 0;
|
||||
while ( i*MCU_PAGE_SIZE < DFU_PACKET_SIZE ) {
|
||||
flash_erase_page(*dat + i*MCU_PAGE_SIZE);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
case CMD_SETADDR:
|
||||
{
|
||||
uint32_t *dat = (uint32_t *)(prog.buf + 1);
|
||||
prog.addr = *dat;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) *
|
||||
dfu_function.wTransferSize);
|
||||
#if defined(PROGRAM_BY_HALF_PAGE)
|
||||
for (i = 0; i < prog.len; i += (MCU_PAGE_SIZE/2)) {
|
||||
uint32_t *dat = (uint32_t *)(prog.buf + i);
|
||||
flash_half_page(baseaddr+i, dat);
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < prog.len; i += 4) {
|
||||
uint32_t *dat = (uint32_t *)(prog.buf + i);
|
||||
flash_program_word(baseaddr + i, *dat);
|
||||
}
|
||||
#endif /* PROGRAM_BY_HALF_PAGE */
|
||||
}
|
||||
flash_lock();
|
||||
|
||||
/* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */
|
||||
usbdfu_state = STATE_DFU_DNLOAD_IDLE;
|
||||
return;
|
||||
case STATE_DFU_MANIFEST:
|
||||
/* USB device must detach, we just reset... */
|
||||
scb_reset_system();
|
||||
return; /* Will never return. */
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int usbdfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf,
|
||||
uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
|
||||
{
|
||||
(void)usbd_dev;
|
||||
|
||||
if ((req->bmRequestType & 0x7F) != 0x21)
|
||||
return 0; /* Only accept class request. */
|
||||
|
||||
switch (req->bRequest) {
|
||||
case DFU_DNLOAD:
|
||||
if ((len == NULL) || (*len == 0)) {
|
||||
usbdfu_state = STATE_DFU_MANIFEST_SYNC;
|
||||
return 1;
|
||||
} else {
|
||||
/* Copy download data for use on GET_STATUS. */
|
||||
prog.blocknum = req->wValue;
|
||||
prog.len = *len;
|
||||
memcpy(prog.buf, *buf, *len);
|
||||
usbdfu_state = STATE_DFU_DNLOAD_SYNC;
|
||||
return 1;
|
||||
}
|
||||
case DFU_CLRSTATUS:
|
||||
/* Clear error and return to dfuIDLE. */
|
||||
if (usbdfu_state == STATE_DFU_ERROR)
|
||||
usbdfu_state = STATE_DFU_IDLE;
|
||||
return 1;
|
||||
case DFU_ABORT:
|
||||
/* Abort returns to dfuIDLE state. */
|
||||
usbdfu_state = STATE_DFU_IDLE;
|
||||
return 1;
|
||||
case DFU_UPLOAD:
|
||||
/* Upload not supported for now. */
|
||||
return 0;
|
||||
case DFU_GETSTATUS: {
|
||||
uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
|
||||
(*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
|
||||
(*buf)[1] = bwPollTimeout & 0xFF;
|
||||
(*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
|
||||
(*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
|
||||
(*buf)[4] = usbdfu_state;
|
||||
(*buf)[5] = 0; /* iString not used here */
|
||||
*len = 6;
|
||||
*complete = usbdfu_getstatus_complete;
|
||||
return 1;
|
||||
}
|
||||
case DFU_GETSTATE:
|
||||
/* Return state with no state transision. */
|
||||
*buf[0] = usbdfu_state;
|
||||
*len = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usbdfu_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
||||
{
|
||||
(void)wValue;
|
||||
|
||||
usbd_register_control_callback(
|
||||
usbd_dev,
|
||||
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
|
||||
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
|
||||
usbdfu_control_request);
|
||||
}
|
||||
|
||||
|
||||
/* This is for some reason missing from libopencm3/l0 flash.c
|
||||
* (I thought it was in the pull request I've done for l0 but
|
||||
* apparently not.)
|
||||
*/
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief Erase a Page of FLASH
|
||||
|
||||
This performs all operations necessary to erase a page in FLASH memory.
|
||||
The page should be checked to ensure that it was properly erased. A page must
|
||||
first be fully erased before attempting to program it.
|
||||
|
||||
Note that the page sizes differ between devices. See the reference manual or
|
||||
the FLASH programming manual for details.
|
||||
|
||||
@param[in] address Memory address of the first word on the page to be erased.
|
||||
*/
|
||||
__FROM_RAM void flash_erase_page(uint32_t address)
|
||||
{
|
||||
while (FLASH_SR & FLASH_SR_BSY);
|
||||
|
||||
FLASH_PECR |= FLASH_PECR_ERASE | FLASH_PECR_PROG;
|
||||
|
||||
MMIO32(address) = (uint32_t)0;
|
||||
|
||||
while (FLASH_SR & FLASH_SR_BSY);
|
||||
|
||||
if ((FLASH_SR & FLASH_SR_EOP) != 0) {
|
||||
FLASH_SR = FLASH_SR_EOP;
|
||||
} /* else error */
|
||||
|
||||
FLASH_PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_PROG);
|
||||
}
|
||||
|
||||
/* End of the bit that's supposed to be in libopencm3/lib/stm32/l0/flash.c */
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
usbd_device *usbd_dev;
|
||||
uint32_t k;
|
||||
|
||||
rcc_periph_clock_enable(RCC_GPIOB);
|
||||
|
||||
gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO3);
|
||||
|
||||
if (gpio_get(GPIOB, GPIO3)) {
|
||||
/* Boot the application if it's valid. */
|
||||
if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
|
||||
/* Set vector table base address. */
|
||||
SCB_VTOR = APP_ADDRESS & 0xFFFF;
|
||||
/* Initialise master stack pointer. */
|
||||
asm volatile("msr msp, %0"::"g"
|
||||
(*(volatile uint32_t *)APP_ADDRESS));
|
||||
/* Jump to application. */
|
||||
(*(void (**)())(APP_ADDRESS + 4))();
|
||||
}
|
||||
}
|
||||
|
||||
// libopencm3 clock setup can be a bit ad-hoc, so let's configure "manually"
|
||||
// 32MHz, PLL supplied by the internal HSI16 oscillator
|
||||
/* Set the flash latency */
|
||||
FLASH_ACR |= FLASH_ACR_LATENCY_1WS;
|
||||
/* Turn on HSI16 */
|
||||
RCC_CR |= RCC_CR_HSI16ON;
|
||||
while ((RCC_CR & RCC_CR_HSI16RDY) == 0);
|
||||
/* Make sure PLL is off (it should be after reset, but ...) */
|
||||
RCC_CR &= ~RCC_CR_PLLON;
|
||||
while (RCC_CR & RCC_CR_PLLRDY);
|
||||
/* Set the PLL source to HSI16 */
|
||||
RCC_CFGR &= ~(1<<16); // RCC_CFGR_PLLSRC
|
||||
/* Set up the PLL */
|
||||
uint32_t reg = RCC_CFGR
|
||||
& ~( (RCC_CFGR_PLLMUL_MASK << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_MASK << RCC_CFGR_PLLDIV_SHIFT));
|
||||
RCC_CFGR = reg | (RCC_CFGR_PLLMUL_MUL4 << RCC_CFGR_PLLMUL_SHIFT)
|
||||
| (RCC_CFGR_PLLDIV_DIV2 << RCC_CFGR_PLLDIV_SHIFT);
|
||||
/* Turn on PLL and switch to it */
|
||||
RCC_CR |= RCC_CR_PLLON;
|
||||
while ((RCC_CR & RCC_CR_PLLRDY) == 0);
|
||||
RCC_CFGR |= RCC_CFGR_SW_PLL;
|
||||
/* Set the peripheral clock frequencies used. */
|
||||
rcc_ahb_frequency = 32000000;
|
||||
rcc_apb1_frequency = 32000000;
|
||||
rcc_apb2_frequency = 32000000;
|
||||
/* end of "manual" clock setup */
|
||||
|
||||
/* Enable VREFINT reference for HSI48 */
|
||||
rcc_periph_clock_enable(RCC_SYSCFG);
|
||||
SYSCFG_CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
|
||||
|
||||
rcc_set_hsi48_source_rc48();
|
||||
crs_autotrim_usb_enable();
|
||||
|
||||
rcc_osc_on(RCC_HSI48);
|
||||
rcc_wait_for_osc_ready(RCC_HSI48);
|
||||
|
||||
/* MCO on PA9, to verify MCU speed with a scope */
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
|
||||
gpio_set_af(GPIOA, GPIO_AF0, GPIO9);
|
||||
RCC_CFGR |= (RCC_CFGR_MCO_SYSCLK << RCC_CFGR_MCO_SHIFT) | (RCC_CFGR_MCOPRE_DIV16 << 28);
|
||||
|
||||
/* for the LED */
|
||||
rcc_periph_clock_enable(RCC_GPIOA);
|
||||
|
||||
usbd_dev = usbd_init(&st_usbfs_v2_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer));
|
||||
usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
|
||||
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15);
|
||||
|
||||
k = 0;
|
||||
|
||||
while (1) {
|
||||
usbd_poll(usbd_dev);
|
||||
if(++k == 10000) {
|
||||
k = 0;
|
||||
gpio_toggle(GPIOA, GPIO15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim: tabstop=4:shiftwidth=4:noexpandtab
|
Loading…
Reference in New Issue