前言
在Gitee上發(fā)現(xiàn)了一個脫機下載器項目,我決定實踐復刻它。首先,我閱讀了項目的代碼,了解其實現(xiàn)原理和功能。接著,我自己進行一個電路圖的繪畫,并通過STM32系列的芯片進行了配置和連接。然后,按照原理圖對代碼進行了適當?shù)男薷暮驼{(diào)整,以適配我的硬件環(huán)境。最后,我進行了編譯、燒錄和調(diào)試的工作,測試了脫機下載器的功能,確保其正常工作。
最后進行了整機的調(diào)試,完成了這個復刻,后續(xù)將添加在線下載功能 ~~~~~
原理圖:
主要運用到的元器件:
STM32F103C8T6,W25Q128,無源晶振(8M),3.3穩(wěn)壓模塊,RGB燈(0805)
使用立創(chuàng)經(jīng)行的換圖與連線,咱就圖個免費 ~~
完成功能
1.離線下載
2.設(shè)置下載地址
3.添加提示音以及狀態(tài)燈
脫機下載器資料獲取
關(guān)注微信公眾號 -- 星之援工作室 發(fā)送關(guān)鍵字(脫機下載器)
????
歡迎關(guān)注微信公眾號星之援工作室,公眾號不定時開源設(shè)計項目
支持單片機,Android系統(tǒng)設(shè)計成品定制,項目代做
請聯(lián)系微信:13648103287
PCB
繪制了兩板圖,一版是直接焊屏幕,一版就是直接買屏幕,個人偏向買屏幕,畢竟少焊點東西啦,節(jié)約時間以及提高容錯率
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文件系統(tǒng)對象 */
DIR DirInfo; /* 目錄信息 */
FILINFO FileInfo; /* 文件信息 */
int8_t name_cnt = 0; /* 文件個數(shù) */
BYTE work[FF_MAX_SS]; /* FatFs文件工作區(qū)域 */
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文件系統(tǒng)掛載
algo_init(); // 下載算法初始化
RES_FS = f_mount(&fs, "", 1); // 掛載文件系統(tǒng)
if (RES_FS == FR_OK)
;
else if (RES_FS == FR_NO_FILESYSTEM) // 如果是新芯片還沒有文件系統(tǒng)
{
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) // 讀文件信息到文件狀態(tài)結(jié)構(gòu)體中
{
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);
// 地址設(shè)置
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文件即可)
百度網(wǎng)盤
鏈接:https://pan.baidu.com/s/1sz5dzR5yzwIxZ_9EqGQ5Yw?pwd=xzy0
提取碼:xzy0?
若需要幫助,請咨詢博主
????END????
聯(lián)系方式 微信號:13648103287