前言
stm32g030k6t6开发板,32K,分为bootloader区+APP区。
bootloader分区,0x8000000 到 0x8001FFF,共4页,8K。其中升级标志位占其中单独1页,2K,0x8001800 到 0x8001FFF。
APP分区,0x8002000 到 0x8007FFF,占12页,24K。
一、bootloader分区功能
读取升级标志位,若为1则跳转到APP区。
升级标志位不为1,则每2s发送一次 C。
uart传输与解析。
传输完成跳转至APP。
二、步骤
1.内存写入与擦除
读、写升级标志位,擦除写入APP。
#define FLASH_SYS_START_ADDR 0x8000000 /* stm32 flash起始地址 */
#define FLASH_UPGRADE_START_ADDR 0x8001800 /* 升级标志位起始地址 */
#define FLASH_UPGRADE_END_ADDR 0x8001FFF /* 升级标志位结束地址 */
#define FLASH_APP_START_ADDR 0x8002000 /* APP起始地址 */
#define FLASH_APP_END_ADDR 0x8007FFF /* APP结束地址 */
#define SYS_FLASH_PAGE_SIZE 2048 /*flash 一页大小 */
#define SYS_FLASH_SIZE 32 /* 所选STM32的FLASH容量大小(单位为K) */
#define SYS_SECTOR_SIZE 2048 /* 2K字节 */
uint32_t app_addr = FLASH_APP_START_ADDR;
/*
* fun : 获取地址Addr在Flash中是第几页。根据每页大小2k
* param : Addr:开头地址
* return : FLASH页数
* note :
*/
static uint32_t GetPage(uint32_t Addr)
{
uint32_t page = 0;
page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
return page;
}
/*
* fun : 读地址
* param : R_Addr:开头地址
* return :
* note :
*/
static uint32_t ST_Flash_Read(uint32_t R_Addr)
{
//直接读地址,可以读取其他类型。
return *(volatile uint32_t *)R_Addr;
}
/*
* fun : 擦除FLASH
* param : s_addr:开头地址,end_addr:结束地址
* return :
* note :
*/
static void Erase_Flash(uint32_t s_addr, uint32_t end_addr)
{
uint32_t SectorError = 0, FirstPage, NbOfPages;
FLASH_EraseInitTypeDef E_FLASH;
/* Get the 1st page to erase */
FirstPage = GetPage(s_addr);
/* Get the number of pages to erase from 1st page */
NbOfPages = GetPage(end_addr) - FirstPage + 1;
E_FLASH.TypeErase = FLASH_TYPEERASE_PAGES;
E_FLASH.Page = FirstPage;
E_FLASH.NbPages = NbOfPages;
if (HAL_FLASHEx_Erase(&E_FLASH, &SectorError) != HAL_OK)
{
}
}
/*
* fun : 写Flash,写之前需要擦除整页flash,修改宏定义
* param : start_address:开头地址,end_address:结束地址,*buf:写入数据,length:写入字节数据个数
* return :
* note :
*/
void Write_Flash(uint32_t start_address,uint32_t end_address,uint64_t *buf, uint16_t length)
{
uint64_t data = 0;
uint16_t i;
if (start_address < FLASH_SYS_START_ADDR || (start_address >= (FLASH_SYS_START_ADDR + SYS_SECTOR_SIZE * SYS_FLASH_SIZE)))
{
//长度不在范围
return;
}
HAL_FLASH_Unlock();//解锁flash
Erase_Flash(start_address , end_address); //擦除Flash
for (i = 0; i < length; i++)
{
data = 0;
data = buf;
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, start_address,data) != HAL_OK)
{
HAL_FLASH_Lock();
return;
}
start_address += 8;
}
HAL_FLASH_Lock();//上锁
}
void FLASH_WriteAPP(uint8_t *data,uint16_t len)
{
if (len == 128 || len == 1024)
{
HAL_FLASH_Unlock();//解锁flash
for (uint8_t i = 0; i < len/8; i++)
{
uint64_t value[1] = {0};
value[0] = ((uint64_t)data[0+i*8]) + ((uint64_t)data[1+i*8]<<(1*8)) + ((uint64_t)data[2+i*8]<<(2*8)) + ((uint64_t)data[3+i*8]<<(3*8)) + \
((uint64_t)data[4+i*8]<<(4*8)) + ((uint64_t)data[5+i*8]<<(5*8)) + ((uint64_t)data[6+i*8]<<(6*8)) + ((uint64_t)data[7+i*8]<<(7*8));
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, app_addr, value[0]) != HAL_OK)
{
HAL_FLASH_Lock();
return;
}
app_addr += 8;
}
HAL_FLASH_Lock();//上锁
}
}
void Flash_EraseAPP(void)
{
HAL_FLASH_Unlock();//解锁flash
Erase_Flash(FLASH_APP_START_ADDR , FLASH_APP_END_ADDR);
HAL_FLASH_Lock();//上锁
app_addr = FLASH_APP_START_ADDR; //写入地址
}
/*
* fun : 读升级标志位
* param :
* return :
* note :
*/
ErrorStatus sys_flash_read_flag(uint32_t *Ram_Addr)
{
*Ram_Addr = ST_Flash_Read(FLASH_UPGRADE_START_ADDR);
return SUCCESS;
}
/*
* fun :
* param :
* return :
* note : 向指定bootloader分区地址写入升级标志位,写入1
*/
void sys_flash_write_sign(void)
{
uint64_t upgrade_sign[1] = {0};
upgrade_sign[0] = 1;
Write_Flash(FLASH_UPGRADE_START_ADDR,FLASH_UPGRADE_END_ADDR,upgrade_sign,1);
}
/*
* fun : 清空升级标志位
* param :
* return :
* note : 向指定bootloader分区地址写入升级标志位,写入0
*/
void flash_clear_sign(void)
{
uint64_t upgrade_sign[1] = {0};
upgrade_sign[0] = 0;
Write_Flash(FLASH_UPGRADE_START_ADDR,FLASH_UPGRADE_END_ADDR,upgrade_sign,1);
}
2.Ymodem协议,uart传输与解析
#define USART2_TX_BUF_SIZE 1024
#define USART2_RX_BUF_SIZE 1024
typedef struct {
uint8_t rx_buff[USART2_RX_BUF_SIZE]; // 接收缓冲区(存储实际数据)
uint16_t rx_len; // 当前接收数据长度
uint8_t rx_sta; // 接收状态标志(如:0=空闲,1=接收中,2=接收完成等)
} uart_param_t;
#define SOH (0x01) /* 128字节数据帧 */
#define STX (0x02) /* 1024字节 数据帧 */
#define EOT (0x04) /* 文件传输结束 */
#define ACK (0x06) /* 接收正确应答 */
#define NAK (0x15) /* 重发当前帧 */
#define CA (0x18) /* 取消传输命令 连续发送五次取消 */
#define CRC16 (0x43) /* 握手信号 “C” */
#define HEAD (0x00) /* 帧头 */
#define DATA (0x01) /* 数据帧 */
#define END (0x02) /* 结束帧 */
#define ENDDATA (0x03) /* 结束帧 */
#define NAK_MAX 5 /* 重传次数上限超过该次数终止传输 */
#define OTHER (0x0f)
extern uint8_t filerxflag, jumpflag, endflag;
uint8_t filerxflag = 0, jumpflag = 0, endflag = 0;
uart_param_t uart_param = {0};
uint8_t nakcount;
uint8_t *filename;
uint8_t *filesize;
#define CRC_CCITT 0x1021
/*函数名称:crc_cal_by_bit;按位计算CRC
函数参数:uint8_t * ptr;指向发送缓冲区的首字节
uint32_t len;要发送的总字节数
函数返回值:uint16_t
多项式采用CRC-CCITT 0x1021
*/
static uint16_t crc_cal(uint8_t *ptr, uint32_t len)
{
uint32_t crc = 0x0000;
while(len-- != 0)
{
for (uint8_t i = 0x80; i != 0; i /=2)
{
crc *= 2;
if((crc&0x10000) !=0) //上一位CRC乘 2后,若首位是1,则除以 0x11021
crc ^= 0x11021;
if((*ptr&i) != 0) //如果本位是1,那么CRC = 上一位的CRC + 本位/CRC_CCITT
crc ^= CRC_CCITT;
}
ptr++;
}
uint16_t retCrc = (uint16_t)(crc & 0xffff);
return retCrc ;
}
static void StopTransfer()
{
uint8_t com[1] = {0};
com[0] = CA;
sys_uart_send(com, 1);
LL_mDelay(10);
sys_uart_send(com, 1);
LL_mDelay(10);
sys_uart_send(com, 1);
LL_mDelay(10);
sys_uart_send(com, 1);
LL_mDelay(10);
sys_uart_send(com, 1);
}
static uint16_t YmodemReceiveData(uint8_t *data, uint16_t length)
{
uint8_t com[1] = {0};
uint16_t datalen, crc16;
if (*data == EOT)
{
return END;
}
if ((*(data+1)) == (~(*(data+2))&0xff))
{
if (*data == SOH)
{
datalen = 128;
}
if (*data == STX)
{
datalen = 1024;
}
crc16 = crc_cal(data+3, datalen);
if (crc16 == (*(data+datalen+3)<<8|*(data+datalen+4)))
{
if (*(data+1) == 0)
{
if (*(data+3) != 0)
{
uint8_t *p, len = 0;
p = data + 3;
while(*p)
{
len++;
p++;
}
memcpy(filename, data+3, len);
p++;
len = 0;
while(*p)
{
len++;
p++;
}
memcpy(filesize, p-len, len);
return HEAD;
}
else
{
return ENDDATA;
}
}
else
{
nakcount = 0;
com[0] = ACK;
sys_uart_send(com, 1);
FLASH_WriteAPP(data+3, datalen);
return DATA;
}
}
else
{
com[0] = NAK;
sys_uart_send(com, 1);
nakcount++;
}
}
else
{
com[0] = NAK;
sys_uart_send(com, 1);
nakcount++;
}
if (nakcount > NAK_MAX)
StopTransfer();
return OTHER;
}
//串口接收函数处理
static void receive_data_handle(uint8_t *data, uint16_t len)
{
uint8_t com[1] = {0};
switch(YmodemReceiveData(data, len))
{
case DATA:
break;
case HEAD:
LL_mDelay(10);
filerxflag = 1;
Flash_EraseAPP();
com[0] = ACK;
sys_uart_send(com, 1);
com[0] = CRC16;
sys_uart_send(com, 1);
break;
case ENDDATA:
LL_mDelay(10);
com[0] = ACK;
sys_uart_send(com, 1);
LL_mDelay(10);
filerxflag = 0;
jumpflag = 1;
break;
case END:
if (endflag == 0)
{
endflag++;
com[0] = NAK;
sys_uart_send(com, 1);
}
else
{
endflag = 0;
com[0] = ACK;
sys_uart_send(com, 1);
LL_mDelay(10);
com[0] = CRC16;
sys_uart_send(com, 1);
}
break;
default:
break;
}
}
//串口接收标志位判断
void sys_uart_receive_handle(void)
{
if (uart_param.rx_sta)
{
if(uart_param.rx_len>0)
{
receive_data_handle(uart_param.rx_buff, uart_param.rx_len);
}
uart_param.rx_sta=0;
memset(uart_param.rx_buff, 0, uart_param.rx_len);
}
}
3.主循环
添加
#include “stm32g0xx_hal.h”
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_FLASH);// AHB1总线组,FLASH外设
int main(void)
{
HAL_Init();
system_init();
sys_uart_init();
while(1)
{
//1.读升级标志位
sys_flash_read_flag(updateflag);
if (updateflag[0] == 1)
{
printf("updateflag[0]=%d\n,jump to app.",updateflag[0]);
JumpToApp();
}
//2.发C
if (filerxflag == 0)
{
com[0] = CRC16;
LL_mDelay(2000);
sys_uart_send(com, 1);
}
//3.传输解析
sys_uart_receive_handle();
//4.传输完成跳转
if (jumpflag)
{
jumpflag = 0;
updateflag[0] = 0;
sys_flash_write_sign();
printf("jump to app.\r\n");
JumpToApp();
}
LL_mDelay(1);
}
}
4.修改配置
点击魔术棒,Target,修改bootloader ROM对应Size 0x2000
点击Debug,选择右上角Settings,点击Flash Download,选择Erase Full Chip,全擦除,修改对应Address Range.
5.APP区
打开system_stm32g0xx.c,添加#define USER_VECT_TAB_ADDRESS,修改为APP起始地址#define VECT_TAB_OFFSET 0x00002000U
main函数里先__enable_irq();
点击魔术棒,修改APP对应起始地址和size。
Flash Download选择Erase Sectors.
添加#include “stm32g0xx_hal_flash.h”
#include “stm32g0xx_hal.h”
添加对应HAL文件
APP实现,收到升级命令后,向升级标志位起始地址写入0.
/*
* fun :
* param :
* return :
* note : 向指定bootloader分区地址写入升级标志位,0x8007000(待修改),写入0
*/
int32_t sysparam_flash_set_upgrade(void)
{
uint64_t upgrade_sign[1] = {0};
upgrade_sign[0] = 0;
Write_Flash(FLASH_SIGN_START_ADDR,FLASH_SIGN_END_ADDR,upgrade_sign,1);//写8字节
return 0;
}
总结
Bootloader区与APP区不能覆盖。
————————————————
版权声明:本文为CSDN博主「转动地球」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fighhti/article/details/154190954
|
|