OTA 分区跳转
1:设置sp,A区起始位置0x08005000,给到sp
2:设置pc,A区起始位置0x08005000+4,改到pc
3:把B区用到的外设,寄存器reset
补充几个点注意:
1.跳转之前必须关闭中断,不管是deinit,还是关闭总中断,反正必须关掉所有的中断;2.app初始化代码必须要手动初始化所有的全局变量,不然靠startup文件的初始化,极有可能有问题,应该可以靠链接文件直接初始化bss段;3.在app中初始化片上外设,必须要先将外设全部停止下来重新初始化。比如adc+dma方式,必须将adc和dma同时停下来再初始化,否者会有问题
视频:
【手把手写【STM32/GD32】OTA升级Boot程序,跳转A区两大关键点 SP和PC寄存器】
【手把手写【STM32/GD32】OTA升级Boot程序,利用函数指针,编写跳转A区程序】
define
可以使用#define
来定义一个宏定义,而不加具体数字。这种情况下,宏定义A可以用来表示一个开关或者标记,用于条件编译或程序中控制代码执行的逻辑。
#define A
#ifdef A
// 这段代码在A被定义时会执行
// ...
#else
// 这段代码在A未被定义时会执行
// ...
#endif
分区下载之flash定义
在配置里面更改下载地址
更改中断向量表偏移量
MDK软件 中,flash download界面中,Download Function
Erase Full Chip
Erase Sectors
Verify
Reset and Run
下载完后复位运行
Do not Erase
bin文件 ,都是4字节的整数倍。从理论上分析, STM32 是32位的单片机,生成的代码也应该是32位(即4字节)的整数倍。
STM32之CRC32
STM32f103G070RBTx 有硬件CRC32,硬件CRC模块可以通过配置寄存器来选择CRC多项式和输入数据的位宽,包括8位、16位和32位。通过输入数据和CRC多项式,硬件CRC模块可以自动计算CRC校验结果,无需CPU参与计算过程。
STM32F103 CRC32只能计算32位,不可以配置
IAP时,需要.bin文件
MDK配置
fromelf.exe --bin --output "@L.bin" "#L"
如此即为成功。
注意:网上有很多生成方法,我用的超子视频的不行(不会搞),上述时比较可行的方案。
还有有时候打开别人的工程,编译,发现底下奇怪的报错,估计时这个过程配置输出二进制文件了,取消勾选Run #1 应该可以解决。
字符串之\0
在C语言中,字符串实际上是由字符数组组成的,数组的最后一个元素是空字符。当我们在C程序中声明一个字符串时,编译器会在我们输入的字符串最后自动添加一个空字符。
例如,如果我们定义一个字符串 char str[ ] = "Hello";,那么编译器会在这个字符串的末尾自动添加一个空字符,使其变成 {'H', 'e', 'l', 'l', 'o', '\0'} 这样的字符数组。
这个空字符的存在非常重要,因为很多C语言库函数会依赖于这个空字符来确定字符串的结束位置,例如strlen()函数用于计算字符串的长度,它会一直读取字符直到遇到空字符才停止计数。
串口接收方案
采用DMA空闲中断 :不定长接收,因为采用DMA空闲中断,而不是靠DMA接收完成中断。所以设置DMA的接收量一般都大一点,不让DMA产生完成中断。
dma_init_struct.number = U0_RX_MAX+1;
//设置最大接收量 max+1:让DMA不产生完成中断
函数健壮性:
函数的健壮性指的是程序被设计的能力,以确保即使对参数的错误传递或存在无效的输入,程序也不会崩溃或发生意外的行为。实现函数的健壮性的一般原则,包括检查和处理所有输入,考虑所有可能的异常情况,避免使用过大或过小的步骤,并且确保程序可以以正确的方式处理所有情况。
关于 如何写出健壮的go函数? 的回答,具有参考意义。
健壮三原则
- 原则一:不要相信任何外部输入的参数。
函数的使用者可能是任何人,对于任何的输入参数我们都要做有效性的边界校验。为了保证函数的健壮性,函数需要对所有输入的参数进行合法性的检查。一旦发现问题,立即终止函数的执行,返回预设的错误值。
- 原则二:不要忽略任何一个错误。
在函数调用逻辑里,我们可能调用标准的或者第三方的库函数,因为对于这种情况 我们不一定能真正调用成功。我们一定要显式地检查这些调用返回的错误值。一旦发现错误,要及时终止函数执行,防止错误继续传播。
- 原则三:不要假定异常不会发生。
统一认知:异常不是错误。错误是可预期的,也是经常会发生的,我们有对应的公开错误码和错误处理预案,但异常却是少见的、意料之外的。通常意义上的异常,指的是硬件异常、操作系统异常、语言运行时异常,还有更大可能是代码中潜在 bug 导致的异常,比如代码中出现了以 0 作为分母,或者是数组越界访问等情况。
函数设计时,我们就需要根据函数的角色和使用场景,考虑是否要在函数内设置异常捕捉和恢复的环节。
健壮的函数意味着,无论调用者如何使用你的函数,你的函数都能以合理的方式处理调用者的任何输入,并给调用者返回预设的、清晰的错误值。即便你的函数发生内部异常,函数也会尽力从异常中恢复,尽可能地不让异常蔓延到整个程序。
大端存储和小端存储 内容管理
大端存储,是将数据的低位字节放到高地址处,高位字节放到低地址处。
小端存储,是将数据的低位字节放到低地址处,高位字节放到高地址处。
大端存储和小端存储记忆时,可以理解为将低位字节放到大端还是小端?大端存储就是将低为放到高地址,小端就是将低位放到低地址,这样方便记忆
为什么会有大小端存储:
对于位数大于8的处理器,寄存器的宽度大于1个字节,那么将会存在如何将多个字节安排在寄存器内,就可以有大小端存储两种方法,大小端存储并没有优劣之分,都是存储的方法
验证C语言的数据的存储是大端还是小端:
//判断大小端
//假设右边是高地址,左边是低地址
int main()
{
int a = 1; // 0x0000 0001
//如果是大端 低位字节放到高地址,高位字节放到低地址
//00 00 00 01
//如果是小端 高位字节放到高地址,低位字节放到低地址
//01 00 00 00
char* p = (char*)&a; //字符指针只读1个字节,读8位
//如果是大端存储,则p读取的值是0
//如果是小端存储,则p读取的值是1
if (*p == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
工程结构 参考:
a. 1_APP
i. 应用层,存放业务逻辑代码文件
b. 2_Device
i. 设备层,存放设备层代码文件
c. 3_protocol
i. 协议层,存放协议层代码文件,比如网络通信协议,蓝牙协议
d. 4_Middleware
i. 中间组件层,例如RTOS实时操作系统,FatFs文件系统
e. 5_Platform
i. 平台层代码文件
f. 6_MoudleDriver
i. 外设模块代码文件
g. 7_HAL
i. 硬件抽象层,存放处理器的内核接口文件和库文件
h. 8_Core
i. 处理器需要的一些配置文件,比如hal库的配置文件Stm32xxx_hal_conf.h,中断处理文件Stm32xxx_it.c和Stm32xxx_it.h,系统初始化文件system_stm32f1xx.c和system_stm32f1xx.h,
ii. 还可以新建stm32f1xx_clk.c和stm32f1xx_clk.h来存放stm32的时钟树配置函数
i. Project
i. 工程文件,启动文件startup.s也放到这里
还可以建立错误信息打印。
注意编号和个数
比如20个区,编号从0-19。编写时注意 1。