求职八股
· AutoTools
· 必要文件
1. Configure.ac
configure.ac(in) -> autoconfig - > configure脚本
初始化宏:
AC_INIT([penguin], [2019.3.6], [[seth@example.com][8]])
AC_OUTPUT
创建Makefile的宏:
AM_INIT_AUTOMAKE #无任何参数
输出目标的宏:
AC_CONFIG_FILES([Makefile])
指定编译器:
AC_PROG_CXX #for C++
AC_PROG_CC #for C
2. Makefile.am
Makefile.am
+ configure.ac
>> Makefile.in
用于指定编译细节:
bin_PROGRAMS = hello
hello_SOURCES = hello.cpp
· 使用流程
- 准备好
configure.ac
Makefile.am
- 执行
aclocal
,生成m4
和cache
- 执行
autoconf
,生成configure
文件 - 执行
automake -a
,生成Makefile.in
文件 - 执行
./configure
,通过Makefile.in
,生成Makefile
文件 - 执行
make && make install
,生成bin
,在/usr/local/bin
下
· Git
简明使用手册
-
git init
-
git clone /path/to/repository
-
git clone username@host:/path/to/repository
-
git add [filename] /git add *
-
git commit -m “代码提交信息”
-
git push origin master
-
git remote add origin [server]
· Makefile
Makefile傻瓜教程
1. 一个简单的例子
让我们从一个简单例子开始,首先我们需要准备三个文件。这三个文件分别代表主程序,工具函数的实现和声明。
//hellomake.c
#include<hellomake.h>
int main()
{
// call a func in another file.
myPrintHelloMake();
return 0;
}
//hellofunc.c
#include<stdio.h>
#include<hellomake.h>
void myPrintHelloMake()
{
printf("Hello, makefiles!\n");
return;
}
//hellomake.h
void myPrintHelloMake();
有了这三个文件,我们可以用下面的指令来编译
gcc -o hellomake hellomake.c hellofunc.c -I.
这条指令编译两个c文件,并且生成可执行文件hellomake。
-I.
参数告诉工gcc从当前目录寻找头文件hellomake.h
。
makefile解决的问题:
- 若更换环境不必重写编译指令
- 只修改部分文件不必全部重编
2. Makefile version1 >> 基本写法
最简单的Makefile文件长这样:
hellomake: hellomake.c hellofun.c #这个叫规则
gcc -o hellomake hellomake.c hellofun.c -I. #这个是实现(注意tab)
#在任何指令之前,都必须加一个tab字符,这是make的要求,不加tab, make会不开心的:)
把这两行写入到一个名为Makefile或makefile的文件中,然后在terminal输入make,系统就会按照makefile文件中定义的规则编译你的代码。
注意:make命令不带参数的话,会默认执行makefile文件中的第一条规则。
此外,通过将命令依赖的文件列表放在:
之后的第一行,make就知道如果其中的任何文件发生更改,就需要执行hellomake规则。但是这样写,make仍然不能有效的只编译最新的更改。
3. Makefile version2 >> 宏定义CC和CFLAG
我们更进一步,让makefile更加高效一点。
CC=gcc
CFLAG=-I.
hellomake:hellomake.o hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o
现在,我们在makefile中定义了一些常量,CC
和CFLAG
,这些常量告诉make如何编译hellomake.c
和hellofunc.c
文件。CC
宏定义了要使用哪个编译器,CFLAG
是一些编译标志。
通过将目标文件hellomake.o
和hellofunc.o
放在依赖列表中,make就知道首先需要编译c文件得到目标文件,然后链接得到可执行文件hellomake。对于大多数小型项目来说,这种makefile已经足够了。
4. Makefile version3 >> 解决C文件依赖哪些h文件
但是上个版本的makefile还忽略了一点:对头文件的依赖。如果你修改了hellomake.h
文件,然后重新执行make,这时候即使需要重新编译c文件,make也不会重编的。为了解决这个问题,我们得告诉make,c文件依赖哪些h文件。
CC=gcc
CFLAG=-I.
DEPS = hellomake.h
%.o:%.c $(DEPS) #Rule 1:若编译*.o 则找*.c和*.h
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: hellomake.o hellofunc.o #Rule 2:若编译hm,则找hm.o hf.o,在r1完成
$(CC) -o hellomake hellomake.o hellofunc.o
$@ 表示目标文件
$^ 表示所有的依赖文件
$< 表示第一个依赖文件
$? 表示比目标还要新的依赖文件列表
在这个版本中,我们新加了一个常量DEPS
,这是c文件依赖的头文件集合。然后,我们又定义了一个适用于所有*.o
文件的规则,这个规则说明.o
文件依赖于同名的.c
文件和DEPS
中包含的头文件。
为了产生.o文件,make需要使用CC
常量定义的编译器编译.c文件。-c
标志表示产生目标文件, -o $@
表示将输出文件命名为:
左边的文件名, $<
表示依赖列表中的第一个项。
5. Makefile version4 >> 使用$@ $^
最后使用特殊的宏$@ $^
做最后一次简化,让编译规则更加通用。$@ $^
分别表示:左边和右边。在下面的例子中,所有的头文件都应该作为DEPS
宏的一部分,所有的目标文件*.o
都应该作为OBJ
宏的一部分。
CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o
%.o:%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
#等于gcc -o hellomake hellomake.o hellofunc.o
6. Makefile version5
如果我们想把头文件,源文件和其他库文件分别放在不同的文件夹,那么makefile该怎么写呢?
另外,我们可以隐藏那些烦人的中间文件(目标文件)吗?当然可以!
下面的makefile定义了include
文件夹,lib
文件夹路径,并且把目标文件放到src
文件夹的子文件夹obj
里面。同时,还定义了一个宏,用于包含任何你想要包含的库,比如math
库-lm
。这个makefile文件应该位于src
目录,注意,这个makefile 还包含了一个规则用于清理source
和obj
文件夹,只需要输入make clean即可。.PHONY
规则可以让make不去改动任何名为clean
的文件(如果有的话)。
IDIR = ../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR=../lib
LIBS=-lm
_DEPS=hellomake.h
DEPS=$(pathsubst %,$(IDIR)/%,$(_DEPS))
_OBJ= hellomake.o hellofunc.o
OBJ = $(pathsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o:%.c $(DEPS)
$(CC -c -o $@ $< $(CFLAGS
hellomake:$(OBJS)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY:clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
现在,我们有了一个非常不错的makefile,你可以对其进行简单的修改来管理中小型软件项目了。你也可以将多个规则写到一个makefile里面,甚至可以在一个规则中调用其他规则。
有关make和makefile的更多资料,敬请参考《GNU make手册》。
· Internet basic
· 关键字
volatile
功能:防止被编译器优化,即每次数据都从内存取,而非寄存器
运行效果:
1.编译
gcc main.c -o main -O3 #O3打开编译优化
2.不加volatile
,则会被认为是无效代码,被编译器优化,进而做不到delay效果。加volatile
则不会被编译器优化,变量每次都从内存读取,不会被认为是无效代码,delay功能正常。
#include <stdio.h>
void delay()
{
volatile int k,l;
for ( k = 0; k < 50000; ++k) {
for ( l = 0; l < 50000; ++l) {
}
}
}
int main() {
printf("111\n");
delay();
printf("222\n");
return 0;
}
static
起修饰作用的关键字。
1.修饰局部变量
功能:只改变生命周期,不改变作用域。不加static在退出test函数时,变量i就被释放。加了static则不释放,可继续累加。但无论加不加,i的作用域都只局限在test中。
运行效果:
不加static
加static
参考代码
#include "stdio.h"
void test()
{
static int i = 0;
i++;
printf("%d ",i);
}
int main()
{
for(int j=0;j<10;j++){
test();
}
}
2.修饰全局变量
功能:将全局变量(所有文件extern可引)修改成静态全局变量(仅在当前文件可用,其他文件不可引)。
3.修饰函数
功能:static修饰的函数会变成静态函数,只能在当前文件内可用。
const
功能:被const修饰的变量是常量,只读。
嵌入式开发
总线协议
异步传输—UART
异步传输:不需要时钟信号,随时可传。但是速度较慢,需要定义好帧的格式以约定开始和结束的标志信号。
UART
名称:通用异步传输收发器
帧格式:如图
接口定义:如图
接口标准:RS2332 & RS485
同步传输—IIC SPI
IIC
协议组成:两根线,时钟线—SCL
,数据线—SDA
。
通信流程:
- 主机发送开始信号,所有从设备进入监听状态;
- 主机发送从机地址(7bits)及读写位(1bit),从机收到后回复应答信号ACK;
- 主机发送从机内部地址(如EEPROM内部的地址,8bits),从机收到回复应答ACK;
- 主机发送数据块给从机,从机收到后回复ACK(此为写数据,读数据要多一个重发地址);
- 主机发送通信结束信号结束通信。
读数据帧 & 写数据帧
开始&结束信号:SCL高,SDA下跳>>开始,SDA上拉>>停止。
总结图
SPI
协议组成:四根线,
- 片选:
CS
- 输出:
MOSI
- 输入:
MISO
- 时钟:
SCK
模式:一主多从
以93C46为例:
写数据:将00001111写入0x01中
- 查阅芯片手册确定指令格式
根据芯片手册确定数据格式,确定待发数据组成是1
01
0000001 00001111
- 配合时序采样
再配合时钟信号SCK在上升沿采样,即可完成数据传输。
读数据
过程类似,唯一需要注意的是,即便MOSI不对从机发送数据,仍然需要发送SCK时钟信号,这是同步传输的特点