【三】常用外设开发
文章目录
- 【三】常用外设开发
-
- [1. wiringPi外设SDK安装](#1. wiringPi外设SDK安装)
- 2.蜂鸣器BB响
- 小插曲:shell脚本
- 3.超声波测距
-
- [1. 测距原理基本说明](#1. 测距原理基本说明)
- 2.时间函数
- 3.结合超声波编写代码
- 4.舵机开发-PWM波
- 5.OLED屏应用-IIC协议
-
- 1.OLED屏幕
- [2 .Orangepi的IIC接口](#2 .Orangepi的IIC接口)
- 3.Oled功能代码阅读
- 4.基于官方库修改OLED代码
- 6.Linux串口开发
1. wiringPi外设SDK安装
c
//直接在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
如下方所示,外设库就完成安装了
物理引脚对应图
c
//官方给的例程路径,进入这个里面我们可以看到有很多例程
cd wiringOP/examples
2.蜂鸣器BB响
1.怎么将其他文件夹里面的文件复制到目前的文件夹?
命令: cd -
返回上次进入的目录
c
cp ../wiringOP/examples/blink.c A
//点点必须加,然后加想要复制的那个文件的路径, 其次是复制为文件重命名的名字
2.修改vim编辑器的tab缩进,显示行数
输入命令:sudo vi /etc/vim/vimrc
c
sudo vi /etc/vim/vimrc
在此位置设置
c
//注意输入的格式,=前后不要留空格
set tabstop=4 //输入按下tab缩进4个
set shiftwidth=4 //批量缩进4个
set nu //显示行数
3.蜂鸣器配合时间函数开发
c
//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.开始编译
c
标准流程:1.编译:gcc 文件名.c
2.运行:./a.out
这个时候如果我们直接编译,它会报错,因为这个是香橙派官方提供的库,编译需要链接它的库,如何查看?
c
//输入命令查看官方Makefile
vi ./wiringOP/examples/Makefile
c
//需要链接的库
-lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
小插曲:shell脚本
那么问题来了?每次编译都需要链接到库,很麻烦,有没有一种方法可以简单一些?有,我们可以写一个shell脚本
1.创建一个脚本
c
vi bulid.sh
2.输入内容
c
gcc beep.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
3.添加权限
c
//这个时候shell脚本是没有权限的,需要给它加一个可执行的权限(注意它的颜色)
chmod +x bulid.sh
4.运行shell脚本
5.shell脚本传参优化
对于上面我们做的脚本,编译的都是写死的文件,如果我想换成别的文件,就很麻烦了,那么我们就需要把这个换成参数传递
c
//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 为执行的文件名:
c
#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
为脚本设置可执行权限,并执行脚本,输出结果如下所示:
c
$ chmod +x test.sh
$ ./test.sh 1 2 3
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3
另外,还有几个特殊字符用来处理参数:
参数处理 说明
c
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
如"$*"用「"」括起来的情况、以"$1 $2 ... $n"的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如"$@"用「"」括起来的情况、以"$1" "$2" ... "$n" 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
示例:
c
#!/bin/bash
echo "Shell 传递参数实例!";
echo "第一个参数为:$1";
echo "参数个数为:$#";
echo "传递的参数作为一个字符串显示:$*";
执行脚本,输出结果如下所示:
c
$ 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.时间函数
函数原型:
c
#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万次会用时多少
c
//计算程序在当前环境中数数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.结合超声波编写代码
c
#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其作用是格式化打印,也叫格式化输出,可以指定输出到一个流文件中,即向输出流中写入数据。
c
//函数原型
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的结构体
c
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
代码实现
c
/*该代码实现的功能是: 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实现
c
#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-3 和i2C-5 的驱动,而H616的外设我们看到只有一个IIC接口,用的是i2C-3
Linux一切皆文件,每个硬件设备"对应"一个文件,由驱动程序提供映射
- 开始测试 i2c, 首先安装 i2c-tools
c
sudo apt-get -y install i2c-tools
c++
//测试能查看iic上挂载设备地址的工具是否有效
sudo i2cdetect -y 3
因为:Linux一切皆文件,每个硬件设备"对应"一个文件,由驱动程序提供映射
我们根据原理图,把OLED屏幕插上,此时的3c就是挂载在iic上的oled屏幕的地址。
3.Oled功能代码阅读
1.首先将官方的例程代码复制一份到我们自己的文件夹中
c++
//将oled例程代码复制到当前工作目录
cp ../wiringOP/examples/oled_demo.c .
不能把官方代码破坏了,嘻嘻嘻!
贴一下官方例程OLED的demo,查看起来很乱,可以用到source insight工具来查看例程代码,会很方便。
c
/*
* 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 "
c
#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.复制范例代码到当前文件夹下:
c++
cp ../wiringOP-master/examples/serialTest.c .
2.查看香橙派TXD,RXD的引脚分布
这里用的是串口5:ttyS5。
因此14行代码改成:
c++
if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0)
4.优化串口例程(改用线程)
c
/*
* 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
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
c
//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
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++
//多文件编译要全部写进去,同时链接到线程库,,因为是基于C库的没有用到orangepi的库,所以不需要用之前的shell脚本
gcc uartTest.c uartTool.c -pthread
gcc uartTool.c uartTest.c -pthread
运行:
c++
./a.out /dev/ttyS5
、