目录
一、WiringPi外设SDK安装
git clone https://github.com/orangepi-xunlong/wiringOP //下载源码
cd wiringOP //进入文件夹
sudo ./build clean //删除编译信息
sudo ./build //编译
通过windows浏览器打开 https://github.com/orangepi-xunlong/wiringOP
下载压缩包
把压缩包通过xtrem传到开发板
解压 unzip xxx.zip
cd xxx
sudo ./build
gpio readall
验证指令:gpio readall
如下方所示,外设库就完成安装了
二、蜂鸣器开发
原理:基本IO口的应用
1. 蜂鸣器配合时间函数开发:
1 #include <stdio.h>
2 #include <wiringPi.h>
3 #include <unistd.h>
4
5 #define BEEP 0 //设置0脚为控制引脚
6
7 int main (void)
8 {
9
10 wiringPiSetup () ;//初始化wiringP库
11 pinMode (BEEP, OUTPUT) ;//设置IO口的输入输出,输出
12 while(1){
13 usleep(500000);
14 digitalWrite (BEEP, LOW) ;//设置IO口输出低电平,蜂鸣器响
15 usleep(500000);
16 digitalWrite (BEEP, HIGH) ;//设置IO口输出高电平,蜂鸣器不响
17 }
18
19 return 0;
20 }
三、超声波测距模块开发
1. 原理说明
超声波测距模块是用来测量距离的一种产品,通过发送和接收超声波,利用时间差和声音传播素的,计算出模块到前方障碍物的距离。
型号:HC-SR04
过程分析:
- 怎么让它发送波
Trig ,给Trig端口至少10us的高电平
- 怎么知道它开始发了
Echo信号,由低电平跳转到高电平,表示开始发送波
- 怎么知道接收了返回波
Echo,由高电平跳转回低电平,表示波回来了
- 怎么算时间
Echo引脚维持高电平的时间!
波发出去的那一下,开始启动定时器
波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
- 怎么算距离
距离 = 速度 (340m/s)* 时间/2
时序图:
2. 时间函数
函数原型
#include <sys/time.h>
int gettimeofday(struct timeval *tv,struct timezone *tz)
gettimeofday()会把目前的时间用tv结构体返回,当时时区的信息则放到tz所指的结构中
struct timeval
{
long tv_sec;//秒
long tv_usec;//微秒
}
测试代码:
//计算程序在当前环境中数数10万次耗时多少
#include <stdio.h>
#include <sys/time.h>
//int gettimeofday(struct timeval *tv,struct timezone *tz)
void cntTest()
{
int i,j;
for(i=0;i<100;i++)
for(j=0;j<1000;j++);
}
int main(){
struct timeval start;
struct timeval end;
gettimeofday(&start, NULL);
cntTest();
gettimeofday(&end, NULL);
long diffTime = 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
printf("difTime = %ld\n",diffTime);
return 0;
}
3. 超声波测距代码实现
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <sys/time.h>
#include <unistd.h>
#define Trig 0
#define Echo 1
double getDistance()
{
double dis;
struct timeval start;
struct timeval end;
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
digitalWrite(Trig, LOW);
usleep(5);
digitalWrite(Trig, HIGH);
usleep(10);
digitalWrite(Trig, LOW);
while(!digitalRead(Echo));
gettimeofday(&start, NULL);
while(digitalRead(Echo));
gettimeofday(&end, NULL);
long diffTime = 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
dis = (double)diffTime/1000000 * 34000 / 2;
return dis;
}
int main()
{
double dis;
if(wiringPiSetup() == -1){
fprintf(stderr,"%s","initwiringPi error");
exit(-1);
}
while(1){
dis = getDistance();
printf("dis = %lf\n",dis);
usleep(500000);
}
return 0;
}
四、sg90舵机开发
1. 舵机基本介绍
如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制。
用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等
常见的有0-90°、0-180°、0-360°
怎么控制舵机
向黄色信号线“灌入”PWM信号。
- PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右
数据:
1.0ms------------45度; 5.0% 对应函数中占空比为500
1.5ms------------90度; 7.5% 对应函数中占空比为750
2.0ms-----------135度; 10.0% 对应函数中占空比为1000
2.5ms-----------180度; 12.5% 对应函数中占空比为1250
2. Linux定时器
分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号。从而实现定时器。
先看itimerval的结构体
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
it_interval:计时器的初始值,一般基于这个初始值来加或减,看控制参数的配置
it_value:程序跑到这里多久后,启动定时器
struct timeval
{
long tv_sec;//秒
long tv_usec;//微秒
}
int setitimer(int which,const struct itimerval *new_value,struct itimerval *old_value);
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
which:三种类型
ITIMER_REAL //数值为0,计时器的值真实递减,发送的信号是SIGALARM。
ITIMER_VIRTUAL //数值为1,进程指向时递减计时器的值,发送的信号是SIGPROF。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号时SIGPROF。
很明显,这边需要捕获对应的信号进行逻辑相关处理 signal(SIGALRM,signal_handler);
返回说明:
成功执行时,返回0。失败返回-1
代码实现每隔1s打印hello:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
static int i = 0;
void signal_handler(int signum)
{
i++;
if(i == 2000){
printf("hello\n");
i = 0;
}
}
int main()
{
struct itimerval itv;
//设置定时器值的定时时间
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500;
//设置定时器开始定时的时间
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 1;
//设置定时器的工作方式
if(setitimer(ITIMER_REAL, &itv, NULL) == -1){
perror("error");
exit(-1);
}
//设置信号处理函数
signal(SIGALRM, signal_handler);
while(1);
return 0;
}
3. Linux定时器控制舵机实战开发:
通过键盘输入控制舵机角度:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>
#define SG90Pin 5
static int i = 0;
int jd;
void signal_handler(int signum)
{
if(i <= jd){
digitalWrite(SG90Pin, HIGH);
}else{
digitalWrite(SG90Pin, LOW);
}
if(i == 40){
i = 0;
}
i++;
}
int main()
{
struct itimerval itv;
jd = 0;
wiringPiSetup();
pinMode(SG90Pin, OUTPUT);
//设置定时器值的定时时间
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500;
//设置定时器开始定时的时间
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 1;
//设置定时器的工作方式
if(setitimer(ITIMER_REAL, &itv, NULL) == -1){
perror("error");
exit(-1);
}
//设置信号处理函数
signal(SIGALRM, signal_handler);
while(1){
printf("input jd: 1-0 2-45 3-90 4-135 5-180\n");
scanf("%d",&jd);
}
return 0;
}
五、OLED屏幕应用-IIC协议
1. 准备过程
- 启动 linux 系统后,先确认一下 i2c-3 的设备节点
从命令运行结果可以观察到系统支持 I2C-3 和 I2C-5 的驱动,而H616的外设我们看到只有一个IIC接口,用的是IIC-3
Linux一切皆文件,每个硬件设备“对应”一个文件,由驱动程序提供映射
- 开始测试i2c,首先安装i2c-tools
sudo apt-get install i2c-tools
2. 显示自己想要的字符:
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "oled.h"
#include "font.h"
int oled_show(struct display_info *disp) {
int i;
char buf[100];
oled_putstrto(disp, 0, 10, "Welcome to My HomeAssitant");
disp->font = font1;
oled_putstrto(disp, 0, 20, " ---This is menu--- ");
disp->font = font1;
oled_send_buffer(disp);
return 0;
}
void show_error(int err, int add) {
//const gchar* errmsg;
//errmsg = g_strerror(errno);
printf("\nERROR: %i, %i\n\n", err, add);
//printf("\nERROR\n");
}
void show_usage(char *progname) {
printf("\nUsage:\n%s <I2C bus device node >\n", progname);
}
int main(int argc, char **argv) {
int e;
char filename[32];
struct display_info disp;
if (argc < 2) {
show_usage(argv[0]);
return -1;
}
memset(&disp, 0, sizeof(disp));
sprintf(filename, "%s", argv[1]);
disp.address = OLED_I2C_ADDR;
disp.font = font2;
e = oled_open(&disp, filename);
e = oled_init(&disp);
oled_show(&disp);
return 0;
}
六、Linux串口开发
1. wiringPi的串口开发及优化
通过拷贝 / w i r i n g O P / e x a m p l e s / s e r i a l T e s t . c /wiringOP/examples/serialTest.c /wiringOP/examples/serialTest.c 文件下的 w i r i n g P i wiringPi wiringPi 有关串口的例程,我们发现代码有些复杂,通过利用收发的多线程运行,进行了修改和优化,实现了串口收发通信:
这是修改前的代码:
/*
* serialTest.c:
* Very simple program to test the serial port. Expects
* the port to be looped back to itself
*
* Copyright (c) 2012-2013 Gordon Henderson. <projects@drogon.net>
***********************************************************************
* This file is part of wiringPi:
* https://projects.drogon.net/raspberry-pi/wiringpi/
*
* wiringPi is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* wiringPi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with wiringPi. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <wiringPi.h>
#include <wiringSerial.h>
int main ()
{
int fd ;
int count ;
unsigned int nextTime ;
if ((fd = serialOpen ("/dev/ttyS2", 115200)) < 0)
{
fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
return 1 ;
}
if (wiringPiSetup () == -1)
{
fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
return 1 ;
}
nextTime = millis () + 300 ;
for (count = 0 ; count < 256 ; )
{
if (millis () > nextTime)
{
printf ("\nOut: %3d: ", count) ;
fflush (stdout) ;
serialPutchar (fd, count) ;
nextTime += 300 ;
++count ;
}
delay (3) ;
while (serialDataAvail (fd))
{
printf (" -> %3d", serialGetchar (fd)) ;
fflush (stdout) ;
}
}
printf ("\n") ;
return 0 ;
}
这是自己修改优化后的代码:
/*
* serialTest.c:
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>
int fd ;
void* SendHandler()
{
char *sendBuf;
sendBuf = (char *)malloc(32*sizeof(char));
while(1){
memset(sendBuf, '\0', 32);
scanf("%s",sendBuf);
while(*sendBuf){
serialPutchar(fd, *sendBuf++);
}
}
}
void* RevHandler()
{
while(1){
while (serialDataAvail (fd))
{
printf ("%c", serialGetchar (fd)) ;
fflush (stdout) ;
}
}
}
int main ()
{
int count ;
unsigned int nextTime ;
pthread_t idSend;
pthread_t idRev;
if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0)
{
fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
return 1 ;
}
pthread_create(&idSend, NULL, SendHandler, NULL);
pthread_create(&idRev, NULL, RevHandler, NULL);
if (wiringPiSetup () == -1)
{
fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
return 1 ;
}
while(1);
return 0 ;
}
运行结果:
2. 不用wiringPi库自己实现串口通信
上面我们通过修改wiringPi库实现了简单版的串口通信代码,下面我们通过对wiringPi库串口代码的理解,自己实现串口通信。
主程序:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include "uartTool.h"
#include "wiringSerial.h"
int fd;
void* readSerial()
{
char buffer[32];
while(1){
memset(buffer, '\0', sizeof(buffer));
myserialGetstring(fd, buffer);
printf("GET->%s\n",buffer);
}
}
void* sendSerial()
{
char buffer[32];
while(1){
memset(buffer, '\0', sizeof(buffer));
scanf("%s",buffer);
myserialSendstring(fd, buffer);
}
}
int main(int argc, char **argv)
{
char deviceName[32] = {'\0'};
pthread_t readt;
pthread_t sendt;
if(argc < 2){
printf("uage:%s /dev/ttyS?\n",argv[0]);
return -1;
}
strcpy(deviceName, argv[1]);
if((fd = myserialOpen (deviceName, 115200)) == -1){
printf("open %s error\n",deviceName);
return -1;
}
pthread_create(&readt, NULL, readSerial, NULL);
pthread_create(&sendt, NULL, sendSerial, NULL);
while(1);
return 0;
}
头文件:
int myserialOpen (const char *device, const int baud);
void myserialSendstring (const int fd, const char *s);
int myserialGetstring (const int fd, char *buffer);
头文件中函数程序:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wiringSerial.h"
int myserialOpen (const char *device, const int baud)
{
struct termios options ;
speed_t myBaud ;
int status, fd ;
switch (baud)
{
case 9600: myBaud = B9600 ; break ;
case 115200: myBaud = B115200 ; break ;
}
if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
return -1 ;
fcntl (fd, F_SETFL, O_RDWR) ;
// Get and modify current options:
tcgetattr (fd, &options) ;
cfmakeraw (&options) ;
cfsetispeed (&options, myBaud) ;
cfsetospeed (&options, myBaud) ;
options.c_cflag |= (CLOCAL | CREAD) ;
options.c_cflag &= ~PARENB ;
options.c_cflag &= ~CSTOPB ;
options.c_cflag &= ~CSIZE ;
options.c_cflag |= CS8 ;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
options.c_oflag &= ~OPOST ;
options.c_cc [VMIN] = 0 ;
options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds)
tcsetattr (fd, TCSANOW, &options) ;
ioctl (fd, TIOCMGET, &status);
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;
ioctl (fd, TIOCMSET, &status);
usleep (10000) ; // 10mS
return fd ;
}
void myserialSendstring (const int fd, const char *s)
{
int ret;
ret = write (fd, s, strlen (s));
if (ret < 0)
printf("Serial Puts Error\n");
}
int myserialGetstring (const int fd, char *buffer)
{
int n_read;
n_read = read(fd, buffer, 32);
return n_read;
}
运行结果:
本次学习记录分享就到这里。
点滴知识靠积累。