keil5 报错问题解决办法以及C语言tips

Warning:equeality comparison with extraneous parentheses

警告:使用无关括号进行相等比较

解决办法:检查哪里多写了没用的括号 比如if语句

Error: L6218E: Undefined symbol EXTI_ClearITPendingBit

错误:L6218E:未定义的符号EXTI_ClearITPendingBit

解决办法:检查工程中是否包含文件stm32f10x_exti.c,点击魔法棒旁边的按键添加文件。

warning:  #175-D: subscript out of range

错误:#175-D:下标超出范围

解决办法:检查使用到的数组下标或者是包含数组的结构体等,是否有越界。

error:  #114: function "MCP3421_Read" was referenced but not defined

错误:#114:函数“MCP3421_Read”被引用但未定义

解决办法:检查该函数是否定义在其他 .h 文件中是否未包含该头文件,检查函数声明与定义是否不同,如果为static函数应改为全局函数。

warning:  #1295-D: Deprecated declaration lcd_init - give arg types

警告:#1295-D:不推荐的声明lcd_init-给定参数类型

解决办法:函数声明中传参修改为void

 error: #513: a value of type “u8 *” cannot be assigned to an entity of type “uint8_t”

错误:#513:不能将“u8*”类型的值分配给“uint8_t”类型的实体

解决办法:检查数据赋值与数据类型是否匹配 ;检查多个数据统一赋值初始化或值传递时个数与类型是否对应。

warning:  #223-D: function "memset" declared implicitly

警告:#223-D:隐式声明的函数“memset”

解决办法:增加头文件。 #include "string.h"

error:  #20: identifier "uart_para" is undefined

错误:#20:标识符“uart_para”未定义

解决办法:找到变量,右键跳转到变量定义位置,发现变量在其他.c文件中定义。

在使用到此变量的.h文件中增加包含此变量.c文件。

error:  unknow type name u16

错误:未知类型名 u16

解决办法:在该 .h 文件开始前 增加头文件 #include "stm32f10x.h"

error: a declaration cannot have a label

错误:声明不能有标签

解决办法:检查变量声明位置,是否在语句之后(变量定义应在函数开始),如果是结构体变量,应检查结构体声明是否包含在switch case中;或者case后加括号,因为case即为标签的一种。

进行debug调试时,发现某几句代码,不执行,简单更换位置仍然不可以,则此时问题应该出现在此语句之后;如果去掉循环可以调试运行增加断点,则检查循环条件。要考虑代码逻辑是否有问题,是否错误。

原因:keil5对代码进行了优化,在调试时,将不可能执行到的语句或无意义的语句,调试时直接跳过。

例如:发现8536 8537行无法添加断点,检查了循环条件无问题,且循环的 j 值有变化。发现8539行直接对变量CRC_RX赋值,则上边8536 8537行运行无意义。keil5在编译后,将这两行直接忽略。(手误,因为应该将8539行 = 写为 +=)

cannot access memory

1、检查程序是否有数组越界或堆栈越界等,考虑大小是否溢出。

2、*** error 122: AGDI: memory read failed (0xFFFFFFFE)

错误:程序烧录不运行;keil软件中DEBUG调试,复位后程序无法进入main函数;调试一直卡死,不知道运行到哪;复位单步调试也无法进入复位初始化位置。

注: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是一种在设备上电后加载固件的机制,它可以用于程序或固件的更新,并且在MCU上电后会首先运行bootload程序进行相应的检测和操作。

1、注意静态变量、静态函数的作用域。只能在当前文件中使用,并且静态变量只初始化一次。如果传输数据时定义tx_buf为静态变量,要考虑多次传输时是否需要对其数组清零。因为数组只有在初始化时可以整体赋值,所以后续清零可以使用以下语句。

memset(数组名, 0x00, sizeof(数组名)); 

2、尽量少使用全局变量。在各自的 .c 文件中使用静态变量,必须跨文件使用时,可以在当前文件中增加局部变量或静态变量的指针,增加一个函数接口去获取变量的地址(即函数中指针获取想要的地址,返回值为指针,调用函数接口)。使用指针可以像全局变量一样全部修改。各自 .c 文件使用静态变量的好处是规范整洁,节省内存。

3、在写协议解析函数时,部分指令需要执行对应操作以及回复数据。底层和应用层要区分开,不能认为是一条指令所需要执行的操作而混合。同时在回复数据时,要将数据回复写在一个整体,方便后续的理解与修改。每增加一个函数功能、协议命令,都要考虑后续的增改删除。

4、在接收数据时,如果用指针来接收数组,要先检验指针是否接收到了数据(即判断指针是否指向NULL),再去判断接收数据的有效性,指针在初始化时要指向具体的地址或NULL,如果没有明确指向可能会成为野指针,考虑指针可能会跨越自己想要指向的范围。

	if(pRxdata == NULL) return 0;

5、结构体、共用体、位域三者结合起来,可以在多种can id 拼接或数据拼接时发挥作用,重点是在使用位域时,注意位域数据类型选择。

6、考虑使用宏去打开关闭功能,使程序的兼容性增加。注意宏的使用时间,在编译时就已经生效。如果要打开宏使用此功能,但运行时可能又不需要,可以考虑增加标志位去判断,不符合条件return。

7、程序写完后要检查函数封装及其调用,当函数嵌套调用时要特别注意循环。比如芯片在级联时,每个函数使用一次for循环来多次重复,当两个函数嵌套时就会使用n*n次。

8、单片机功耗降低可以考虑降低单片机的主频,最简单的是直接关闭不使用的GPIO,修改为模拟输入,速度降低。(先将所有的都调整为模拟输入,后在单独重新初始化要使用的GPIO)

9、GPIO初始化可以考虑分输入和输出两部分。好处是修改方便,可以单独出一个.c文件存放GPIO初始化函数。工程量大时方便查找内容。创建函数少。

//关闭不使用的GPIO 降低功耗
void mid_gpio_default(void)
{
	u8 i = 0;
	GPIO_InitTypeDef	GPIO_InitStructure;
	

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, DISABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, DISABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, DISABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, DISABLE);
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, DISABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, DISABLE);

}
//GPIO初始化示例
typedef enum
{
	DISCState,
	CHGState,
	BATTID,
	Input_Max
}GPIO_Input_Max;

typedef enum
{
	POWERON,
	DISCEN,
	CHGEN,
	Output_Max
}GPIO_Output_Max;

typedef struct
{
	u8 input_ch;
	u32 apb2_port;
	void * port;
	u16 pin;
	u8 mode;
}GPIO_Ch_Cfg;

const GPIO_Ch_Cfg GPIOInputChCfg[Input_Max] =
{
	{DISCState,	RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_1, GPIO_Mode_IPU},
	{CHGState,	RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_2, GPIO_Mode_IPU},
	{BATTID,	RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_1, GPIO_Mode_IN_FLOATING},
};

const GPIO_Ch_Cfg GPIOOutputChCfg[Output_Max] =
{
	{POWERON,	RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_15, GPIO_Mode_Out_PP},
	{DISCEN,	RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_0,  GPIO_Mode_Out_PP},
	{CHGEN,		RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_10, GPIO_Mode_Out_PP},
};

//输入IO
void mid_gpioin_init(void)
{
	//Disc_State PA1
	//Chg_State PA2
	//batt id PB1
	
	u8 tmp_ch;
	GPIO_InitTypeDef	GPIO_InitStructure;
	
	for(tmp_ch = 0; tmp_ch < Input_Max; tmp_ch++)
	{
		RCC_APB2PeriphClockCmd(GPIOInputChCfg[tmp_ch].apb2_port, ENABLE);
		GPIO_InitStructure.GPIO_Pin = GPIOInputChCfg[tmp_ch].pin;
		GPIO_InitStructure.GPIO_Mode = GPIOInputChCfg[tmp_ch].mode;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOInputChCfg[tmp_ch].port, &GPIO_InitStructure);
	}
}

//输出IO
void mid_gpioout_init(void)
{
	//COMM_EN PA15
	//Disc_EN PB0
	//Chg_EN PB10
	
	u8 tmp_ch;
	GPIO_InitTypeDef	GPIO_InitStructure;
	
	for(tmp_ch = 0; tmp_ch < Input_Max; tmp_ch++)
	{
		RCC_APB2PeriphClockCmd(GPIOOutputChCfg[tmp_ch].apb2_port, ENABLE);
		GPIO_InitStructure.GPIO_Pin = GPIOOutputChCfg[tmp_ch].pin;
		GPIO_InitStructure.GPIO_Mode = GPIOOutputChCfg[tmp_ch].mode;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOOutputChCfg[tmp_ch].port, &GPIO_InitStructure);
		
		GPIO_WriteBit(GPIOOutputChCfg[tmp_ch].port,GPIOOutputChCfg[tmp_ch].pin,Bit_RESET);
	}
	
}

10、keil在使用过程中,仿真乱跳不进入循环等乱七八糟问题,可以考虑将等级调低。

学习一下

第1章_瑞萨MCU零基础入门系列教程之单片机程序的设计模式_挨踢民工biubiu的博客-CSDN博客

第2章_瑞萨MCU零基础入门系列教程之面向过程与面向对象_挨踢民工biubiu的博客-CSDN博客

  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值