前言
在Gitee上发现了一个脱机下载器项目,我决定实践复刻它。首先,我阅读了项目的代码,了解其实现原理和功能。接着,我自己进行一个电路图的绘画,并通过STM32系列的芯片进行了配置和连接。然后,按照原理图对代码进行了适当的修改和调整,以适配我的硬件环境。最后,我进行了编译、烧录和调试的工作,测试了脱机下载器的功能,确保其正常工作。
最后进行了整机的调试,完成了这个复刻,后续将添加在线下载功能 ~~~~~
原理图:
主要运用到的元器件:
STM32F103C8T6,W25Q128,无源晶振(8M),3.3稳压模块,RGB灯(0805)
使用立创经行的换图与连线,咱就图个免费 ~~
完成功能
1.离线下载
2.设置下载地址
3.添加提示音以及状态灯
脱机下载器资料获取
关注微信公众号 -- 星之援工作室 发送关键字(脱机下载器)
➡️➡️
欢迎关注微信公众号星之援工作室,公众号不定时开源设计项目
支持单片机,Android系统设计成品定制,项目代做
请联系微信:13648103287
PCB
绘制了两板图,一版是直接焊屏幕,一版就是直接买屏幕,个人偏向买屏幕,毕竟少焊点东西啦,节约时间以及提高容错率
3D效果
正面
反面
实物图
主要代码展示
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"
#include "key.h"
#include "led.h"
#include "oled.h"
#include "ff.h"
#include "flash_blob.h"
#include "offline.h"
#include "hex2bin.h"
#include "stmflash.h"
FRESULT Res;
FIL fnew; /* file objects */
FATFS fs; /* FatFs文件系统对象 */
DIR DirInfo; /* 目录信息 */
FILINFO FileInfo; /* 文件信息 */
int8_t name_cnt = 0; /* 文件个数 */
BYTE work[FF_MAX_SS]; /* FatFs文件工作区域 */
char Name_Buffer[20][20]; /* 文件名 */
int8_t Select_file = 0; /* 选中下载文件 */
uint32_t flash_addr = 0x8000000;
uint8_t mode = 0;
int main(void)
{
uint8_t RES_FS = 0;
uint8_t addr[4];
HAL_Init();
SystemClock_Config();
OLED_Init();
MX_USB_DEVICE_Init();
MX_KEY_Init();
MX_LED_Init();
// 赋值falsh
// STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t *)addr, 4);
// flash_addr = ((addr[0] << 8 | addr[1]) << 8 | addr[2]) << 8 | addr[3];
// FATFS文件系统挂载
algo_init(); // 下载算法初始化
RES_FS = f_mount(&fs, "", 1); // 挂载文件系统
if (RES_FS == FR_OK)
;
else if (RES_FS == FR_NO_FILESYSTEM) // 如果是新芯片还没有文件系统
{
OLED_ShowString(0, 2, "Fatfs Format..", 12);
f_mkfs("", 0, work, sizeof(work));
OLED_ShowString(0, 2, "Format Finished", 12);
}
else
OLED_ShowString(0, 2, "Fatfs Failed..", 12);
// f_unlink("write.bin");
// 读取文件名到文件列表
if (f_opendir(&DirInfo, (const TCHAR *)"0:") == FR_OK) // 读取根目录下文件信息
{
f_readdir(&DirInfo, &FileInfo);
while (f_readdir(&DirInfo, &FileInfo) == FR_OK) // 读文件信息到文件状态结构体中
{
if (!FileInfo.fname[0])
break;
strcpy(Name_Buffer[name_cnt], FileInfo.fname);
if (strstr(Name_Buffer[name_cnt], ".BIN"))
{
name_cnt++;
if (name_cnt >= 20) // 最多保存20个文件名
break;
}
if (strstr(Name_Buffer[name_cnt], ".HEX"))
{
name_cnt++;
if (name_cnt >= 20) // 最多保存20个文件名
break;
}
}
}
while (1)
{
menu();
}
/* USER CODE END 3 */
}
void menu(void)
{
static uint8_t mode_status = 0;
switch (mode)
{
case 0: // 功能选择
select_function();
break;
case 1: // 离线下载模式
Downloader();
break;
case 2: // flash下载地址
FlashAddr_Set();
break;
}
if (mode != mode_status)
{
mode_status = mode;
OLED_Clear();
}
}
void OLED_ShowMenu(uint8_t num)
{
switch (num)
{
case 0: // 离线下载
OLED_ShowCH(24, 0, 0);
OLED_ShowCH(36, 0, 1);
OLED_ShowCH(48, 0, 2);
OLED_ShowCH(60, 0, 3);
// 地址设置
OLED_ShowCH(24, 2, 4);
OLED_ShowCH(36, 2, 5);
OLED_ShowCH(48, 2, 6);
OLED_ShowCH(60, 2, 7);
break;
case 1: // 文件选择
OLED_ShowCH(0, 0, 8);
OLED_ShowCH(12, 0, 9);
OLED_ShowCH(24, 0, 10);
OLED_ShowCH(36, 0, 11);
break;
case 2: // 地址
OLED_ShowString(0, 0, "FLASH", 12);
OLED_ShowCH(40, 0, 12);
OLED_ShowCH(52, 0, 13);
OLED_ShowCH(64, 0, 4);
OLED_ShowCH(76, 0, 5);
break;
}
}
void select_function(void)
{
static uint8_t t = 1;
uint8_t key;
key = KEY_Scan();
if (key == KEY0_PRES)
{
t++;
if (t > 2)
t = 1;
}
else if (key == KEY1_PRES) // 返回主页面
{
mode = 0;
t = 1;
}
else if (key == KEY2_PRES) // 确认
{
mode = t;
t = 1;
}
switch (t)
{
case 1:
OLED_ShowString(0, 2, " ", 12);
OLED_ShowString(0, 0, "->", 12);
break;
case 2:
OLED_ShowString(0, 0, " ", 12);
OLED_ShowString(0, 2, "->", 12);
break;
}
OLED_ShowMenu(0);
}
void Downloader(void)
{
uint8_t key = 0;
key = KEY_Scan();
if (key == KEY0_PRES)
{
Select_file++;
OLED_ShowString(0, 2, " ", 12);
}
else if (key == KEY1_PRES) // 返回主页面
mode = 0;
else if (key == KEY2_PRES)
Auto_Fash();
if (name_cnt == 0)
Select_file = 0;
else
{
if (Select_file >= name_cnt)
Select_file = 0;
else if (Select_file < 0)
Select_file = name_cnt - 1;
}
OLED_ShowMenu(1);
OLED_ShowString(0, 2, (uint8_t *)Name_Buffer[Select_file], 12);
if (name_cnt == 0)
OLED_ShowNum(90, 0, Select_file, 2, 12);
else
OLED_ShowNum(90, 0, Select_file + 1, 2, 12);
OLED_ShowString(102, 0, "/", 12);
OLED_ShowNum(114, 0, name_cnt, 2, 12);
}
void FlashAddr_Set(void)
{
static uint8_t k = 0, k1 = 0;
static uint8_t setmode = 0;
static uint8_t ad[4];
static uint8_t addr[8];
uint8_t key = 0, i;
addr[7] = (flash_addr / 0x01) % 16;
addr[6] = (flash_addr / 0x10) % 16;
addr[5] = (flash_addr / 0x100) % 16;
addr[4] = (flash_addr / 0x1000) % 16;
addr[3] = (flash_addr / 0x10000) % 16;
addr[2] = (flash_addr / 0x100000) % 16;
addr[1] = (flash_addr / 0x1000000) % 16;
addr[0] = (flash_addr / 0x10000000) % 16;
OLED_ShowMenu(2);
OLED_ShowString(0, 2, "0x", 12);
while (1)
{
for (i = 0; i < 8; i++)
{
if (addr[i] < 10)
OLED_ShowChar(18 + 6 * i, 2, addr[i] + 48, 12);
else
OLED_ShowChar(18 + 6 * i, 2, addr[i] + 65 - 10, 12);
}
key = KEY_Scan();
if (key == KEY0_PRES)
{
if (setmode == 0)
{
OLED_ShowString(18 + 6 * k, 3, "|", 12);
setmode = 1;
}
else if (setmode == 1)
{
addr[k]++;
if (addr[k] > 15)
addr[k] = 0;
}
}
else if (key == KEY1_PRES)
{
if (setmode == 0)
{
mode = 0;
break;
}
else if (setmode == 1)
{
k++;
if (k >= 8)
k = 0;
}
}
else if (key == KEY2_PRES)
{
if (setmode == 0)
{
// flash_addr = 0;
ad[0] = addr[0] << 4 | addr[1];
ad[1] = addr[2] << 4 | addr[3];
ad[2] = addr[4] << 4 | addr[5];
ad[3] = addr[6] << 4 | addr[7];
flash_addr = ((ad[0] << 8 | ad[1]) << 8 | ad[2]) << 8 | ad[3];
STMFLASH_Write(FLASH_SAVE_ADDR, (uint16_t *)ad, 4);
HAL_Delay(1000);
mode = 0;
k = 0;
break;
}
else if (setmode == 1)
{
setmode = 0;
OLED_ShowString(18 + 6 * k1, 3, " ", 12);
}
}
if (k1 != k)
{
OLED_ShowString(18 + 6 * k1, 3, " ", 12);
OLED_ShowString(18 + 6 * k, 3, "|", 12);
k1 = k;
}
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %drn", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
演示视频
USB文件(自己使用自己的HEX文件即可)
百度网盘
链接:https://pan.baidu.com/s/1sz5dzR5yzwIxZ_9EqGQ5Yw?pwd=xzy0
提取码:xzy0
若需要帮助,请咨询博主
⚠️⚠️END⚠️⚠️
联系方式 微信号:13648103287