使用香橙派 学习Linux的串口开发

串口的回顾 & 硬件接线

关于串口也是之前学习过很多次了,详见:

认识串口 和 蓝牙模块HC08_hc08蓝牙模块_mjmmm的博客-CSDN博客

串口的再认识-CSDN博客

香橙派提供了两路串口第一路就是在刷机时串口连接的引脚(对应驱动ttyS0),第二路就是物理引脚8和10(对应驱动ttyS5

此处要请出老朋友CH340,这次连接物理引脚8和10的第二路串口: 

在使用串口连接香橙派的时候,使用的Mobaxterm就可以视为一个串口助手,但Moba更多的是提供一个基于指令交互的平台,所以串口助手的使用还是选择之前用过的AI Thinker:

在实际应用中,单片机作为比较简单的芯片,可以去负责数据的采集,然后通过串口接到相对高级的香橙派或其他芯片,香橙派读取数据并进行复杂的数据分析或开发,包括人工智能,UI,网络等在单片机中难以实现的功能,同时通过串口给单片机发送各种指令。

基于wiringPI库的串口开发

关于串口的代码,wiringPI库同样提供了demo代码:

cp一份过来:

(也可以使用SourceInsight来读代码!)

首先,发现打开默认的demo打开的是串口2的驱动,所以此处要改成串口5的驱动:

然后编译运行:(显示的就是串口助手中发来的字符的ASCII码形式)

串口助手中记得勾选HEX显示:(发送的就是16进制的0到256)

 

基于demo的优化

可以使用之前学习的线程相关概念来优化这个demo,关于线程的知识之前也学过,详见:

线程_mjmmm的博客-CSDN博客

Linux线程 --- 生产者消费者模型(C语言)-CSDN博客

serial_wiringPI_test.c:

#include <stdio.h>
#include <string.h>
#include <errno.h>#include <wiringPi.h>
#include <wiringSerial.h>#include <pthread.h>
#include <stdlib.h>void *read_serial(void *arg)
{char *sendbuf;sendbuf = (char *)malloc(32*sizeof(char));char *p = sendbuf;while(1){memset(sendbuf,'\0',32*sizeof(char));fgets(sendbuf,sizeof(sendbuf),stdin);//scanf("%s",sendbuf);while(*sendbuf != '\0'){serialPutchar (*((int *)arg), *sendbuf) ; //串口打印数据的函数 serialPutchar()sendbuf++;}sendbuf = p;}pthread_exit(NULL);}void *write_serial(void *arg)
{while(1){while(serialDataAvail (*((int *)arg))){ //当串口有数据的时候进入whileprintf ("%c", serialGetchar (*((int *)arg))) ; //串口接收数据的函数serialGetchar()fflush (stdout) ;}}pthread_exit(NULL);
}int main ()
{int fd ;int ret;pthread_t read_thread;pthread_t write_thread;if ((fd = serialOpen ("/dev/ttyS5", 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 ;}ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);if(ret != 0){printf("read_serial create error\n");return 1;}ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);if(ret != 0){printf("write_serial create error\n");return 1;}pthread_join(read_thread,NULL);pthread_join(write_thread,NULL);return 0 ;
}

实现效果:

发送数据:

接收数据:

也可以一边发一边接,因为经过优化,接和发被封装在了不同的线程中! 

Linux原生串口开发 

通过sourceinsight查看跳转wiringPI库实现的串口代码,就会发现函数的实现并不困难,所以可以尝试不使用wiringPI库,自己通过Linux封装函数实现串口的通讯。

首先观察wiringPi库,其对于串口最核心的就是三个函数,serialOpen()serialPutchar()serialGetchar()所以我就自己写一个C文件来实现这三个函数(其实所谓的自己实现就是根据sourceinsight跳转这三个函数,然后删去一些我认为在使用中不必要的代码,与其说是自己实现,更不如说是对这三个函数进行一个删减,精简化),然后创建一个关于它的h文件,最后在串口通讯的函数里添加这个我写的h文件,使用我自己实现的这三个函数来完成串口的通讯。

步骤为:编写mjm_uart_tool.c -> 编写mjm_uart_tool.h -> 编写serial_mjm_test.c调用mjm_uart_tool.h来实现和刚刚使用wiringPI相同的效果

mjm_uart_tool.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) ; // 10mSreturn 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;
}int serialDataAvail (const int fd) //用来判断串口有无数据的函数,直接复制黏贴过来的
{int result ;if (ioctl (fd, FIONREAD, &result) == -1)return -1 ;return result ;
}

mjm_uart_tool.h:

int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, const char *s);
int serialGetstring (const int fd, char *buffer);
int serialDataAvail (const int fd);

serial_mjm_test.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 <errno.h>
#include "mjm_uart_tool.h"void *read_serial(void *arg)
{char *sendbuf;sendbuf = (char *)malloc(32*sizeof(char));while(1){memset(sendbuf,'\0',32*sizeof(char));fgets(sendbuf,sizeof(sendbuf),stdin);serialSendstring (*((int *)arg), sendbuf) ;}pthread_exit(NULL);}void *write_serial(void *arg)
{char readbuf[32] = {'\0'};while(1){while(serialDataAvail (*((int *)arg))){serialGetstring (*((int *)arg),readbuf) ;printf("-> %s\n",readbuf);memset(readbuf,'\0',32);}}pthread_exit(NULL);
}int main ()
{int fd ;int ret;pthread_t read_thread;pthread_t write_thread;if ((fd = myserialOpen ("/dev/ttyS5", 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 ;}*/ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);if(ret != 0){printf("read_serial create error\n");return 1;}ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);if(ret != 0){printf("write_serial create error\n");return 1;}pthread_join(read_thread,NULL);pthread_join(write_thread,NULL);return 0 ;
}

注意,由于这里没有使用wiringPI库,所以编译不需要使用之前的build.sh脚本,直接使用gcc就可以,但是要记得链线程的库:

gcc serial_mjm_test.c mjm_uart_tool.c -lpthread

实现效果: 

发送数据:

接收数据:

可见,此时,我将serialOpen()serialPutchar()serialGetchar(),替换成了自己的myserialOpen()serialSendstring()serialGetstring();(还原封不动照搬了serialDataAvail函数),然后实现了和刚刚类似的效果,甚至还有所改进,因为我实现的接收函数可以直接介绍一整个字符串,所以可以在之前打印“->”用于区分,但是原来的serialgetchar是一个字符一个字符接收,很难实现这样的效果。

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

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

相关文章

MAC MINI 2012安装Montery折腾笔记

MAC MINI 2012安装Montery折腾笔记&#xff08;作为电视盒子/远程开发机&#xff09; 起因&#xff1a; 手头有个mac mini&#xff0c;2018年买的2手。一直都是10.12系统&#xff0c;处理python和苹果开发都受制于旧系统&#xff0c;很多软件也装不上&#xff0c;于是有了升级…

python有限差分法求解一维热传导方程

​1、方程及其离散 1.1一维热传导方程 1.2离散化 设定步长&#xff0c;依据上述方程得到递推关系&#xff1a; 2、python求解实现 import numpy as np import matplotlib.pyplot as plth 0.1#空间步长 N 30#空间步数 dt 0.0001#时间步长 M 10000#时间的步数 A dt/(h**2)…

cv::Mat 的常见操作方法

cv::Mat是OpenCV库中用于处理图像和矩阵的主要数据结构。以下是一些常见的cv::Mat操作方法&#xff1a; 创建和初始化 cv::Mat::Mat(): 创建一个空的cv::Mat对象。cv::Mat::Mat(int rows, int cols, int type): 创建一个指定行数、列数和数据类型的cv::Mat对象。cv::Mat::Mat(i…

Java8实战-总结29

Java8实战-总结29 并行数据处理与性能并行流将顺序流转换为并行流测量流性能 并行数据处理与性能 到目前为止&#xff0c;Stream接口最重要的好处是可以对这些集合执行操作流水线&#xff0c;能够自动利用计算机上的多个内核。 例如&#xff0c;在Java 7之前&#xff0c;并行…

如何开发你的第一个Vue.js插件:完整指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

代码随想录二刷day30

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、力扣332. 重新安排行程二、力扣51. N 皇后三、力扣37. 解数独 一、力扣332. 重新安排行程 class Solution {private LinkedList<String> res;private Li…

ModbusTCP服务端

1在Device下&#xff0c;添加设备net&#xff1a; 公交车。 2在net下添加 ModbusTCP

大模型从入门到应用——LangChain:代理(Agents)-[代理执行器(Agent Executor):结合使用Agent和VectorStore]

分类目录&#xff1a;《大模型从入门到应用》总目录 代理执行器接受一个代理和工具&#xff0c;并使用代理来决定调用哪些工具以及以何种顺序调用。本文将参数如何结合使用Agent和VectorStore。这种用法是将数据加载到VectorStore中&#xff0c;并希望以Agent的方式与之进行交互…

【深度学习】 Python 和 NumPy 系列教程(十九):Matplotlib详解:2、3d绘图类型(5)3D等高线图(3D Contour Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图&#xff08;3D Line Plot&#xff09; 2. 3D散点图&#xff08;3D Scatter Plot&#xff09; 3. 3D条形图&#xff08;3D Bar Plot&#xff09; 4. 3D曲面图…

MySQL数据库详解 三:索引、事务和存储引擎

文章目录 1. 索引1.1 索引的概念1.2 索引的作用1.3 如何实现索引1.4 索引的缺点1.5 建立索引的原则依据1.6 索引的分类和创建1.6.1 普通索引1.6.2 唯一索引1.6.3 主键索引1.6.4 组合索引1.6.5 全文索引 1.7 查看索引1.8 删除索引 2. 事务2.1 事务的概念2.2 事务的ACID特性2.2.1…

人机中的事实与价值时空、排序

人机结合智能与事实价值融合分析确实是未来解决复杂疑难问题的基本策略之一。该策略利用人类智慧和机器智能相结合&#xff0c;结合有效的事实和价值分析方法&#xff0c;以更全面、准确、高效地解决问题。 通过人机结合&#xff0c;可以充分发挥人类的主观能动性、判断力和创造…

2023年 python结合excel实现快速画图(零基础快速入门)

目录 1.适用人群 2.环境配置 3.基本用法 3.1 数据读取 3.2 数据分析 3.3 数据组装 3.4 制表&#xff1a; 4.快速提升 5.效果展示 1.适用人群 电脑有python环境&#xff0c;会python基本使用&#xff0c;需要短时间内完成大量画图任务的数据分析的人群。&#xff08;有…

逆向-beginners之goto

#include <stdio.h> int main() { printf("begin.\n"); goto exit; printf("skip me!\n"); exit: printf("end\n"); } #if 0 某些情况下还必须使用这种语句 goto直接编译成了JMP。这两个指令的效果完全相同&#xff…

JDK17特性

文章目录 一、JAVA17概述二、语法层面的变化1.密封类2.switch模式匹配&#xff08;预览&#xff09; 三、API层面变化1.Vector API&#xff08;第二个孵化器&#xff09;2.特定于上下文的反序列化过滤器 四、其他变化1.恢复始终严格的浮点语义2.JEP 增强型伪随机数生成器3.JEP …

Linux命令:free命令

目录 一、简介二、free -h1、free列和available列的区别2、swap空间 三、free -h -s 2 -c 2 一、简介 free 命令可以显示Linux系统中 空闲的、已用的物理内存 及 swap内存,及 被内核使用的buffer。在Linux系统监控的工具中&#xff0c;free命令是最经常使用的命令之一。 free…

C++库函数——map与set

目录 1.关联式容器是什么&#xff1f; 2.键值对 3.set ①set的介绍 ②set的模板参数列表 ③set的构造 ④set的迭代器 ⑤set的容量 ⑥set的修改与操作 ⑦set的使用举例 4.multiset ①multiset的介绍 ②multiset的使用举例 5.map ①map的介绍 ②map的模版参数列表…

HuggingFace Transformer

NLP简介 HuggingFace简介 hugging face在NLP领域最出名&#xff0c;其提供的模型大多都是基于Transformer的。为了易用性&#xff0c;Hugging Face还为用户提供了以下几个项目&#xff1a; Transformers(github, 官方文档): Transformers提供了上千个预训练好的模型可以用于不…

关于不停机发布新版本程序的方式

“不停机发布新版本程序”&#xff0c;暂且这么称呼吧&#xff0c;其实就是所说的滚动发布、灰度发布、金丝雀发布和蓝绿发布。 之所以会总结性地提一下这几个概念&#xff0c;主要是本次出门游历&#xff0c;流浪到了乌兰察布市四王子旗&#xff0c;在这儿遇上了个有趣儿的家伙…

目标检测YOLO实战应用案例100讲-基于单阶段网络的小目标检测

目录 前言 目标检测的研究现状 小目标检测的研究现状

基于讯飞人脸算法(调用API进行人脸比对)

先看结果 必须遥遥领先 所需准备 这里我调用了&#xff1a; 人脸比对 API 文档 | 讯飞开放平台文档中心https://www.xfyun.cn/doc/face/xffaceComparisonRecg/API.html#%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E 代码里所涉及的APPID、APISecret、APIKey 皆从讯飞的控制台获取&…