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

news2024/9/22 5:23:08

【三】常用外设开发

文章目录

  • 【三】常用外设开发
    • 1. wiringPi外设SDK安装
    • 2.蜂鸣器BB响
      • 1.怎么将其他文件夹里面的文件复制到目前的文件夹?
      • 2.修改vim编辑器的tab缩进,显示行数
      • 3.蜂鸣器配合时间函数开发
    • 小插曲:shell脚本
    • 3.超声波测距
      • 1. 测距原理基本说明
      • 2.时间函数
      • 3.结合超声波编写代码
    • 4.舵机开发-PWM波
      • 1.舵机基本介绍
      • 2.Linux定时器
      • 3.舵机开发
    • 5.OLED屏应用-IIC协议
      • 1.OLED屏幕
      • 2 .Orangepi的IIC接口
      • 3.Oled功能代码阅读
      • 4.基于官方库修改OLED代码
    • 6.Linux串口开发
      • 1.串口基本认知
      • 2串口接线方式
      • 3.基于wiringPi的串口开发
      • 4.优化串口例程(改用线程)
      • 5.Linux原生串口(不使用wiringpi库)

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1964811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TapData 信创数据源 | 国产信创数据库 TiDB 数据迁移指南,加速国产化进程,推进自主创新建设

随着国家对自主可控的日益重视&#xff0c;目前在各个行业和区域中面临越来越多的国产化&#xff0c;采用有自主知识产权的国产数据库正在成为主流。长期以来&#xff0c;作为拥有纯国产自研背景的 TapData&#xff0c;自是非常重视对于更多国产信创数据库的数据连接器支持&…

EasyTwin的动画系统已经到了next level?快来一探究竟!

在实际的数字孪生项目场景建设中&#xff0c;水利项目中的洪水推演、工业领域的工程施工模拟、车间产线运转、机械装置和零件配置展示等项目场景&#xff0c;都对动画效果有很强的使用需求&#xff0c;这是对渲染软件和设计师能力的极大考验&#x1f198;。 别担心&#xff01…

使用人工智能在乳腺癌筛查中的早期影响指标| 文献速递-AI辅助的放射影像疾病诊断

Title 题目 Early Indicators of the Impact of Using AI in Mammography Screening for Breast Cancer 使用人工智能在乳腺癌筛查中的早期影响指标 01 文献速递介绍 基于人群的乳腺癌筛查通过使用乳房X线摄影成功地降低了乳腺癌的死亡率&#xff0c;但这给乳腺放射科医生…

【通俗理解】自相似性探索——从分形到递归的奇妙之旅

【通俗理解】自相似性探索——从分形到递归的奇妙之旅 自相似性的奇妙比喻 你可以把自相似性比作一个“无限镜子”&#xff0c;它能够在不同的尺度上反射出相同的图案或结构。就像是在一面两面镜之间放置一个物体&#xff0c;镜子中的倒影会无限重复&#xff0c;每个倒影都与原…

韦东山瑞士军刀项目自学之中断控制LED

使用库函数设置外部中断控制LED 重点&#xff1a;在设置GPIO为外部中断控制源时&#xff0c;你至少要先检查一下信号是不是来自于你所设置的那个端口&#xff01;因为EXTI并不是对每个端口单独引线&#xff0c;而是将所有组的同编号的部分接口只用一个EXTI进行控制&#xff0c…

【最新】cuda和cudnn和显卡驱动的对应关系

NV官方文档Support Matrix — NVIDIA cuDNN v9.2.1 documentation下列的非常清楚&#xff0c;如图&#xff1a;

Cocos Creator2D游戏开发(9)-飞机大战(7)-爆炸效果

这个爆炸效果我卡在这里好长时间,视频反复的看, 然后把代码反复的测试,修改,终于给弄出来 视频中这段,作者也是修改了好几次, 跟着做也走了不少弯路; 最后反正弄出来了; 有几个坑; ① 动画体创建位置是enemy_prefab ② enemy_prefab预制体下不用放动画就行; ③ 代码中引用Anima…

入门 PyQt6 看过来(案例)18~ 表格属性

QTableWidget是常用的显示数据表格控件&#xff0c;是QTableView的子类&#xff0c;它使用标准的数据模型&#xff0c;并且其单元格数据是通过QTableWidgetItem对象来实现的。 QTableWidget类常用方法如下表&#xff1a; 方法描述setRowCount(行数)设置表格行数setColumnCount…

【C++高阶】哈希:全面剖析与深度学习

目录 &#x1f680; 前言一&#xff1a; &#x1f525; unordered系列关联式容器1.1 unordered_map1.2 unordered_set 二&#xff1a; &#x1f525; 哈希的底层结构 ⭐ 2.1 哈希概念⭐ 2.2 哈希冲突⭐ 2.3 哈希函数⭐ 2.4 哈希冲突解决2.4.1 &#x1f304;闭散列2.4.2 &#x…

【课程系列06】某乎AI大模型全栈工程师-第6期

网盘链接 链接&#xff1a;https://pan.baidu.com/s/1QLkRW_DmIm1q9XvNiOGwtQ --来自百度网盘超级会员v6的分享 课程目标 【知乎大模型课程】学习的四个维度 &#x1f449;指挥层&#xff1a;学高阶指令工程 AI编程等&#xff0c;指挥大模型完成90%代码任务&#xff0c;包…

MySql理解RR(可重复读)事务隔离级别

demo&#xff0c;理解mysql的可重复读隔离级别&#xff0c;当前读、快照读的区别 如下图&#xff0c;表sys_user中我同时开启三个事务连接&#xff1a; session1&#xff1a; 当session1开启事务时&#xff0c;mysql使用快照读保存事务开始前的数据&#xff0c;所以这条事务…

数据仓库及离线数仓架构、实时数仓架构

往期推荐 大数据HBase图文简介-CSDN博客 数仓常见名词解析和名词之间的关系-CSDN博客 目录 0. 前言 0.1 浅谈维度建模 0.2 数据分析模型 1. 何为数据仓库 1.1 为什么不直接用业务平台的数据而要建设数仓&#xff1f; 1.2 数据仓库特征 1.3 数据仓库和数据库区别 1.4 以…

VULNHUB-XXE漏洞 靶机

1.导入打开虚拟机 然后没账号密码~ 虚拟机虚拟网络编辑器是net 可以知道虚拟机的ip池 直接拿工具扫描端口 御剑 Zenmap namp 都可以 然后打开这个端口 扫描一下目录发现有个robots.txt 有个/xxe/应该是个路径 打开看看 admin.php是个文件 有个登录 试了试弱口令没办法 抓…

谁是金融核心系统市场第一名?中兴通讯旗下金篆GoldenDB

从IDC发布的《中国银行业本地部署分布式事务型数据库市场份额&#xff0c;2023》报告来看&#xff0c;中兴通讯旗下的金篆GoldenDB以24.8%的市场份额在银行业本地部署分布式数据库市场中独占鳌头&#xff0c;排名“第一”。    毕竟关系着人民群众的财产安全&#xff0c;银行…

upload-labs靶场1-19关秒杀攻略

Pass-01 打开第一关&#xff0c;我们先上传一个jpg文件看看这个文件上传功能正不正常 发现可以正常上传&#xff0c;并且访问这个图片也能正常访问&#xff0c;然后我们尝试上传php文件&#xff0c;发现弹出个警告&#xff0c;不让上传 我们上传php时开启抓包&#xff0c;发现抓…

VMware、Yum、VIM、Zsh、Oh My Zsh - 搭建属于你的虚拟机

目录 VMware 虚拟机搭建 下载 Centos7 镜像 创建虚拟机 虚拟机网络配置 常用软件配置 yum 配置 vim 安装及配置 zsh 安装 on my zsh 用起来 基本安装 插件 zsh-augosuggestions&#xff08;自动补全&#xff09; 插件 zsh-syntax-highlighting&#xff08;语法高亮…

数据结构:二叉树(链式结构)

文章目录 1. 二叉树的链式结构2. 二叉树的创建和实现相关功能2.1 创建二叉树2.2 二叉树的前&#xff0c;中&#xff0c;后序遍历2.2.1 前序遍历2.2.2 中序遍历2.2.3 后序遍历 2.3 二叉树节点个数2.4 二叉树叶子结点个数2.5 二叉树第k层结点个数2.6 二叉树的深度/高度2.7 二叉树…

Vitis HLS 完美嵌套循环通过 m_axi 接口读取DDR 的迭代次数细粒度控制实验 — 问题描述

1 自媒体账号 目前运营的自媒体账号如下&#xff1a; 哔哩哔哩 【雪天鱼】: 雪天鱼个人主页-bilibili.comCSDN 【雪天鱼】: 雪天鱼-CSDN博客 QQ 学习交流群 FPGA科研硕博交流群 910055563 (进群有一定的学历门槛&#xff0c;长期未发言会被请出群聊&#xff0c;主要交流FPG…

str字符串常量,字符指针char * str的存储方式,以及如何赋值的

在使用字符指针时&#xff0c;第一种为通常的情况。 这次主要关注第二种情况&#xff1a;把字符串赋值给字符指针变量&#xff0c;pa中存储的不是这串字符&#xff0c;而是字符串的首字符的地址。 abcdef作为一个常量字符串程序会把其放在一个单独的内存区域&#xff0c;pa存储…

C++ Primer 总结索引 | 第十七章:标准库特殊设施

仍有一些标准库设施在很多应用中都是有用的&#xff1a;tuple、bitset、正则表达式以及随机数。还将介绍一些附加的I/O库功能&#xff1a;格式控制、未格式化I/O和随机访问 1、tuple 类型 1、tuple 是类似 pair 的模板。不同 tuple 类型的成员类型也不同&#xff0c;但一个 t…