学习CH549过程中遇到的简单问题与解决
程序介绍
本次程序是想通过学习仿照例程中的2外部中断-key、3定时器中断-led、5_串口通讯来编写一个实现系统时钟与按键中断功能的程序。
具体思路与原理性内容不再阐述,教程链接如下:以 CH549 为例的 51 教程 。
本文用于记录学习过程中遇到的少见错误。
问题与解决
重复包含
由于我的按键中断源文件key.c中的按键消抖需要用到time.c中的延时函数delay_ms(),所以我一开始就将头文件time.h包含再了key.h中。

而我在main.c文件中又同时包含了time.h、key.h文件,这个时候编译就会产生time.h的重复定义报错
inc/time.h:17: error 0: Duplicate symbol 'sys_time', symbol IGNORED //错误0:重复的符号“sys_time”,符号IGNORED
inc/time.h:17: error 177: previously defined here //错误177:以前在此定义
inc/time.h:19: error 71: interrupt no '1' already has a service routine 'timer0_isr' //错误71:中断号“1”已具有服务例程“timer0_isr”
为了防止头文件重复包含,我们需要在头文件中加入以下三行代码
#ifndef __TIME_H //需要具有唯一性,一般直接用头文件名
#define __TIME_H
//在此处原本的头文件内容
#endif
这三行代码是一个简单的判断函数,可以有效防止重复定义,意思是:
if(__TIME_H未被定义)
{
定义 __TIME_H
头文件内容
}
endif
extern修饰符理解错误
我在key.c文件中定义了一个整形变量f_key_pressed作为按键标志位,而在主函数的while循环中需要判断这个标志位。现在我有两种思路来实现,要么将它作为函数返回值在主函数中调用函数,要么用修饰符extern修饰这个变量。我选择了第二种。
extern修饰变量有2种格式
① int a;
② int a = 0;
③ extern int a = 0;
④ extern int a;
其中①②③ 是定义,并且②③基本完全等效都是定义整形变量a并赋初值0。
而④是一个声明,声明其他文件有这个全局变量,相当于就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,可以使用。“
但④仅仅是一个声明,所以我当时使用的时候出现了“未定义”的报错。
extern int i; //声明,不是定义
int i; //声明,也是定义
再写一遍加深印像。
所以我需要再key.c中定义全局变量f_key_pressed,然后在头文件key.h中加入声明:extern int f_key_pressed;
static修饰符理解错误
static 可以用于修饰局部变量,全局变量,函数;
static修饰局部变量时,会影响局部变量的生命周期,为整个周期。
static修饰全局变量时,全局变量本身具有外部链接属性,可以通过extern修饰被其他源文件调用,而static修饰全局变量时,这个全局变量的外部链接属性变为内部链接属性,其他源文件就可以再使用这个全局变量了,作用域就变小了。
static修饰函数时,这个函数的外部链接属性变为内部链接属性,是其他源文件就可以再使用这个函数了。
而我在上网查资料时眼睛完美跳过了“再”,将意思理解成了转向了完全相反的方向。
头文件错误
假设源文件A.c中的函数,调用了B.c的函数或定义,包含了头文件B.h,但是A.h中却没有包含B.h。
那么在调用该函数就有可能产生错误。在main.c文件中会出现以下2种情况。
#include "B.h"
#include "A.h"
//正确,因为在包含A.h文件时已经包含了B.h。
和
#include "A.h"
#include "B.h"
//错误,因为在包含A.h文件时,A.c内部函数时找不到B.c中的声明
而这种情况产生的报错一般都是很难从报错信息中提取出正确的修改方向的。
所以我们在使用非官方库的时候可以将常用库放在前方 ,减小错误概率。而在代码测试的时候将常用库放在后方,以检测出更多问题。