错误:#114:函数“MCP3421_Read”被引用但未定义
解决办法:检查该函数是否定义在其他 .h 文件中是否未包含该头文件,检查函数声明与定义是否不同,如果为static函数应改为全局函数。
警告:#1295-D:不推荐的声明lcd_init-给定参数类型
解决办法:函数声明中传参修改为void
错误:#513:不能将“u8*”类型的值分配给“uint8_t”类型的实体
解决办法:检查数据赋值与数据类型是否匹配 ;检查多个数据统一赋值初始化或值传递时个数与类型是否对应。
警告:#223-D:隐式声明的函数“memset”
解决办法:增加头文件。 #include "string.h"
错误:#20:标识符“uart_para”未定义
解决办法:找到变量,右键跳转到变量定义位置,发现变量在其他.c文件中定义。
在使用到此变量的.h文件中增加包含此变量.c文件。
错误:未知类型名 u16
解决办法:在该 .h 文件开始前 增加头文件 #include "stm32f10x.h"
错误:声明不能有标签
解决办法:检查变量声明位置,是否在语句之后(变量定义应在函数开始),如果是结构体变量,应检查结构体声明是否包含在switch case中;或者case后加括号,因为case即为标签的一种。
进行debug调试时,发现某几句代码,不执行,简单更换位置仍然不可以,则此时问题应该出现在此语句之后;如果去掉循环可以调试运行增加断点,则检查循环条件。要考虑代码逻辑是否有问题,是否错误。
原因:keil5对代码进行了优化,在调试时,将不可能执行到的语句或无意义的语句,调试时直接跳过。
例如:发现8536 8537行无法添加断点,检查了循环条件无问题,且循环的 j 值有变化。发现8539行直接对变量CRC_RX赋值,则上边8536 8537行运行无意义。keil5在编译后,将这两行直接忽略。(手误,因为应该将8539行 = 写为 +=)
1、检查程序是否有数组越界或堆栈越界等,考虑大小是否溢出。
错误:程序烧录不运行;keil软件中DEBUG调试,复位后程序无法进入main函数;调试一直卡死,不知道运行到哪;复位单步调试也无法进入复位初始化位置。
在Keil菜单栏点击View——Registers Window,在寄存器查看窗口查找R14(LR)的值。
原因:因为程序运行没有从对应位置执行。如果板子有bootloader,则考虑boot被擦除掉了,重新烧录boot。
如果没有则点击keil中的魔法棒,将ROM修改(如下图所示),同时,如果代码中有下行代码也应将其注释。
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x3000);
Bootload是一种用于程序或固件更新的机制,它允许在设备上电后,通过引导加载程序(bootloader)来检测并加载新的固件。引用中提到,bootload通常被设置在MCU的起始地址处,例如0x08000000,并且通常会根据编译大小而定。在MCU上电后,会先从0x00000000的地址处开始运行,然后bootload程序会进行上电检测工作,确定无问题后会跳转至APP执行其中的代码。中,作者分享了关于bootload的一些内容,并指出自己还有很多需要学习的地方。bootload可以用于实现对应用程序的升级,这个过程可能会涉及到一些错误和需要补充的知识。作者表示将会在后续的分享中补充这一部分的知识。
总之,bootload是一种在设备上电后加载固件的机制,它可以用于程序或固件的更新,并且在MCU上电后会首先运行bootload程序进行相应的检测和操作。
1、注意静态变量、静态函数的作用域。只能在当前文件中使用,并且静态变量只初始化一次。如果传输数据时定义tx_buf为静态变量,要考虑多次传输时是否需要对其数组清零。因为数组只有在初始化时可以整体赋值,所以后续清零可以使用以下语句。
memset(数组名, 0x00, sizeof(数组名));
2、尽量少使用全局变量。在各自的 .c 文件中使用静态变量,必须跨文件使用时,可以在当前文件中增加局部变量或静态变量的指针,增加一个函数接口去获取变量的地址(即函数中指针获取想要的地址,返回值为指针,调用函数接口)。使用指针可以像全局变量一样全部修改。各自 .c 文件使用静态变量的好处是规范整洁,节省内存。
3、在写协议解析函数时,部分指令需要执行对应操作以及回复数据。底层和应用层要区分开,不能认为是一条指令所需要执行的操作而混合。同时在回复数据时,要将数据回复写在一个整体,方便后续的理解与修改。每增加一个函数功能、协议命令,都要考虑后续的增改删除。
4、在接收数据时,如果用指针来接收数组,要先检验指针是否接收到了数据(即判断指针是否指向NULL),再去判断接收数据的有效性,指针在初始化时要指向具体的地址或NULL,如果没有明确指向可能会成为野指针,考虑指针可能会跨越自己想要指向的范围。
if(pRxdata == NULL) return 0;
5、结构体、共用体、位域三者结合起来,可以在多种can id 拼接或数据拼接时发挥作用,重点是在使用位域时,注意位域数据类型选择。
学习一下
第1章_瑞萨MCU零基础入门系列教程之单片机程序的设计模式_挨踢民工biubiu的博客-CSDN博客
第2章_瑞萨MCU零基础入门系列教程之面向过程与面向对象_挨踢民工biubiu的博客-CSDN博客