32单片机开发bootloader程序

一,单片机为什么要使用bootloader

1、使用bootloader的好处

         1) 程序隔离:可以同时存在多个程序,只要flash空间够大,或者通过外挂flash,可以实现多个程序共存,在多个程序之间切换使用。

        2)方便程序升级和后期维护:多个程序相互独立运行,可以在一个程序对另一个程序更新,普通单片机程序只能通过isp或者jtag、swd等调试接口实现程序烧录。而使用bootloader程序则可以通过usart、485、can、iic、spi、sd、4g、wifi卡等等任意可以实现数据传输的通信方式进行设备ota升级,也不必必须依赖烧录器。

2、不建议使用bootloader的原因

        1)占用flash空间:多一个程序必然会多占一部分flash空间。

        2)增加程序烧录的步骤:项目量产时出厂烧录程序会不太方便。

       

二、设计要点

        使用bootloader至少需要开发两个程序,也就是创建两个工程,一个bootloader程序,一个应用程序;bootloader程序负责初始化部分硬件,提供一些通信方式实现应用程序的ota功能;如果有多个应用程序在bootloader程序内需要对应用程序进行启动选择。

        1、flash分区,两个程序要想不相互影响,需要将两个程序烧录到flash的不同位置

        2、flash编程,要实现程序ota功能,需要提供对单片机flash的编程功能。

        3、初始化通信接口,规定ota协议。

        4、配置中断向量表地址。

        5、配置堆栈地址

        5、跳转应用程序地址。

三、工程配置

        1、bootloader程序flash地址配置

        

        2、app程序flash地址配置

        

四、关键函数代码

        1、Flash编程函数

                不同单片机flash编程函数也不一样,可以自行修改,这里只提供实现思路。

#define FMC_PAGE_SIZE           ((uint16_t)0x800U)
uint8_t fmc_tmp_page[FMC_PAGE_SIZE];void flash_program(uint32_t addr,uint8_t *data,uint16_t size)
{uint32_t prog_addr = (uint32_t)addr;uint8_t * data_addr = data;uint16_t i,j;uint16_t pages;uint16_t pg_idx = 0;uint16_t wr_size = size;uint32_t * pdata;uint32_t * pobj = (uint32_t *)fmc_tmp_page;if(size == 0){return;}else if(size < FMC_PAGE_SIZE-prog_addr%FMC_PAGE_SIZE){pages = 1;}else{pages = 1+(size-prog_addr%FMC_PAGE_SIZE+FMC_PAGE_SIZE-1)/FMC_PAGE_SIZE;}/* unlock the flash program/erase controller */fmc_unlock();/* clear all pending flags */fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);for(i=0;i<pages;i++){pg_idx = prog_addr%FMC_PAGE_SIZE;prog_addr = prog_addr/FMC_PAGE_SIZE*FMC_PAGE_SIZE;pdata = (uint32_t*)prog_addr;wr_size = FMC_PAGE_SIZE-pg_idx<size?FMC_PAGE_SIZE-pg_idx:size;size -= wr_size;for(j=0;j<FMC_PAGE_SIZE/4;j++){pobj[j]=*pdata;pdata++;}for(j=pg_idx;j<wr_size+pg_idx;j++){fmc_tmp_page[j]=*(data_addr);data_addr++;}fmc_page_erase(prog_addr);/* clear all pending flags */fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);/* program flash */for(j=0;j<FMC_PAGE_SIZE/4;j++){fmc_word_program(prog_addr+j*4, pobj[j]);fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);}prog_addr += FMC_PAGE_SIZE;}/* lock the main FMC after the erase operation */fmc_lock();
}

        2、修改中断向量表地址

                部分单片机库函数未提供修改向量表地址函数,这里我自己模仿写了个。

void BootLoader_SetVectorTable(uint32_t NVIC_VectTab,uint32_t Offset)
{SCB->VTOR =  NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
}

        3、修改主堆栈地址

                  网上很多实现都过于复杂,写了一大堆汇编代码,我这里只尽量只用c语言的方式去实现,便于理解与调用。

void BootLoader_MSP(uint32_t addr)
{__ASM volatile("LDR	r2, [addr]");__ASM volatile("MSR	msp, r2");
}

        4、跳转应用程序

                一个函数完成所有功能。

void BootLoader_App_Startup(uint32_t offset)
{application_t app;uint32_t msp_addr = (FLASH_BASE|offset);uint32_t * entry_addr = (uint32_t *)(FLASH_BASE|offset|0x4);app = (application_t)*entry_addr;BootLoader_SetVectorTable(FLASH_BASE,offset);BootLoader_MSP(msp_addr);app();
}

        5、使用例程

#define APP_OFFSET_ADDR		0x10000int main(void)
{Debug_UartCfg();while(1){delay_ms(500);debug_printf("hello,0x%x!\r\n",123);BootLoader_App_Startup(APP_OFFSET_ADDR);}
}

 

五、关键库全部代码

        

//bootloader.c#include "bootloader.h"#define FMC_PAGE_SIZE           ((uint16_t)0x800U)
uint8_t fmc_tmp_page[FMC_PAGE_SIZE];void BootLoader_MSP(uint32_t addr)
{__ASM volatile("LDR	r2, [addr]");__ASM volatile("MSR	msp, r2");
}void BootLoader_SetVectorTable(uint32_t NVIC_VectTab,uint32_t Offset)
{SCB->VTOR =  NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
}void BootLoader_App_Startup(uint32_t offset)
{application_t app;uint32_t msp_addr = (FLASH_BASE|offset);uint32_t * entry_addr = (uint32_t *)(FLASH_BASE|offset|0x4);app = (application_t)*entry_addr;BootLoader_SetVectorTable(FLASH_BASE,offset);BootLoader_MSP(msp_addr);app();
}void flash_program(uint32_t addr,uint8_t *data,uint16_t size)
{uint32_t prog_addr = (uint32_t)addr;uint8_t * data_addr = data;uint16_t i,j;uint16_t pages;uint16_t pg_idx = 0;uint16_t wr_size = size;uint32_t * pdata;uint32_t * pobj = (uint32_t *)fmc_tmp_page;if(size == 0){return;}else if(size < FMC_PAGE_SIZE-prog_addr%FMC_PAGE_SIZE){pages = 1;}else{pages = 1+(size-prog_addr%FMC_PAGE_SIZE+FMC_PAGE_SIZE-1)/FMC_PAGE_SIZE;}/* unlock the flash program/erase controller */fmc_unlock();/* clear all pending flags */fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);for(i=0;i<pages;i++){pg_idx = prog_addr%FMC_PAGE_SIZE;prog_addr = prog_addr/FMC_PAGE_SIZE*FMC_PAGE_SIZE;pdata = (uint32_t*)prog_addr;wr_size = FMC_PAGE_SIZE-pg_idx<size?FMC_PAGE_SIZE-pg_idx:size;size -= wr_size;for(j=0;j<FMC_PAGE_SIZE/4;j++){pobj[j]=*pdata;pdata++;}for(j=pg_idx;j<wr_size+pg_idx;j++){fmc_tmp_page[j]=*(data_addr);data_addr++;}fmc_page_erase(prog_addr);/* clear all pending flags */fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);/* program flash */for(j=0;j<FMC_PAGE_SIZE/4;j++){fmc_word_program(prog_addr+j*4, pobj[j]);fmc_flag_clear(FMC_FLAG_BANK0_END);fmc_flag_clear(FMC_FLAG_BANK0_WPERR);fmc_flag_clear(FMC_FLAG_BANK0_PGERR);}prog_addr += FMC_PAGE_SIZE;}/* lock the main FMC after the erase operation */fmc_lock();
}

//bootloader.h#ifndef		_BOOTLOADER_H_
#define		_BOOTLOADER_H_#include "gd32f30x.h"typedef	void (*application_t)(void);void BootLoader_MSP(uint32_t addr);void BootLoader_SetVectorTable(uint32_t NVIC_VectTab,uint32_t Offset);void BootLoader_App_Startup(uint32_t addr);#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/383773.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【树状数组】2659. 将数组清空

本文涉及知识点 树状数组 LeetCode2659. 将数组清空 给你一个包含若干 互不相同 整数的数组 nums &#xff0c;你需要执行以下操作 直到数组为空 &#xff1a; 如果数组中第一个元素是当前数组中的 最小值 &#xff0c;则删除它。 否则&#xff0c;将第一个元素移动到数组的…

监测Nginx访问日志状态码,并做相应动作

文章目录 引言I 监测 Nginx 访问日志情况,并做相应动作1.1 前提准备1.2 访问日志 502 情况,重启 bttomcat9服务1.3 其他案例:访问日志 502 情况,重启 php-fpm 服务II 将Shell 脚本check499.sh包装成systemd服务2.1 创建systemd服务2.2 配置service2.3 开机启动2.4 其他常用…

内网对抗-隧道技术篇防火墙组策略FRPNPSChiselSocks代理端口映射C2上线

知识点&#xff1a; 1、隧道技术篇-传输层-工具项目-Frp&Nps&Chisel 2、隧道技术篇-传输层-端口转发&Socks建立&C2上线Frp Frp是专注于内网穿透的高性能的反向代理应用&#xff0c;支持TCP、UDP、HTTP、HTTPS等多种协议。可以将内网服务以安全、便捷的方式通过…

垃圾桶为什么要装缓冲器?

在我们日常生活中&#xff0c;垃圾桶是一个再常见不过的物品。然而&#xff0c;您是否留意过垃圾桶盖上的缓冲器&#xff1f;这个看似不起眼的小装置&#xff0c;其实有着不可忽视的重要作用。首先&#xff0c;垃圾桶装缓冲器能够有效地降低噪音。想象一下&#xff0c;在一个安…

【文心智能体】00后疯感工牌生成器,低代码工作流的简单应用以及图片快速响应解决方案,干活满满,不容错过哦

背景 文心智能体平台&#xff0c;开启新一轮活动&#xff0c;超级创造营持续百日活动。 在AI 浪潮席卷的今天&#xff0c;如雨后春笋般丛生的 AI 应用&#xff0c;昭告着时代风口显然已随之到来。 如何能把握住时代红利&#xff0c;占据风口&#xff0c;甚至打造新风向&#x…

基于微信小程序+SpringBoot+Vue的自习室选座与门禁系统(带1w+文档)

基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 本课题研究的研学自习室选座与门禁系统让用户在小程序端查看座位&#xff0c;预定座位&#xff0c;支付座位价格&#xff0c;该系统让用户预定座位…

人工智能:大语言模型提示注入攻击安全风险分析报告下载

大语言模型提示注入攻击安全风险分析报告下载 今天分享的是人工智能AI研究报告&#xff1a;《大语言模型提示注入攻击安全风险分析报告》。&#xff08;报告出品方&#xff1a;大数据协同安全技术国家工程研究中心安全大脑国家新一代人工智能开放创新平台&#xff09; 研究报告…

57 数据链路层

用于两个设备&#xff08;同一种数据链路节点&#xff09;之间传递 目录 对比理解“数据链路层” 和 “网络层”以太网 2.1 认识以太网 2.2 以太网帧格式MAC地址 3.1 认识MAC地址 3.2 对比理解MAC地址和IP地址局域网通信MTU 5.1 认识MTU 5.2 MTU对ip协议的影响 5.3 MTU对UDP的…

sql_exporter通过sql收集业务数据并通过prometheus+grafana展示

下载并解压安装sql_exporter wget https://github.com/free/sql_exporter/releases/download/0.5/sql_exporter-0.5.linux-amd64.tar.gz #解压 tar xvf sql_exporter-0.5.linux-amd64.tar.gz -C /usr/local/修改主配置文件 cd /usr/local/ mv sql_exporter-0.5.linux-amd64 s…

Vue 实现电子签名并生成签名图片

目录 前言项目结构代码实现 安装依赖创建签名画布组件生成签名图片 总结相关阅读 1. 前言 电子签名在现代Web应用中越来越普遍&#xff0c;例如合同签署、确认表单等。本文将介绍如何使用Vue.js实现一个简单的电子签名功能&#xff0c;并将签名生成图片。 2. 项目结构 项…

【Simple PIR】单服务器开源最快匿踪查询算法解析

7月17日&#xff0c;我们在《隐私计算匿踪查询技术深入浅出》中介绍了关于隐私计算中匿踪查询的定义和常见算法&#xff0c;并引出了前沿算法Simple PIR的介绍&#xff0c;本次将对Simple PIR进行正式的算法原理介绍。 1. Simple PIR快览 1.1 性能介绍 Simple PIR是Alexandra…

机器学习驱动的智能化电池管理技术与应用

目录 主要内容 电池管理技术概述 电池的工作原理与关键性能指标 电池管理系统的核心功能 SOC估计 SOH估计 寿命预测 故障诊断 人工智能机器学习 基础 人工智能的发展 机器学习的关键概念 机器学习在电池管理中的应用案例介绍 人工智能在电池荷电状态估计中的…

信号的运算

信号实现运算&#xff0c;首先要明确&#xff0c;电路此时为负反馈电路&#xff0c;当处于深度负反馈时&#xff0c;可直接使用虚短虚断。负反馈相关内容可见&#xff1a;放大电路中的反馈_基极反馈-CSDN博客https://blog.csdn.net/qq_63796876/article/details/140438759 一、…

C++ 鼠标轨迹API【神诺科技SDK】

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;使得神诺科技 能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.…

深入学习H264和H265

目录 前言 一 什么是H264/H265&#xff1f; H.264 (MPEG-4 AVC) H.265 (HEVC) 二 为什么要学习H264和H265&#xff1f; 1. 深入理解视频压缩原理 2. 硬件优化与集成 3. 调试与故障排除 4. 持续的技术更新 三 NAL&#xff08;Network Abstraction Layer&#xff09;详解…

如何找到最快解析速度的DNS

如何找到最快解析速度的DNS DNS&#xff0c;即域名系统&#xff08;Domain Name System&#xff09;&#xff0c;是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使用户更方便地访问互联网&#xff0c;而不用记住能够被机器直接读取的IP数…

实现领域驱动设计(DDD)系列详解:领域模型的持久化

领域驱动设计主要通过限界上下文应对复杂度&#xff0c;它是绑定业务架构、应用架构和数据架构的关键架构单元。设计由领域而非数据驱动&#xff0c;且为了保证定义了领域模型的应用架构和定义了数据模型的数据架构的变化方向相同&#xff0c;就应该在领域建模阶段率先定义领域…

【Python第三方库】PyQt5安装与应用

文章目录 引言安装PYQT5基于Pyqt5的简单桌面应用常用的方法与属性QtDesigner工具使用与集成窗口类型QWidget和QMainWindow区别 UI文件加载方式直接加载UI文件的方式显示窗口转化py文件进行显示窗口 PyQt5中常用的操作信号与槽的设置绑定页面跳转 引言 PyQt5是一个流行的Python…

Modbus转BACnet/IP网关的技术实现与应用

引言 随着智能建筑和工业自动化的快速发展&#xff0c;不同通信协议之间的数据交换也变得日益重要。Modbus和BACnet/IP是两种广泛应用于自动化领域的通信协议&#xff0c;Modbus以其简单性和灵活性被广泛用于工业自动化&#xff0c;而BACnet/IP则在楼宇自动化系统中占据主导地…

“微软蓝屏”全球宕机,敲响基础软件自主可控警钟

上周五&#xff0c;“微软蓝屏”“感谢微软 喜提假期”等词条冲上热搜&#xff0c;全球百万打工人受此影响&#xff0c;共同见证这一历史性事件。据微软方面发布消息称&#xff0c;旗下Microsoft 365系列服务出现访问中断。随后在全球范围内&#xff0c;包括企业、政府、个人在…