首页
学习
活动
专区
工具
TVP
发布

5-无人驾驶小车系列之多文件编程及Makefile

本文将给大家带来系列教程:无人驾驶小车系列之一小车运行控制《树莓派无人驾驶小车系列教程》

分为以下几个模块进行讲解:

本课目录

一、组件介绍

二、模块接线

三、树莓派使用

四、wiringPi GPIO控制

五、超声波测距

六、小车转动控制

七、按键控制小车运行

八、多线程并发控制

九、多文件编程及Makefile

十、opencv图像处理

十一、红绿灯检测识别

十二、道路交通标志检测识别

九、多文件编程及Makefile制

1、C语言模块化编程

组织代码和文件的目的是为了使团队合作更加有效,使软件项目有良好的可扩展性、可维护性、可移植性、可裁减、可测试性,防止错误发生,提高软件的稳定性。通常情况下,软件项目采用层次化结构和模块化开发的方法。

层次化结构:

一个嵌入式软件项目可能有驱动层、操作系统层、功能层、应用程序层, 每一个层使用它的下层提供的接口,并为它的上层提供调用接口。

模块化开发:

模块则是每一个层中完成一个功能的单元,例如驱动层的每一个设备的驱动就是一个模块,应用层的每个应用程序就是一个模块,模块使用下层提供的接口和同层其他模块提供的接口,完成特定功能,为上层和同层的其他模块提供调用接口。

这里的接口是指一个功能模块暴露出来的,提供给其他模块的访问具体功能的方法。

根据C语言的特点,使用*.c文件实现模块的功能,

使用*.h文件暴露单元的接口,在*.h文件里声明外部其他模块可能是用的函数,数据类型、全局变量、类型定义、宏定义和常量定义.外部模块只需包含*.h文件就可以使用相应的功能.

当然,模块可以在细化为子模块.

2、文件组织的基本建议

根据C语言的特点,并借鉴一些成熟软件项目代码,总结C项目中代码文件组织的基本建议:

1) 使用层次化和模块化的软件开发模型,且每一个模块只能使用所在层和下一层模块提供的接口。

2) 每个模块的文件包存在独立的一个文件夹中。通常情况下,实现一个模块的文件不止一个,这些相关的文件应该保存在一个文件夹中.

3) 用于模块裁减的条件编译宏保存在一个独立的文件里,便于软件裁减.

4) 硬件相关代码和操作系统相关代码与纯C代码相对独立保存,以便于软件移植.

5) 声明和定义分开,使用*.h文件暴露模块需要提供给外部的函数,宏,类型,常量,全局变量,尽量做到模块对外部透明,用户在使用模块功能时不需要了解具体的实现,文件一旦发布,要修改一定要很慎重,

6) 文件夹和文件命名要能够反映出模块的功能.

7) 正式版本和测试版本使用统一文件,使用宏控制是否产生测试输出。

8) 必要的注释不可缺少。

3、头文件书写格式

#ifndef MY_INCLUDE_H

#define MY_INCLUDE_H

宏定义

类型定义

函数声明

#endif

注意:

包含extern "C",使C的程序可以在C++编译器被编译。

#ifdef __cplusplus

extern "C"{

#endif

#ifdef __cplusplus

}

#enfif

被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;未加extern “C”声明时的编译方式,作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。

4、源文件书写格式

#include “my_include.h”

函数定义

*.c文件是C语言中生成汇编代码和机器码的内容,要注意以下建议:

1) 命名方式为: 模块名.c

2) 用static修饰本地的数据和函数。

3) 不要使用extern。这是在*.h中使用的,可以被包含进来。

4) 无论什么时候定义内部的对象,确保独立与其他执行文件。

5) 这个文件里必须包含相应功能函数。

上面介绍了一些C文件组织的建议,用于提高C语言项目的质量,在以后的C项目组织中,学习面向对象和COM的思想,将这些思想加入到C程序中,能够写出更高质量的代码。上面的建议在具体的项目里应该灵活运用,另外,C工程中经常有一些汇编代码文件,这些文件也要使有*.h头文件暴露其中的数据和函数,以便其他*.c文件包含使用。

5. 本项目的文件结构

car.h

car.cpp

keyboard.h

keyboard.cpp

hc_sr04.h

hc_sr04.cpp

main.cpp

Makefile

6. 多文件编译

一般来说,无论是C还是C++,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile),一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。

编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来 链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给 中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

多文件编译链接过程

g++ -c main.cpp # 主线程

g++ -c keyboard.cpp#按键控制线程

g++ -c car.cpp#小车运行线程

g++ -c hc_sr04.cpp#超声波线程

g++ main.o hc_sr04.o car.o keyboard.o -o a.out -lpthread -lwiringPi

7.Makefile

目标 : 依赖条件 (注意冒号两边有空格)

     命令   (注意前面用tab键开头)

解释一下:

1) 目标可以是一个或多个,可以是Object File,也可以是执行文件,甚至可以是一个标签。

2) 依赖条件就是生成目标所需要的文件或目标

3) 命令就是生成目标所需要执行的脚本

总结一下,即一条makefile规则规定了编译的依赖关系,也就是目标文件依赖于条件,生成规则用命令来描述。在编译时,如果依赖条件的文件比目标更新的话,就会执行生成命令来更新目标。

Makefile示例:

CC = g++

# 可执行文件

TARGET = car

# CPP原文件

SRCS = car.cpp main.cpp keyboard.cpp hc_sr04.cpp

# 目标文件

OBJS = $(SRCS:.cpp=.o)

# 库文件 图像处理/GPIO/多线程

DLIBS = -lopencv_core -lopencv_imgproc -lopencv_highgui -lwiringPi -lpthread

# 链接为可执行文件

# $^ : 所有的依赖文件,即 .o , $

# $@ :目标,可执行文件,即 $

$(TARGET) : $(OBJS)

$(CC) -o $@ $^ $(DLIBS)

.PHONY:clean #伪目标

clean:

rm -rf $(TARGET) $(OBJS)

# 编译规则 $@代表目标文件 $< 代表第一个依赖文件

%.o:%.cpp

$(CC) -o $@ -c $

梁国际

智能物联教学总监

7年智能物联开发,先后参与多媒体便携设备,数字机顶盒,网络智能盒、互联网HiFi家庭影院等产品的技术开发。

熟悉HTTP/RTP/RTSP/RTMP/HLS等流媒体传输协议。长期致力于嵌入式Linux的研究开发,熟悉ARM体系结构/嵌入式开发流程/Linux设备驱动/USB子系统/网络子系统/嵌入式Linux移植。

擅长技术领域:C/C++语言、Linux系统编程、ARM体系结构、Linux内核与驱动、流媒体与网络协议开发/OpenCV图像处理。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20171230G09JAI00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券