【香橙派系列教程】(三)常用外设开发

【三】常用外设开发

1. wiringPi外设SDK安装

//直接在Linux底下安装
git clone https://github.com/orangepi-xunlong/wiringOP //下载源码
cd wiringOP //进入文件夹
sudo ./build clean //清除编译信息
sudo ./build //编译

//如果安装不成功,那么就在windows机上先安装下来再传到Linux上
通过windows浏览器打开https://github.com/orangepi-xunlong/wiringOP
下载压缩包
把压缩包通过MobaXterm_Personal传到开发板
解压 unzip xxx.zip
cd xxx
sudo ./build
gpio readall   //查看所有引脚状态

在这里插入图片描述

验证指令: gpio readall

如下方所示,外设库就完成安装了

在这里插入图片描述

物理引脚对应图

在这里插入图片描述

//官方给的例程路径,进入这个里面我们可以看到有很多例程
cd wiringOP/examples

在这里插入图片描述

请添加图片描述

2.蜂鸣器BB响

1.怎么将其他文件夹里面的文件复制到目前的文件夹?

命令:cd - 返回上次进入的目录

cp ../wiringOP/examples/blink.c A
//点点必须加,然后加想要复制的那个文件的路径,  其次是复制为文件重命名的名字

在这里插入图片描述

2.修改vim编辑器的tab缩进,显示行数

vim编辑器
输入命令:sudo vi /etc/vim/vimrc

sudo vi /etc/vim/vimrc

在这里插入图片描述

在此位置设置

//注意输入的格式,=前后不要留空格
set tabstop=4    //输入按下tab缩进4个
set shiftwidth=4	//批量缩进4个
set nu	//显示行数

在这里插入图片描述

3.蜂鸣器配合时间函数开发

//beep.c
#include <stdio.h>
#include <wiringPi.h>
#include <unistd.h>
#define BEEP 0   //设置针脚0为蜂鸣器的控制引脚
int main (void)
{
    wiringPiSetup () ;//初始化wiringPi库
    pinMode (BEEP, OUTPUT) ;//设置IO口的输入输出,输出
    while(1){
        //sleep(1);
        usleep(100000);
        digitalWrite (BEEP, HIGH) ; //设置IO口输出低电平,蜂鸣器响
        //sleep(1);
        usleep(100000);
        digitalWrite (BEEP, LOW) ;  //设置IO口输出低电平,蜂鸣器响
   }
    return 0;
}

4.开始编译

标准流程:1.编译:gcc 文件名.c
		 2.运行:./a.out

这个时候如果我们直接编译,它会报错,因为这个是香橙派官方提供的库,编译需要链接它的库,如何查看?

//输入命令查看官方Makefile
vi ./wiringOP/examples/Makefile

在这里插入图片描述

//需要链接的库
 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt

在这里插入图片描述

小插曲:shell脚本

那么问题来了?每次编译都需要链接到库,很麻烦,有没有一种方法可以简单一些?有,我们可以写一个shell脚本

1.创建一个脚本

vi bulid.sh

2.输入内容

gcc beep.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt

在这里插入图片描述

3.添加权限

//这个时候shell脚本是没有权限的,需要给它加一个可执行的权限(注意它的颜色)
chmod +x bulid.sh

在这里插入图片描述

4.运行shell脚本

在这里插入图片描述

5.shell脚本传参优化

对于上面我们做的脚本,编译的都是写死的文件,如果我想换成别的文件,就很麻烦了,那么我们就需要把这个换成参数传递

//1.之前的
gcc beep.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
//2.传参后
shell脚本传参数机制:
美元符号就是参数
echo $0
echo $1
gcc $1 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt   

在这里插入图片描述

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……

实例
以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh 1 2 3

执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

另外,还有几个特殊字符用来处理参数:

参数处理 说明

$#	传递到脚本的参数个数
$*	以一个单字符串显示所有向脚本传递的参数。
     如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$	脚本运行的当前进程ID号
$!	后台运行的最后一个进程的ID号
$@	与$*相同,但是使用时加引号,并在引号中返回每个参数。
    如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-	显示Shell使用的当前选项,与set命令功能相同。
$?	显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

示例:

#!/bin/bash
echo "Shell 传递参数实例!";
echo "第一个参数为:$1";
echo "参数个数为:$#";
echo "传递的参数作为一个字符串显示:$*";

执行脚本,输出结果如下所示:

$ chmod +x test.sh 
$ ./test.sh 1 2 3
第一个参数为:1
参数个数为:3
传递的参数作为一个字符串显示:1 2 3

3.超声波测距

1. 测距原理基本说明

超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离

在这里插入图片描述

型号:HC-SR04

接线参考:模块除了两个电源引脚外,还有Trig,Echo引脚

在这里插入图片描述

  • 怎么让它发波

    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;/*微妙*/
};

冷知识:Linux系统时间=GMT时间+时区偏移
在Linux系统中,系统时间以从1970年1月1日00:00:00(也被称为UNIX纪元)开始经过的秒数表示。而GMT时间是以格林威治标准时间为基准的世界标准时间。
时区偏移是指本地时间相对于CMI时间的差异。不同的时区有不同的时区偏移值,例如中国位于东八区,时区偏移为+8小时。
所以,要将GMT时间转换为Linux系统时间,只需将GMT时间加上相应的时区偏移即可。注意:在Linux系统中,时间是以UTC(协调世界时)的方式存储和处理的,与GMT时间略有不同。但由于时区偏移相同,所以在实际计算中可以将GMT时间视为UTC时间。

测试代码

香橙派在Linux下数数10万次会用时多少

//计算程序在当前环境中数数10万次耗时多少微秒
#include <sys/time.h>
#include <stdio.h>
//int gettimeofday(struct timeval *tv,struct timezone *tz )
void mydelay()
{
    int i,j;
    for(i=0;i<100;i++){
        for(j=0;j<1000;j++);
   }
}
int main()
{
    struct timeval startTime;
    struct timeval stopTime;
    gettimeofday(&startTime,NULL);
    mydelay();
    gettimeofday(&stopTime,NULL);
    long diffTime = 1000000*(stopTime.tv_sec - startTime.tv_sec) + 
(stopTime.tv_usec - startTime.tv_usec);//它的秒和微秒分别存在不同的的变量中,要全部加起来
    printf("全志H6的Linux数100000耗时%ldus\n",diffTime);
    return 0;
}

3.结合超声波编写代码

#include <stdio.h>
#include <sys/time.h>//时间函数
#include <wiringPi.h>//官方外设库
#include <stdlib.h>	//exit()
#include <unistd.h>//usleep()
#define Trig 0
#define Echo 1
double getDistance()
{
    double dis;
    struct timeval start;
    struct timeval stop;
    pinMode(Trig, OUTPUT);
    pinMode(Echo, INPUT);
    digitalWrite(Trig ,LOW);
    usleep(5);
    digitalWrite(Trig ,HIGH);
    usleep(10);
    digitalWrite(Trig ,LOW);
    /*above init CSB*/
    while(!digitalRead(Echo));
    gettimeofday(&start,NULL);
    while(digitalRead(Echo));
    gettimeofday(&stop,NULL);
    long diffTime = 1000000*(stop.tv_sec-start.tv_sec)+(stop.tv_usec - start.tv_usec);
    printf("diffTime = %ld\n",diffTime);
    dis = (double)diffTime/1000000 * 34000 / 2;
    return dis;
}
int main()
{
    double dis;

    if(wiringPiSetup() == -1)//官方库初始化错误
    {
        fprintf(stderr,"%s","initWringPi error");
        exit(-1);
   }
    while(1){
        dis = getDistance();
        printf("dis = %lf\n",dis);
        usleep(500000);
   }
    return 0;
}

补充上面fprintf( )函数的用法:

fprintf其作用是格式化打印,也叫格式化输出,可以指定输出到一个流文件中,即向输出流中写入数据。

//函数原型
int fprintf(FILE *stream, const char *format, ...)

参数说明:
stream:输出流FILE *。
format :是字符串,是要被写入到流 stream 中的文本。文本可以是format标签可被随后的附加参数中指定的值替换。
argument:附加的参数列表

也就是这样的形式:fprintf(FILE*,%s%d”,“ADC”,66” );

其中Linux的输出数据流stream提供了5种标准的流:
1.stdin 标准输入
2.stdout 标准输出
3.stderr 标准错误
4.stdprn 标准打印机
5.stdaux 标准串行设备

4.舵机开发-PWM波

1.舵机基本介绍

如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等
常见的有0-90°、0-180°、0-360°

在这里插入图片描述

怎么控制转角?
向黄色信号线“灌入”PWM信号。
PWM波的频率不能太高,50hz,即周期=1/频率=1/50=0.02s,20ms左右数据:
不同的PWM波形对应不同的旋转角度,以20ms为周期,50hz为频率的PWM波

在这里插入图片描述

定时器需要定时20ms,关心的单位0.5ms, 20ms = 0.5ms * 40:

切分成40份

2.Linux定时器

分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号。从而实现定时器。
先看itimerval的结构体

struct itimerval
{
    /* Value to put into `it_value' when the timer expires. */
    //程序跑到这之后,多久启动定时器 
    struct timeval it_interval;
    /* Time to the next timer expiration. */
    //计时器的初始值,一般基于这个初始值来加或者来减,看控制函数的参数配置(设定开始生效,启动定时器的时间)
    struct timeval it_value;
};

    struct timeval
    {
        __time_t 		tv_sec; /* Seconds. */
        __suseconds_t 	tv_usec; /* Microseconds. */
    }; 

/********************************************************************************/
int setitimer (__itimer_which_t __which,
               const struct itimerval *__restrict __new,
               struct itimerval *__restrict __old)
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
//1.参数1
which:三种类型
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
//2.参数2
对应结构体时间戳的地址
//3.参数3
NULL。很明显,这边需要捕获对应的信号进行逻辑相关处理 signal(SIGALRM,signal_handler);
//返回值
成功执行时,返回0。失败返回-1

代码实现

/*该代码实现的功能是: 1s后开启定时器,然后每隔1s向终端打印hello。*/
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
static int i;
void signal_handler(int signum)
{
    i++;
    //2000*0.5ms=1000ms=1s
    if(i == 2000){
        printf("hello\n");
        i = 0;
    }
}
int main()
{
    struct itimerval itv;

    //设定定时时间,定时500us=0.5ms
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;
    //设定开始生效,启动定时器的时间
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;
    //设定定时方式
    if( -1 == setitimer(ITIMER_REAL, &itv, NULL)){
        perror("error");
        exit(-1);
    }
//每次定时器爆表,会产生一些信号,通过信号函数,捕捉这些信号,每次产生信号就捕捉,进到信号中断处理函数里处理
    //信号处理
    signal(SIGALRM,signal_handler);
    while(1);
    return 0;
}

这种方法需要注意的是,一个进程只能创建一个定时器

3.舵机开发

SG90编程实现:键盘输入不同的值,让舵机转动,软件PWM实现

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>
#define SG90Pin 5
int jd;
static int i = 0;
void signal_handler(int signum)
{
    if(i <= jd){
        digitalWrite(SG90Pin, HIGH);
    }else{
        digitalWrite(SG90Pin, LOW);
    }
    //40*0.5ms=20ms
    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 = 0;
    //设定定时方式
    if( -1 == setitimer(ITIMER_REAL, &itv, NULL)){
        perror("error");
        exit(-1);
    }
    //信号处理
    signal(SIGALRM,signal_handler);
    while(1){
        printf("input jd: 1-0 2-45 3-90 4-135 \n");
        scanf("%d",&jd);
    }
    return 0;
}

5.OLED屏应用-IIC协议

1.OLED屏幕

具体对OLED屏幕的使用,在这里不做详解

在这里插入图片描述

2 .Orangepi的IIC接口

  • 由 26pin 的原理图可知, Orange Pi Zero 2 可用的 i2c 为 i2c3

在这里插入图片描述

/dev:设备驱动文件目录

  • 启动 linux 系统后, 先确认下/dev 下存在 i2c-3 的设备节点
    从命令运行结果能观察到系统支持i2C-3i2C-5的驱动,而H616的外设我们看到只有一个IIC接口,用的是i2C-3
    Linux一切皆文件,每个硬件设备“对应”一个文件,由驱动程序提供映射

在这里插入图片描述
在这里插入图片描述

  • 开始测试 i2c, 首先安装 i2c-tools
sudo apt-get -y install i2c-tools

在这里插入图片描述

//测试能查看iic上挂载设备地址的工具是否有效
sudo i2cdetect -y 3

因为:Linux一切皆文件,每个硬件设备“对应”一个文件,由驱动程序提供映射

我们根据原理图,把OLED屏幕插上,此时的3c就是挂载在iic上的oled屏幕的地址。
在这里插入图片描述

3.Oled功能代码阅读

1.首先将官方的例程代码复制一份到我们自己的文件夹中

//将oled例程代码复制到当前工作目录
cp ../wiringOP/examples/oled_demo.c .

不能把官方代码破坏了,嘻嘻嘻!

在这里插入图片描述

贴一下官方例程OLED的demo,查看起来很乱,可以用到source insight工具来查看例程代码,会很方便。

/*
 * Copyright (c) 2015, Vladimir Komendantskiy
 * MIT License
 *
 * SSD1306 demo of block and font drawing.
 */

//
// fixed for OrangePiZero by HypHop
//

#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_demo(struct display_info *disp) {
	int i;
	char buf[100];

	//putstrto(disp, 0, 0, "Spnd spd  2468 rpm");
	//	oled_putstrto(disp, 0, 9+1, "Spnd cur  0.46 A");
	oled_putstrto(disp, 0, 9+1, "Welcome       to");
	disp->font = font1;
	//	oled_putstrto(disp, 0, 18+2, "Spnd tmp    53 C");
	oled_putstrto(disp, 0, 18+2, "----OrangePi----");
	disp->font = font2;
	//	oled_putstrto(disp, 0, 27+3, "DrvX tmp    64 C");
	oled_putstrto(disp, 0, 27+3, "This is 0.96OLED");
	oled_putstrto(disp, 0, 36+4, "");
	oled_putstrto(disp, 0, 45+5, "");
	disp->font = font1;
	//	oled_putstrto(disp, 0, 54, "Total cur  2.36 A");
	oled_putstrto(disp, 0, 54, "*****************");
	oled_send_buffer(disp);

	disp->font = font3;
	for (i=0; i<100; i++) {
		sprintf(buf, "Spnd spd  %d rpm", i);
		oled_putstrto(disp, 0, 0, buf);
		oled_putstrto(disp, 135-i, 36+4, "===");
		oled_putstrto(disp, 100, 0+i/2, ".");
		oled_send_buffer(disp);
	}
	//oled_putpixel(disp, 60, 45);
	//oled_putstr(disp, 1, "hello");

	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);

	if (e < 0) {
		show_error(1, e);
	} else {
		e = oled_init(&disp);
	if (e < 0) {
		show_error(2, e);
	} else {
		printf("---------start--------\n");
		if (oled_demo(&disp) < 0)
			show_error(3, 777);
			printf("----------end---------\n");
		}
	}

	return 0;
}

就这样我们先烧录一下官方的代码来玩一下

编译:./bulid.sh oled_demo.c

运行sudo ./a.out /dev/i2c-3

这里执行代码的时候要加上驱动 /dev/i2c-3

为什么要加?

因为代码里需要一个这么参数,这个参数就需要加上驱动目录/dev/i2C -3

在这里插入图片描述

实验现象:

在这里插入图片描述

4.基于官方库修改OLED代码

**功能描述:**显示font1,2,3字号的"Welcome to linux "

#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_demo(struct display_info *disp) {
	int i;
	char buf[100];

    disp->font = font1;
	oled_putstrto(disp, 0, 9+1, "Welcome to linux");
	
	disp->font = font2;
	oled_putstrto(disp, 0, 18+2, "Welcome to linux");
	
	disp->font = font3;
	oled_putstrto(disp, 0, 27+3, "Welcome to linux");

	oled_send_buffer(disp);

	//oled_putpixel(disp, 60, 45);
	//oled_putstr(disp, 1, "hello");

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);

	if (e < 0) {
		show_error(1, e);
	} else {
		e = oled_init(&disp);
	if (e < 0) {
		show_error(2, e);
	} else {
		(oled_demo(&disp)
	}

	return 0;
}

说明:

先设置好disp->font = font1;的字体。

再去调用: oled_putstrto(disp, 0,1 , "Welcome to linux ");字体才能生效。

如果不初始化disp->font = font1;的字体,直接不显示。

个人测试1号字体最大,3号最小(没去深究font对应字的大小)。

在这里插入图片描述

6.Linux串口开发

1.串口基本认知

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢

  • 是设备间接线通信的一种方式
  • 数据一位一位地顺序传送
  • 双向通信,全双工
  • 传送速度相对较慢

2串口接线方式

RXD:数据输入引脚,数据接受;

TXD:数据发送引脚,数据发送;

交叉接线

3.基于wiringPi的串口开发

1.复制范例代码到当前文件夹下:

cp ../wiringOP-master/examples/serialTest.c  .

在这里插入图片描述

2.查看香橙派TXD,RXD的引脚分布

这里用的是串口5:ttyS5。

在这里插入图片描述

因此14行代码改成:

 if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0)

在这里插入图片描述

4.优化串口例程(改用线程)

/*
* serialTest.c:
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>

#include <unistd.h>//sleep函数
int fd;//定义全局变量,让所有线程都访问到这个文件
void* Sendhandler()
{
    char *sendBuf;
    sendBuf = (char *)malloc(32*sizeof(32));
    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)
    {
    	sleep(10);
    }
    return 0 ;
}

在这里插入图片描述

5.Linux原生串口(不使用wiringpi库)

uartTool.c

//uartTool.c
#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 serialSendstring (const int fd, const char *s)
{
    int ret;
    ret = write (fd, s, strlen (s));
    if (ret < 0)
        printf("Serial Puts Error\n");
}
int serialGetstring (const int fd, char *buffer)
{
    int n_read;
    n_read = read(fd, buffer,32);
    return n_read;
}

uartTool.h

//uartTool.h
int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, const char *s);
int serialGetstring (const int fd, char *buffer);

uartTest.c

//uartTest.c 
#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 <unistd.h>
#include <pthread.h>
#include "uartTool.h"
int fd;
void* readSerial()
{
    char buffer[32];
    while(1){
        memset(buffer,'\0',sizeof(buffer));
        serialGetstring(fd, buffer);
        printf("GET->%s\n",buffer);
    }
}
void* sendSerial()
{
    char buffer[32];
    while(1){
        memset(buffer,'\0',sizeof(buffer));
        scanf("%s",buffer);
        serialSendstring(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){sleep(10);}
}

编译

//多文件编译要全部写进去,同时链接到线程库,,因为是基于C库的没有用到orangepi的库,所以不需要用之前的shell脚本
gcc uartTest.c uartTool.c -pthread
    gcc uartTool.c uartTest.c  -pthread

运行

./a.out /dev/ttyS5

image-20240331210841639

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘猫.exe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值