文章目录
前言
树莓派作为一款受欢迎的微型计算机主板,凭借其丰富的外设接口、强大的处理能力和开源的生态系统,成为了学习嵌入式系统开发、物联网(IoT)应用的理想平台。本文通过介绍控制部分设备学习树莓派
树莓派外设开发
树莓派的外设开发涉及到通过其GPIO(通用输入输出)引脚与外部设备进行交互。这些设备包括但不限于LED灯、传感器、电机驱动器等。为了有效地进行外设开发,首先需要熟悉树莓派的硬件规格,包括其GPIO引脚的功能分配、电流限制等。
树莓派的接口
GPIO通用输入输出
- Input
- 输入设备如: 人体传感器、烟雾、火焰等
- output
- 输出设备如:继电器、蜂鸣器等
其他接口
- PWM:可调节占空比的方波
- 串口uart:
- IIC
- SPI
- IIS
- 其他特定硬件接口(flash等)
wiringPi库
wiringPi库是常用的IO控制库,使用C语言进行开发的,提供了丰富的接口:GPIO控制、中断、多线程等等。
安装wiringPi
- 判断是否已经安装wiringPi库
- 使用命令
gpio -v
如果出现版本则已安装 - 使用
gpio readall
如果出现引脚标识则已安装
- 使用命令
- 安装wiringPi
- 在安装前需要确保你的树莓派连接到了网络,并且已经安装了git工具包,可以使用命令:
sudo apt-get install git-core
安装git工具 - 使用
git clone https://github.com/WiringPi/WiringPi
获取到github中最新源代码 - 进入wiringPi目录
cd wiringPi
- 执行
./build
编译安装wiringPi库
- 在安装前需要确保你的树莓派连接到了网络,并且已经安装了git工具包,可以使用命令:
- 使用
gpio readall
查看效果
wiringPi的常用函数
初始化函数
使用wiringPi时,你必须在执行任何操作前初始化树莓派,否则程序不能正常工作。
可以调用下表函数之一进行初始化,它们都会返回一个int , 返回 -1 表示初始化失败。
函数 | 状态 | 描述 |
---|---|---|
int wiringPiSetup (void) | 返回:执行状态,-1表示失败 | 当使用这个函数初始化树莓派引脚时,程序使用的是wiringPi 引脚编号表。引脚的编号为 0~16需要root权限 |
int wiringPiSetupGpio (void) | 返回执行状态,-1表示失败 | 当使用这个函数初始化树莓派引脚时,程序中使用的是BCM GPIO 引脚编号表。需要root权限 |
wiringPiSetupPhys(void) | 不常用,不做介绍 | \ |
wiringPiSetupSys (void) ; | 不常用,不做介绍 | \ |
通用GPIO控制函数
函数 | 状态 | 描述 |
---|---|---|
void pinMode (int pin, int mode) | pin:配置的引脚 mode:指定引脚的IO模式可取的值:INPUT、OUTPUT、PWM_OUTPUT,GPIO_CLOCK | 作用:配置引脚的IO模式注意:只有wiringPi 引脚编号下的1脚(BCM下的18脚) 支持PWM输出只有wiringPi编号下的7(BCM下的4号)支持GPIO_CLOCK输出 |
void digitalWrite (int pin, int value) | pin:控制的引脚 value:引脚输出的电平值。 可取的值:HIGH,LOW分别代表高低电平 | 让对一个已近配置为输出模式的 引脚 输出指定的电平信号 |
int digitalRead (int pin) | pin:读取的引脚 返回:引脚上的电平,可以是LOW HIGH 之一 | 读取一个引脚的电平值 LOW HIGH ,返回 |
void analogWrite(int pin, int value) | pin:引脚 value:输出的模拟量 | 模拟量输出 树莓派的引脚本身是不支持AD转换的,也就是不能使用模拟量的API, 需要增加另外的模块 |
int analogRead (int pin) | pin:引脚 返回:引脚上读取的模拟量 | 模拟量输入 树莓派的引脚本身是不支持AD转换的,也就是不能使用模拟量的API 需要增加另外的模块 |
void pwmWrite (int pin, int value) | pin:引脚 value:写入到PWM寄存器的值,范围在0~1024之间 | 输出一个值到PWM寄存器,控制PWM输出。 pin只能是wiringPi 引脚编号下的1脚(BCM下的18脚) |
void pullUpDnControl (int pin, int pud) | pin:引脚 pud:拉电阻模式 可取的值:PUD_OFF 不启用任何拉电阻。关闭拉电阻。 PUD_DOWN 启用下拉电阻,引脚电平拉到GND PUD_UP 启用上拉电阻,引脚电平拉到3.3v | 对一个设置IO模式为 INPUT 的输入引脚设置拉电阻模式。与Arduino不同的是,树莓派支持的拉电阻模式更丰富。树莓派内部的拉电阻达50K欧姆 |
wiringPi的基本使用
在使用wiringPi进行开发时,需要包含头文件 #include<wiringPi.h>。凡是写wiringPi的程序,都包含这个头文件。
随后通过命令gcc xxx.c -lwiringPi
进行程序的编译
控制继电器
继电器介绍
继电器是一种电子控制器件,它具有控制系统(又称输入回路)和被控制系统(又称输出回路),通常应用于自动控制电路中,它实际上是用较小的电流去控制较大电流的一种“自动开关”。
操作继电器
硬件接线
三个引脚就可以控制继电器,VCC、GND和一个IO口就可以了。
代码编写
代码分为初始化,判断,控制
代码如下:
#include <wiringPi.h>
#include <stdio.h>
#define SWITCHER 7
int main()
{
int cmd = 0;
// 初始化
if(-1 == wiringPiSetup())
{
printf("初始化失败\n");
return -1;
}
pinMode(SWITCHER,OUTPUT);
while(1)
{
printf("请输入命令值(0,:关闭1,:打开)->\n");
scanf("%d",&cmd);
getchar();
if(cmd == 0)
{
digitalWrite(SWITCHER,HIGH);
}else if(cmd == 1)
{
digitalWrite(SWITCHER,LOW);
}
else
{
printf("输入有误\n");
}
}
return 0;
}
代码运行结果,当我们编译运行这个代码时,我们可以通过输入1或者0,控制GPIO.7口的电平进而控制继电器的开关
引脚状态如下:
实现了一个寄存器的控制后,当我们拿到一个寄存器组的时候,也是一样的使用了。
超声波测距实验
HC-SR04超声波模块
超声波测距模块是根据超声波遇障碍反射的原理进行测距的,能够发送超声波、接收超声波并通过处理,输出一段和发送与接收间隔时间相同的高电平信号,是常用的测距模块之一。HC-SR04是最常用的超声波测距模块之一,HC-SR04超声波模块可提供2cm~400cm的非接触式距离感测功能,测距精度可达3mm,工作电压为5V;内部模块包括超声波发射器、接收器与控制电路。
超声波测距原理
让它发送波:给Trig端口至少10us的高电平
开始发送波:Echo信号由低电平跳转到高电平
接收返回波:Echo信号由高电平跳转回低电平
计算时间:Echo引脚维持高电平的时间!
开始发送波,启动定时器,接收到返回波,停止计时器
计算距离:测试距离=(高电平时间*声速(340m/s))/2
时序图
代码实现
时间函数gettimeofday()
在c语言中,可以使用函数gettimeofday(),获取到精确的时间。他的精度可以到微妙,是c语言的标准库
函数原型
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tv,
struct timezone *_Nullable restrict tz);
int settimeofday(const struct timeval *tv,
const struct timezone *_Nullable tz);
在man手册中的解释如下:
The tv argument is a struct timeval (as specified in <sys/time.h>):
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
and gives the number of seconds and microseconds since the Epoch (see time(2)).
The tz argument is a struct timezone:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中
在gettimeofday()函数中tv或者tz都可以为空。如果为空则就不返回其对应的结构体。
函数执行成功后返回0,失败后返回-1,错误代码存于errno中。
使用方法
- 使用gettimeofday计算代码执行时长:
#include <sys/time.h>
struct timeval start,end;
//timezone 参数若不使用则传入NULL即可.
gettimeofday(&start, NULL );
//…executing…
gettimeofday(&end, NULL );
double timeuse = ( end.tv_sec - start.tv_sec ) + (end.tv_usec - start.tv_usec)/1000000.0;
printf("time=%f\n",timeuse);
- 获取当前时间
#include <sys/time.h>
#include <time.h>
struct timeval now_time;
struct tm *p;
gettimeofday(&now_time, NULL);
p = gmtime(&now_time.tv_sec); //借助强转,实际上now_time.tv_sec就是unix时间戳
printf("%d-%02d-%02d %02dh:%02dm:%02ds:%ldus\n", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, 8 + p->tm_hour, p->tm_min, p->tm_sec, now_time.tv_usec);
测距代码的实现
硬件接线
启动流程图
源代码
hcse04.c
#include "hcsr04.h"
#include <stdio.h>
#include <wiringPi.h>
#include <sys/time.h>
#define TRIG 0
#define ECHO 1
void ultraInit(void)
{
pinMode(TRIG,OUTPUT);
pinMode(ECHO,INPUT);
}
float disMeasure(void)
{
struct timeval tv1,tv2;
float dis = 0.0;
int differTimeus = 0;
digitalWrite(TRIG,LOW);
delayMicroseconds(2);
digitalWrite(TRIG,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG,LOW);
while(digitalRead(ECHO) != 1);
// 开始计时
gettimeofday(&tv1,NULL);
while(digitalRead(ECHO) != 0);
gettimeofday(&tv2,NULL);
differTimeus = tv2.tv_sec * 1000000 + tv2.tv_usec - tv1.tv_sec * 1000000 - tv1.tv_usec;
dis = (float)differTimeus * 0.034 / 2;
return dis;
}
hcsr04.h
#define TRIG 0
#define ECHO 1
void ultraInit(void);
float disMeasure(void);
main.c
#include "hcsr04.h"
#include <stdio.h>
#include <wiringPi.h>
#include <time.h>
int main()
{
float dis;
if(wiringPiSetup() == -1)
{
printf("setup WiringPi faild\n");
return -1;
}
ultraInit();
while(1)
{
dis = disMeasure();
printf("距离:%.2f cm\n",dis);
delay(500);
}
return 0;
}