目录
十、Makefile的简单知识
为了方便我们编译,这里学习一下Makefile的简单知识
1、准备文件
main.c 调用一个头文件实现一个简单的功能
#include <stdio.h>
#include "function.h"
int main()
{
printfhello();
int a = abc(2,3,4);
printf("%d\n",a);
return 0;
}
abc.c
#include <stdio.h>
int abc(int a, int b, int c)
{
return a+b+c;
}
printfhello.c
#include <stdio.h>
void printfhello()
{
printf("printfhello\n");
}
function.h
#ifndef _FUNCTIONS_H_
#define _FUNCTIONS_H_
void printfhello();
int abc(int a, int b, int c);
#endif // !_FUNCTIONS_H_
2、Makefile的简单使用 1
Makefile文件:解释注释里都有
#运行make命令,make命令会去找当前目录下面的Makefile文件,如果你的文件名不是Makefile,也可以指定文件名
#make -f file
## VERSION 1
#生成的可执行程序为main,main的生成依赖于main.c abc.c printfhello.c
main:main.c abc.c printfhello.c
#gcc前面一定时tab而不是空格
gcc -Wall -o main main.c abc.c printfhello.c
使用make运行Makefile文件
这样就不用我们每次编译都输入gcc....很麻烦,但这样也有缺点,就是每次修改编译文件时,会将每一个文件·都编译,如果文件过多就很慢,下面进行简单修改。
3、Makefile的简单使用 2
Makefile
## VERSION 2
#编译器
CXX = gcc
#目标
TARGET = main
OBJ = main.o printfhello.o abc.o
#寻找名为TARGET的文件,没有则依赖于OBJ生成如果OBJ有更新的则执行下一条命令: $(CXX) -o $(TARGET) $(OBJ)
$(TARGET): $(OBJ)
$(CXX) -o $(TARGET) $(OBJ)
main.o:main.c
$(CXX) -c main.c
printfhello.o:printfhello.c
$(CXX) -c printfhello.c
abc.o:abc.c
$(CXX) -c abc.c
这样修改之后每次执行Makefile文件时仅仅会对更新的文件进行编译,下面来实验一下:
第一次编译因为所以的文件都是新的所以所有的文件都参与编译:
现在我对abc.c文件进行修改(没啥改动就是加了个空格),再次执行make,发现仅仅对修改的文件进行了编译。
但是这样还有一个问题,如果有很多.c文件的话,那我如果写Makefile不就很烦琐了,所以为了可以更加的灵活,对Makefile进行简单修改
4、Makefile的简单使用 3
Makefile
## VERSION 3
# 编译器
CXX = gcc
# 要生成的可执行文件名
TARGET = main
# patsubst替换,将SRC里面.c文件替换成.o文件
OBJ = main.o printfhello.o abc.o
CXXFLAGS = -c -Wall
# 这里按需修改
# $@就是$(TARGET) $^就是$(OBJ)
# 寻找名为"@(也就是TARGET)"的文件,若没有则依赖于"^(也就是OBJ)"生成。如果^(也就是OBJ)有更新则执行下一条语句
$(TARGET): $(OBJ)
$(CXX) -o $@ $^
#写一个统一的规则来生成.o文件 $<是指%.c
%.o: %.c
$(CXX) $(CXXFLAGS) $< -o $@
# 当执行"make clean"时删除所生成的.o文件,与生成的可执行文件"TARGET"
.PHONY: clean
clean:
rm -f *.o $(TARGET)
这里我们写了一个统一的规则来生成.o文件,只要是这个目录下的.c文件,运行make是我们会将所有的.c文件生成.o 运行一下,正常
这里我们对任意一个文件进行修改,再运行
这里只重新编译了我修改的printfhello.c文件。
这里使用make clean可以执行clean,这里的功能是移除所有生成的.o文件,还有生成的可执行文件main,很方便
我们虽然对Makefile文件进行修改后简单方便了很多,在添加.c文件时只要在OBJ里加上对应的.o文件就可以了,但是每次添加一个文件都要修改一次Makefile,当文件很多时仍然会感到很繁琐,那这里对Makefile文件再进行简单修改
5、Makefile的简单使用
# VERSION 4 最终版 使用时按需修改,比如CXX、TARGET...
#运行make命令,make命令会去找当前目录下面的Makefile文件,如果你的文件名不是Makefile,也可以指定文件名
# 编译器
CXX = gcc
# 要生成的可执行文件名
TARGET = test
# 所以的当前文件的.c文件都放在SRC文件中
SRC = $(wildcard *.c)
# patsubst替换,将SRC里面.c文件替换成.o文件
OBJ = $(patsubst %.c, %.o, $(SRC))
# 这里按需修改
CXXFLAGS = -c -Wall
CFLAGS = -lwiringPi -Wall
# $@就是$(TARGET) $^就是$(OBJ)
# 寻找名为"@(也就是TARGET)"的文件,若没有则依赖于"^(也就是OBJ)"生成。如果^(也就是OBJ)有更新则执行下一条语句
$(TARGET): $(OBJ)
$(CXX) -o $@ $^ $(CFLAGS)
# 写一个统一的规则来生成.o文件 $<是指%.c
%.o: %.c
$(CXX) $(CXXFLAGS) $< -o $@
# 当执行"make clean"时删除所生成的.o文件,与生成的可执行文件"TARGET"
.PHONY: clean
clean:
rm -f *.o $(TARGET)
在这个文件里我们实现了自动识别当前目录下最新的.c文件,然后生成对应的.o文件,这时候我们再添加文件就不用对Makefile文件进行修改了,只要开始时修改一下生成的可执行文件名
执行一下看看:正常
对abc.c文件进行修改再执行一下
一切正常,当然还有很多可以实验观察对比的地方,比如删除某个文件,执行时会怎么样这里就不作过多说明了,大家自己试一试
十一、光照度传感器 bh1750
1、bh1750.c
// Filename: bh1750.c
// Function: 光照传感器bh1750
#include <wiringPi.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
// #include <stdlib.h>
#include <unistd.h>
// #include <time.h>
#include <sys/time.h>
#include <string.h>
#include <sys/ioctl.h>
//sudo gcc -o bh1750 bh1750.c
//具体查看手册中的Instruction Set Architecture
//1、发送上电命令 0000 0001
//2、发送测量命令(有一次测量、连续测量)这里选择连续高分辨模式2 0001 0001
//3、等待测量结束 延时一段时间,120ms~180ms,我这里选择200ms
//4、读取数据
//5、计算结果
//i2c设备地址
#define I2C_ADDR 0x23
int main()
{
printf("hello word\n");
printf("%x\n",I2C_ADDR);
int fd;
char buf[3];
char val;
// char value;
float flight;
//获取设备地址
fd = open("/dev/i2c-1",O_RDWR);
if(fd < 0)
{
printf("打开文件错误:%s\r\n",strerror(errno));
return 1;
}
if(ioctl(fd,I2C_SLAVE,I2C_ADDR)<0)
{
printf("ioctl错误:%s\r\n",strerror(errno));
return 1;
}
printf("%x\n",fd);
//发送上电命令 0x01
val = 0x01;
printf("%x\n",val);
if(write(fd,&val,1)<0)
{
printf("上电失败\n");
}
//发送测量命令,连续高分辨率模式2
val = 0x11;
// printf("%x\n",val);
if(write(fd,&val,1)<0)
{
printf("开启高分辨率模式2\n");
}
//高分辨率测量需等待时间最长,平均120ms,最大180ms,这里取200ms
usleep(200000);
while (1)
{
if(read(fd,&buf,3))
{
//光照强度 = (寄存器值[15 : 0] * 分辨率)/1.2 单位:勒克斯lx
flight = (buf[0]*256 +buf[1])*0.5/1.2;
printf("光照度%6.2flx\r\n", flight);
}
else
{
printf("读取错误\n");
}
usleep(200000);
}
return 0;
}
2、Makefile
上一节简单学习了Makefile在这里试用一下
## VERSION 4
CXX = gcc
TARGET = bh1750
#所以的当前文件的.c文件都放在SRC文件中
SRC = $(wildcard *.c)
#patsubst替换,将SRC里面.c文件替换成.o文件
OBJ = $(patsubst %.c, %.o, $(SRC))
CXXFLAGS = -c -Wall
$(TARGET): $(OBJ)
$(CXX) -o $@ $^ -lwiringPi
%.o: %.c
$(CXX) $(CXXFLAGS) $< -o $@
.PHONY: clean
clean:
rm -f *.o $(TARGET)
使用后感觉很好用:
运行生成的可执行文件
使用make clean 删除生成的文件
十二、温湿度传感器DHT11
1、DHT11.c
/******************************************
* 1、DHT11上电后(DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令),
* 测试环境温湿度数据,并记录数据,同时DHT11的DATA数据线由上拉电阻拉高一直保持高电平,
* 此时DHT11的DATA引脚处于输入状态时刻检测外部信号。
* 2、微处理器的/0设置为输出同时输出低电平,且低电平保持时间不能小于18ms(最大不得超过30ms),
* 然后微处理器的I/0设置为输入状态,由于上拉电阻,微处理器的I/0即DHT11的DATA数据线也随之变高,
* 等待DHT11作出回答信号
* 3、DHT11的DATA引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后DHT11的DATA引脚
* 处于输出状态,输出83微秒的低电平作为应答信号,紧接着输出87微秒的高电平通知外设准备接收数据,
* 微处理器的I/O此时处于输入状态,检测到/O有低电平(DHT11回应信号)后,等待87微秒的高电平后的数据接收。
* 4、由DHT11的DATA引脚输出40位数据,微处理器根据1/0电平的变化接收40位数据,位数据
* 微秒的低电平和23-27微秒的高电平,位数据“1”的格式为: 54微秒的低电平加68-74微秒的高电平
* 的格式为:54
*
*/
#include <stdio.h>
#include <wiringPi.h>
#include <time.h>
//定义GPIO口
#define Data 0
/**DHT11读取温湿度
* @param temperature_int:温度整数部分
* @param temperature_dec: 温度小数部分
* @param humidity_int: 湿度整数部分
* @param humidity_dec: 湿度小数部分
*/
char DHT11_Read(char* temperature_int, char* temperature_dec, char* humidity_int, char* humidity_dec)
{
unsigned char t, i, j;
// unsigned char t, i;
unsigned char buf[5] = {0, 0, 0, 0, 0};
//主机发送信号 让DHT11工作
pinMode(Data, OUTPUT);
// pullUpDnControl(Data, PUD_UP); //将GPIO口打开
digitalWrite(Data, 0); // 1:向Data输出低电平20ms,18~30ms,主机发送开始信号
delay(23);
digitalWrite(Data, 1); // 2:向Data输出高电平30us,主机拉高20~40us
delayMicroseconds(30);
pinMode(Data, INPUT);
//DHT11响应
while(digitalRead(Data) && t < 100) // 3:DHT11响应83us低电平,这里检测一下,100us后还是高电平说明出错了
{
t++;
delayMicroseconds(1);
}
if(t > 100)return 1;
t = 0;
while(!digitalRead(Data) && t < 100) // 4:DHT11响应87us高电平
{
t++;
delayMicroseconds(1);
}
if(t > 100)return 2;
//数据采集5个八位数据
for(i = 0;i < 5;i++) // DHT11向主机发送5个8位数据
{
buf[i]=0;
for(j = 0;j < 8;j++)
{
buf[i] <<= 1;
t = 0;
while(digitalRead(Data) && t < 100)
{
t++;
delayMicroseconds(1);
}
if(t >= 100)return 3;
t = 0;
while(!digitalRead(Data) && t < 100)
{
t++;
delayMicroseconds(1);
}
if(t >= 100)return 4;
delayMicroseconds(40);
if(1 == digitalRead(Data))
{
buf[i] += 1;
}
}
}
//buf[0]:前八位表示湿度整数部分 buf[1]:9-16位表示湿度小数部分 buf[2]:17-24位为温度整数部分
//buf[3]:25-32位为温度小数部分 buf[4]:33-40位为校验位
if(buf[0] + buf[1] + buf[2] + buf[3] != buf[4])return 5;
*humidity_int = buf[0];
*humidity_dec = buf[1];
*temperature_int = buf[2];
*temperature_dec = buf[3];
return 0;
}
int main()
{
printf("hello world\n");
char humidity_int;
char humidity_dec;
char temperature_int;
char temperature_dec;
char value;
// FILE *fp = NULL;
if(wiringPiSetup() < 0)return 1;
while(1)
{
delay(1000);
value = DHT11_Read(&temperature_int, &temperature_dec, &humidity_int, &humidity_dec);
switch (value)
{
case 0:
printf("\nhumidity = %d\n",humidity_int);
printf("temperature = %d.%d\n",temperature_int,temperature_dec);
break;
case 1:
printf("\nerr1\n");
break;
case 2:
printf("\nerr2\n");
break;
case 3:
printf("\nerr3\n");
break;
// case 4:
// printf("\nerr4\n");
// break;
case 5:
printf("\nerr5\n");
break;
default:
break;
}
}
// return 0;
}
2、Makefile
CXX = gcc
TARGET = DHT11
#所以的当前文件的.c文件都放在SRC文件中
SRC = $(wildcard *.c)
#patsubst替换,将SRC里面.c文件替换成.o文件
OBJ = $(patsubst %.c, %.o, $(SRC))
CXXFLAGS = -c -Wall
$(TARGET): $(OBJ)
$(CXX) -o $@ $^ -lwiringPi
%.o: %.c
$(CXX) $(CXXFLAGS) $< -o $@
.PHONY: clean
clean:
rm -f *.o $(TARGET)