{"id":15060262,"url":"https://github.com/mytechnotalent/stm32f4_persistent_flash_driver","last_synced_at":"2025-04-10T05:50:59.203Z","repository":{"id":226675290,"uuid":"769361233","full_name":"mytechnotalent/STM32F4_Persistent_Flash_Driver","owner":"mytechnotalent","description":"An STM32F4, persistent flash driver written entirely in Assembler.","archived":false,"fork":false,"pushed_at":"2024-03-31T15:57:06.000Z","size":13510,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T07:04:22.270Z","etag":null,"topics":["arm","armv7","assembler","assembly","assembly-language","assembly-language-programming","embedded","embedded-devices","embedded-systems","flash","mcu","reverse-engineering","stm32f401","stm32f401cc","stm32f401ccu6"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mytechnotalent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-08T22:18:46.000Z","updated_at":"2024-10-10T07:10:54.000Z","dependencies_parsed_at":"2025-02-16T19:43:08.213Z","dependency_job_id":null,"html_url":"https://github.com/mytechnotalent/STM32F4_Persistent_Flash_Driver","commit_stats":null,"previous_names":["mytechnotalent/stm32f4_persistent_flash_driver"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_Persistent_Flash_Driver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_Persistent_Flash_Driver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_Persistent_Flash_Driver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FSTM32F4_Persistent_Flash_Driver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mytechnotalent","download_url":"https://codeload.github.com/mytechnotalent/STM32F4_Persistent_Flash_Driver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166926,"owners_count":21058480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arm","armv7","assembler","assembly","assembly-language","assembly-language-programming","embedded","embedded-devices","embedded-systems","flash","mcu","reverse-engineering","stm32f401","stm32f401cc","stm32f401ccu6"],"created_at":"2024-09-24T22:55:17.145Z","updated_at":"2025-04-10T05:50:59.182Z","avatar_url":"https://github.com/mytechnotalent.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://github.com/mytechnotalent/STM32F4_Persistent_Flash_Driver/blob/main/STM32F4_Persistent_Flash_Driver.png?raw=true\"\u003e\n\n## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering-Tutorial)\n\n\u003cbr\u003e\n\n# STM32F4 Persistent Flash Driver\nAn STM32F4, persistent flash driver written entirely in Assembler.\n\n\u003cbr\u003e\n\n# Code\n```assembler\n/**\n * FILE: main.s\n *\n * DESCRIPTION:\n * This file contains the assembly code for a STM32F401 persistent flash driver utilizing the STM32F401CC6 \n * microcontroller.\n *\n * AUTHOR: Kevin Thomas\n * CREATION DATE: March 7, 2024\n * UPDATE DATE: March 31, 2024\n *\n * ASSEMBLE AND LINK w/ SYMBOLS:\n * 1. arm-none-eabi-as -g main.s -o main.o\n * 2. arm-none-eabi-ld main.o -o main.elf -T STM32F401CCUX_FLASH.ld\n * 3. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c \"program main.elf verify reset exit\"\n * ASSEMBLE AND LINK w/o SYMBOLS:\n * 1. arm-none-eabi-as -g main.s -o main.o\n * 2. arm-none-eabi-ld main.o -o main.elf -T STM32F401CCUX_FLASH.ld\n * 3. arm-none-eabi-objcopy -O binary --strip-all main.elf main.bin\n * 3. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c \"program main.bin 0x08000000 verify reset exit\"\n * DEBUG w/ SYMBOLS:\n * 1. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg\n * 2. arm-none-eabi-gdb main.elf\n * 3. target remote :3333\n * 4. monitor reset halt\n * 5. l\n * DEBUG w/o SYMBOLS:\n * 1. openocd -f interface/stlink.cfg -f target/stm32f4x.cfg\n * 2. arm-none-eabi-gdb main.bin\n * 3. target remote :3333\n * 4. monitor reset halt\n * 5. x/8i $pc\n */\n\n\n.syntax unified\n.cpu cortex-m4\n.fpu softvfp\n.thumb\n\n\n/**\n * The start address for the .data section defined in linker script.\n */\n.word _sdata\n\n/**\n * The end address for the .data section defined in linker script.\n */\n.word _edata\n\n/**\n * The start address for the initialization values of the .data section\n * defined in linker script.\n */\n.word _sidata\n\n/**\n * The start address for the .bss section defined in linker script.\n */\n.word _sbss\n\n/**\n * The end address for the .bss section defined in linker script.\n */\n.word _ebss\n\n\n/**\n * Provide weak aliases for each Exception handler to the Default_Handler.\n * As they are weak aliases, any function with the same name will override\n * this definition.\n */\n.macro weak name\n  .global \\name\n  .weak \\name\n  .thumb_set \\name, Default_Handler\n  .word \\name\n.endm\n\n\n/**\n * Initialize the .isr_vector section.\n * The .isr_vector section contains vector table.\n */\n.section .isr_vector, \"a\"\n\n/**\n * The STM32F401CCUx vector table. Note that the proper constructs must be placed on this to ensure that it ends up\n * at physical address 0x00000000.\n */\n.global isr_vector\n.type isr_vector, %object\nisr_vector:\n  .word _estack\n  .word Reset_Handler\n   weak NMI_Handler\n   weak HardFault_Handler\n   weak MemManage_Handler\n   weak BusFault_Handler\n   weak UsageFault_Handler\n  .word 0\n  .word 0\n  .word 0\n  .word 0\n   weak SVC_Handler\n   weak DebugMon_Handler\n  .word 0\n   weak PendSV_Handler\n   weak SysTick_Handler\n  .word 0\n   weak EXTI16_PVD_IRQHandler                              // EXTI Line 16 interrupt PVD through EXTI line detection \n   weak TAMP_STAMP_IRQHandler                              // Tamper and TimeStamp interrupts through the EXTI line\n   weak EXTI22_RTC_WKUP_IRQHandler                         // EXTI Line 22 interrupt RTC Wakeup interrupt, EXTI line\n   weak FLASH_IRQHandler                                   // FLASH global interrupt\n   weak RCC_IRQHandler                                     // RCC global interrupt\n   weak EXTI0_IRQHandler                                   // EXTI Line0 interrupt\n   weak EXTI1_IRQHandler                                   // EXTI Line1 interrupt\n   weak EXTI2_IRQHandler                                   // EXTI Line2 interrupt\n   weak EXTI3_IRQHandler                                   // EXTI Line3 interrupt\n   weak EXTI4_IRQHandler                                   // EXTI Line4 interrupt\n   weak DMA1_Stream0_IRQHandler                            // DMA1 Stream0 global interrupt\n   weak DMA1_Stream1_IRQHandler                            // DMA1 Stream1 global interrupt\n   weak DMA1_Stream2_IRQHandler                            // DMA1 Stream2 global interrupt\n   weak DMA1_Stream3_IRQHandler                            // DMA1 Stream3 global interrupt\n   weak DMA1_Stream4_IRQHandler                            // DMA1 Stream4 global interrupt\n   weak DMA1_Stream5_IRQHandler                            // DMA1 Stream5 global interrupt\n   weak DMA1_Stream6_IRQHandler                            // DMA1 Stream6 global interrupt\n   weak ADC_IRQHandler                                     // ADC1 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak EXTI9_5_IRQHandler                                 // EXTI Line[9:5] interrupts\n   weak TIM1_BRK_TIM9_IRQHandle                            // TIM1 Break interrupt and TIM9 global interrupt\n   weak TIM1_UP_TIM10_IRQHandler                           // TIM1 Update interrupt and TIM10 global interrupt\n   weak TIM1_TRG_COM_TIM11_IRQHandler                      // TIM1 T/C interrupts, TIM11 global interrupt\n   weak TIM1_CC_IRQHandler                                 // TIM1 Capture Compare interrupt\n   weak TIM2_IRQHandler                                    // TIM2 global interrupt\n   weak TIM3_IRQHandler                                    // TIM3 global interrupt\n   weak TIM4_IRQHandler                                    // TIM4 global interrupt\n   weak I2C1_EV_IRQHandler                                 // I2C1 event interrupt\n   weak I2C1_ER_IRQHandler                                 // I2C1 error interrupt\n   weak I2C2_EV_IRQHandler                                 // I2C2 event interrupt\n   weak I2C2_ER_IRQHandler                                 // I2C2 error interrupt\n   weak SPI1_IRQHandler                                    // SPI1 global interrupt\n   weak SPI2_IRQHandler                                    // SPI2 global interrupt\n   weak USART1_IRQHandler                                  // USART1 global interrupt\n   weak USART2_IRQHandler                                  // USART2 global interrupt\n  .word 0                                                  // reserved\n   weak EXTI15_10_IRQHandler                               // EXTI Line[15:10] interrupts\n   weak EXTI17_RTC_Alarm_IRQHandler                        // EXTI Line 17 interrupt / RTC Alarms (A and B) EXTI\n   weak EXTI18_OTG_FS_WKUP_IRQHandler                      // EXTI Line 18 interrupt / USBUSB OTG FS Wakeup EXTI\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak DMA1_Stream7_IRQHandler                            // DMA1 Stream7 global interrupt\n  .word 0                                                  // reserved\n   weak SDIO_IRQHandler                                    // SDIO global interrupt\n   weak TIM5_IRQHandler                                    // TIM5 global interrupt\n   weak SPI3_IRQHandler                                    // SPI3 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak DMA2_Stream0_IRQHandler                            // DMA2 Stream0 global interrupt\n   weak DMA2_Stream1_IRQHandler                            // DMA2 Stream1 global interrupt\n   weak DMA2_Stream2_IRQHandler                            // DMA2 Stream2 global interrupt\n   weak DMA2_Stream3_IRQHandler                            // DMA2 Stream3 global interrupt\n   weak DMA2_Stream4_IRQHandler                            // DMA2 Stream4 global interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak OTG_FS_IRQHandler                                  // USB On The Go FS global interrupt\n   weak DMA2_Stream5_IRQHandler                            // DMA2 Stream5 global interrupt\n   weak DMA2_Stream6_IRQHandler                            // DMA2 Stream6 global interrupt\n   weak DMA2_Stream7_IRQHandler                            // DMA2 Stream7 global interrupt\n   weak USART6_IRQHandler                                  // USART6 global interrupt\n   weak I2C3_EV_IRQHandler                                 // I2C3 event interrupt\n   weak I2C3_ER_IRQHandler                                 // I2C3 error interrupt\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n  .word 0                                                  // reserved\n   weak SPI4_IRQHandler                                    // SPI4 global interrupt\n\n/**\n * @brief  This code is called when processor starts execution.\n *\n *         This is the code that gets called when the processor first\n *         starts execution following a reset event. We first define and init \n *         the bss section and then define and init the data section, after which\n *         the application supplied main routine is called.\n *\n * @param  None\n * @retval None\n */\n.type Reset_Handler, %function\n.global Reset_Handler\nReset_Handler:\n  LDR   R4, =_estack                                       // load address at end of the stack into R0\n  MOV   SP, R4                                             // move address at end of stack into SP\n  LDR   R4, =_sdata                                        // copy the data segment initializers from flash to SRAM\n  LDR   R5, =_edata                                        // copy the data segment initializers from flash to SRAM\n  LDR   R6, =_sidata                                       // copy the data segment initializers from flash to SRAM\n  MOVS  R7, #0                                             // copy the data segment initializers from flash to SRAM\n  B     .Reset_Handler_Loop_Copy_Data_Init                 // branch\n.Reset_Handler_Copy_Data_Init:\n  LDR   R8, [R6, R7]                                       // copy the data segment initializers into registers\n  STR   R8, [R4, R7]                                       // copy the data segment initializers into registers\n  ADDS  R7, R7, #4                                         // copy the data segment initializers into registers\n.Reset_Handler_Loop_Copy_Data_Init:\n  ADDS  R8, R4, R7                                         // initialize the data segment\n  CMP   R8, R5                                             // initialize the data segment\n  BCC   .Reset_Handler_Copy_Data_Init                      // branch if carry is clear\n  LDR   R6, =_sbss                                         // copy the bss segment initializers from flash to SRAM\n  LDR   R8, =_ebss                                         // copy the bss segment initializers from flash to SRAM\n  MOVS  R7, #0                                             // copy the bss segment initializers from flash to SRAM\n  B     .Reset_Handler_Loop_Fill_Zero_BSS                  // branch\n.Reset_Handler_Fill_Zero_BSS:\n  STR   R7, [R6]                                           // zero fill the bss segment\n  ADDS  R6, R6, #4                                         // zero fill the bss segment\n.Reset_Handler_Loop_Fill_Zero_BSS:\n  CMP   R6, R8                                             // zero fill the bss segment\n  BCC   .Reset_Handler_Fill_Zero_BSS                       // branch if carry is clear\n  BL    main                                               // call function\n\n/**\n * @brief  This code is called when the processor receives and unexpected interrupt.\n *\n *         This is the code that gets called when the processor receives an\n *         unexpected interrupt.  This simply enters an infinite loop, preserving\n *         the system state for examination by a debugger.\n *\n * @param  None\n * @retval None\n */\n.type Default_Handler, %function\n.global Default_Handler\nDefault_Handler:\n  BKPT                                                     // set processor into debug state\n  B.N   Default_Handler                                    // call function, force thumb state\n\n\n/**\n * Initialize the .text section.\n * The .text section contains executable code.\n */\n.section .text\n\n/**\n * @brief  Entry point for initialization and setup of specific functions.\n *\n *         This function is the entry point for initializing and setting up specific functions.\n *         It calls other functions to enable certain features and then enters a loop for further execution.\n *\n * @param  None\n * @retval None\n */\n.type main, %function\n.global main\nmain:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  BL    Unlock_Flash                                       // call function\n  BL    Erase_Sector_5_Flash                               // call function\n  BL    Verify_FLASH_SR_BSY_Bit_Cleared                    // call function\n  BL    Enable_Write_To_Flash                              // call function\n  LDR   R0, =0x0803FFFC                                    // addr to write data, resides in sector 5\n  LDR   R1, =0xDEADBEEF                                    // data to write to the sector 5 addr\n  BL    Write_To_Flash                                     // call function                                     \n  BL    Verify_FLASH_SR_BSY_Bit_Cleared                    // call function\n  BL    Lock_Flash                                         // call function\n  BL    Loop                                               // call function\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables the unlocking of flash memory for write access.\n *\n * @details This function unlocks the flash memory for write access by configuring the\n *          necessary key values, FLASH_KEYR and FLASH_OPTKEYR.\n *\n * @param   None\n * @retval  None\n */\nUnlock_Flash:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023C04                                    // load address of FLASH_KEYR register\n  LDR   R5, =0x45670123                                    // load the KEY1 value\n  LDR   R6, =0xCDEF89AB                                    // load the KEY2 value\n  STR   R5, [R4]                                           // store value into FLASH_KEYR register\n  STR   R6, [R4]                                           // store value into FLASH_KEYR register\n  LDR   R4, =0x40023C08                                    // load address of FLASH_OPTKEYR register\n  LDR   R5, =0x08192A3B                                    // load the OPTKEY1 value\n  LDR   R6, =0x4C5D6E7F                                    // load the OPTKEY2 value\n  STR   R5, [R4]                                           // store value into FLASH_OPTKEYR register\n  STR   R6, [R4]                                           // store value into FLASH_OPTKEYR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enables the erasing of the sector 5 flash area of memory.\n *\n * @details This function erases Sector 5 of the flash memory by setting the necessary\n *          control bits in the FLASH_CR register.\n *\n * @param   None\n * @retval  None\n */\nErase_Sector_5_Flash:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023C10                                    // load address of FLASH_CR register\n  LDR   R5, [R4]                                           // load value inside FLASH_CR register\n  ORR   R5, #(1\u003c\u003c16)                                       // set the STRT bit\n  ORR   R5, #(1\u003c\u003c5)                                        // set the SNB bit\n  ORR   R5, #(1\u003c\u003c3)                                        // set the SNB bit\n  ORR   R5, #(1\u003c\u003c1)                                        // set the SER bit\n  STR   R5, [R4]                                           // store value into FLASH_CR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Verify the BSY bit is cleared.\n *\n * @details This function verifies that the BSY (Busy) bit in the FLASH_SR register is\n *          cleared, indicating that the flash memory is no longer busy with an ongoing\n *          operation.\n *\n * @param   None\n * @retval  None\n */\nVerify_FLASH_SR_BSY_Bit_Cleared:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023C0C                                    // load address of FLASH_SR register\n  LDR   R5, [R4]                                           // load value inside FLASH_SR register\n  TST   R5, (1\u003c\u003c16)                                        // read the BSY bit, if 1, then BNE\n  BNE   Verify_FLASH_SR_BSY_Bit_Cleared                    // branch if not equal\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Enable write access to flash.\n *\n * @details This function enables write access to flash memory.\n *\n * @param   None\n * @retval  None\n */\nEnable_Write_To_Flash:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023C10                                    // load address of FLASH_CR register\n  LDR   R5, [R4]                                           // load value inside FLASH_CR register\n  ORR   R5, #(1\u003c\u003c0)                                        // set the PG bit\n  ORR   R5, #(1\u003c\u003c9)                                        // set the PSIZE bit\n  BIC   R5, #(1\u003c\u003c8)                                        // clear the PSIZE bit\n  STR   R5, [R4]                                           // store value into FLASH_CR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Write data to a specific sector 5 flash address.\n *\n * @details  This function assumes that the specified address falls within sector 5 of the\n *           flash memory and that the flash write operations have been properly configured\n *           and enabled in the system.\n *\n * @param   R0: Address to write within the sector 5 flash.\n * @param   R1: Data to write within the sector 5 flash address.\n *\n * @retval  None\n */\nWrite_To_Flash:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  MOV   R4, R0                                             // copy first arg into R4\n  MOV   R5, R1                                             // copy second arg into R5\n  STR   R5, [R4]                                           // store data into sector 5 addr\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief   Lock flash when not in use.\n *\n * @details This function locks the flash memory, preventing any further write access.\n *\n * @param   None\n * @retval  None\n */\nLock_Flash:\n  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack\n  LDR   R4, =0x40023C10                                    // load address of FLASH_CR register\n  LDR   R5, [R4]                                           // load value inside FLASH_CR register\n  MOV   R5, #(1\u003c\u003c31)                                       // set the LOCK bit, clear every other bits\n  STR   R5, [R4]                                           // store value into FLASH_CR register\n  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack\n  BX    LR                                                 // return to caller\n\n/**\n * @brief  Infinite loop function.\n *\n *         This function implements an infinite loop using an unconditional branch (B) statement.\n *         It is designed to keep the program running indefinitely by branching back to itself.\n *\n * @param  None\n * @retval None\n */\nLoop:\n  B     .                                                  // branch infinite loop\n\n\n/**\n * Initialize the .rodata section.\n * The .rodata section is used for constants and static strings.\n */\n.section .rodata\n\n\n/**\n * Initialize the .data section.\n * The .data section is used for initialized global or static variables.\n */\n.section .data\n\n\n/**\n * Initialize the .bss section.\n * The .bss section is typically used for uninitialized global or static variables.\n */\n.section .bss\n```\n\n\u003cbr\u003e\n\n## License\n[MIT](https://raw.githubusercontent.com/mytechnotalent/STM32F4_Persistent_Flash_Driver/main/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fstm32f4_persistent_flash_driver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmytechnotalent%2Fstm32f4_persistent_flash_driver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fstm32f4_persistent_flash_driver/lists"}