当前位置: 首页 > news >正文

HC32F460_BootLoader

目录

  • 实现目标
  • Flash划分
  • Bootloader
      • 串口接收关键代码
      • 固件烧录和跳转关键代码
  • APP
  • 实验
  • 潜在问题

实现目标

  • Bootloader接收串口发送的固件,通过按键触发烧录到Flash里
  • 通过按键触发BootLoader程序跳转到APP中

Flash划分

  • 00扇区到31扇区 0x0000_0000 到 0x0001_FFFF (256KB)BootLoader
  • 32扇区到63扇区 0x0002_0000 到 0x0003_FFFF (256KB)APP
    注意:APP中63扇区末尾不可写,这里代码量不大不做处理。

Bootloader

串口接收关键代码

定义128KB的固件缓存数组,通过UART + DMA接受固件到数组之中。


/* DMAC */
#define DMA_UNIT                        (M4_DMA1)
#define DMA_CH                          (DmaCh0)
#define DMA_TRG_SEL                     (EVT_USART1_RI)

/* USART channel definition */
#define USART_CH                        (M4_USART1)

/* USART baudrate definition */
#define USART_BAUDRATE                  (38400ul)

/* USART RX Port/Pin definition */
#define USART_RX_PORT                   (PortA)
#define USART_RX_PIN                    (Pin03)
#define USART_RX_FUNC                   (Func_Usart1_Rx)

/* USART TX Port/Pin definition */
#define USART_TX_PORT                   (PortA)
#define USART_TX_PIN                    (Pin05)
#define USART_TX_FUNC                   (Func_Usart1_Tx)

/* USART interrupt  */
#define USART_EI_NUM                    (INT_USART1_EI)
#define USART_EI_IRQn                   (Int001_IRQn)


uint8_t recv_buf[128*1024] = {0};	


void BSP_DMA_Init(void)
{
    stc_dma_config_t stcDmaInit;

    /* Enable peripheral clock */
    PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);

    /* Enable DMA. */
    DMA_Cmd(DMA_UNIT,Enable);

    /* Initialize DMA. */
    MEM_ZERO_STRUCT(stcDmaInit);
    stcDmaInit.u16BlockSize = 1u; 		/* 1 block */
    stcDmaInit.u32SrcAddr = ((uint32_t)(&USART_CH->DR)+2ul); 	/* Set source address. */
    stcDmaInit.u32DesAddr = (uint32_t)(recv_buf);     		/* Set destination address. */
    stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix;  /* Set source address mode. */
    stcDmaInit.stcDmaChCfg.enDesInc = AddressIncrease;  /* Set destination address mode. */
    stcDmaInit.stcDmaChCfg.enIntEn = Disable;       /* Enable interrupt. */
    stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit;   /* Set data width 8bit. */
    DMA_InitChannel(DMA_UNIT, DMA_CH, &stcDmaInit);

    /* Enable the specified DMA channel. */
    DMA_ChannelCmd(DMA_UNIT, DMA_CH, Enable);

    /* Clear DMA flag. */
    DMA_ClearIrqFlag(DMA_UNIT, DMA_CH, TrnCpltIrq);

    /* Enable peripheral circuit trigger function. */
    PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);

    /* Set DMA trigger source. */
    DMA_SetTriggerSrc(DMA_UNIT, DMA_CH, DMA_TRG_SEL);
	
}

void BSP_UART_Init(void)
{
    const stc_usart_uart_init_t stcInitCfg = {
        UsartIntClkCkNoOutput,
        UsartClkDiv_16,
        UsartDataBits8,
        UsartDataLsbFirst,
        UsartOneStopBit,
        UsartParityNone,
        UsartSampleBit8,
        UsartStartBitFallEdge,
        UsartRtsEnable,
    };
	
	PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_USART1, Enable);
	
    /* Initialize USART IO */
    PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
    PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);
    /* Initialize USART */
    uint8_t enRet = USART_UART_Init(USART_CH, &stcInitCfg);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }
    else
    {
    }

    /* Set baudrate */
    enRet = USART_SetBaudrate(USART_CH, USART_BAUDRATE);
    if (enRet != Ok)
    {
        while (1)
        {
        }
    }
    else
    {
    }
	
    /*Enable TX && RX && RX interrupt function*/
    USART_FuncCmd(USART_CH, UsartTx, Enable);
    USART_FuncCmd(USART_CH, UsartRx, Enable);	
}

固件烧录和跳转关键代码

将固件缓存数组中的数据烧录到Flash中去


#define FLASH_SECTOR_START_ADDR 0x00020000

	while(1)
	{
		if(Get_KEY1() == Reset)	//按下第一个按键烧录程序
		{
			EFM_Unlock();
			EFM_FlashCmd(Enable);
			while(Set != EFM_GetFlagStatus(EFM_FLAG_RDY));
			
			uint8_t sector_num = sizeof(recv_buf)/0x2000;		//计算扇区数量,一个扇区的存储空间是0x2000=8KB
	for(uint8_t i = 0; i < sector_num; i++)			//遍历所有扇区
	{
		EFM_SectorErase(FLASH_SECTOR_START_ADDR + 0x2000*i);	//扇区擦除
		for(uint32_t j = 0; j < 0x2000 / 4; j++)	//遍历一个扇区中的所有字		
		{			
			EFM_SingleProgram(FLASH_SECTOR_START_ADDR + 0x2000*i + j*4,	//写入数据
									*((uint32_t *)(recv_buf + 0x2000*i) + j));
		}
	}
	EFM_Lock();
			LED0_TOGGLE();	//黄灯慢闪
			vTaskDelay(200);
		}
		if(Get_KEY2() == Reset)	//按下第一个按键跳转
		{
			EFM_InstructionCacheCmd(Disable);
			JumpAddress = *(__IO uint32_t*) (0x20000 + 4);
			Jump_To_Application = (pFunction) JumpAddress;
			__set_MSP(*(__IO uint32_t*) 0x20000);
			Jump_To_Application();
		}
		LED0_TOGGLE();	//黄灯快闪
		vTaskDelay(100);

	}

APP

APP代码要注意两件事情

  • 修改Flash内存分布
  • 修改SCB-VTOR

在这里插入图片描述
在这里插入图片描述
APP中只有一个蓝灯闪烁的程序

		LED1_TOGGLE();	//蓝灯闪烁
		vTaskDelay(100);

APP代码要先通过Keil fromelf.exe生成bin,再通过HexEdit转换为可发送的固件字符,
在这里插入图片描述
在这里插入图片描述

实验

1,BootLoader启动,黄灯快闪
2,通过串口助手将固件发送到固件缓存数组中
在这里插入图片描述
3,等待发送完成之后,按下按键,使得存放在ram中的固件缓存数组烧录到位于0x20000的Flash中,此时黄灯慢闪一次
4,等待黄灯恢复快闪,按下按键,跳转到APP中
5,蓝灯快闪,APP运行

潜在问题

只考虑测试固件烧录和跳转目的,上述测试没有问题。但是考虑到实际实现时候,以下问题都要解决:
1,由于DMA目的地指针没有复位手段,UART只能接收一次固件
2,UART接收到的固件未经校验完整性
3,单片机内部开辟128KB的完整固件缓存数组较为奢侈
4,烧录固件过程中断电,设备将会变砖

相关文章:

  • 构建智能AI数字人:一站式源码开发指南
  • 【Http和Https区别】
  • 企业数据集成:实现高效调拨出库自动化
  • excel中VBA宏的使用方法?
  • 水果生鲜农产品推荐系统 协同过滤余弦函数推荐水果生鲜农产品 Springboot Vue Element-UI前后端分离 代码+开发文档+视频教程
  • JavaScript 年后复习
  • 【算法】初等数论
  • Spring事务原理 二
  • 【hot100】刷题记录(25)-实现Trie
  • EVM系区块链开发网节点搭建及测试详细文档
  • 对VQ-VAE中EMA方式更新码本的理解
  • RT-Thread+STM32L475VET6——USB鼠标模拟
  • HTTP状态码完整梳理及适用场景
  • 报表控件stimulsoft操作:使用 CDN 服务部署 Stimulsoft 组件
  • 第15届 蓝桥杯 C++编程青少组中/高级选拔赛 202401 真题答案及解析
  • [250222] Kimi Latest 模型发布:尝鲜最新特性与追求稳定性的平衡 | SQLPage v0.33 发布
  • QT闲记-工具栏
  • nginx反向代理以及负载均衡(常见案例)
  • 容器和虚拟机选择对比
  • windows的CMD命令提示符
  • 发布了一个网站 显示建设中/平台seo什么意思
  • 禹州网站建设/网站运维
  • 淘宝网网站设计分析/模板建站的网站
  • 点击排名优化/企业seo排名有 名
  • 阿里云服务器上如何做网站/搜索引擎推广成功的案例
  • 艺术类 网站建设方案/新手怎么做电商运营