树莓派wiringPi库详解,官方外设开发
文章目录
wiringPi是一个很棒的树莓派IO控制库,使用C语言开发,提供了丰富的接口:GPIO控制,中断,多线程,等等。
一、安装wiringPi库
有些树莓派镜像文件自带wiringPi库,所以无须安装,如果没有请自行参看下方安装
进入 wiringPi的github (https://git.drogon.net/?p=wiringPi;a=summary) 下载安装包。点击页面的第一个链接的右边的snapshot,下载安装压缩包。
然后进入安装包所在的目录执行以下命令:
tar xfz wiringPi-98bcb20.tar.gz //98bcb20为版本标号,可能不同
cd wiringPi-98bcb20
./build
查看是否安装了命令:
gpio -v
查看引脚命令:
gpio readall
二、wiringPi库API大全
在使用wiringPi库时,你需要包含头文件 #include <wiringPi.h>
。编译时都需要链接该库。
1.硬件初始化函数
使用wiringPi时,你必须在执行任何操作前初始化树莓派,否则程序不能正常工作。
可以调用下表函数之一进行初始化,它们都会返回一个int , 返回 -1 表示初始化失败。
int wiringPiSetup (void) | 返回:执行状态,-1表示失败 | 当使用这个函数初始化树莓派引脚时,程序使用的是wiringPi 引脚编号表。引脚的编号为 0~16,需要root权限 |
---|---|---|
int wiringPiSetupGpio (void) | 返回执行状态,-1表示失败 | 当使用这个函数初始化树莓派引脚时,程序中使用的是BCM GPIO 引脚编号表,需要root权限 |
wiringPiSetupPhys(void) | 不常用,不做介绍 | |
wiringPiSetupSys (void) | 不常用,不做介绍 |
2.通用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欧姆 |
3.时间控制函数
将执行流暂停 指定的微秒数(1000微秒 = 1毫秒 = 0.001秒)。
因为Linux本身是多线程的,所以实际暂停时间可能会长一些。参数是unsigned int 类型,最大延时时间可达71分钟
unsigned int millis (void) | 这个函数返回 一个 从你的程序执行 wiringPiSetup 初始化函数(或者wiringPiSetupGpio ) 到 当前时间 经过的 毫秒数。 返回类型是unsigned int,最大可记录 大约49天的毫秒时长。 |
---|---|
unsigned int micros (void) | 这个函数返回 一个 从你的程序执行 wiringPiSetup 初始化函数(或者wiringPiSetupGpio ) 到 当前时间 经过的 微秒数。 返回类型是unsigned int,最大可记录 大约71分钟的时长。 |
void delay (unsigned int howLong) | 将当前执行流暂停 指定的毫秒数。因为Linux本身是多线程的,所以实际暂停时间可能会长一些。参数是unsigned int 类型,最大延时时间可达49天 |
void delayMicroseconds (unsigned int howLong) | 将执行流暂停 指定的微秒数(1000微秒 = 1毫秒 = 0.001秒)。 因为Linux本身是多线程的,所以实际暂停时间可能会长一些。参数是unsigned int 类型,最大延时时间可达71分钟 |
4.串口通信
串口API
使用时需要包含头文件:#include <wiringSerial.h>
这个是Linux下的标准IO库函数,需要包含头文件#include <unistd.h>
当要接收的数据量过大时,wiringPi建议使用这个函数。
int serialOpen (char *device, int baud) | device:串口的地址,在Linux中就是设备所在的目录。 默认一般是"/dev/ttyAMA0",我的是这样的。 baud:波特率 返回:正常返回文件描述符,否则返回-1失败。 | 打开并初始串口 |
---|---|---|
void serialClose (int fd) | fd:文件描述符 | 关闭fd关联的串口 |
void serialPutchar (int fd, unsigned char c) | fd:文件描述符 c:要发送的数据 | 发送一个字节的数据到串口 |
void serialPuts (int fd, char *s) | fd:文件描述符 s:发送的字符串,字符串要以’\0’结尾 | 发送一个字符串到串口 |
void serialPrintf (int fd, char *message, …) | fd:文件描述符 message:格式化的字符串 | 像使用C语言中的printf一样发送数据到串口 |
int serialDataAvail (int fd) | fd:文件描述符 返回:串口缓存中已经接收的,可读取的字节数,-1代表错误 | 获取串口缓存中可用的字节数。 |
int serialGetchar (int fd) | fd:文件描述符 返回:读取到的字符 | 从串口读取一个字节数据返回。 如果串口缓存中没有可用的数据,则会等待10秒,如果10后还有没,返回-1 所以,在读取前,做好通过serialDataAvail判断下。 |
void serialFlush (int fd) | fd:文件描述符 | 刷新,清空串口缓冲中的所有可用的数据。 |
*size_t write (int fd,const void * buf,size_t count) | fd:文件描述符 buf:需要发送的数据缓存数组 count:发送buf中的前count个字节数据 返回:实际写入的字符数,错误返回-1 | 这个是Linux下的标准IO库函数,需要包含头文件#include <unistd.h> 当要发送到的数据量过大时,wiringPi建议使用这个函数。 |
*size_t read(int fd,void * buf ,size_t count); | fd:文件描述符 buf:接受的数据缓存的数组 count:接收的字节数. 返回:实际读取的字符数。 | 这个是Linux下的标准IO库函数,需要包含头文件#include <unistd.h> 当要接收的数据量过大时,wiringPi建议使用这个函数。 |
串口通信配置
也就是将调试接口修改为通信接口,同时也要关闭蓝牙功能。(仅供了解,推荐使用下面的多串口)
注意,目前搜到的大多数描述树莓派 4 串口的文章,大多数开头都是禁用下蓝牙,这个做法针对树莓派0-3 是必须的,因为本身串口不够用,但对树莓派 4 来说并不需要,因为有额外 4 个串口可以利用,默认配置好的两串口一个用于蓝牙(UART0)另一个是 miniUART 可以保留设置。此方面的文章大多都是一个流程,原因是参考的最初版本是树莓派 3 的设置;树莓派 4 的额外串口设置可以参考下面的多串口通信配置。
/* 修改 cmdline.txt文件 */
>cd /boot
>sudo vim cmdline.txt
删除【】之间的部分
dwc_otg.lpm_enable=0 【console=ttyAMA0,115200】 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
/*下列代码由于不同的固件库可能没有,则不用修改*,如果没有,完成上述之后重启即可/
/*修改 inittab文件 */
>cd /etc/
>sudo vim inittab
注释掉最后一行内容:,在前面加上 # 号
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
//重启
sudo reboot
树莓派的串口是设备名是:ttyAMA0
多串口通信配置
串口1:/dev/ttyS0 mini串口:mini串口的时钟源由cpu提供,会随主频的变化而变化,通信不稳定,但可以通过固定CPU主频来稳定通信。(很麻烦)
串口0:/dev/ttyAMA0 硬件串口有独立的时钟源,通信稳定,但默认分配成了蓝牙功能。可以通过禁用蓝牙等操作来使用这个串口。
- 展示所有串口命令
dtoverlay -a | grep uart
展示 pi4 中所有串口
pi@pi:~ $ dtoverlay -a | grep uart
midi-uart0
midi-uart1
midi-uart2
midi-uart3
midi-uart4
midi-uart5
miniuart-bt
qca7000-uart0
uart0
uart1
uart2
uart3
uart4
uart5
- 查看特定串口信息
dtoverlay -h uart2
pi@raspberrypi:~ $ dtoverlay -h uart2
Name: uart2
Info: Enable uart 2 on GPIOs 0-3
Usage: dtoverlay=uart2,<param>
Params: ctsrts Enable CTS/RTS on GPIOs 2-3 (default off)
相关信息会展现 GPIOs 与新的 UART 串口的分配:0-3 对应 UART2, 4-7 对应 UART3,8-11 对应 UART 4,以及 12-15 对应 GUIO 5
树莓派0、1、2、3 | UART0(PL011) UART1(mini UART) | 由于本身串口不够用,故需要禁用下蓝牙 (默认配置好的两串口一个用于蓝牙(UART0)另一个是miniUART) |
---|---|---|
树莓派4B | UART0(PL011) UART1(mini UART) UART2(PL011) UART3(PL011) UART4(PL011) UART5(PL011) | 对树莓派4B来说并不需要,因为有额外 4 个串口可以利用。 |
3. 配置开启串口 UART2-5
sudo vi /boot/firmware/config.txt
或者:具体看目录下有哪个
sudo vi /boot/config.txt
在结尾添加:
dtoverlay=uart2
dtoverlay=uart3
dtoverlay=uart4
dtoverlay=uart5
重启后查看是否生效:
ls /dev/ttyAMA*
观察到:
pi@pi:~ $ ls /dev/ttyAMA*
/dev/ttyAMA2 /dev/ttyAMA3 /dev/ttyAMA4 /dev/ttyAMA5
下面给出各 UART 串口与 GPIO 对应关系,一般使用建议UART2-5。
引脚号为wiringPi编码 | TX | RX | 所指向的设备节点 |
---|---|---|---|
UART0 | 15 | 16 | -> ttyAMA0 |
UART2 | 30 | 31 | -> ttyAMA1 |
UART3 | 7 | 21 | -> ttyAMA2 |
UART4 | 10 | 13 | -> ttyAMA3 |
UART5 | 26 | 23 | -> ttyAMA4 |
注:UART0 对应的 ttyAMA0,UART1 对应的 ttyS0,UART2 到 UART5 对应的 ttyAMA1 到 ttyAMA4。
串口自发自收测试
现在我们先测试 UART2 是否启用成功,比较简单的测试方式是将其 TXD 和 RXD 相连,自发自收。
根据上方对应关系,UART2 对应 TXD2 和 RXD2,对应 30和 31,对应 ttyAMA1
如果跟下方一样就证明已经开启
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> ted = serial.Serial(port="/dev/ttyAMA1", baudrate=9600)
>>> ted.write("Hello World".encode("gbk"))
11
>>> ted.read(11)
b'Hello World'
>>>
同理也可以测试其他的串口
pi@raspberrypi:~ $ python3 Python 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import serial >>> ted = serial.Serial(port="/dev/ttyAMA1", baudrate=9600) >>> ted.write("Hello World".encode("gbk")) 11 >>> ted.read(11) b'Hello World' >>> ted3 = serial.Serial(port="/dev/ttyAMA2", baudrate=9600) >>> ted3.write("Hello No.3".encode("gbk")) 10 >>> ted3.read(10) b'Hello No.3' >>> ted4 = serial.Serial(port="/dev/ttyAMA3", baudrate=9600) >>> ted4.write("Hello No.4".encode("gbk")) 10 >>> ted4.read(10) b'Hello No.4' >>> ted5 = serial.Serial(port="/dev/ttyAMA4", baudrate=9600) >>> ted5.write("Hello No.5".encode("gbk")) 10 >>> ted5.read(10) b'Hello No.5' >>>
串口间通信测试
测试 UART2 和 UART3 间的通信
wiringPi编码 | ||
---|---|---|
UART2 | TXD2 (30) | RXD2 (31) |
UART3 | RXD3 (21) | TXD3 (7) |
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import serial
>>> ted = serial.Serial(port="/dev/ttyAMA1", baudrate=9600)
>>> ted3 = serial.Serial(port="/dev/ttyAMA2", baudrate=9600)
>>> ted.write("Msg from UART2...".encode("gbk"))
17
>>> ted3.read(17)
b'Msg from UART2...'
>>> ted3.write("Msg from UART3...".encode("gbk"))
17
>>> ted.read(17)
b'Msg from UART3...'
>>>
OK 挺顺利,UART2 和 UART3 间通信正常。
测试代码
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd;
char cmd;
char *str;
str=(char*)malloc(1024);
fd=serialOpen("/dev/ttyAMA1",115200); //打开并初始串口
if(fd == -1){
printf("uart error\n");
}
if(wiringPiSetup() == -1){ //初始化树莓派引脚
printf("init wiringPi error!\n");
return -1;
}
while(1){
// serialPutchar(fd,'c');
// serialPuts(fd,"aaaaaaaaa\r\n");
while(serialDataAvail(fd)<=0); //获取串口缓存中可用的字节数
cmd = serialGetchar(fd); //从串口读取一个字节数据返回
// memset(str,0,1024);
// sprintf(str,"get cmd:%c\r\n",cmd);
// serialPuts(fd,str); //发送一个字符串到串口
printf("get data:cmd=%c\n",cmd);
serialPrintf (fd, "get cmd:%c\r\n", cmd); //发送数据到串口
if(cmd=='q'){
serialPuts(fd,"quit\r\n");
printf("quit\n");
break;
}
// delayMicroseconds(1000000);
}
free(str);
serialClose(fd); //关闭fd关联的串口
return 0;
}
编译:
gcc demo.c -lwiringPi
5.中断
很少使用
6.多线程
使用linux C库里面的多线程
7.软件PWM
树莓派硬件上支持的PWM输出的引脚有限,为了突破这个限制,wiringPi提供了软件实现的PWM输出API。
需要包含头文件:#include <softPwm.h>
编译时需要添pthread库链接 -lpthread
pin:用来作为软件PWM输出的引脚
initalValue:引脚输出的初始值
pwmRange:PWM值的范围上限
建议使用100.
返回:0表示成功。
int softPwmCreate (int pin, int initialValue, int pwmRange) | pin:用来作为软件PWM输出的引脚 initalValue:引脚输出的初始值 pwmRange:PWM值的范围上限 建议使用100. 返回:0表示成功。 |
---|
使用一个指定的pin引脚创建一个模拟的PWM输出引脚 void softPwmWrite (int pin, int value)
pin:通过softPwmCreate创建的引脚
value:PWM引脚输出的值
更新引脚输出的PWM值
其他
https://www.cnblogs.com/lulipro/p/5992172.html