首页 > 编程语言 >Bootloader/IAP零基础入门(1.1) —— 设计一个Bootloader引导进入APP的程序,包含中断向量偏移

Bootloader/IAP零基础入门(1.1) —— 设计一个Bootloader引导进入APP的程序,包含中断向量偏移

时间:2024-03-29 13:58:18浏览次数:33  
标签:1.1 中断向量 usart APP rx 地址 Bootloader

前言

(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:[email protected],此消息至2025年1月1日前均有效
(2)在上一章节中,我们详细介绍了如何让Bootloader引导进入APP程序。但是上一章节的工程是无法使用中断和RTOS的,为什么呢?本文将会详细介绍。
(3)上一章节博客地址:Bootloader/IAP零基础入门(1.0) —— 设计一个Bootloader引导进入APP的程序,不含中断向量偏移
(4)系列教程仓库链接:Github仓库
(5)注意:本章节只是做一个Bootloader引导进入APP的程序教程。虽然可以实现引导程序,但是并没有实现Flash擦除功能,因此并不能算真正的Bootloader。

前期准备

APP程序修改

(1)这里延续使用使用上一章节的工程。只不过在APP程序中,加入一个串口回传机制,需要使用到串口中断。
(2)首先初始化串口,波特率115200,使能串口中断。

在这里插入图片描述

(3)进入usart.c文件,按Ctrl+F搜索USER CODE BEGIN 0,补充如下代码。

/* USER CODE BEGIN 0 */
uint16_t g_usart_rx_sta = 0;
uint8_t g_rx_buffer[1];  /* HAL库使用的串口接收缓冲 */
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];

int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}

/* USER CODE END 0 */

在这里插入图片描述

(4)补充串口接受回调函数,在usart.c中按Ctrl+F搜索USER CODE BEGIN 1,补充如下代码。

/* USER CODE BEGIN 1 */
/**
 * @brief       串口数据接收回调函数
                数据处理在这里进行
 * @param       huart:串口句柄
 * @retval      无
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)                    /* 如果是串口1 */
    {
        if ((g_usart_rx_sta & 0x8000) == 0)             /* 接收未完成 */
        {
            if (g_usart_rx_sta & 0x4000)                /* 接收到了0x0d(即回车键) */
            {
                if (g_rx_buffer[0] != 0x0a)             /* 接收到的不是0x0a(即不是换行键) */
                {
                    g_usart_rx_sta = 0;                 /* 接收错误,重新开始 */
                }
                else                                    /* 接收到的是0x0a(即换行键) */
                {
                    g_usart_rx_sta |= 0x8000;           /* 接收完成了 */
                }
            }
            else                                        /* 还没收到0X0d(即回车键) */
            {
                if (g_rx_buffer[0] == 0x0d)
                    g_usart_rx_sta |= 0x4000;
                else
                {
                    g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
                    g_usart_rx_sta++;

                    if (g_usart_rx_sta > (USART_REC_LEN - 1))
                    {
                        g_usart_rx_sta = 0;             /* 接收数据错误,重新开始接收 */
                    }
                }
            }
        }

        HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, 1);
    }
}
/* USER CODE END 1 */

(5)在usart.h文件中补充如下代码。

/* USER CODE BEGIN Private defines */
#define USART_REC_LEN               200         /* 定义最大接收字节数 200 */
/* USER CODE BEGIN Prototypes */
extern uint16_t g_usart_rx_sta;
extern uint8_t g_rx_buffer[1];  /* HAL库使用的串口接收缓冲 */
/* 接收缓冲, 最大USART_REC_LEN个字节. */
extern uint8_t g_usart_rx_buf[USART_REC_LEN];
/* USER CODE END Prototypes */

(5)在main.c文件按Ctrl+F搜索USER CODE BEGIN 1,补充如下代码。

  /* USER CODE BEGIN 1 */
  uint8_t len = 0;
  uint16_t times = 0;
  /* USER CODE END 1 */

(6)在main.c文件按Ctrl+F搜索USER CODE BEGIN 3,补充如下代码。

    /* USER CODE BEGIN 3 */
		if (g_usart_rx_sta & 0x8000)        /* 接收到了数据? */
		{
				len = g_usart_rx_sta & 0x3fff;  /* 得到此次接收到的数据长度 */
				printf("\r\n您发送的消息为:\r\n");

				HAL_UART_Transmit(&huart1,(uint8_t*)g_usart_rx_buf, len, 1000);    /* 发送接收到的数据 */
				while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) != SET);          /* 等待发送结束 */
				printf("\r\n\r\n");             /* 插入换行 */
				g_usart_rx_sta = 0;
		}
		else
		{
				times++;

				if (times % 500 == 0)
				{
						printf("\r\n正点原子 STM32开发板 串口实验\r\n");
						printf("正点原子@ALIENTEK\r\n\r\n\r\n");
				}

				if (times % 500 == 0) printf("请输入数据,以回车键结束\r\n");

				if (times % 100  == 0) /* 闪烁LED,提示系统正在运行. */
				{
					HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
					HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
				}

				HAL_Delay(10);
		}
  }
  /* USER CODE END 3 */

Bootloader修改

(1)本章节的Bootloader和上一章节相比,只是多了下面这一行代码。

SCB->VTOR = AppStartFlash;     //APP的中断向量表是从0x8002000起始

在这里插入图片描述

工程整合

(1)工程整合与上一章节一样。因为每次配置STM32CubeMXKeil MDK的相关设置都会被初始化默认状态。因此,这里我们需要重新配置一下APPKeil MDK配置。具体的配置原因不做赘述,上一章节已作详细说明。

在这里插入图片描述

Bootloader代码讲解

VTOR寄存器与中断的关系

(1)我们发现,这一章节的Bootloader只是对VTOR寄存器进行了一次重新赋值。为什么不直接放在上一章节讲解?明明就一句代码的事情。这肯定是因为大有讲究。
(2)对于M3/M4内核的芯片,我们首先需要明白一点。CPU是怎么知道哪里是中断函数,地址对应的函数到底是串口中断还是外部中断。
(3)这个时候我们就需要看看M3权威指南7.3 向量表章节了。
(4)从下面我们可以获取到以下信息:

  • 当发生异常时候,M3/M4内核芯片需要在中断向量表中找到异常函数地址。
  • 中断向量表默认在0地址处。
  • 0xE000_ED08地址处的VTOR寄存器可以指示中断向量表的起始地址。
  • VTOR寄存器的低7位必须都为0。
  • VTOR寄存器上电复位的bit7-bit28全为0。结合低7位必须为0,可以得出第二天结论,即M3/M4内核芯片中断向量表上电复位时候,默认为0地址处。(注意,因为STM32F103存在地址映射功能,因此0地址与0x0800 0000对应)

在这里插入图片描述

M3/M4中断是流程

(1)上面我们知道了VTOR寄存器可以指示中断向量表的起始地址。而这个寄存器复位指示的向量表起始地址为0地址(STM32芯片中,0地址与0x0800 0000对应)
(2)现在我们以复位中断为例,假设我们按下复位键,此时会触发复位中断。
<1>CPU首先会根据VTOR寄存器找到向量表的起始地址0x08000000
<2>然后CPU自己记得,复位中断是在向量表的起始地址偏移4字节位置,也就是0x08000004
<3>此时CPU读取0x08000004地址处的数据,并且跳转到这个地址。例如这里0x08000004地址处存放08000145,因为Thumb指令集的原因,bit0必须为1,因此实际上应该跳转的是08000144地址处。
<4>我们看08000144地址处,就能够发现这里是复位中断。其他中断执行流程与这个同理。都是CPU先去向量表起始地址,再根据芯片固化的中断偏移地址+向量表起始地址,找到存储中断函数地址处,然后根据这个存储的数据跳转到该地址执行任务。

在这里插入图片描述

为什么APP程序需要进行中断向量偏移

(1)有了上面的基础,回答这个问题其实显而易见了。首先,我们知道,APP的程序起始地址不会是在0x0800 0000,而是在我们设置的0x800 2000处。因此,APP的中断向量表并不是在0x0800 0000,而是在0x800 2000。于是,我们的Bootloader执行完程序之后,跳转到APP程序之前,需要设置VTOR寄存器进行中断向量偏移。

拓展思考

不支持中断向量偏移的芯片怎么办?

(1)学习了上面的教程之后,我们对Bootloader引导进入APP程序又有了进一步的了解。这个时候善于思考的同学肯定要说了,我这里讲的程序是只针对M3/M4内核的芯片吗?
(2)因为我对不同的内核了解也有限,目前只对M3/M4内核略懂,因此我也不敢保证所有内核芯片都是这样的。于是我们就要考虑一个问题,如果有些内核的芯片,并不支持中断向量表进行偏移,应该怎么办?
<1>方法很简单,我直接跳转到APP Flash的起始地址,进行偏移即可。原理与上一章节所说的,Bootloader跳转到APP程序相同,不做赘述。

#define AppStartFlash 0x8002000UL
typedef void (*HandlerFunction)(void);    //定义一个函数指针类型
void Reset_Handler(void)
{
	HandlerFunction APP_Handler = (HandlerFunction)*(volatile uint32_t *)(AppStartFlash + (4 * 1));
	APP_Handler();
}
void NMI_Handler(void)
{
	HandlerFunction APP_Handler = (HandlerFunction)*(volatile uint32_t *)(AppStartFlash + (4 * 2));
	APP_Handler();
}
void HardFault_Handler(void)
{
	HandlerFunction APP_Handler = (HandlerFunction)*(volatile uint32_t *)(AppStartFlash + (4 * 3));
	APP_Handler();
}
//...
void PendSV_Handler(void)
{
	HandlerFunction APP_Handler = (HandlerFunction)*(volatile uint32_t *)(AppStartFlash + (4 * 14));
	APP_Handler();
}
//...

Bootloader的中断向量有存在的必要吗?

(1)现在我们对中断流程,以及中断向量表已经拥有了一定的认识。那么,我们是否能够思考一个问题。中断向量表对于Bootloader真的一定有存在的价值吗?
<1>这个时候我们又需要回到中断向量表的意义了。我们知道,中断向量表是存储在FlashVTOR指示的地址处,当发生中断时候,CPU会找到VTOR指示的地址,再根据是什么类型的中断进行地址偏移,最终跳转到中断函数里面。
<2>但是,假如我们的Bootloader并不会产生任何的中断,那有存在的意义吗?如果你想要尽可能的压缩空间,并且真的不会使用到中断,那么Bootloader是可以不要中断向量表的。
(2)这个时候我们再思考一下,假如我们只用到了串口1中断函数,并且想要尽可能的压缩Bootloader程序空间,中断向量表又应该如何处理呢?
<1>因为我们知道,CPU是根据VTOR寄存器指示的中断向量表起始地址进行偏移,而我们这里又只需要用到串口1的中断函数,CPU的这个中断偏移用户是无法修改的。因此,我们可以得出结论,如果Bootloader只需要使用串口1的中断,那么串口1中断之前的中断向量表都是需要进行保留的。串口1中断后面的中断向量表,可以进行删除。
(3)对于不支持中断向量表偏移的芯片,Bootloader能够删除中断向量表吗?
<1>这个显然是不可以的,之前Bootloader删除中断向量表,是因为Bootloader不需要使用中断,并且进入APP程序之前就能够让中断向量表进行偏移。因此并不会影响到APP程序的中断。
<2>但是如果是不支持中断向量表偏移的芯片,将Bootloader的中断向量表删除了,之后进入APP程序之后,发生中断将会产生未知的错误。

参考

(1)中科院:嵌入式与微机原理总复习
(2)CM3权威指南
(3)百问网:29_异常向量表基地址无法修改时怎么办

标签:1.1,中断向量,usart,APP,rx,地址,Bootloader
From: https://blog.csdn.net/qq_63922192/article/details/137123772

相关文章

  • 关于各种app的推荐算法有浅感
    2024-03-2913:12:43不知道从什么开始,或许一开始就一直是如此,总是期待着有人可以把解决问题的方法给喂到自己的嘴边,当遇到学习上的缺乏动力时,竟然会觉得是抖音和社交软件戒得太久,以至于总是沉浸在自己的世界里面。这种想法并不全错,长期不与人交流,不与同行或者同学交流,会不知道自......
  • ssm基于Android的XX校园交流APP
    摘要随着互联网时代的发展,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,随着各行业的不断发展,XX校园交流APP建设也逐渐进入了信息化的进程。这个App的设计主要包括前台页面的设计和方便用户互动的后端数据库,而前端软件......
  • uniapp(全端兼容) - 最新移动端评论区讨论点赞回复功能,可发表文字或图片评论|点赞|回
    效果图在uniapp小程序/h5网页网站/安卓苹果app/nvue等(全平台完美兼容)开发中,实现评论区、讨论区功能详细教程,uniapp评论区用户可发布图片、视频、文字进行评论,其他用户可进行“无限级|盖楼评论区”,点赞评论、回复评论、删除评论(自动计算刷新,不影响布局),当评论大于n条时自......
  • 【全开源】JAVA游戏陪玩系统源码陪练APP源码H5源码电竞系统源码支持Android+IOS+H5_博
    “游戏陪玩系统源码、陪练APP源码、H5源码及电竞系统源码”,这些创新技术的融合,为电竞爱好者与游戏玩家带来前所未有的互动体验。通过我们的源码,您可以轻松搭建起一个功能完备的游戏陪玩平台,无论是寻找技术高超的陪练,还是与志同道合的玩家组队竞技,都能在这个平台上得到满足。同......
  • 【全开源】JAVA二手车交易二手车市场系统源码支持微信小程序+微信公众号+H5+APP_博纳
    二手车交易二手车市场系统源码微信小程序——买卖无忧,交易更便捷在日益繁荣的二手车市场中,寻找一个高效、可靠的交易平台成为了买卖双方共同的需求。为满足这一需求,我们推出了二手车交易二手车市场系统源码微信小程序,让买卖无忧,交易更便捷。这款微信小程序整合了丰富的二手车......
  • H5网页调用APP原生分享菜单 方法:mcloudshare://advert?imgUrl=图标链接&link=分享的链
    要在H5网页中调用APP原生的分享菜单,你可以通过以下步骤实现:创建分享按钮或触发分享的交互元素,例如一个按钮或链接。在按钮的点击事件处理程序中,使用JavaScript生成一个调用APP分享功能的URL。根据你提供的信息,生成的URL格式如下:mcloudshare://advert?imgUrl=图......
  • 【毕业设计】基于SpringBoot 和uniapp的食堂点餐系统
    效果源码下载文档一、概述由于互联网的飞速发展,饭店的点餐也要进行时代化的创新,由以前的人工点餐到现在的系统点单,大大减少了人力资源的利用。总体设计图由于互联网的飞速发展,饭店的点餐也要进行时代化的创新,由以前的人工点餐到现在的系统点单,大大减少了......
  • yolov部署到app
    今天把模型部署到app上        ......
  • 说说 HTTP1.0/1.1/2.0 的区别?
     一、HTTP1.0HTTP协议的第二个版本,第一个在通讯中指定版本号的HTTP协议版本HTTP1.0 浏览器与服务器只保持短暂的连接,每次请求都需要与服务器建立一个TCP连接服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求简单来讲,每次与服务器交互,都需要新......
  • Android基于MediaBroswerService的App实现概述,android零基础入门
    谷歌官方提供了MediaBroswerService,通过其可以帮助我们实现上述的需求。MediaBroswerServiceAndroid多媒体架构Android多媒体播放采用client,server架构,一个server可以对应多个client,client在使用的时候需要先连接到server,双方通过设置的一些callback来进行状态的同步。......