STM32 ISP升级设计(HAL完整例程及下载界面软件)

STM32 ISP升级设计(HAL完整例程及下载界面软件)

STM32的芯片分为各种资源等级,对于内部FLASH空间比较充裕的型号,可以采用IAP升级方式,自行定义接口升级协议及代码实现(参考STM32 IAP 升级设计)。对于内部FLASH空间比较紧张的情况(如STM32F0, STM32L0, STM32G0, STM32C0系列等),采用ISP升级方式是一种节约代码空间的方式,ISP升级方式可用的升级接口有限指定,升级过程的协议代码位于特定的只读FLASH空间里.

ISP设计相关官方文档

用于ISP设计的官方关键文档:
1.各型号MCU可用于ISP升级的接口(AN2606)
2. 串口ISP升级协议(AN3155)

ISP模式进入方式

  1. 硬件进入模式: 通过BOOT管脚的电平预置,并使得芯片重启后进入ISP模式,具体BOOT管脚的电平预置从上面所列AN2606文档中获得。

  2. 软件进入模式及基本协议设计:软件触发的ISP, 实现触发进入ISP的程序段,必须位于main函数最前端,否则各个芯片会出现各种异常,如握手信号出错,Flash写失败等等。为了进行握手触发进入ISP状态,需要利用内部FLASH用户区域的最高字地址,通过某种方式如串口指令触发,写最高字地址值为0x55555555, 并触发软件复位中断;在程序main函数的最前面,检测检测内部FLASH用户区域的最高字地址里的值是否为0x55555555,如果是,修改为0xaaaaaaaa,跳转到ISP固件地址,如果不是,继续执行后面的正常程序。在外部与ISP固件程序交互过程中,需要先发送指令设置去除读保护,而此操作会触发MCU重启,因此重启在检测最高字地址里的值是否为0x55555555后,也要检测到最高字地址里的值是否为0xaaaaaaaa, 如果是0xaaaaaaaa则修改值为0x00000000, 仍然跳转到ISP固件,然后就可以进行擦除和写入Flash。另外,不要采用选项字节做控制,因为从经验上看,选项字节读稳定,写容易出问题。也不采用外部存储做控制,因为需要先例化外部管脚,从而引入代码复杂度和软件ISP失效风险。这里操作Flash时采用了保护性写函数设计(可参考https://blog.csdn.net/hwytree/article/details/103907992的设计原理)。ISP固件地址从上面所列AN2606文档中获得:
    在这里插入图片描述

STM32 ISP软件进入模式范例

这里以STM32L031K6T6为例,以STM32CUBEIDE为开发工具,实现ISP软件进入模式。ISP升级接口选择为USART2。

首先建立工程并设置时钟,例程只用到一个USART2,时钟随意设置即可,采用内部外部时钟皆可:
在这里插入图片描述
设置USART2异步中断模式:
在这里插入图片描述
采用9600波特率:
在这里插入图片描述
具体的实现代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
//Written by Pegasus Yu
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stm32l0xx_hal_rcc.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>

#define IspAdr 0x1FF00000 //Check STM AN2606 doc for ISP firmware address
#define FlashLastAdr 0x08007FFC
typedef void (*pFunction)(void);
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t Uart2_RxBuff[1];
uint8_t Uart2_TxBuff[1];
uint8_t Uart2_RxStatus = 0;


void Flash_WriteLastWord(uint32_t data)
{
	uint32_t icount;
	uint32_t lpage[FLASH_PAGE_SIZE/4] = {0};

	FLASH_EraseInitTypeDef fe;
	uint32_t PageError = 0;

	for (icount = 0; icount<(FLASH_PAGE_SIZE/4-1); icount++)
	{
 		lpage[icount]=*((uint32_t *)((FlashLastAdr/FLASH_PAGE_SIZE)*FLASH_PAGE_SIZE+icount*4));
	}
        lpage[FLASH_PAGE_SIZE/4-1] = data;


	HAL_FLASH_Unlock();  //unlock
    fe.TypeErase = FLASH_TYPEERASE_PAGES;
    fe.PageAddress = FlashLastAdr;
    fe.NbPages = 1;
    HAL_FLASHEx_Erase(&fe, &PageError);  //Erase page
    FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE*2);

    for (icount = 0; icount<(FLASH_PAGE_SIZE/4); icount++)
    {
    	HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD , (FlashLastAdr/FLASH_PAGE_SIZE)*FLASH_PAGE_SIZE+icount*4, lpage[icount]);
    	FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    }

	HAL_FLASH_Lock();
}

void jump2app(uint32_t Addr)
{
         __set_MSP(*(uint32_t*)Addr);
         ((pFunction) (*(uint32_t*) (Addr + 4)))();
}


void if_isp_boot(void)
{

            if ( ((*((uint32_t*)FlashLastAdr))== 0x55555555) || ((*((uint32_t*)FlashLastAdr))== 0xaaaaaaaa) )
        	{
        		if ((*((uint32_t*)FlashLastAdr))== 0x55555555) Flash_WriteLastWord(0xaaaaaaaa);
        		else Flash_WriteLastWord(0);

        		__HAL_RCC_CLEAR_RESET_FLAGS();
                jump2app(IspAdr);
                while(1);
        	}

}

void reset2isp(void)  //soft reset
{
	    __set_PRIMASK(1);
        NVIC_SystemReset();
}

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
 	if_isp_boot();
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if (Uart2_RxStatus == 0)
	  {
		 Uart2_RxStatus = 1;
		 MX_USART2_UART_Init(); //must for serial-port stability
		 HAL_UART_Receive_IT(&huart2, Uart2_RxBuff, 1);
	  }
	  HAL_Delay(1);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_DIV4;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
  RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses 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_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 9600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{

	if (Uart2_RxBuff[0]==0x55)
	{
		Uart2_TxBuff[0] = 0x55;
		HAL_UART_Transmit(&huart2, Uart2_TxBuff, 1, 0x2710);

		Flash_WriteLastWord(0x55555555);
		reset2isp();
	}
	else
	{
	Uart2_TxBuff[0] = 0x00;
	HAL_UART_Transmit(&huart2, Uart2_TxBuff, 1, 0x2710);

	Uart2_RxBuff[0] = 0;
	Uart2_RxStatus = 0;
	}
	return;
}

/* 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 */

  /* 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,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

上位机协议实现注意事项

自行设计上位机软件时相关的协议注意事项:

  1. 指令配合方面,进行写存储区域前,该存储区域需要先去除读保护,然后进行擦除,也即写指令不带擦除功能。
  2. 一般先发0x7F握手指令触发STM32回复0x79后再发送功能指令。而后续功能指令不需要先发7F握手指令。

指令操作发送范例(16进制):

7F(后续获取版本和支持的命令列表)
00 FF

7F(后续获取版本和读保护状态)
01 FE

7F(后续获取芯片ID)
02 FD

7F (后续读0x08000000地址8个字节)
11 EE (ACK) 08 00 00 00 08 (ACK) 07 F8 (ACK)

7F(后续跳转执行,发送FLASH程序起始地址,从起始地址+4的地址取出复位向量地址,转向执行)
21 DE (ACK) 08 00 00 00 08 (ACK)

7F(后续从地址0x08000000写4个字节,注意: N+1 must be a multiple of 4.)
31 CE (ACK) 08 00 00 00 08 (ACK) (地址0x08000000) 03 01 02 03 04 07 (ACK)

擦除指令(43和44只支持其中一个):
7F(后续擦除整片)
43 BC (ACK) FF 00 (ACK) --Need to check if supported
7F(后续擦除2页,第一页和第二页)
43 BC (ACK) 01 00 01 00(ACK)

7F(后续擦除整片)
44 BB (ACK) FF FF 00 (ACK) (erase whole Flash) --Need to check if supported
7F(后续擦除bank1)
44 BB (ACK) FF FE 01 (ACK) (erase Flash bank1) – Need to check if supported
7F(后续擦除bank2)
44 BB (ACK) FF FD 02 (ACK) (erase Flash bank2) – Need to check if supported
7F(后续擦除2页,第一页和第二页)
44 BB (ACK) 00 01 00 00 00 01 00 (ACK)

7F(后续使能2页写保护,第一页和第二页)
63 9C (ACK) 01 00 01 00 (ACK) (芯片重启)
7F(后续去除所有扇区写保护)
73 8c (ACK) (芯片重启)

7F(后续使能读保护)
82 7D (ACK ACK) 芯片重启)
7F(后续去除读保护)
92 6d (ACK ACK) 芯片重启)

一项操作展示:
在这里插入图片描述
在这里插入图片描述

上位机下载界面软件

这里设计的上位机下载界面软件与STM32官方设计的软件有一处不同,官方的软件会读取芯片型号以确定FLASH空间,这里的设计是手动设置FLASH空间大小,因此对于最新推出的芯片型号,官方的软件没有升级无法识别型号则无法支持,这里设计的软件依然可以支持进行ISP升级。
在这里插入图片描述
在这里插入图片描述
点击软件上面的“?”号有操作过程说明。

完整例程下载

STM32L031 ISP升级上位机软件及嵌入式例程下载地址:
https://download.csdn.net/download/hwytree/79566728

STM32F030 ISP升级上位机软件及嵌入式例程下载地址:
https://download.csdn.net/download/hwytree/85526340

STM32C011 ISP升级上位机软件及嵌入式例程下载地址:
https://download.csdn.net/download/hwytree/86214570

STM32G030 ISP升级上位机软件及嵌入式例程下载地址:
https://download.csdn.net/download/hwytree/86214622

注意事项

  1. STM32 ISP升级上位机软件即可用于硬件进入模式也可用于软件进入模式
  2. 软件进入模式操作过程中,串口通讯不能被打断(如受干扰),否则版本没有下载完成,则重启也不能重新进入ISP软件进入模式,这是和IAP升级的区别,IAP升级过程异常,重启芯片后可以重新进行IAP升级。解决的方式可以自行设计,主要是对BOOT管脚和RESET管脚的控制,在出现异常时,通过BOOT管脚电平预置和RESET管脚控制重启,以进入硬件进入ISP模式。而在正常情况下,无需控制BOOT管脚和RESET管脚即可实现ISP升级,效果如同IAP升级模式。
  3. 先采用其他串口工具如上协议所述控制STM32芯片进入ISP升级状态,或者采用BOOT管脚和RESET管脚控制进入ISP升级状态,再使用ISP升级上位机软件下载版本。
  4. 二进制下载版本文件的生成,参见STM32 IAP 升级设计, 与IAP升级生成下载版本文件的方式一致。

–End–

STM32F103C8T6是一款基于ARM Cortex-M3内核的微控制器,它广泛应用于嵌入式系统开发。HAL库(Hardware Abstraction Layer)是ST公司提供的一套硬件抽象层库,用于简化STM32微控制器的驱动开发。 HAL库提供了一系列的函数和接口,用于配置和控制STM32微控制器的外设,如GPIO、USART、SPI、I2C等。通过使用HAL库,开发者可以更加方便地进行外设的初始化、配置和操作,从而加快开发速度。 以下是一个使用STM32F103C8T6和HAL库的简单例程,用于点亮一个LED灯: ```c #include "stm32f1xx_hal.h" void SystemClock_Config(void); static void MX_GPIO_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } void Error_Handler(void) { while (1) { } } #ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif ``` 这个例程使用了GPIOC的13号引脚(对应板上的LED灯),通过调用`HAL_GPIO_TogglePin`函数来实现LED灯的闪烁。在`main`函数中,通过循环不断地切换LED灯的状态,实现了LED灯的点亮和熄灭。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PegasusYu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值