嵌入式初学者问题

174 阅读7分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

.c文件与.h文件

.c文件和.h文件之间的联系

       .C:写具体函数(函数体)

       .H:是.C的头文件,里面包含函数的定义,可以让其他.C函数调用。(函数声明,结构体,头文件等)

       一般都在头文件中进行函数,变量声明,宏声明,结构体声明,而在C文件中去进行变量定义,函数实现。

       所以,.h文件可以被多个.c文件引用(头文件),可以一改全改。

       include的过程完全可以"看成"是一个文件拼接的过程。

  • 在h文件中可以写函数体,只要在任何一个C文件包含此.h文件就可以将这个函数编译成目标文件的一部分(但编译是以C文件为单位的,如果不在任何C文件中包含此.h文件的话,这段代码就形同虚设)
  • 一般都在h文件中进行函数,变量声明,宏声明,结构体声明,在C文件中去进行变量定义,函数实现
  • 声明告诉编译器这些符号的含义
  • 声明在回答who的问题,定义回答how的问题
  • “定义”兼具“声明”的功能,任何变量、函数必须先声明后使用(否则编译器找不到)
  • 编译是以c文件为单位的,编译器是不会编译h文件的
  • #include本身只是一个简单的文件包含预处理命令,即用include的文件的内容代替该行语句

ifndef,define,endif

       作用:防止重复定义产生错误

#ifndef _feed_dog_h //如果到目前为止还没有定义过“_feed_dog_h”这个宏
#define _feed_dog_h //则定义“_feed_dog_h”这个宏

extern void feed_dog(void); //声明一个外部函数

#endif //“#ifndef”到此结束

.h文件编译

       在一个项目中,会有.h文件被多次引用,这样.h文件就会被放到多个.c文件中被多次编译,我们要尽量避免这样的多次声明.从而提高效率。

在这里插入图片描述

       上面的D.h文件中就会重复出现两个int a();的声明,这样就有点重复了,这时条件编译宏就派上了用场:

在这里插入图片描述

这样就不会重复定义了。

部分名词解释

       总线:信息的传递通道,连接各种外设和芯片,详细的介绍可以看stm32的总线AMBA、AHB、APB

       一般说来,有系统总线AHB和外设总线APB。

变量声明和变量定义

       变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。

       变量声明:用于向程序表明变量的类型和名字(这句话可以理解成,变量声明告诉编译器某个符号的类型)。

       定义也是声明:当定义变量时我们声明了它的类型和名字。

       extern是声明不是定义:通过使用extern关键字声明变量名而不定义它,表示extern变量的定义不在这个文件中,请到其它.c文件中寻找。

       变量在使用前必须被被定义或者声明。

       在一个程序中,变量只能定义一次,却可以声明多次。

       定义分配存储空间,而声明不会。

函数的声明与定义

       函数定义的时候自带声明的性质

       函数的声明告诉编译器,函数有哪些传入参数,分别是什么类型,函数有返回值吗,是什么类型。函数的定义告诉编译器函数如何实现

       两者区别起来比较简单,带有{ }的就是定义,否则就是声明。

结构体

       声明结构体类型格式

格式:
struct 结构体名
{
  成员列表;
}变量名列表;

实例:
struct _GPIO
{
  int TMODER;
  int Tser;
}


       注:变量名列表结构体声明的结构体变量,可不写,以后再定义

       例如:struct _GPIO age,number;//就定义了两个结构体变量age和number

       定义结构体变量后,结构体成员变量的引用方法是

       例如我们上面定义了的age结构体变量,我要输出他的成员变量Tser,那么应该写为:

printf("%d",age.Tser);//也就是 结构体变量名.成员变量名,没什么特殊的

       定义结构体指针变量,依然基于上面的_GPIO结构体 定义结构体指针变量:

struct _GPIO  *age;

是的,只是结构体变量名前面加了*,但是要访问结构体变量成员时应该这样写:

printf("%d",age->Tser);

初始化一个外设

       这里 PPP 代表任意外设。

  1. 在主应用文件中,声明一个结构 PPP_InitTypeDef,例如:
PPP_InitTypeDef PPP_InitStructure;

这里 PPP_InitStructure 是一个位于内存中的工作变量,用来初始化一个或者多个外设 PPP。

  1. 为变量 PPP_InitStructure 的各个结构成员填入允许的值。可以采用以下 2 种方式:

a)按照如下程序设置整个结构体

PPP_InitStructure.member1 = val1;
PPP_InitStructure.member2 = val2;
PPP_InitStructure.memberN = valN;
/* where N is the number of the structure members */

       以上步骤可以合并在同一行里,用以优化代码大小:PPP_InitTypeDef PPP_InitStructure = { val1, val2,.., valN}

b)仅设置结构体中的部分成员:这种情况下,用户应当首先调用函数 PPP_SturcInit(..)来初始化变量PPP_InitStructure,然后再修改其中需要修改的成员。这样可以保证其他成员的值(多为缺省值)被正确填入。

PPP_StructInit(&PPP_InitStructure);
PP_InitStructure.memberX = valX;
PPP_InitStructure.memberY = valY;
/*where X and Y are the members the user wants to configure*/
  1. 调用函数 PPP_Init(..)来初始化外设 PPP。

  2. 在这一步,外设 PPP 已被初始化。可以调用函数 PPP_Cmd(..)来使能之。

PPP_Cmd(PPP, ENABLE);

       可以通过调用一系列函数来使用外设。每个外设都拥有各自的功能函数。更多细节参阅 Section3 外设固件概述。

       注:

  1. 在设置一个外设前,必须调用以下一个函数来使能它的时钟:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ENABLE);
  1. 可以调用函数 PPP_Deinit(..)来把外设 PPP 的所有寄存器复位为缺省值: PPP_DeInit(PPP)
  2. 在外设设置完成以后,继续修改它的一些参数,可以参照如下步骤:
PPP_InitStucture.memberX = valX;
PPP_InitStructure.memberY = valY; /* where X and Y are the only
members that user wants to modify*/
PPP_Init(PPP, &PPP_InitStructure);

工程模板

1core_cm4.h	//内核功能的定义,比如NVIC相关寄存器的结构体和Systick配置;
2core_cm4_simd.h	//包含与编译器相关的处理
3core_cmFunc.h	//内核核心功能接口头文件
4core_cmInstr.h	//包含一些内核核心专用指令

5startup_stm32f40_41xxx.s	//devices vector table for MDK-ARM toolchain
6stm32f4xx.h	//contains all the peripheral register's definitions, bits definitions 
				//and memory mapping for STM32F4xx devices
7system_stm32f4xx.c	//contains the system clock configuration for STM32F4xx devices,
						//关键函数SystemInit(),系统启动文件startup_stm32f40_41xxx.s里面调用
8stm32f4xx_conf.h	//里面包含了我们需要使用的标准库,对于需要使用的库需要在其中加以说明include,		
					//stm32f4xx.h里面可以看到对其include
					
9stm32f4xx_it.c	//provides template for all exceptions handler and 
          			// peripherals interrupt service routine.
10main.c		//主程序
11led.c 	//对于关键使用硬件的驱动配置文件

在这里插入图片描述