IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【蓝牙串口无线烧写程序】适用于STM32F405RG的Bootloader -> 正文阅读

[嵌入式]【蓝牙串口无线烧写程序】适用于STM32F405RG的Bootloader

【下载链接】
https://pan.baidu.com/s/1Inyqiwm3L88lFsCQJar-8g(提取码:h7pp)

【使用说明】
(1)用ST-Link或JLink将Bootloader程序烧写到单片机中
(2)拔掉ST-Link或JLink调试器,复位单片机
(3)配置要烧写的程序的起始地址为0x8008000,程序区大小为“原大小-0x8000”,即0x100000-0x8000=0xf8000

(4)勾选“Create HEX file”

(5)打开STM32F4xx_HAL_Driver/system_stm32f4xx.c,取消USER_VECT_TAB_ADDRESS的注释,将VECT_TAB_OFFSET的值修改为0x8000

(6)编译程序,打开蓝牙串口无线烧写软件,选择生成的hex文件,将要烧写的程序烧写到子程序区

请注意:子程序里面必须要定时喂狗(IWDG),否则每隔10秒就会复位一次。

【Bootloader代码】

下载程序使用的串口为UART4,可以自行修改。

// 本程序用内部晶振(HSI)作为系统时钟, 没有用到外部晶振(HSE), 所以不需要指定HSE_VALUE的值
// 本程序已选择-O3优化。若要用ST-Link调试本程序, 请改选-O0优化
#include <stdio.h>
#include <stm32f4xx.h>
#include <string.h>
#include "common.h"
#include "DFU.h"

// 若想要直接在Keil中下载并用ST-Link调试子程序, 则需要禁用CRC校验
#define START_ADDR 0x08004000 // 一定要保证这个地址不在DFU程序的地址范围中, 否则擦除Flash会直接Hard Error
                              // 可以在项目属性中设置IROM1的Size, 限制主程序的大小, 防止主程序过大
                              // 若主程序太大, 可以在C/C++选项卡中开启-O3优化减小主程序大小
#define RESERVED_SIZE 0x4000 // 请参考system_stm32xxx.c中描述的VECT_TAB_OFFSET的对齐方式
                             // 里面有这样一句话: Vector Table base offset field. This value must be a multiple of 0xXXX.
                             // 必须保证设置RESERVED_SIZE的值能被0xXXX整除, 并且在START_ADDR所在的Flash页上, RESERVED_SIZE能占满整页

#define HARDWARE_CRC 0 // 启用硬件CRC校验
#define PROGRAM_ENABLE 1 // 是否真正烧写Flash (调试用)
#define PRINTF_ENABLE 1 // 是否开启调试输出
#define UART_FIFOCNT 35 // 数据缓冲区个数
#define UART_MTU 512 // 每个缓冲区的大小 (必须为4的倍数)

#if UART_MTU % 4 != 0
#error "UART_MTU must be a multiple of 4"
#endif

static int dfu_process_crc_config(const CRCConfig *config);
static int dfu_process_firmware_download(void);
static int dfu_start_transfer(void);

DMA_HandleTypeDef hdma12, hdma14;
IWDG_HandleTypeDef hiwdg;
static const uint16_t flash_sectors[FLASH_SECTOR_TOTAL] = {16, 16, 16, 16, 64, 128, 128, 128, 128, 128, 128, 128}; // Flash各扇区的大小
static const uint32_t crc_disable __attribute((at(START_ADDR + 4))) = 0x54545454; // 默认禁用CRC, 这样ST-Link下载了主程序后能马上下载子程序
static uint8_t uart_data[UART_FIFOCNT][UART_MTU + 1];
static volatile uint8_t uart_front, uart_rear, uart_busy;
static volatile uint32_t uart_frontaddr, uart_rearaddr;
static DeviceResponse device_response;
static FirmwareInfo firmware_info;

#if HARDWARE_CRC
// 注意: 不是所有的STM32单片机都能使用自定义CRC校验多项式
CRC_HandleTypeDef hcrc;

static uint8_t calc_crc8(const void *data, int len)
{
  assert_param(HAL_CRC_GetState(&hcrc) == HAL_CRC_STATE_READY);
  return (uint8_t)HAL_CRC_Calculate(&hcrc, (uint32_t *)data, len);
}

static void crc_init(void)
{
  __HAL_RCC_CRC_CLK_ENABLE();
  
  hcrc.Instance = CRC;
  hcrc.Init.CRCLength = CRC_POLYLENGTH_8B;
  hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
  hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
  hcrc.Init.GeneratingPolynomial = POLYNOMIAL_CRC8 & 0xff;
  hcrc.Init.InitValue = 0;
  hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
  hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
  hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
  HAL_CRC_Init(&hcrc);
}
#else
static uint8_t calc_crc8(const void *data, int len)
{
  const uint8_t *p = data;
  int i, j;
  uint16_t temp = 0;

  if (len != 0)
    temp = p[0] << 8;

  for (i = 1; i <= len; i++)
  {
    if (i != len)
      temp |= p[i];
    for (j = 0; j < 8; j++)
    {
      if (temp & 0x8000)
        temp ^= POLYNOMIAL_CRC8 << 7;
      temp <<= 1;
    }
  }
  return temp >> 8;
}

#define crc_init()
#endif

// 请在这里配置串口收发所用的DMA通道
// 注意文件最后还有DMA中断的函数名也要改
static void dfu_dma_init(void)
{
  if (hdma12.Instance != NULL)
    return;
  
  __HAL_RCC_DMA1_CLK_ENABLE();
  
  hdma12.Instance = DMA1_Stream2;
  hdma12.Init.Channel = DMA_CHANNEL_4;
  hdma12.Init.Direction = DMA_PERIPH_TO_MEMORY; // 注意方向不要写反了(1)
  hdma12.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  hdma12.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma12.Init.MemInc = DMA_MINC_ENABLE;
  hdma12.Init.Mode = DMA_NORMAL;
  hdma12.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma12.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma12.Init.Priority = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(&hdma12);
  __HAL_LINKDMA(&huart4, hdmarx, hdma12); // 注意方向不要写反了(2)
  
  hdma14.Instance = DMA1_Stream4;
  hdma14.Init.Channel = DMA_CHANNEL_4;
  hdma14.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma14.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  hdma14.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma14.Init.MemInc = DMA_MINC_ENABLE;
  hdma14.Init.Mode = DMA_NORMAL;
  hdma14.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma14.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma14.Init.Priority = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(&hdma14);
  __HAL_LINKDMA(&huart4, hdmatx, hdma14);
}

static int dfu_process(void)
{
  int header_valid = 0;
  int i, j, len;
  uint8_t sync[16];
  uint32_t header;
  CRCConfig crccfg;
  
  // 发送成功进入DFU模式的回应
  memset(sync, 0xcd, sizeof(sync));
  HAL_UART_Transmit(&huart4, sync, sizeof(sync), HAL_MAX_DELAY);
  printf("Enter DFU mode!\n");
  
  // 接收请求信息
  i = 0;
  do
  {
    // 跳过冗余的0xab同步信号, 寻找头部字段
    HAL_IWDG_Refresh(&hiwdg);
    memset(uart_data[0], 0, UART_MTU);
    HAL_UART_Receive(&huart4, uart_data[0], UART_MTU, 1000);
    for (j = 0; j < UART_MTU; j++)
    {
      if (uart_data[0][j] != 0xab)
        break;
    }
    
    len = UART_MTU - j;
    if (len >= 4)
    {
      memcpy(&header, &uart_data[0][j], 4);
      switch (header)
      {
        case HEADER_CRC_CONFIG:
          // CRC校验配置
          printf("header=HEADER_CRC_CONFIG\n");
          if (len >= sizeof(CRCConfig))
          {
            memcpy(&crccfg, &uart_data[0][j], sizeof(CRCConfig));
            if (calc_crc8(&crccfg, sizeof(CRCConfig)) == 0)
              header_valid = 1;
          }
          break;
        case HEADER_FIRMWARE_INFO:
          // 固件信息头
          printf("header=HEADER_FIRMWARE_INFO\n");
          if (len >= sizeof(FirmwareInfo))
          {
            memcpy(&firmware_info, &uart_data[0][j], sizeof(FirmwareInfo));
            if (calc_crc8(&firmware_info, sizeof(FirmwareInfo)) == 0)
              header_valid = 1;
          }
          break;
        default:
          printf("Invalid header: 0x%x\n", header);
          break;
      }
      printf("header_valid=%d\n", header_valid);
    }
    
    if (!header_valid)
    {
      // 请求重传头部
      printf("Failed to receive header!\n");
      i++;
      if (i == 5)
        return -1;
      
      device_response.addr = 0xffffffff;
      device_response.size = 0xffffffff;
      device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
      HAL_UART_Transmit(&huart4, (uint8_t *)&device_response, sizeof(DeviceResponse), HAL_MAX_DELAY);
    }
  } while (!header_valid);
  
  // 处理请求
  switch (header)
  {
    case HEADER_CRC_CONFIG:
      return dfu_process_crc_config(&crccfg);
    case HEADER_FIRMWARE_INFO:
      return dfu_process_firmware_download();
    default:
      return -1;
  }
}

/* 处理CRC校验配置请求 */
static int dfu_process_crc_config(const CRCConfig *config)
{
  int i;
  uint8_t buffer[8];
  uint8_t *value = (uint8_t *)START_ADDR + 5;
  uint32_t addr;
#if PROGRAM_ENABLE
  uint32_t err;
  FLASH_EraseInitTypeDef erase;
#endif
  
  memcpy(buffer, (void *)START_ADDR, sizeof(buffer));
  buffer[5] = (config->crc_enabled) ? 0x00 : 0x54;
  
  if (*value != buffer[5])
  {
    addr = FLASH_BASE;
    for (i = 0; i < _countof(flash_sectors); i++)
    {
      addr += flash_sectors[i] * 1024;
      if (addr > START_ADDR)
        break;
    }
    
#if PROGRAM_ENABLE
    HAL_FLASH_Unlock();
    erase.Banks = FLASH_BANK_1;
    erase.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase.Sector = i;
    erase.NbSectors = 1;
    erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
    HAL_FLASHEx_Erase(&erase, &err);
  
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, START_ADDR, __UNALIGNED_UINT32_READ(buffer));
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, START_ADDR + 4, __UNALIGNED_UINT32_READ(buffer + 4));
    HAL_FLASH_Lock();
#endif
  }
  
  device_response.addr = (uintptr_t)value;
  device_response.size = (memcmp((void *)START_ADDR, buffer, sizeof(buffer)) == 0);
  device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
  HAL_UART_Transmit(&huart4, (uint8_t *)&device_response, sizeof(device_response), HAL_MAX_DELAY);
  return 0;
}

/* 处理固件下载请求 */
static int dfu_process_firmware_download(void)
{
  int i, ret;
  uint32_t err, ticks;
  uint32_t maxsize, size;
  HAL_StatusTypeDef status;
#if PROGRAM_ENABLE
  uint32_t addr[3];
  FLASH_EraseInitTypeDef erase;
#endif
  
  maxsize = (FLASH_BASE + FLASH_SIZE) - (START_ADDR + RESERVED_SIZE);
  printf("Available flash: %u\n", maxsize);
  printf("Size: %u\n", firmware_info.size);
  printf("Address: 0x%08x - 0x%08x\n", firmware_info.start_addr, firmware_info.end_addr - 1);
  printf("Entry Point: 0x%08x\n", firmware_info.entry_point);
  printf("Checksum: 0x%02x\n", firmware_info.firmware_checksum);
  if (firmware_info.size > maxsize)
  {
    // Flash容量不够
    device_response.addr = 0;
    device_response.size = maxsize;
    device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
    HAL_UART_Transmit(&huart4, (uint8_t *)&device_response, sizeof(DeviceResponse), HAL_MAX_DELAY);
    return -1;
  }
  else if (firmware_info.start_addr != START_ADDR + RESERVED_SIZE)
  {
    // 程序的起始地址不正确
    printf("Invalid firmware address!\n");
    device_response.addr = START_ADDR + RESERVED_SIZE;
    device_response.size = 0;
    device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
    HAL_UART_Transmit(&huart4, (uint8_t *)&device_response, sizeof(DeviceResponse), HAL_MAX_DELAY);
    return -1;
  }
  else if (firmware_info.start_addr + firmware_info.size != firmware_info.end_addr)
  {
    printf("Incorrect firmware size!\n");
    return -1;
  }
  
  // 利用中断, 并行接收数据
  HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
  HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
  HAL_NVIC_EnableIRQ(UART4_IRQn);
  dfu_dma_init();
  uart_front = 0;
  uart_frontaddr = START_ADDR + RESERVED_SIZE;
  uart_rear = 0;
  uart_rearaddr = uart_frontaddr;
  dfu_start_transfer();
  
#if PROGRAM_ENABLE
  // 擦除需要的Flash页
  HAL_IWDG_Refresh(&hiwdg);
  HAL_FLASH_Unlock();
  
  addr[0] = FLASH_BASE;
  addr[2] = firmware_info.end_addr - 1; // 子程序结束地址
  memset(&erase, 0xff, sizeof(FLASH_EraseInitTypeDef));
  for (i = 0; i < _countof(flash_sectors); i++)
  {
    // i为扇区号, addr[0]为扇区i的起始地址, addr[1]为扇区i的结束地址
    addr[1] = addr[0] + flash_sectors[i] * 1024 - 1;
    if (START_ADDR >= addr[0] && START_ADDR <= addr[1])
      erase.Sector = i;
    if (addr[2] >= addr[0] && addr[2] <= addr[1])
    {
      erase.NbSectors = i - erase.Sector + 1;
      break;
    }
    addr[0] = addr[1] + 1;
  }
  erase.Banks = FLASH_BANK_1;
  erase.TypeErase = FLASH_TYPEERASE_SECTORS;
  erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
  printf("Erase sector %d~%d\n", erase.Sector, erase.Sector + erase.NbSectors - 1);
  HAL_FLASHEx_Erase(&erase, &err);
  
  // 将程序大小和CRC校验码保存到RESERVED区域, 并启用CRC校验
  HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, START_ADDR, firmware_info.size);
  HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, START_ADDR + 4, firmware_info.firmware_checksum);
#endif
  
  while (uart_frontaddr != firmware_info.end_addr)
  {
    // 等待FIFO中有数据
    err = 0;
    ticks = HAL_GetTick();
    HAL_IWDG_Refresh(&hiwdg);
    while (uart_front == uart_rear)
    {
      if (HAL_GetTick() - ticks > 3500)
      {
        // 进入到这里面, 一般是因为传输过程中丢失了某些字节, 无法构成完整的数据包, DMA传输无法完成
        // (这不同于HAL_UART_RxCpltCallback里面的CRC错误, 只有收到了完整的数据包, 但CRC校验不通过, 才认定为CRC错误)
        // 也可能是因为用户取消了程序烧写
        HAL_IWDG_Refresh(&hiwdg);
        printf("Data timeout!\n");
        status = HAL_UART_Abort(&huart4); // 以阻塞方式强行中止中断或DMA方式的串口传输
        if (status == HAL_OK)
          uart_busy = 0; // 中止成功
        
        // 注意HAL_UART_Abort和HAL_UART_Abort_IT的区别
        // HAL_UART_Abort在函数返回时已经中止成功, 不会触发回调函数
        // HAL_UART_Abort_IT是以中断方式强行中止中断或DMA方式的串口传输
        // 函数返回时不一定已经中止完毕, 直到HAL_UART_AbortCpltCallback回调函数触发时, 才算中止完毕
        
        // 请求上位机重传数据包
        err++;
        if (err == 3)
          break; // 10秒还没有反应, 强行退出
        ticks = HAL_GetTick();
        dfu_start_transfer();
      }
    }
    if (uart_front == uart_rear)
      break;
    
    // 计算数据量
    maxsize = firmware_info.end_addr - uart_frontaddr;
    size = UART_MTU;
    if (size > maxsize)
      size = maxsize;
    
    // 烧写Flash
    // 注意: 当地址不能被4整除时, 不能用*(uint32_t *)
    //       应该用*(__packed uint32_t *), 即__UNALIGNED_UINT32_READ, 否则会导致Hard Error
#if PROGRAM_ENABLE
    HAL_IWDG_Refresh(&hiwdg);
    for (i = 0; i < size; i += 4)
      HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, uart_frontaddr + i, __UNALIGNED_UINT32_READ(uart_data[uart_front] + i));
#endif
    
    // 释放FIFO
    uart_front = (uart_front + 1) % UART_FIFOCNT;
    uart_frontaddr += size;
  }
  
  // 结束请求
  assert_param(!uart_busy);
  HAL_IWDG_Refresh(&hiwdg);
  HAL_NVIC_DisableIRQ(DMA1_Stream2_IRQn);
  HAL_NVIC_DisableIRQ(DMA1_Stream4_IRQn);
  HAL_NVIC_DisableIRQ(UART4_IRQn);
  if (uart_frontaddr == firmware_info.end_addr)
  {
    device_response.addr = uart_frontaddr;
    device_response.size = 0;
    device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
    HAL_UART_Transmit(&huart4, (uint8_t *)&device_response, sizeof(DeviceResponse), HAL_MAX_DELAY);
    ret = 0;
  }
  else
    ret = -1;
  
#if PROGRAM_ENABLE
  HAL_FLASH_Lock();
#endif
  return ret;
}

/* 请求主机传输固件数据 */
static int dfu_start_transfer(void)
{
  uint32_t maxsize;
  
  if (uart_busy)
    return -1;
  if (huart4.gState != HAL_UART_STATE_READY)
    return -1; // 如果UART Handle被锁, 则暂不启动传输, 稍后在TxCallback里面重试 (STM32H7就有这种问题)
  if ((uart_rear + 1) % UART_FIFOCNT == uart_front)
    return -1; // FIFO已满
  
  maxsize = firmware_info.end_addr - uart_rearaddr;
  if (maxsize == 0)
    return -1; // 固件已传输完毕
  
  HAL_IWDG_Refresh(&hiwdg);
  uart_busy = 1;
  device_response.addr = uart_rearaddr;
  device_response.size = UART_MTU;
  if (device_response.size > maxsize)
    device_response.size = maxsize;
  device_response.checksum = calc_crc8(&device_response, sizeof(DeviceResponse) - 1);
  HAL_UART_Transmit_DMA(&huart4, (uint8_t *)&device_response, sizeof(DeviceResponse));
  HAL_UART_Receive_DMA(&huart4, uart_data[uart_rear], device_response.size + 1);
  return 0;
}

static int dfu_sync(void)
{
  int i;
  uint8_t sync[16];
  HAL_StatusTypeDef status;
  
  status = HAL_UART_Receive(&huart4, sync, sizeof(sync), 100);
  if (status == HAL_OK)
  {
    for (i = 0; i < sizeof(sync); i++)
    {
      if (sync[i] != 0xab)
        break;
    }
    if (i == sizeof(sync))
      return 0;
  }
  return -1;
}

static void iwdg_init(void)
{
#ifdef __HAL_RCC_DBGMCU_CLK_ENABLE
  __HAL_RCC_DBGMCU_CLK_ENABLE();
#endif
#ifdef __HAL_DBGMCU_FREEZE_IWDG
  __HAL_DBGMCU_FREEZE_IWDG();
#endif
  
  hiwdg.Instance = IWDG;
  hiwdg.Init.Prescaler = IWDG_PRESCALER_128;
  hiwdg.Init.Reload = 2499; // 超时时间为(2499+1)*4ms=10s
  //hiwdg.Init.Window = 4095; // 有的STM32的IWDG有Window参数, 必须要设置
  HAL_IWDG_Init(&hiwdg);
}

static int jump_to_application(void)
{
  static Runnable run;
  const uint8_t *crc = (const uint8_t *)(START_ADDR + 4);
  const uint32_t *size = (const uint32_t *)START_ADDR;
  uint32_t *addr = (uint32_t *)(START_ADDR + RESERVED_SIZE + 4);
  uint32_t *msp = (uint32_t *)(START_ADDR + RESERVED_SIZE);
  uint32_t value;
  
  if (*(crc + 1) == 0x54)
  {
    // CRC校验已关闭
    printf("CRC check has been disabled!\n");
  }
  else
  {
    // 进行CRC校验
    if (START_ADDR + RESERVED_SIZE + *size > FLASH_BASE + FLASH_SIZE)
    {
      printf("Program data error!\n");
      return -1;
    }
    
    value = calc_crc8((const uint8_t *)START_ADDR + RESERVED_SIZE, *size);
    if (*crc == value)
      printf("CRC passed! addr=0x%08x, msp=0x%08x\n", *addr, *msp);
    else
    {
      printf("CRC failed! 0x%02x!=0x%02x\n", *crc, value);
      return -1;
    }
  }
  
  if ((*msp & 0xf0000000) != 0x20000000 || (*addr & 0xff000000) != 0x08000000)
  {
    printf("Program data error!\n");
    return -1;
  }
  
  printf("Jump to application...\n");
  run = (Runnable)*addr;
  HAL_DeInit(); // 关闭所有外设和中断, 防止子程序的运行受到主程序的影响 (很重要)
  
  // 一旦修改了栈顶指针, 就不能再使用函数中的局部变量, 否则可能会出现Hard Error错误
  // 但可以使用static变量, 所以run变量必须声明为static
  __set_MSP(*msp); // 修改栈顶指针
  run();
  return 0;
}

int main(void)
{
  int dfu_cnt = 3; // DFU请求同步次数 (数值越大, 上位机检测到设备的成功率越高, 但开机后程序启动越慢)
  int ret;
  
  HAL_Init();
  
  iwdg_init();
  usart_init(115200);
  printf_enable(PRINTF_ENABLE);
  printf("STM32F405RG DFU\n");
  printf("SystemCoreClock=%u\n", SystemCoreClock);
  
  crc_init();
  while (1)
  {
    while (dfu_cnt > 0)
    {
      HAL_IWDG_Refresh(&hiwdg);
      dfu_cnt--;
      ret = dfu_sync(); // 接收主机的DFU请求
      if (ret == 0)
      {
        // 成功进入DFU模式
        ret = dfu_process(); // DFU处理
        
        // 暂停, 等待上位机下发命令
        printf("Send any command to continue...\n");
        while (__HAL_UART_GET_FLAG(&huart4, UART_FLAG_RXNE) == RESET)
          HAL_IWDG_Refresh(&hiwdg);
        HAL_UART_Receive(&huart4, uart_data[0], 1, HAL_MAX_DELAY);
        
        if (ret == 0)
        {
          dfu_cnt = 0; // 程序下载成功后, 启动程序
          HAL_Delay(2000); // 延迟足够的时间, 保证即使是用有线串口, 也能在打开串口调试助手后看到第一行调试信息
        }
        else
          dfu_cnt = 10; // 程序下载失败时, 不运行程序, 而是重新进入下载模式
      }
    }
    
    // 启动用户程序 (若启动成功, 则函数不返回)
    HAL_IWDG_Refresh(&hiwdg);
    jump_to_application();
    
    // 启动用户程序失败, 再次进入DFU模式
    HAL_Delay(250);
    dfu_cnt = 1;
  }
}

void DMA1_Stream2_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma12);
}

void DMA1_Stream4_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma14);
}

void UART4_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart4);
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)
    printf("USART1 error: 0x%x!\n", huart->ErrorCode);
  else if (huart->Instance == UART4)
    printf("UART4 error: 0x%x!\n", huart->ErrorCode);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  uint8_t crc;
  
  // 进行CRC校验
  crc = calc_crc8(uart_data[uart_rear], device_response.size + 1);
  if (crc == 0)
  {
    uart_rear = (uart_rear + 1) % UART_FIFOCNT; // 校验通过, 接收下一个数据包
    uart_rearaddr += device_response.size;
  }
  else
    printf("Data CRC failed!\n");
  
  uart_busy = 0;
  dfu_start_transfer();
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  dfu_start_transfer();
}

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:50:39  更:2021-07-25 11:51:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 7:02:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码