# JLinkDownLoad **Repository Path**: tzb666/jlink-down-load ## Basic Information - **Project Name**: JLinkDownLoad - **Description**: 实现JLink下载算法,可以将数据下载到外部FLASH上 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: jlink_downloan_main_project - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2025-10-02 - **Last Updated**: 2026-02-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JLink下载算法制作 ## 制作模板工程 ### 找到模板工程位置 Keil安装目录/ARM/fLASH/_Template ![](Docs/1模板工程位置.png) 模板工程头文件 ![](Docs/2模板工程头文件位置.png) ### 添加必要文件 ![](Docs/3完整工程文件.png) ![](Docs/4完整包含目录.png) ### 修改魔术棒配置 ![](Docs/5修改Device.png) ![](Docs/6修改Output.png) ![](Docs/7修改User.png) ``` 使用STM32F4的标准库,想要使用HAL库也行,但是文件体积太大这样也不好。 USE_STDPERIPH_DRIVER,STM32F411xE ``` ![](Docs/8修改C&C++.png) ``` 将axf文件复制出来重命名为FLM文件 cmd.exe /C copy "Objects\%L" ".\@L.FLM" ``` ## 移植外部FLASH驱动 ### 修改w25qxx源文件 ``` /****************************************************************************** * Copyright (C) 2025 TTLzi. * * All Rights Reserved. * * @file w25qxx.c * * @par dependencies * "w25qxx.h" * "stm32f4xx.h" * * @author TTLzi * * @brief w25qxx简易驱动源文件 * * @version V1.0 * * @date 2025.10.02 * * @note 1 tab = 4 spaces ******************************************************************************/ /******************************** Includes ***********************************/ #include "w25qxx.h" /* Add your Include */ #include "stm32f4xx.h" /******************************** Includes ***********************************/ /******************************** Defines ***********************************/ /******************************** Defines ***********************************/ /******************************** Declare ************************************/ static void _w25qxx_set_nss_level(uint8_t level); static uint8_t _w25qxx_readwrite_byte(uint8_t tx_byte); /******************************** Declare ************************************/ /******************************** Variables **********************************/ static uint8_t w25qxx_write_buffer[4096]; /******************************** Variables **********************************/ /******************************** Functions **********************************/ static void _w25qxx_set_nss_level(uint8_t level) { /* Add your Code */ if(level) GPIO_SetBits(GPIOA, GPIO_Pin_4); else GPIO_ResetBits(GPIOA, GPIO_Pin_4); } static uint8_t _w25qxx_readwrite_byte(uint8_t tx_byte) { /* Add your Code */ while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, tx_byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } uint8_t w25qxx_init (void) { uint16_t device_id = 0; GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); _w25qxx_set_nss_level(1); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1); RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE); RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); _w25qxx_readwrite_byte(0xff); /*< 唤醒-芯片休眠时读取设备ID会出错 >*/ w25qxx_wakeup(); /*< 读ID >*/ w25qxx_get_device_id(&device_id); if(W25Q64 != device_id) return 1; return 0; } void w25qxx_deinit (void) { } void w25qxx_sleep (void) { uint8_t cmds[] = {W25X_PowerDown}; /*< 发送 休眠 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_set_nss_level(1); } void w25qxx_wakeup (void) { uint8_t cmds[] = {W25X_ReleasePowerDown}; /*< 发送 唤醒 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_set_nss_level(1); } void w25qxx_write_enable (void) { uint8_t cmds[] = {W25X_WriteEnable}; /*< 发送 写使能 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_set_nss_level(1); } void w25qxx_write_disable (void) { uint8_t cmds[] = {W25X_WriteDisable}; /*< 发送 写失能 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_set_nss_level(1); } void w25qxx_set_status (uint8_t status) { uint8_t cmds[] = {W25X_WriteStatusReg,0}; cmds[1] = status; /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 发送 写状态寄存器 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); /*< 发送 状态寄存器 数据 >*/ _w25qxx_readwrite_byte(cmds[1]); _w25qxx_set_nss_level(1); } void w25qxx_get_status (uint8_t * status) { uint8_t cmds[] = {W25X_ReadStatusReg}; /*< 发送 读状态寄存器 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); /*< 读取 状态寄存器 数据 >*/ * status = _w25qxx_readwrite_byte(0xff); _w25qxx_set_nss_level(1); } void w25qxx_wait_busy (void) { uint8_t status = 0; do { w25qxx_get_status(&status); }while(0x01 == (status & 0x01)); } void w25qxx_erase_chip (void) { uint8_t cmds[] = {W25X_ChipErase}; /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 写使能 >*/ w25qxx_write_enable(); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 发送 擦除芯片 命令 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_set_nss_level(1); /*< 等待BUSY >*/ w25qxx_wait_busy(); } void w25qxx_erase_sector (uint32_t addr) { uint8_t cmds[] = {W25X_SectorErase,0,0,0}; addr <<= 12; cmds[1] = (uint8_t)(addr >> 16); cmds[2] = (uint8_t)(addr >> 8); cmds[3] = (uint8_t)(addr >> 0); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 写使能 >*/ w25qxx_write_enable(); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 发送 扇区擦除命令 和 扇区擦除地址 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_readwrite_byte(cmds[1]); _w25qxx_readwrite_byte(cmds[2]); _w25qxx_readwrite_byte(cmds[3]); _w25qxx_set_nss_level(1); /*< 等待BUSY >*/ w25qxx_wait_busy(); } void w25qxx_get_device_id (uint16_t * device_id) { uint8_t cmds[] = {W25X_ManufactDeviceID,0,0,0}; _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_readwrite_byte(cmds[1]); _w25qxx_readwrite_byte(cmds[2]); _w25qxx_readwrite_byte(cmds[3]); * device_id |= _w25qxx_readwrite_byte(0xff) << 8; * device_id |= _w25qxx_readwrite_byte(0xff); _w25qxx_set_nss_level(1); } void w25qxx_page_program (uint8_t * pdata, uint32_t addr, uint16_t size) { uint8_t cmds[] = {W25X_PageProgram,0,0,0}; uint16_t i; cmds[1] = (uint8_t)(addr >> 16); cmds[2] = (uint8_t)(addr >> 8); cmds[3] = (uint8_t)(addr >> 0); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 写使能 >*/ w25qxx_write_enable(); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 发送 页编程命令 和 页编程地址 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_readwrite_byte(cmds[1]); _w25qxx_readwrite_byte(cmds[2]); _w25qxx_readwrite_byte(cmds[3]); /*< 发页编程数据 >*/ for (i = 0; i < size; i++) { _w25qxx_readwrite_byte(pdata[i]); } _w25qxx_set_nss_level(1); /*< 等待BUSY >*/ w25qxx_wait_busy(); } void w25qxx_write_nocheck (uint8_t * pdata, uint32_t addr, uint16_t size) { uint16_t pageremain= 256 - addr % 256; if (size <= pageremain) pageremain = size; while (1) { w25qxx_page_program(pdata,addr,pageremain); if (size == pageremain) break; else { pdata += pageremain; addr += pageremain; size -= pageremain; if (size > 256) pageremain = 256; else pageremain = size; } } } void w25qxx_write (uint8_t * pdata, uint32_t addr, uint16_t size) { uint32_t secpos; uint16_t secoffset; uint16_t secremain; uint16_t i; uint8_t * ptr; ptr = w25qxx_write_buffer; secpos = addr / 4096; secoffset = addr % 4096; secremain = 4096 - secoffset; if (size <= secremain) secremain = size; while (1) { w25qxx_read(ptr, secpos * 4096, 4096); for (i = 0; i < secremain; i++) { if (ptr[secoffset + i] != 0XFF) break; } if (i < secremain) { w25qxx_erase_sector(secpos); for (i = 0; i < secremain; i++) { ptr[i + secoffset] = pdata[i]; } w25qxx_write_nocheck(ptr, secpos * 4096, 4096); } else w25qxx_write_nocheck(pdata, addr, secremain); if (size == secremain) break; else { secpos++; secoffset = 0; pdata += secremain; addr += secremain; size -= secremain; if (size > 4096) secremain = 4096; else secremain = size; } } } void w25qxx_read (uint8_t * pdata, uint32_t addr, uint16_t size) { uint8_t cmds[] = {W25X_ReadData,0,0,0}; uint16_t i; cmds[1] = (uint8_t)(addr >> 16); cmds[2] = (uint8_t)(addr >> 8); cmds[3] = (uint8_t)(addr >> 0); /*< 等待BUSY >*/ w25qxx_wait_busy(); /*< 发送 读命令 和 读地址 >*/ _w25qxx_set_nss_level(0); _w25qxx_readwrite_byte(cmds[0]); _w25qxx_readwrite_byte(cmds[1]); _w25qxx_readwrite_byte(cmds[2]); _w25qxx_readwrite_byte(cmds[3]); /*< 读数据 >*/ for (i = 0; i < size; i++) { pdata[i] = _w25qxx_readwrite_byte(0xff); } _w25qxx_set_nss_level(1); } /******************************** Functions **********************************/ ``` ### 修改w25qxx头文件 ``` /****************************************************************************** * Copyright (C) 2025 TTLzi. * * All Rights Reserved. * * @file w25qxx.h * * @par dependencies * "***.h" * * @author TTLzi * * @brief w25qxx简易驱动头文件 * * @version V1.0 * * @date 2025.10.02 * * @note 1 tab = 4 spaces ******************************************************************************/ #ifndef __W25QXX_H__ #define __W25QXX_H__ /******************************** Includes ***********************************/ #include #include /******************************** Includes ***********************************/ /******************************** Defines ***********************************/ #define W25Q80 0XEF13 #define W25Q16 0XEF14 #define W25Q32 0XEF15 #define W25Q64 0XEF16 #define W25Q128 0XEF17 #define NM25Q80 0X5213 #define NM25Q16 0X5214 #define NM25Q32 0X5215 #define NM25Q64 0X5216 #define NM25Q128 0X5217 #define NM25Q256 0X5218 #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F /******************************** Defines ***********************************/ /******************************** Declare ************************************/ /******************************** Declare ************************************/ /******************************** Variables **********************************/ /******************************** Variables **********************************/ /******************************** Functions **********************************/ uint8_t w25qxx_init (void); void w25qxx_deinit (void); void w25qxx_sleep (void); void w25qxx_wakeup (void); void w25qxx_write_enable (void); void w25qxx_write_disable (void); void w25qxx_set_status (uint8_t status); void w25qxx_get_status (uint8_t * status); void w25qxx_wait_busy (void); void w25qxx_erase_chip (void); void w25qxx_erase_sector (uint32_t addr); void w25qxx_get_device_id (uint16_t * device_id); void w25qxx_page_program (uint8_t * pdata, uint32_t addr, uint16_t size); void w25qxx_write_nocheck (uint8_t * pdata, uint32_t addr, uint16_t size); void w25qxx_write (uint8_t * pdata, uint32_t addr, uint16_t size); void w25qxx_read (uint8_t * pdata, uint32_t addr, uint16_t size); /******************************** Functions **********************************/ #endif // End of __W25QXX_H__ ``` ## 修改源文件 ### 修改FlashDev.c ``` /**************************************************************************//** * @file FlashDev.c * @brief Flash Device Description for New Device Flash * @version V1.0.0 * @date 10. January 2018 ******************************************************************************/ /* * Copyright (c) 2010-2018 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FlashOS.h" // FlashOS Structures struct FlashDevice const FlashDevice = { FLASH_DRV_VERS, // Driver Version, do not modify! "W25Q64_8M_FLM", // Device Name EXTSPI, // Device Type FLASH_BASE_ADDR, // Device Start Address 8*1024*1024, // Device Size in Bytes (256kB) 4096, // Programming Page Size 0, // Reserved, must be 0 0xFF, // Initial Content of Erased Memory 100, // Program Page Timeout 100 mSec 3000, // Erase Sector Timeout 3000 mSec // Specify Size and Address of Sectors 4*1024, 0x00000000, // Sector Size 8kB (8 Sectors) SECTOR_END }; ``` ### 修改FlashPrg.c ``` /**************************************************************************//** * @file FlashPrg.c * @brief Flash Programming Functions adapted for New Device Flash * @version V1.0.0 * @date 10. January 2018 ******************************************************************************/ /* * Copyright (c) 2010-2018 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FlashOS.h" // FlashOS Structures #include "stm32f4xx.h" #include "w25qxx.h" /* Mandatory Flash Programming Functions (Called by FlashOS): int Init (unsigned long adr, // Initialize Flash unsigned long clk, unsigned long fnc); int UnInit (unsigned long fnc); // De-initialize Flash int EraseSector (unsigned long adr); // Erase Sector Function int ProgramPage (unsigned long adr, // Program Page Function unsigned long sz, unsigned char *buf); Optional Flash Programming Functions (Called by FlashOS): int BlankCheck (unsigned long adr, // Blank Check unsigned long sz, unsigned char pat); int EraseChip (void); // Erase complete Device unsigned long Verify (unsigned long adr, // Verify Function unsigned long sz, unsigned char *buf); - BlanckCheck is necessary if Flash space is not mapped into CPU memory space - Verify is necessary if Flash space is not mapped into CPU memory space - if EraseChip is not provided than EraseSector for all sectors is called */ static uint8_t read_buf[4096]; /* * Initialize Flash Programming Functions * Parameter: adr: Device Base Address * clk: Clock Frequency (Hz) * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) * Return Value: 0 - OK, 1 - Failed */ int Init (unsigned long adr, unsigned long clk, unsigned long fnc) { /* Add your Code */ uint8_t retval = 0; SystemInit(); retval = w25qxx_init(); if(retval) return 1; return (0); // Finished without Errors } /* * De-Initialize Flash Programming Functions * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) * Return Value: 0 - OK, 1 - Failed */ int UnInit (unsigned long fnc) { /* Add your Code */ return (0); // Finished without Errors } /* * Erase complete Flash Memory * Return Value: 0 - OK, 1 - Failed */ int EraseChip (void) { /* Add your Code */ w25qxx_erase_chip(); return (1); // Finished without Errors } /* * Erase Sector in Flash Memory * Parameter: adr: Sector Address * Return Value: 0 - OK, 1 - Failed */ int EraseSector (unsigned long adr) { /* Add your Code */ uint32_t sector = 0;//扇区编号 adr -= FLASH_BASE_ADDR; sector = adr /4096;//扇区的大小是4096 计算出了扇区的编号 w25qxx_erase_sector(sector); return (0); // Finished without Errors } /* * Program Page in Flash Memory * Parameter: adr: Page Start Address * sz: Page Size * buf: Page Data * Return Value: 0 - OK, 1 - Failed */ int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) { /* Add your Code */ adr -= FLASH_BASE_ADDR; w25qxx_write_nocheck(buf,adr,sz); return (0); // Finished without Errors } unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) { /* Add your Code */ unsigned long remain = sz; //剩余的字节数 unsigned long current_add = 0;//当前的地址 unsigned int index = 0;//用于buf的索引 current_add = adr - FLASH_BASE_ADDR; while(remain >= 4096) { w25qxx_read(read_buf,current_add,4096); for(int i=0;i<4096;i++) { if(read_buf[i] != buf[index+i]) return adr+index+i; } current_add += 4096; remain -= 4096; index += 4096; } w25qxx_read(read_buf,current_add,remain); for(int i=0;i