STM32F407用USB和串口烧写程序-----主要参考的安富莱的教程
系统bootloader
STM32的系统存储区自带bootloader,此程序是ST在芯片出厂时烧录进去的,主要用于将用户应用程序下载到芯片内部Flash。支持USB、SPI、I2C、CAN、UART等接口方式下载。 我使用的是407,所以用bootloader下载程序的话只能是USB,USART1,USART3和CAN
如何进入系统bootloader
有两种方式可以进入系统的boot loader 一种是通过设置BOOT0引脚,将该引脚置高可以上电后直接进入系统boot loader。 令一种方法是通过程序跳转到系统bootloader。 跳转前需要注意以下问题 1、禁止所有外设时钟 2、禁止使用PLL 3、禁止所有中断 4、清除所有中断挂起标志。
以上部分内容在芯片参考手册的2.4章节,自举配置这一部分也有简单说明。
用串口和USB更新程序
用串口烧写程序 (需要使用RS232接口)
1、首先将BOOT0置高,进入系统bootloader 2、打开软件 STM32CubeProgrammer,如下图所示,选择UART,设置串口、波特率、以及偶检验(因为上电后串口默认是偶校验),点击 connect按钮,下面的信息框里会显示是否连接 成功。 3、选择烧写文件,点击Start Programm…,就开始烧写程序了。
USB烧写程序
1、同样需要将BOOT0置高进入系统bootloader 2、打开软件 STM32CubeProgrammer,选择USB,点击2处的刷新可以看到USB接口(必须先进入系统bootloader才可以),点击connect连接。后面的烧写方式与UART方式相同。
通过程序跳转到系统bootloader
如果我们的硬件电路不方便引出BOOT脚,那么就可以使用程序跳转的方式。 1、我们可以在自己的APP程序中,加入一个跳转程序,例如按下某一个按钮就跳转到系统bootloader,然后就可以用以上两种方式烧写程序,但是这种方式有一个弊端,就是如果升级程序的时候突然断开连接了,那么单片机中就没有程序了,必须再重新用SW的方式烧写一遍才行。 2、我们可以写两个程序,一个引导程序,一个APP程序,这两个程序在flash中的位置是不一样的,这样在更新APP程序的时候,即便发送异常,也不会破坏引导程序。 主要程序如下所示
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_1)==GPIO_PIN_RESET)
{
JumpToBootloader();
}
else
{
JumpToApp();
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
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_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
#define DISABLE_INT() __set_PRIMASK(1)
#define ENABLE_INT() __set_PRIMASK(0)
static void JumpToBootloader(void)
{
uint32_t i=0;
void (*SysMemBootJump)(void);
__IO uint32_t BootAddr = 0x1FFF0000;
DISABLE_INT();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
HAL_RCC_DeInit();
for (i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
ENABLE_INT();
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4)));
__set_MSP(*(uint32_t *)BootAddr);
__set_CONTROL(0);
SysMemBootJump();
}
static void JumpToApp(void)
{
uint32_t i=0;
void (*SysMemBootJump)(void);
__IO uint32_t BootAddr = 0x8004000;
DISABLE_INT();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
HAL_RCC_DeInit();
for (i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
ENABLE_INT();
SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4)));
__set_MSP(*(uint32_t *)BootAddr);
__set_CONTROL(0);
SysMemBootJump();
}
在JumpToApp函数中,系统的APP地址定义的是0x8004000,为什么选这个地址呢,因为在把引导程序烧进去之后,用J-Flash软件读取了一下Read back–>Selected sectors; 读取完毕之后发现引导程序大概占用了0xE70左右的大小。于是APP程序的地址就从第二个扇区开始了。 同时APP程序还要设置一下,如下图所示:0x7C00是大小,就是芯片的Flash大小减去第一个扇区的大小。 然后同时也要在APP程序里面修改中断向量表的位置,在文件system_stm32f4xx.c文件中。
这样就完成了,仅对USB烧写程序进行了程序跳转到系统bootloader方法的实验,UART仅用BOOT脚的方式做了实验。但是应该都是可行的。
|