flabbergast's ARM/STM32 examples using libopencm3
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

usbdfu.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * This file is part of the libopencm3 project.
  3. *
  4. * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
  5. *
  6. * This library is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser 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 library 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 Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this library. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <string.h>
  20. #include <libopencm3/stm32/rcc.h>
  21. #include <libopencm3/stm32/gpio.h>
  22. #include <libopencm3/stm32/syscfg.h>
  23. #include <libopencm3/stm32/crs.h>
  24. #include <libopencm3/stm32/flash.h>
  25. #include <libopencm3/cm3/scb.h>
  26. #include <libopencm3/usb/usbd.h>
  27. #include <libopencm3/usb/dfu.h>
  28. #define APP_ADDRESS 0x08002000
  29. #define PROGRAM_BY_HALF_PAGE
  30. #define DFU_PACKET_SIZE 1024
  31. #define MCU_PAGE_SIZE 128
  32. /* Note: DFU_PACKET_SIZE has to be an integer multiple of MCU_PAGE_SIZE */
  33. /* DFU_PACKET_SIZE needs to match the DfuSe string below */
  34. /* This is supposed to be in libopencm3/lib/stm32/l0/flash.c but isn't */
  35. void flash_erase_page(uint32_t address);
  36. #if defined(PROGRAM_BY_HALF_PAGE)
  37. #define __FROM_RAM __attribute__((__long_call__, section(".data"),optimize("Os")))
  38. void flash_half_page(uint32_t address, uint32_t *data);
  39. __FROM_RAM void flash_half_page(uint32_t address, uint32_t *data)
  40. {
  41. uint16_t i;
  42. __asm__ volatile ("CPSID I\n");
  43. FLASH_PECR |= FLASH_PECR_PROG | FLASH_PECR_FPRG;
  44. i = 0;
  45. while(i < (MCU_PAGE_SIZE/8)) { /* half a page, 4byte words */
  46. /* Address doesn't need to be increased ... */
  47. MMIO32(address) = *data;
  48. data++;
  49. i++;
  50. }
  51. while (FLASH_SR & FLASH_SR_BSY);
  52. __asm__ volatile ("CPSIE I\n");
  53. if ((FLASH_SR & FLASH_SR_EOP) != 0) {
  54. FLASH_SR = FLASH_SR_EOP;
  55. } /* else error */
  56. FLASH_PECR &= ~(FLASH_PECR_PROG | FLASH_PECR_FPRG);
  57. }
  58. #endif /* PROGRAM_BY_HALF_PAGE */
  59. /* Commands sent with wBlockNum == 0 as per ST implementation. */
  60. #define CMD_SETADDR 0x21
  61. #define CMD_ERASE 0x41
  62. uint8_t usbd_control_buffer[DFU_PACKET_SIZE];
  63. static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
  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. const struct usb_device_descriptor dev = {
  71. .bLength = USB_DT_DEVICE_SIZE,
  72. .bDescriptorType = USB_DT_DEVICE,
  73. .bcdUSB = 0x0200,
  74. .bDeviceClass = 0,
  75. .bDeviceSubClass = 0,
  76. .bDeviceProtocol = 0,
  77. .bMaxPacketSize0 = 64,
  78. .idVendor = 0x0483,
  79. .idProduct = 0xDF11,
  80. .bcdDevice = 0x0200,
  81. .iManufacturer = 1,
  82. .iProduct = 2,
  83. .iSerialNumber = 3,
  84. .bNumConfigurations = 1,
  85. };
  86. const struct usb_dfu_descriptor dfu_function = {
  87. .bLength = sizeof(struct usb_dfu_descriptor),
  88. .bDescriptorType = DFU_FUNCTIONAL,
  89. .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
  90. .wDetachTimeout = 255,
  91. .wTransferSize = DFU_PACKET_SIZE,
  92. .bcdDFUVersion = 0x011A,
  93. };
  94. const struct usb_interface_descriptor iface = {
  95. .bLength = USB_DT_INTERFACE_SIZE,
  96. .bDescriptorType = USB_DT_INTERFACE,
  97. .bInterfaceNumber = 0,
  98. .bAlternateSetting = 0,
  99. .bNumEndpoints = 0,
  100. .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
  101. .bInterfaceSubClass = 1,
  102. .bInterfaceProtocol = 2,
  103. /* The ST Microelectronics DfuSe application needs this string.
  104. * The format isn't documented... */
  105. .iInterface = 4,
  106. .extra = &dfu_function,
  107. .extralen = sizeof(dfu_function),
  108. };
  109. const struct usb_interface ifaces[] = {{
  110. .num_altsetting = 1,
  111. .altsetting = &iface,
  112. }};
  113. const struct usb_config_descriptor config = {
  114. .bLength = USB_DT_CONFIGURATION_SIZE,
  115. .bDescriptorType = USB_DT_CONFIGURATION,
  116. .wTotalLength = 0,
  117. .bNumInterfaces = 1,
  118. .bConfigurationValue = 1,
  119. .iConfiguration = 0,
  120. .bmAttributes = 0xC0,
  121. .bMaxPower = 0x32,
  122. .interface = ifaces,
  123. };
  124. static const char *usb_strings[] = {
  125. "flabbergast",
  126. "L052 DFU Bootloader",
  127. "STM32L052C8Tx",
  128. /* This string is used by ST Microelectronics' DfuSe utility. */
  129. "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
  130. };
  131. /* Notes about the dfuse string above:
  132. * /<start_address>/<number>*<page_size><multiplier><memtype>
  133. * <number>: how many pages
  134. * <page_size>: self explanatory
  135. * <multiplier>: 'B'(ytes), 'K'(ilobytes), 'M'(egabytes)
  136. * <memtype>: the bottom three bits are significant:
  137. * writeable|erasable|readable
  138. * subsequent blocks separated by commas
  139. *
  140. * Using the internal page size: "@Internal Flash /0x08000000/64*128Ba,448*128Bg"
  141. * Using 1K blocks: "@Internal Flash /0x08000000/8*001Ka,56*001Kg"
  142. */
  143. static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout)
  144. {
  145. switch (usbdfu_state) {
  146. case STATE_DFU_DNLOAD_SYNC:
  147. usbdfu_state = STATE_DFU_DNBUSY;
  148. *bwPollTimeout = 100;
  149. return DFU_STATUS_OK;
  150. case STATE_DFU_MANIFEST_SYNC:
  151. /* Device will reset when read is complete. */
  152. usbdfu_state = STATE_DFU_MANIFEST;
  153. return DFU_STATUS_OK;
  154. default:
  155. return DFU_STATUS_OK;
  156. }
  157. }
  158. static void usbdfu_getstatus_complete(usbd_device *usbd_dev, struct usb_setup_data *req)
  159. {
  160. int i;
  161. (void)req;
  162. (void)usbd_dev;
  163. switch (usbdfu_state) {
  164. case STATE_DFU_DNBUSY:
  165. flash_unlock();
  166. if (prog.blocknum == 0) {
  167. switch (prog.buf[0]) {
  168. /* code to protect the bootloader? */
  169. case CMD_ERASE:
  170. {
  171. uint32_t *dat = (uint32_t *)(prog.buf + 1);
  172. i = 0;
  173. while ( i*MCU_PAGE_SIZE < DFU_PACKET_SIZE ) {
  174. flash_erase_page(*dat + i*MCU_PAGE_SIZE);
  175. i++;
  176. }
  177. }
  178. case CMD_SETADDR:
  179. {
  180. uint32_t *dat = (uint32_t *)(prog.buf + 1);
  181. prog.addr = *dat;
  182. }
  183. }
  184. } else {
  185. uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) *
  186. dfu_function.wTransferSize);
  187. #if defined(PROGRAM_BY_HALF_PAGE)
  188. for (i = 0; i < prog.len; i += (MCU_PAGE_SIZE/2)) {
  189. uint32_t *dat = (uint32_t *)(prog.buf + i);
  190. flash_half_page(baseaddr+i, dat);
  191. }
  192. #else
  193. for (i = 0; i < prog.len; i += 4) {
  194. uint32_t *dat = (uint32_t *)(prog.buf + i);
  195. flash_program_word(baseaddr + i, *dat);
  196. }
  197. #endif /* PROGRAM_BY_HALF_PAGE */
  198. }
  199. flash_lock();
  200. /* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */
  201. usbdfu_state = STATE_DFU_DNLOAD_IDLE;
  202. return;
  203. case STATE_DFU_MANIFEST:
  204. /* USB device must detach, we just reset... */
  205. scb_reset_system();
  206. return; /* Will never return. */
  207. default:
  208. return;
  209. }
  210. }
  211. static int usbdfu_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf,
  212. uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
  213. {
  214. (void)usbd_dev;
  215. if ((req->bmRequestType & 0x7F) != 0x21)
  216. return 0; /* Only accept class request. */
  217. switch (req->bRequest) {
  218. case DFU_DNLOAD:
  219. if ((len == NULL) || (*len == 0)) {
  220. usbdfu_state = STATE_DFU_MANIFEST_SYNC;
  221. return 1;
  222. } else {
  223. /* Copy download data for use on GET_STATUS. */
  224. prog.blocknum = req->wValue;
  225. prog.len = *len;
  226. memcpy(prog.buf, *buf, *len);
  227. usbdfu_state = STATE_DFU_DNLOAD_SYNC;
  228. return 1;
  229. }
  230. case DFU_CLRSTATUS:
  231. /* Clear error and return to dfuIDLE. */
  232. if (usbdfu_state == STATE_DFU_ERROR)
  233. usbdfu_state = STATE_DFU_IDLE;
  234. return 1;
  235. case DFU_ABORT:
  236. /* Abort returns to dfuIDLE state. */
  237. usbdfu_state = STATE_DFU_IDLE;
  238. return 1;
  239. case DFU_UPLOAD:
  240. /* Upload not supported for now. */
  241. return 0;
  242. case DFU_GETSTATUS: {
  243. uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
  244. (*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
  245. (*buf)[1] = bwPollTimeout & 0xFF;
  246. (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
  247. (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
  248. (*buf)[4] = usbdfu_state;
  249. (*buf)[5] = 0; /* iString not used here */
  250. *len = 6;
  251. *complete = usbdfu_getstatus_complete;
  252. return 1;
  253. }
  254. case DFU_GETSTATE:
  255. /* Return state with no state transision. */
  256. *buf[0] = usbdfu_state;
  257. *len = 1;
  258. return 1;
  259. }
  260. return 0;
  261. }
  262. static void usbdfu_set_config(usbd_device *usbd_dev, uint16_t wValue)
  263. {
  264. (void)wValue;
  265. usbd_register_control_callback(
  266. usbd_dev,
  267. USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
  268. USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
  269. usbdfu_control_request);
  270. }
  271. /* This is for some reason missing from libopencm3/l0 flash.c
  272. * (I thought it was in the pull request I've done for l0 but
  273. * apparently not.)
  274. */
  275. /*---------------------------------------------------------------------------*/
  276. /** @brief Erase a Page of FLASH
  277. This performs all operations necessary to erase a page in FLASH memory.
  278. The page should be checked to ensure that it was properly erased. A page must
  279. first be fully erased before attempting to program it.
  280. Note that the page sizes differ between devices. See the reference manual or
  281. the FLASH programming manual for details.
  282. @param[in] address Memory address of the first word on the page to be erased.
  283. */
  284. __FROM_RAM void flash_erase_page(uint32_t address)
  285. {
  286. while (FLASH_SR & FLASH_SR_BSY);
  287. FLASH_PECR |= FLASH_PECR_ERASE | FLASH_PECR_PROG;
  288. MMIO32(address) = (uint32_t)0;
  289. while (FLASH_SR & FLASH_SR_BSY);
  290. if ((FLASH_SR & FLASH_SR_EOP) != 0) {
  291. FLASH_SR = FLASH_SR_EOP;
  292. } /* else error */
  293. FLASH_PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_PROG);
  294. }
  295. /* End of the bit that's supposed to be in libopencm3/lib/stm32/l0/flash.c */
  296. int main(void)
  297. {
  298. usbd_device *usbd_dev;
  299. uint32_t k;
  300. rcc_periph_clock_enable(RCC_GPIOB);
  301. gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO3);
  302. if (gpio_get(GPIOB, GPIO3)) {
  303. /* Boot the application if it's valid. */
  304. if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
  305. /* Set vector table base address. */
  306. SCB_VTOR = APP_ADDRESS & 0xFFFF;
  307. /* Initialise master stack pointer. */
  308. asm volatile("msr msp, %0"::"g"
  309. (*(volatile uint32_t *)APP_ADDRESS));
  310. /* Jump to application. */
  311. (*(void (**)())(APP_ADDRESS + 4))();
  312. }
  313. }
  314. // libopencm3 clock setup can be a bit ad-hoc, so let's configure "manually"
  315. // 32MHz, PLL supplied by the internal HSI16 oscillator
  316. /* Set the flash latency */
  317. FLASH_ACR |= FLASH_ACR_LATENCY_1WS;
  318. /* Turn on HSI16 */
  319. RCC_CR |= RCC_CR_HSI16ON;
  320. while ((RCC_CR & RCC_CR_HSI16RDY) == 0);
  321. /* Make sure PLL is off (it should be after reset, but ...) */
  322. RCC_CR &= ~RCC_CR_PLLON;
  323. while (RCC_CR & RCC_CR_PLLRDY);
  324. /* Set the PLL source to HSI16 */
  325. RCC_CFGR &= ~(1<<16); // RCC_CFGR_PLLSRC
  326. /* Set up the PLL */
  327. uint32_t reg = RCC_CFGR
  328. & ~( (RCC_CFGR_PLLMUL_MASK << RCC_CFGR_PLLMUL_SHIFT)
  329. | (RCC_CFGR_PLLDIV_MASK << RCC_CFGR_PLLDIV_SHIFT));
  330. RCC_CFGR = reg | (RCC_CFGR_PLLMUL_MUL4 << RCC_CFGR_PLLMUL_SHIFT)
  331. | (RCC_CFGR_PLLDIV_DIV2 << RCC_CFGR_PLLDIV_SHIFT);
  332. /* Turn on PLL and switch to it */
  333. RCC_CR |= RCC_CR_PLLON;
  334. while ((RCC_CR & RCC_CR_PLLRDY) == 0);
  335. RCC_CFGR |= RCC_CFGR_SW_PLL;
  336. /* Set the peripheral clock frequencies used. */
  337. rcc_ahb_frequency = 32000000;
  338. rcc_apb1_frequency = 32000000;
  339. rcc_apb2_frequency = 32000000;
  340. /* end of "manual" clock setup */
  341. /* Enable VREFINT reference for HSI48 */
  342. rcc_periph_clock_enable(RCC_SYSCFG);
  343. SYSCFG_CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;
  344. rcc_set_hsi48_source_rc48();
  345. crs_autotrim_usb_enable();
  346. rcc_osc_on(RCC_HSI48);
  347. rcc_wait_for_osc_ready(RCC_HSI48);
  348. /* MCO on PA9, to verify MCU speed with a scope */
  349. gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
  350. gpio_set_af(GPIOA, GPIO_AF0, GPIO9);
  351. RCC_CFGR |= (RCC_CFGR_MCO_SYSCLK << RCC_CFGR_MCO_SHIFT) | (RCC_CFGR_MCOPRE_DIV16 << 28);
  352. /* for the LED */
  353. rcc_periph_clock_enable(RCC_GPIOA);
  354. usbd_dev = usbd_init(&st_usbfs_v2_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer));
  355. usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
  356. gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15);
  357. k = 0;
  358. while (1) {
  359. usbd_poll(usbd_dev);
  360. if(++k == 10000) {
  361. k = 0;
  362. gpio_toggle(GPIOA, GPIO15);
  363. }
  364. }
  365. }
  366. // vim: tabstop=4:shiftwidth=4:noexpandtab