├── .gitignore ├── CHANGELOG ├── README.md ├── examples ├── blinky │ ├── Makefile │ ├── blinky.c │ └── blinky.ld └── cdcacm │ ├── Makefile │ ├── cdcacm.c │ ├── cdcacm.ld │ └── cdcacm_test.py ├── scripts ├── black_magic_probe_flash.scr ├── dfu.py └── stm32_mem.py └── src ├── Makefile ├── luftboot.c └── luftboot.ld /.gitignore: -------------------------------------------------------------------------------- 1 | mapfile 2 | *.hex 3 | *.bin 4 | *.pyc 5 | *.o 6 | *.elf 7 | src/luftboot 8 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | == v1.1 == 2 | 3 | * Changed VID/PID to Open Moko provided ones. 4 | * Bootloader is selfprotecting it's flash memory. 5 | * Fixed issue where the bootloader was only allowing to flash 128kb of flash memory. 6 | 7 | == v1.0 == 8 | 9 | Initial release of luftboot 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Luftboot 2 | ======== 3 | 4 | This software is part of the Paparazzi UAV Project. 5 | 6 | Luftboot is the bootloader for stm32 based autopilots to enable upgrading the 7 | autopilot firmware using usb. It implements the dfu standard. 8 | 9 | Luftboot is based on the usbdfu bootloader implementation by Gareth McMullin 10 | for the Black Magic Probe project (http://www.blacksphere.co.nz/main/blackmagic). 11 | 12 | Building 13 | -------- 14 | 15 | Call 'make' inside the src directory. 16 | 17 | Luftboot needs libopencm3 and an appropriate arm-none-eabi gcc cross compiler. 18 | If you don't have these in your PATH, specify them with e.g. 19 | 20 | make LIBOPENCM3=path/to/libopencm3 PREFIX=path/to/bin/arm-none-eabi- 21 | 22 | By default, Luftboot uses a 12MHz external clock to PLL it to 72MHz. If no external 23 | clock is available, or for any other reason, one can build Luftboot to use the 8MHz 24 | internal RC oscillator to PLL it to 48MHz. Call make as follows from inside ./src: 25 | 26 | make LUFTBOOT_USE_48MHZ_INTERNAL_OSC=1 27 | 28 | Startup sequence 29 | ---------------- 30 | 31 | At bootup, Luftboot uses several criteria (in this order) to decide whether to jump 32 | to the payload or to initiate the bootloader: 33 | * On boot, if the payload is NOT valid, start the bootloader 34 | * If the flag is set indicating we just downloaded a payload, jump to payload 35 | * If the payload is forcing the bootloader (using GPIO state after core-only reset) 36 | start the bootloader 37 | * If the ADC2 pin on Lisa/M is grounded, jump to payload (i.e. "skip bootloader" jumper) 38 | * If voltage is present on the USB vbus, start the bootloader 39 | * Otherwise, jump to payload 40 | 41 | Notes 42 | ----- 43 | 44 | Big bootloader 45 | 46 | If the bootloader ever gets bigger than 0xFFFF in the main() of sourcecode there is a set vector table base address. 47 | '''SCB_VTOR = APP_ADDRESS & 0xFFFF;''' 48 | 49 | This only will work if the bootloader reserved space is less than 0xFFFF, i.e. the app address is less than 0xFFFF. This utilizes the aliased memory location of the application vector table, but it seems to work just fine using the unaliased address, i.e. just APP_ADDRESS directly. 50 | -------------------------------------------------------------------------------- /examples/blinky/Makefile: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE ?= arm-none-eabi- 2 | CC = $(CROSS_COMPILE)gcc 3 | OBJCOPY = $(CROSS_COMPILE)objcopy 4 | LIBOPENCM3 ?= /usr/local/arm-none-eabi 5 | OOCD_INTERFACE ?= flossjtag 6 | OOCD_TARGET ?= stm32 7 | OOCD = openocd 8 | Q = @ 9 | 10 | 11 | CFLAGS += -Istm32/include -mcpu=cortex-m3 -mthumb -msoft-float -DSTM32F1 -I$(LIBOPENCM3)/include 12 | LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ 13 | -Wl,-T,blinky.ld -nostartfiles -lc -lnosys -Wl,-Map=mapfile \ 14 | -mthumb -march=armv7 -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -L$(LIBOPENCM3)/lib/stm32/f1 15 | LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 16 | 17 | all: blinky.bin blinky.hex 18 | 19 | blinky: blinky.o 20 | $(CC) $^ -o $@ $(LDFLAGS) 21 | 22 | blinky.bin: blinky 23 | $(OBJCOPY) -O binary $^ $@ 24 | 25 | blinky.hex: blinky 26 | $(OBJCOPY) -O ihex $^ $@ 27 | 28 | clean: 29 | -rm -rf blinky.o blinky blinky.bin blinky.hex mapfile 30 | -------------------------------------------------------------------------------- /examples/blinky/blinky.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Paparazzi UAV project. 3 | * 4 | * Copyright (C) 2009 Uwe Hermann 5 | * Copyright (C) 2011 Piotr Esden-Tempski 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | /* Set STM32 to 72 MHz. */ 25 | void clock_setup(void) 26 | { 27 | rcc_clock_setup_in_hse_12mhz_out_72mhz(); 28 | 29 | /* Enable GPIOB, GPIOC, and AFIO clocks. */ 30 | rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); 31 | rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); 32 | rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN); 33 | rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN); 34 | } 35 | 36 | void gpio_setup(void) 37 | { 38 | /* Set GPIO12 (in GPIO port C) to 'output push-pull'. */ 39 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, 40 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO8); 41 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, 42 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO4); 43 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 44 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO15); 45 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 46 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO2); 47 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 48 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO5); 49 | 50 | AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_JNTRST; 51 | 52 | /* preconfigure the led's */ 53 | gpio_clear(GPIOA, GPIO8); /* switch on led */ 54 | gpio_set(GPIOB, GPIO4); /* switch off led */ 55 | gpio_clear(GPIOC, GPIO15); /* switch on led */ 56 | } 57 | 58 | int main(void) 59 | { 60 | int i; 61 | unsigned char counter = 0; 62 | 63 | clock_setup(); 64 | gpio_setup(); 65 | 66 | counter = 0; 67 | 68 | /* Full bank blink to indicate reset */ 69 | 70 | for (counter=0; counter < 2; counter++) { 71 | for (i = 0; i < 800000; i++) /* Wait a bit. */ 72 | __asm__("nop"); 73 | 74 | gpio_clear(GPIOA, GPIO8); /* LED on/off */ 75 | gpio_clear(GPIOB, GPIO4); /* LED on/off */ 76 | gpio_clear(GPIOC, GPIO15); /* LED on/off */ 77 | gpio_clear(GPIOC, GPIO2); /* LED on/off */ 78 | gpio_clear(GPIOC, GPIO5); /* LED on/off */ 79 | 80 | for (i = 0; i < 800000; i++) /* Wait a bit. */ 81 | __asm__("nop"); 82 | 83 | gpio_set(GPIOA, GPIO8); /* LED on/off */ 84 | gpio_set(GPIOB, GPIO4); /* LED on/off */ 85 | gpio_set(GPIOC, GPIO15); /* LED on/off */ 86 | gpio_set(GPIOC, GPIO2); /* LED on/off */ 87 | gpio_set(GPIOC, GPIO5); /* LED on/off */ 88 | } 89 | 90 | counter = 0; 91 | 92 | /* Blink the LED (PC12) on the board. */ 93 | while (1) { 94 | 95 | counter++; 96 | if (counter & (1 << 0)) { 97 | gpio_clear(GPIOA, GPIO8); /* LED on/off */ 98 | } else { 99 | gpio_set(GPIOA, GPIO8); /* LED on/off */ 100 | } 101 | 102 | if (counter & (1 << 1)) { 103 | gpio_clear(GPIOB, GPIO4); /* LED on/off */ 104 | } else { 105 | gpio_set(GPIOB, GPIO4); /* LED on/off */ 106 | } 107 | 108 | if (counter & (1 << 2)) { 109 | gpio_clear(GPIOC, GPIO15); /* LED on/off */ 110 | } else { 111 | gpio_set(GPIOC, GPIO15); /* LED on/off */ 112 | } 113 | 114 | if (counter & (1 << 3)) { 115 | gpio_clear(GPIOC, GPIO2); /* LED on/off */ 116 | } else { 117 | gpio_set(GPIOC, GPIO2); /* LED on/off */ 118 | } 119 | 120 | if (counter & (1 << 4)) { 121 | gpio_clear(GPIOC, GPIO5); /* LED on/off */ 122 | } else { 123 | gpio_set(GPIOC, GPIO5); /* LED on/off */ 124 | } 125 | 126 | for (i = 0; i < 800000; i++) /* Wait a bit. */ 127 | __asm__("nop"); 128 | 129 | if (counter == 32) { 130 | /* 131 | * Set the bootloader force pin to be input pull-down and low. 132 | * After the cortex core reset this setting is being carried 133 | * over to the bootloader indicating that we want to stay in 134 | * the bootloader. 135 | */ 136 | gpio_clear(GPIOC, GPIO0); 137 | gpio_set_mode(GPIOC, GPIO_MODE_INPUT, 138 | GPIO_CNF_INPUT_PULL_UPDOWN, GPIO0); 139 | scb_reset_core(); /* reset the cortex core */ 140 | } 141 | } 142 | 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /examples/blinky/blinky.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopenstm32 project. 3 | * 4 | * Copyright (C) 2010 Thomas Otto 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* Define memory regions. */ 21 | MEMORY 22 | { 23 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 120K 24 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 25 | } 26 | 27 | /* Include the common ld script from libopenstm32. */ 28 | INCLUDE cortex-m-generic.ld 29 | 30 | -------------------------------------------------------------------------------- /examples/cdcacm/Makefile: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE ?= arm-none-eabi- 2 | CC = $(CROSS_COMPILE)gcc 3 | OBJCOPY = $(CROSS_COMPILE)objcopy 4 | LIBOPENCM3 ?= /usr/local/arm-none-eabi 5 | OOCD_INTERFACE ?= flossjtag 6 | OOCD_TARGET ?= stm32 7 | OOCD = openocd 8 | Q = @ 9 | 10 | 11 | CFLAGS += -Istm32/include -mcpu=cortex-m3 -mthumb -msoft-float -DSTM32F1 -I$(LIBOPENCM3)/include 12 | LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ 13 | -Wl,-T,cdcacm.ld -nostartfiles -lc -lnosys -Wl,-Map=mapfile \ 14 | -mthumb -march=armv7 -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -L$(LIBOPENCM3)/lib/stm32/f1 15 | LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 16 | 17 | all: cdcacm.bin cdcacm.hex 18 | 19 | cdcacm: cdcacm.o 20 | $(CC) $^ -o $@ $(LDFLAGS) 21 | 22 | cdcacm.bin: cdcacm 23 | $(OBJCOPY) -O binary $^ $@ 24 | 25 | cdcacm.hex: cdcacm 26 | $(OBJCOPY) -O ihex $^ $@ 27 | 28 | clean: 29 | -rm -rf cdcacm.o cdcacm cdcacm.bin cdcacm.hex mapfile 30 | -------------------------------------------------------------------------------- /examples/cdcacm/cdcacm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2010 Gareth McMullin 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | static void parse_rx(char buf[], int len); 27 | 28 | static const struct usb_device_descriptor dev = { 29 | .bLength = USB_DT_DEVICE_SIZE, 30 | .bDescriptorType = USB_DT_DEVICE, 31 | .bcdUSB = 0x0200, 32 | .bDeviceClass = USB_CLASS_CDC, 33 | .bDeviceSubClass = 0, 34 | .bDeviceProtocol = 0, 35 | .bMaxPacketSize0 = 64, 36 | .idVendor = 0x0483, 37 | .idProduct = 0x5740, 38 | .bcdDevice = 0x0200, 39 | .iManufacturer = 1, 40 | .iProduct = 2, 41 | .iSerialNumber = 3, 42 | .bNumConfigurations = 1, 43 | }; 44 | 45 | /* 46 | * This notification endpoint isn't implemented. According to CDC spec it's 47 | * optional, but its absence causes a NULL pointer dereference in the 48 | * Linux cdc_acm driver. 49 | */ 50 | static const struct usb_endpoint_descriptor comm_endp[] = {{ 51 | .bLength = USB_DT_ENDPOINT_SIZE, 52 | .bDescriptorType = USB_DT_ENDPOINT, 53 | .bEndpointAddress = 0x83, 54 | .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, 55 | .wMaxPacketSize = 16, 56 | .bInterval = 255, 57 | }}; 58 | 59 | static const struct usb_endpoint_descriptor data_endp[] = {{ 60 | .bLength = USB_DT_ENDPOINT_SIZE, 61 | .bDescriptorType = USB_DT_ENDPOINT, 62 | .bEndpointAddress = 0x01, 63 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 64 | .wMaxPacketSize = 64, 65 | .bInterval = 1, 66 | }, { 67 | .bLength = USB_DT_ENDPOINT_SIZE, 68 | .bDescriptorType = USB_DT_ENDPOINT, 69 | .bEndpointAddress = 0x82, 70 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 71 | .wMaxPacketSize = 64, 72 | .bInterval = 1, 73 | }}; 74 | 75 | static const struct { 76 | struct usb_cdc_header_descriptor header; 77 | struct usb_cdc_call_management_descriptor call_mgmt; 78 | struct usb_cdc_acm_descriptor acm; 79 | struct usb_cdc_union_descriptor cdc_union; 80 | } __attribute__((packed)) cdcacm_functional_descriptors = { 81 | .header = { 82 | .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), 83 | .bDescriptorType = CS_INTERFACE, 84 | .bDescriptorSubtype = USB_CDC_TYPE_HEADER, 85 | .bcdCDC = 0x0110, 86 | }, 87 | .call_mgmt = { 88 | .bFunctionLength = 89 | sizeof(struct usb_cdc_call_management_descriptor), 90 | .bDescriptorType = CS_INTERFACE, 91 | .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, 92 | .bmCapabilities = 0, 93 | .bDataInterface = 1, 94 | }, 95 | .acm = { 96 | .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), 97 | .bDescriptorType = CS_INTERFACE, 98 | .bDescriptorSubtype = USB_CDC_TYPE_ACM, 99 | .bmCapabilities = 0, 100 | }, 101 | .cdc_union = { 102 | .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), 103 | .bDescriptorType = CS_INTERFACE, 104 | .bDescriptorSubtype = USB_CDC_TYPE_UNION, 105 | .bControlInterface = 0, 106 | .bSubordinateInterface0 = 1, 107 | } 108 | }; 109 | 110 | static const struct usb_interface_descriptor comm_iface[] = {{ 111 | .bLength = USB_DT_INTERFACE_SIZE, 112 | .bDescriptorType = USB_DT_INTERFACE, 113 | .bInterfaceNumber = 0, 114 | .bAlternateSetting = 0, 115 | .bNumEndpoints = 1, 116 | .bInterfaceClass = USB_CLASS_CDC, 117 | .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, 118 | .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, 119 | .iInterface = 0, 120 | 121 | .endpoint = comm_endp, 122 | 123 | .extra = &cdcacm_functional_descriptors, 124 | .extralen = sizeof(cdcacm_functional_descriptors) 125 | }}; 126 | 127 | static const struct usb_interface_descriptor data_iface[] = {{ 128 | .bLength = USB_DT_INTERFACE_SIZE, 129 | .bDescriptorType = USB_DT_INTERFACE, 130 | .bInterfaceNumber = 1, 131 | .bAlternateSetting = 0, 132 | .bNumEndpoints = 2, 133 | .bInterfaceClass = USB_CLASS_DATA, 134 | .bInterfaceSubClass = 0, 135 | .bInterfaceProtocol = 0, 136 | .iInterface = 0, 137 | 138 | .endpoint = data_endp, 139 | }}; 140 | 141 | static const struct usb_interface ifaces[] = {{ 142 | .num_altsetting = 1, 143 | .altsetting = comm_iface, 144 | }, { 145 | .num_altsetting = 1, 146 | .altsetting = data_iface, 147 | }}; 148 | 149 | static const struct usb_config_descriptor config = { 150 | .bLength = USB_DT_CONFIGURATION_SIZE, 151 | .bDescriptorType = USB_DT_CONFIGURATION, 152 | .wTotalLength = 0, 153 | .bNumInterfaces = 2, 154 | .bConfigurationValue = 1, 155 | .iConfiguration = 0, 156 | .bmAttributes = 0x80, 157 | .bMaxPower = 0x32, 158 | 159 | .interface = ifaces, 160 | }; 161 | 162 | char serial[9]; 163 | 164 | static const char *usb_strings[] = { 165 | "x", 166 | "Black Sphere Technologies", 167 | "CDC-ACM Demo", 168 | serial, 169 | }; 170 | 171 | static int cdcacm_control_request(struct usb_setup_data *req, u8 **buf, 172 | u16 *len, void (**complete)(struct usb_setup_data *req)) 173 | { 174 | (void)complete; 175 | (void)buf; 176 | 177 | switch (req->bRequest) { 178 | case USB_CDC_REQ_SET_CONTROL_LINE_STATE: { 179 | /* 180 | * This Linux cdc_acm driver requires this to be implemented 181 | * even though it's optional in the CDC spec, and we don't 182 | * advertise it in the ACM functional descriptor. 183 | */ 184 | char buf[10]; 185 | struct usb_cdc_notification *notif = (void*)buf; 186 | 187 | /* We echo signals back to host as notification. */ 188 | notif->bmRequestType = 0xA1; 189 | notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; 190 | notif->wValue = 0; 191 | notif->wIndex = 0; 192 | notif->wLength = 2; 193 | buf[8] = req->wValue & 3; 194 | buf[9] = 0; 195 | // usbd_ep_write_packet(0x83, buf, 10); 196 | return 1; 197 | } 198 | case USB_CDC_REQ_SET_LINE_CODING: 199 | if (*len < sizeof(struct usb_cdc_line_coding)) 200 | return 0; 201 | 202 | return 1; 203 | } 204 | return 0; 205 | } 206 | 207 | static void cdcacm_data_rx_cb(u8 ep) 208 | { 209 | (void)ep; 210 | 211 | char buf[64]; 212 | int len = usbd_ep_read_packet(0x01, buf, 64); 213 | 214 | if (len) { 215 | /* blocking usb write */ 216 | while (usbd_ep_write_packet(0x82, buf, len) == 0) 217 | ; 218 | buf[len] = 0; 219 | 220 | parse_rx(buf, len); 221 | } 222 | 223 | gpio_toggle(GPIOC, GPIO5); 224 | } 225 | 226 | static void parse_rx(char buf[], int len) 227 | { 228 | int i; 229 | char key[] = "reboot"; 230 | static int state = 0; 231 | 232 | for (i=0; i 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* Define memory regions. */ 21 | MEMORY 22 | { 23 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 120K 24 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 25 | } 26 | 27 | /* Include the common ld script from libopenstm32. */ 28 | INCLUDE cortex-m-generic.ld 29 | -------------------------------------------------------------------------------- /examples/cdcacm/cdcacm_test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # 3 | # This file is part of the libopencm3 project. 4 | # 5 | # Copyright (C) 2011 Piotr Esden-Tempski 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import serial 22 | import signal 23 | import sys 24 | import time 25 | 26 | def signal_handler(signal, frame): 27 | print 'You pressed Ctrl+C!' 28 | ser.close(); 29 | sys.exit(0) 30 | 31 | signal.signal(signal.SIGINT, signal_handler); 32 | 33 | ser = serial.Serial('/dev/cu.usbmodemDEM1', 115200, timeout=0.05); 34 | 35 | error = 0 36 | 37 | tim = time.time(); 38 | 39 | cycles = 0 40 | 41 | while 1==1: 42 | 43 | ser.write("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}"); 44 | 45 | buf = ser.read(1024); 46 | 47 | cycles += 1 48 | 49 | if buf != "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}" : 50 | error+=1 51 | 52 | print "received " + buf + " errors: " + str(error) + " cycles: " + str(cycles) + " runtime: " + str(time.time() - tim) 53 | 54 | #time.sleep(0.1); 55 | 56 | ser.close(); 57 | -------------------------------------------------------------------------------- /scripts/black_magic_probe_flash.scr: -------------------------------------------------------------------------------- 1 | monitor version 2 | monitor swdp_scan 3 | attach 1 4 | monitor option erase 5 | monitor option 0x1FFFF802 0x00FF 6 | monitor option 0x1FFFF804 0x00FF 7 | monitor option 0x1FFFF806 0x00FF 8 | monitor option 0x1FFFF808 0x00FF 9 | monitor option 0x1FFFF80A 0x00FF 10 | monitor option 0x1FFFF80C 0x00FF 11 | monitor option 0x1FFFF80E 0x00FF 12 | load 13 | -------------------------------------------------------------------------------- /scripts/dfu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # dfu.py: Access USB DFU class devices 4 | # Copyright (C) 2009 Black Sphere Technologies 5 | # Copyright (C) 2012 Transition Robotics Inc. 6 | # Written by Gareth McMullin 7 | # Modified by Piotr Esden-Tempski 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | 23 | import usb 24 | 25 | DFU_DETACH_TIMEOUT = 1000 26 | 27 | # DFU Requests 28 | DFU_DETACH = 0x00 29 | DFU_DNLOAD = 0x01 30 | DFU_UPLOAD = 0x02 31 | DFU_GETSTATUS = 0x03 32 | DFU_CLRSTATUS = 0x04 33 | DFU_GETSTATE = 0x05 34 | DFU_ABORT = 0x06 35 | 36 | # DFU States 37 | STATE_APP_IDLE = 0x00 38 | STATE_APP_DETACH = 0x01 39 | STATE_DFU_IDLE = 0x02 40 | STATE_DFU_DOWNLOAD_SYNC = 0x03 41 | STATE_DFU_DOWNLOAD_BUSY = 0x04 42 | STATE_DFU_DOWNLOAD_IDLE = 0x05 43 | STATE_DFU_MANIFEST_SYNC = 0x06 44 | STATE_DFU_MANIFEST = 0x07 45 | STATE_DFU_MANIFEST_WAIT_RESET = 0x08 46 | STATE_DFU_UPLOAD_IDLE = 0x09 47 | STATE_DFU_ERROR = 0x0a 48 | DFU_STATUS_OK = 0x00 49 | 50 | # DFU Status cides 51 | DFU_STATUS_ERROR_TARGET = 0x01 52 | DFU_STATUS_ERROR_FILE = 0x02 53 | DFU_STATUS_ERROR_WRITE = 0x03 54 | DFU_STATUS_ERROR_ERASE = 0x04 55 | DFU_STATUS_ERROR_CHECK_ERASED = 0x05 56 | DFU_STATUS_ERROR_PROG = 0x06 57 | DFU_STATUS_ERROR_VERIFY = 0x07 58 | DFU_STATUS_ERROR_ADDRESS = 0x08 59 | DFU_STATUS_ERROR_NOTDONE = 0x09 60 | DFU_STATUS_ERROR_FIRMWARE = 0x0a 61 | DFU_STATUS_ERROR_VENDOR = 0x0b 62 | DFU_STATUS_ERROR_USBR = 0x0c 63 | DFU_STATUS_ERROR_POR = 0x0d 64 | DFU_STATUS_ERROR_UNKNOWN = 0x0e 65 | DFU_STATUS_ERROR_STALLEDPKT = 0x0f 66 | 67 | class dfu_status(object): 68 | def __init__(self, buf): 69 | self.bStatus = buf[0] 70 | self.bwPollTimeout = buf[1] + (buf[2]<<8) + (buf[3]<<16) 71 | self.bState = buf[4] 72 | self.iString = buf[5] 73 | 74 | 75 | class dfu_device(object): 76 | def __init__(self, dev, conf, iface): 77 | self.dev = dev 78 | self.conf = conf 79 | self.iface = iface 80 | self.handle = self.dev.open() 81 | try: 82 | self.handle.setConfiguration(conf) 83 | except: pass 84 | self.handle.claimInterface(iface.interfaceNumber) 85 | if type(self.iface) is usb.Interface: 86 | self.index = self.iface.interfaceNumber 87 | else: self.index = self.iface 88 | 89 | def detach(self, wTimeout=255): 90 | self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS | 91 | usb.RECIP_INTERFACE, DFU_DETACH, 92 | None, value=wTimeout, index=self.index) 93 | 94 | def download(self, wBlockNum, data): 95 | self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS | 96 | usb.RECIP_INTERFACE, DFU_DNLOAD, 97 | data, value=wBlockNum, index=self.index) 98 | 99 | def upload(self, wBlockNum, length): 100 | return self.handle.controlMsg(usb.ENDPOINT_IN | 101 | usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_UPLOAD, 102 | length, value=wBlockNum, index=self.index) 103 | 104 | def get_status(self): 105 | buf = self.handle.controlMsg(usb.ENDPOINT_IN | 106 | usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_GETSTATUS, 107 | 6, index=self.index) 108 | return dfu_status(buf) 109 | 110 | def clear_status(self): 111 | self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS | 112 | usb.RECIP_INTERFACE, DFU_CLRSTATUS, 113 | "", index=0) 114 | 115 | def get_state(self): 116 | buf = self.handle.controlMsg(usb.ENDPOINT_IN | 117 | usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_GETSTATE, 118 | 1, index=self.index) 119 | return buf[0] 120 | 121 | def abort(self): 122 | self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS | 123 | usb.RECIP_INTERFACE, DFU_ABORT, 124 | None, index=self.index) 125 | 126 | 127 | def make_idle(self): 128 | retries = 3 129 | while retries: 130 | try: 131 | status = self.get_status() 132 | except: 133 | self.clear_status() 134 | continue 135 | 136 | retries -= 1 137 | 138 | if status.bState == STATE_DFU_IDLE: 139 | return True 140 | 141 | if ((status.bState == STATE_DFU_DOWNLOAD_SYNC) or 142 | (status.bState == STATE_DFU_DOWNLOAD_IDLE) or 143 | (status.bState == STATE_DFU_MANIFEST_SYNC) or 144 | (status.bState == STATE_DFU_UPLOAD_IDLE) or 145 | (status.bState == STATE_DFU_DOWNLOAD_BUSY) or 146 | (status.bState == STATE_DFU_MANIFEST)): 147 | self.abort() 148 | continue 149 | 150 | if status.bState == STATE_DFU_ERROR: 151 | self.clear_status() 152 | continue 153 | 154 | if status.bState == STATE_APP_IDLE: 155 | self.detach(DFU_DETACH_TIMEOUT) 156 | continue 157 | 158 | if ((status.bState == STATE_APP_DETACH) or 159 | (status.bState == STATE_DFU_MANIFEST_WAIT_RESET)): 160 | usb.reset(self.handle) 161 | return False 162 | 163 | raise Exception 164 | 165 | def finddevs(): 166 | devs = [] 167 | for bus in usb.busses(): 168 | for dev in bus.devices: 169 | for conf in dev.configurations: 170 | for ifaces in conf.interfaces: 171 | for iface in ifaces: 172 | if ((iface.interfaceClass == 0xFE) and 173 | (iface.interfaceSubClass == 0x01)): 174 | devs.append((dev, conf, iface)) 175 | return devs 176 | 177 | 178 | if __name__ == "__main__": 179 | devs = finddevs() 180 | if not devs: 181 | print "No devices found!" 182 | exit(-1) 183 | 184 | for dfu in devs: 185 | handle = dfu[0].open() 186 | try: 187 | man = handle.getString(dfu[0].iManufacturer, 30) 188 | product = handle.getString(dfu[0].iProduct, 30) 189 | serial = handle.getString(dfu[0].iSerialNumber, 40) 190 | except: 191 | print "Could not access descriptions strings of a DFU device. Maybe the OS driver is claiming it?" 192 | continue 193 | 194 | print "Device %s: ID %04x:%04x %s - %s - %s" % (dfu[0].filename, 195 | dfu[0].idVendor, dfu[0].idProduct, man, product, serial) 196 | print "%r, %r" % (dfu[1], dfu[2]) 197 | print "Finished scanning for devices." 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /scripts/stm32_mem.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # stm32_mem.py: STM32 memory access using USB DFU class 4 | # Copyright (C) 2011 Black Sphere Technologies 5 | # Written by Gareth McMullin 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | from time import sleep 21 | import struct 22 | from sys import stdout, argv 23 | 24 | import usb 25 | import dfu 26 | 27 | APP_ADDRESS = 0x08002000 28 | SECTOR_SIZE = 2048 29 | 30 | CMD_GETCOMMANDS = 0x00 31 | CMD_SETADDRESSPOINTER = 0x21 32 | CMD_ERASE = 0x41 33 | 34 | def stm32_erase(dev, addr): 35 | erase_cmd = struct.pack("" 70 | print 71 | 72 | devs = dfu.finddevs() 73 | if not devs: 74 | print "No devices found!" 75 | exit(-1) 76 | 77 | for dev in devs: 78 | dfudev = dfu.dfu_device(*dev) 79 | try: 80 | man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30) 81 | product = dfudev.handle.getString(dfudev.dev.iProduct, 30) 82 | serial = dfudev.handle.getString(dfudev.dev.iSerialNumber, 40) 83 | except: 84 | print "Could not access the description strings of a DFU device. Maybe the OS driver is claiming it?" 85 | continue 86 | if man == "Black Sphere Technologies": break 87 | if man == "Transition Robotics Inc.": break 88 | if man == "STMicroelectronics": break 89 | 90 | print "Device %s: ID %04x:%04x %s - %s - %s" % (dfudev.dev.filename, 91 | dfudev.dev.idVendor, dfudev.dev.idProduct, man, product, serial) 92 | 93 | try: 94 | state = dfudev.get_state() 95 | except: 96 | print "Failed to read device state! Assuming APP_IDLE" 97 | state = dfu.STATE_APP_IDLE 98 | if state == dfu.STATE_APP_IDLE: 99 | dfudev.detach() 100 | print "Run again to upgrade firmware." 101 | exit(0) 102 | 103 | dfudev.make_idle() 104 | 105 | try: 106 | bin = open(argv[1], "rb").read() 107 | except: 108 | print "Could not open binary file." 109 | raise 110 | 111 | addr = APP_ADDRESS 112 | while bin: 113 | print ("Programming memory at 0x%08X\r" % addr), 114 | stdout.flush() 115 | stm32_erase(dfudev, addr) 116 | stm32_write(dfudev, bin[:SECTOR_SIZE]) 117 | 118 | bin = bin[SECTOR_SIZE:] 119 | addr += SECTOR_SIZE 120 | 121 | stm32_manifest(dfudev) 122 | 123 | print "\nAll operations complete!\n" 124 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= arm-none-eabi- 2 | 3 | CC = $(PREFIX)gcc 4 | OBJCOPY = $(PREFIX)objcopy 5 | 6 | GDB?=$(PREFIX)gdb 7 | 8 | LIBOPENCM3 ?= libopencm3 9 | 10 | # flashing options 11 | OOCD_INTERFACE ?= flossjtag 12 | OOCD_TARGET ?= stm32 13 | OOCD = openocd 14 | BMP_PORT = 15 | 16 | VERSION = V1.1 17 | DEV_SERIAL = NSERIAL 18 | 19 | # Check if we are using gcc-arm-embedded compiler 20 | # if so use nano.specs to save space 21 | ifeq ($(shell $(CC) -specs=nano.specs 2>&1 | grep nano.specs),) 22 | ifeq ($(V),1) 23 | $(info Using gcc-arm-embedded, nano specs enabled!!!) 24 | endif 25 | SPECS = -specs=nano.specs 26 | else 27 | ifeq ($(V),1) 28 | $(info Not using gcc-arm-embedded, nano specs disabled!!!) 29 | endif 30 | endif 31 | 32 | CFLAGS += -Os -g3 -Istm32/include -mcpu=cortex-m3 -mthumb -msoft-float -DSTM32F1 \ 33 | -I$(LIBOPENCM3)/include --function-sections --data-sections \ 34 | -DVERSION="\"$(VERSION)\"" -DDEV_SERIAL="\"$(DEV_SERIAL)\"" 35 | LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ 36 | -Wl,-T,luftboot.ld $(SPECS) -nostartfiles -lc -lnosys -Wl,-Map=mapfile \ 37 | -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd -msoft-float -L$(LIBOPENCM3)/lib \ 38 | -Wl,--gc-sections 39 | LDFLAGS_BOOT += -Wl,--section-start=.devserial=0x8001FF0 40 | LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 41 | 42 | ifneq ($(V),1) 43 | Q := @ 44 | NULL := 2>/dev/null 45 | else 46 | LDFLAGS_BOOT += -Wl,--print-gc-sections 47 | endif 48 | 49 | ifeq ($(LUFTBOOT_USE_48MHZ_INTERNAL_OSC),1) 50 | CFLAGS += -DLUFTBOOT_USE_48MHZ_INTERNAL_OSC=1 51 | endif 52 | 53 | all: luftboot.bin luftboot.hex 54 | 55 | luftboot.elf: luftboot.o 56 | @echo " CC luftboot.elf" 57 | $(Q)$(CC) -o $@ $^ $(LDFLAGS_BOOT) 58 | 59 | luftboot.bin: luftboot.elf 60 | @echo " create luftboot.bin" 61 | $(Q)$(Q)$(OBJCOPY) -O binary $^ $@ 62 | 63 | luftboot.hex: luftboot.elf 64 | @echo " create luftboot.hex" 65 | $(Q)$(OBJCOPY) -O ihex $^ $@ 66 | 67 | %.o: %.c 68 | @printf " CC $( 5 | * Copyright (C) 2011-2012 Piotr Esden-Tempski 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef VERSION 33 | #define VERSION "" 34 | #endif 35 | 36 | #ifndef DEV_SERIAL 37 | #define DEV_SERIAL "NSERIAL" 38 | #endif 39 | 40 | #define APP_ADDRESS 0x08002000 41 | #define SECTOR_SIZE 2048 42 | 43 | /* Commands sent with wBlockNum == 0 as per ST implementation. */ 44 | #define CMD_SETADDR 0x21 45 | #define CMD_ERASE 0x41 46 | 47 | #define FLASH_OBP_RDP 0x1FFFF800 48 | #define FLASH_OBP_WRP10 0x1FFFF808 49 | /* Defines user option register as per Table 5 on Page 55 of RM0008 (STM32 50 | * Reference Manual) 51 | */ 52 | #define FLASH_OBP_DATA0 0x1FFFF804 53 | 54 | #define FLASH_OBP_RDP_KEY 0x5aa5 55 | 56 | static const char 57 | dev_serial[] __attribute__((section (".devserial"))) = DEV_SERIAL; 58 | 59 | /* We need a special large control buffer for this device: */ 60 | uint8_t usbd_control_buffer[SECTOR_SIZE]; 61 | 62 | static enum dfu_state usbdfu_state = STATE_DFU_IDLE; 63 | 64 | static struct { 65 | uint8_t buf[sizeof(usbd_control_buffer)]; 66 | uint16_t len; 67 | uint32_t addr; 68 | uint16_t blocknum; 69 | } prog; 70 | 71 | const struct usb_device_descriptor dev = { 72 | .bLength = USB_DT_DEVICE_SIZE, 73 | .bDescriptorType = USB_DT_DEVICE, 74 | .bcdUSB = 0x0200, 75 | .bDeviceClass = 0, 76 | .bDeviceSubClass = 0, 77 | .bDeviceProtocol = 0, 78 | .bMaxPacketSize0 = 64, 79 | .idVendor = 0x1D50, 80 | .idProduct = 0x600F, 81 | .bcdDevice = 0x0100, 82 | .iManufacturer = 1, 83 | .iProduct = 2, 84 | .iSerialNumber = 3, 85 | .bNumConfigurations = 1, 86 | }; 87 | 88 | const struct usb_dfu_descriptor dfu_function = { 89 | .bLength = sizeof(struct usb_dfu_descriptor), 90 | .bDescriptorType = DFU_FUNCTIONAL, 91 | .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, 92 | .wDetachTimeout = 255, 93 | .wTransferSize = SECTOR_SIZE, 94 | .bcdDFUVersion = 0x011A, 95 | }; 96 | 97 | const struct usb_interface_descriptor iface = { 98 | .bLength = USB_DT_INTERFACE_SIZE, 99 | .bDescriptorType = USB_DT_INTERFACE, 100 | .bInterfaceNumber = 0, 101 | .bAlternateSetting = 0, 102 | .bNumEndpoints = 0, 103 | .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */ 104 | .bInterfaceSubClass = 1, 105 | .bInterfaceProtocol = 2, 106 | 107 | /* The ST Microelectronics DfuSe application needs this string. 108 | * The format isn't documented... */ 109 | .iInterface = 4, 110 | 111 | .extra = &dfu_function, 112 | .extralen = sizeof(dfu_function), 113 | }; 114 | 115 | const struct usb_interface ifaces[] = {{ 116 | .num_altsetting = 1, 117 | .altsetting = &iface, 118 | }}; 119 | 120 | const struct usb_config_descriptor config = { 121 | .bLength = USB_DT_CONFIGURATION_SIZE, 122 | .bDescriptorType = USB_DT_CONFIGURATION, 123 | .wTotalLength = 0, 124 | .bNumInterfaces = 1, 125 | .bConfigurationValue = 1, 126 | .iConfiguration = 0, 127 | .bmAttributes = 0xC0, 128 | .bMaxPower = 0x32, 129 | 130 | .interface = ifaces, 131 | }; 132 | 133 | /** contains DEV_SERIAL and unique chip ID. 134 | * chars: 7 (serial) + 1 (space) + 24 (id) + '\0' 135 | */ 136 | static char serial_no[7+1+24+1]; 137 | 138 | static inline char *get_serial_string(char *s); 139 | 140 | static const char *usb_strings[] = { 141 | "Transition Robotics Inc.", 142 | "Lisa/M (Upgrade) " VERSION, 143 | serial_no, 144 | /* This string is used by ST Microelectronics' DfuSe utility */ 145 | "@Internal Flash /0x08000000/4*002Ka,124*002Kg" 146 | }; 147 | 148 | static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout) 149 | { 150 | switch(usbdfu_state) { 151 | case STATE_DFU_DNLOAD_SYNC: 152 | usbdfu_state = STATE_DFU_DNBUSY; 153 | *bwPollTimeout = 100; 154 | return DFU_STATUS_OK; 155 | 156 | case STATE_DFU_MANIFEST_SYNC: 157 | /* Device will reset when read is complete */ 158 | usbdfu_state = STATE_DFU_MANIFEST; 159 | return DFU_STATUS_OK; 160 | 161 | default: 162 | return DFU_STATUS_OK; 163 | } 164 | } 165 | 166 | static void usbdfu_getstatus_complete(usbd_device *device, 167 | struct usb_setup_data *req) 168 | { 169 | int i; 170 | (void)req; 171 | 172 | switch(usbdfu_state) { 173 | case STATE_DFU_DNBUSY: 174 | 175 | flash_unlock(); 176 | if(prog.blocknum == 0) { 177 | if ((*(uint32_t*)(prog.buf+1) < 0x8002000) || 178 | (*(uint32_t*)(prog.buf+1) >= 0x8040000)) { 179 | usbd_ep_stall_set(device, 0, 1); 180 | return; 181 | } 182 | switch(prog.buf[0]) { 183 | case CMD_ERASE: 184 | flash_erase_page(*(uint32_t*)(prog.buf+1)); 185 | case CMD_SETADDR: 186 | prog.addr = *(uint32_t*)(prog.buf+1); 187 | } 188 | } else { 189 | uint32_t baseaddr = prog.addr + 190 | ((prog.blocknum - 2) * 191 | dfu_function.wTransferSize); 192 | for(i = 0; i < prog.len; i += 2) 193 | flash_program_half_word(baseaddr + i, 194 | *(uint16_t*)(prog.buf+i)); 195 | } 196 | flash_lock(); 197 | 198 | /* We jump straight to dfuDNLOAD-IDLE, 199 | * skipping dfuDNLOAD-SYNC 200 | */ 201 | usbdfu_state = STATE_DFU_DNLOAD_IDLE; 202 | return; 203 | 204 | case STATE_DFU_MANIFEST: 205 | /* Mark DATA0 register that we have just downloaded the code */ 206 | if((FLASH_OBR & 0x3FC00) != 0x00) { 207 | flash_unlock(); 208 | FLASH_CR = 0; 209 | flash_erase_option_bytes(); 210 | flash_program_option_bytes(FLASH_OBP_RDP, 0x5AA5); 211 | flash_program_option_bytes(FLASH_OBP_WRP10, 0x03FC); 212 | flash_program_option_bytes(FLASH_OBP_DATA0, 0xFF00); 213 | flash_lock(); 214 | } 215 | /* USB device must detach, we just reset... */ 216 | scb_reset_system(); 217 | return; /* Will never return */ 218 | default: 219 | return; 220 | } 221 | } 222 | 223 | static enum usbd_request_return_codes usbdfu_control_request(usbd_device *device, 224 | struct usb_setup_data *req, uint8_t **buf, 225 | uint16_t *len, 226 | void (**complete)(usbd_device *device, 227 | struct usb_setup_data *req)) 228 | { 229 | 230 | if((req->bmRequestType & 0x7F) != 0x21) 231 | return USBD_REQ_NOTSUPP; /* Only accept class request */ 232 | 233 | switch(req->bRequest) { 234 | case DFU_DNLOAD: 235 | if((len == NULL) || (*len == 0)) { 236 | usbdfu_state = STATE_DFU_MANIFEST_SYNC; 237 | return USBD_REQ_HANDLED; 238 | } else { 239 | /* Copy download data for use on GET_STATUS */ 240 | prog.blocknum = req->wValue; 241 | prog.len = *len; 242 | memcpy(prog.buf, *buf, *len); 243 | usbdfu_state = STATE_DFU_DNLOAD_SYNC; 244 | return USBD_REQ_HANDLED; 245 | } 246 | case DFU_CLRSTATUS: 247 | /* Clear error and return to dfuIDLE */ 248 | if(usbdfu_state == STATE_DFU_ERROR) 249 | usbdfu_state = STATE_DFU_IDLE; 250 | return USBD_REQ_HANDLED; 251 | case DFU_ABORT: 252 | /* Abort returns to dfuIDLE state */ 253 | usbdfu_state = STATE_DFU_IDLE; 254 | return USBD_REQ_HANDLED; 255 | case DFU_UPLOAD: 256 | /* Upload not supported for now */ 257 | return USBD_REQ_NOTSUPP; 258 | case DFU_GETSTATUS: { 259 | uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec 260 | */ 261 | 262 | (*buf)[0] = usbdfu_getstatus(&bwPollTimeout); 263 | (*buf)[1] = bwPollTimeout & 0xFF; 264 | (*buf)[2] = (bwPollTimeout >> 8) & 0xFF; 265 | (*buf)[3] = (bwPollTimeout >> 16) & 0xFF; 266 | (*buf)[4] = usbdfu_state; 267 | (*buf)[5] = 0; /* iString not used here */ 268 | *len = 6; 269 | 270 | *complete = usbdfu_getstatus_complete; 271 | 272 | return USBD_REQ_HANDLED; 273 | } 274 | case DFU_GETSTATE: 275 | /* Return state with no state transision */ 276 | *buf[0] = usbdfu_state; 277 | *len = 1; 278 | return USBD_REQ_HANDLED; 279 | } 280 | 281 | return USBD_REQ_NOTSUPP; 282 | } 283 | 284 | static inline void gpio_init(void) 285 | { 286 | /* Enable GPIOA, GPIOB, GPIOC, and AFIO clocks. */ 287 | rcc_periph_clock_enable(RCC_GPIOA); 288 | rcc_periph_clock_enable(RCC_GPIOB); 289 | rcc_periph_clock_enable(RCC_GPIOC); 290 | rcc_periph_clock_enable(RCC_AFIO); 291 | 292 | /* LED1 */ 293 | /* Set GPIO8 (in GPIO port A) to 'output push-pull'. */ 294 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, 295 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO8); 296 | 297 | /* JTAG_TRST */ 298 | /* Set GPIO4 (in GPIO port B) to 'output push-pull'. */ 299 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, 300 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO4); 301 | 302 | AFIO_MAPR |= AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_JNTRST; 303 | 304 | /* LED2, ADC4, ADC6 */ 305 | /* Set GPIO15, GPIO5, GPIO2 (in GPIO port C) to 'output push-pull'. */ 306 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, 307 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO15 | GPIO5 | GPIO2); 308 | 309 | /* Preconfigure the LEDs. */ 310 | gpio_set(GPIOA, GPIO8); 311 | gpio_set(GPIOB, GPIO4); 312 | gpio_set(GPIOC, GPIO15 | GPIO5 | GPIO2); 313 | } 314 | 315 | void led_set(int id, int on) 316 | { 317 | if (on) { 318 | switch (id) { 319 | case 0: 320 | gpio_clear(GPIOA, GPIO8); /* LED1 On */ 321 | break; 322 | case 1: 323 | gpio_clear(GPIOB, GPIO4); /* JTAG_TRST On */ 324 | break; 325 | case 2: 326 | gpio_clear(GPIOC, GPIO2); /* ADC6 On */ 327 | break; 328 | case 3: 329 | gpio_clear(GPIOC, GPIO5); /* ADC4 On */ 330 | break; 331 | case 4: 332 | gpio_clear(GPIOC, GPIO15); /* LED2 On */ 333 | break; 334 | } 335 | } else { 336 | switch (id) { 337 | case 0: 338 | gpio_set(GPIOA, GPIO8); /* LED1 On */ 339 | break; 340 | case 1: 341 | gpio_set(GPIOB, GPIO4); /* JTAG_TRST On */ 342 | break; 343 | case 2: 344 | gpio_set(GPIOC, GPIO2); /* ADC6 On */ 345 | break; 346 | case 3: 347 | gpio_set(GPIOC, GPIO5); /* ADC4 On */ 348 | break; 349 | case 4: 350 | gpio_set(GPIOC, GPIO15); /* LED2 On */ 351 | break; 352 | } 353 | } 354 | } 355 | 356 | static inline void led_advance(void) 357 | { 358 | static int state = 0; 359 | 360 | if (state < 5) { 361 | led_set(state, 1); 362 | } else if (state < 10) { 363 | led_set(state - 5, 0); 364 | } else if (state < 15) { 365 | led_set(14 - state, 1); 366 | } else if (state < 20) { 367 | led_set(19 - state, 0); 368 | } 369 | 370 | state++; 371 | if(state == 20) state = 0; 372 | 373 | } 374 | 375 | bool gpio_force_bootloader() 376 | { 377 | /* Force the bootloader if the GPIO state was changed to indicate this 378 | in the application (state remains after a core-only reset) 379 | Skip bootloader if the "skip bootloader" pin is grounded 380 | Force bootloader if the USB vbus is powered 381 | Skip bootloader otherwise */ 382 | 383 | /* Check if we are being forced by the payload. */ 384 | if (((GPIO_CRL(GPIOC) & 0x3) == 0x0) && 385 | ((GPIO_CRL(GPIOC) & 0xC) == 0x8) && 386 | ((GPIO_IDR(GPIOC) & 0x1) == 0x0)){ 387 | return true; 388 | } else { 389 | /* Enable clock for the "skip bootloader" pin bank and check 390 | * for it 391 | */ 392 | rcc_periph_clock_enable(RCC_GPIOC); 393 | gpio_set_mode(GPIOC, GPIO_MODE_INPUT, 394 | GPIO_CNF_INPUT_PULL_UPDOWN, GPIO0); 395 | gpio_set(GPIOC, GPIO0); 396 | 397 | if (!gpio_get(GPIOC, GPIO0)) { 398 | /* If pin grounded, disable the pin bank and return */ 399 | gpio_set_mode(GPIOC, GPIO_MODE_INPUT, 400 | GPIO_CNF_INPUT_FLOAT, GPIO0); 401 | rcc_periph_clock_disable(RCC_GPIOC); 402 | return false; 403 | } 404 | /* Disable the pin bank */ 405 | gpio_set_mode(GPIOC, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, 406 | GPIO0); 407 | rcc_periph_clock_disable(RCC_GPIOC); 408 | 409 | /* Enable clock for the "USB vbus" pin bank and check for it */ 410 | rcc_periph_clock_enable(RCC_GPIOA); 411 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 412 | GPIO_CNF_INPUT_PULL_UPDOWN, GPIO9); 413 | gpio_clear(GPIOA, GPIO9); 414 | 415 | if (gpio_get(GPIOA, GPIO9)) { 416 | /* If vbus pin high, disable the pin bank and return */ 417 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 418 | GPIO_CNF_INPUT_FLOAT, GPIO9); 419 | rcc_periph_clock_disable(RCC_GPIOA); 420 | return true; 421 | } 422 | /* Disable the pin bank */ 423 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, 424 | GPIO9); 425 | rcc_periph_clock_disable(RCC_GPIOA); 426 | } 427 | 428 | return false; 429 | } 430 | 431 | int main(void) 432 | { 433 | /* Check if the application is valid. */ 434 | if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { 435 | /* Check if we have just downloaded the new code by looking at 436 | * the DATA0 option register or that we do NOT want to force 437 | * the bootloader 438 | */ 439 | if (((FLASH_OBR & 0x3FC00) == 0x00) || 440 | (!gpio_force_bootloader() && 1)) { 441 | /* If we DID just download new code, reset that data 442 | * register 443 | */ 444 | if((FLASH_OBR & 0x3FC00) == 0x00) { 445 | flash_unlock(); 446 | FLASH_CR = 0; 447 | flash_erase_option_bytes(); 448 | /* Flash read unprotect */ 449 | flash_program_option_bytes(FLASH_OBP_RDP, 0x5AA5); 450 | /* Write protect first 4 flash pages */ 451 | flash_program_option_bytes(FLASH_OBP_WRP10, 0x03FC); 452 | /* Write data register that we downloaded the code 453 | * and want to jump the app 454 | */ 455 | flash_program_option_bytes(FLASH_OBP_DATA0, 456 | 0x00FF); 457 | flash_lock(); 458 | } 459 | /* Set vector table base address. */ 460 | SCB_VTOR = APP_ADDRESS & 0xFFFF; 461 | /* Initialise master stack pointer. */ 462 | asm volatile("msr msp, %0"::"g" 463 | (*(volatile uint32_t *)APP_ADDRESS)); 464 | /* Jump to application. */ 465 | (*(void (**)())(APP_ADDRESS + 4))(); 466 | } 467 | } 468 | 469 | if ((FLASH_WRPR & 0x03) != 0x00) { 470 | flash_unlock(); 471 | FLASH_CR = 0; 472 | flash_erase_option_bytes(); 473 | flash_program_option_bytes(FLASH_OBP_RDP, FLASH_OBP_RDP_KEY); 474 | flash_program_option_bytes(FLASH_OBP_WRP10, 0x03FC); 475 | } 476 | 477 | #if LUFTBOOT_USE_48MHZ_INTERNAL_OSC 478 | #pragma message "Luftboot using 8MHz internal RC oscillator to PLL it to 48MHz." 479 | rcc_clock_setup_in_hsi_out_48mhz(); 480 | #else 481 | #pragma message "Luftboot using 12MHz external clock to PLL it to 72MHz." 482 | rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE12_72MHZ]); 483 | #endif 484 | 485 | rcc_periph_clock_enable(RCC_OTGFS); 486 | 487 | gpio_init(); 488 | 489 | systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); 490 | systick_set_reload(900000); 491 | systick_interrupt_enable(); 492 | systick_counter_enable(); 493 | 494 | /* Get serial number */ 495 | get_serial_string(serial_no); 496 | 497 | usbd_device *device = usbd_init(&stm32f107_usb_driver, &dev, &config, 498 | usb_strings, 4, usbd_control_buffer, 499 | sizeof(usbd_control_buffer)); 500 | usbd_register_control_callback( device, 501 | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, 502 | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, 503 | usbdfu_control_request); 504 | 505 | while (1) 506 | usbd_poll(device); 507 | } 508 | 509 | /** get serial number as combination of DEV_SERIAL and unique chip ID. 510 | * first 7 chars are DEV_SERIAL, space, then 24 chars unique chip ID. 511 | */ 512 | static inline char *get_serial_string(char *s) 513 | { 514 | int i; 515 | for(i = 0; i < 7; i++) { 516 | s[i] = dev_serial[i]; 517 | } 518 | s[i] = ' '; 519 | desig_get_unique_id_as_string(&s[8], 25); 520 | 521 | return s; 522 | } 523 | 524 | void sys_tick_handler() 525 | { 526 | led_advance(); 527 | } 528 | -------------------------------------------------------------------------------- /src/luftboot.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopenstm32 project. 3 | * 4 | * Copyright (C) 2010 Thomas Otto 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | /* Define memory regions. */ 21 | MEMORY 22 | { 23 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K 24 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 25 | } 26 | 27 | /* Include the common ld script from libopenstm32. */ 28 | INCLUDE cortex-m-generic.ld 29 | 30 | --------------------------------------------------------------------------------