Linux系统编程7-串口通信
序号 | 内容 | 链接 |
---|---|---|
1 | 多进程 | 点我访问 |
2 | 进程间通信 | 点我访问 |
3 | 多线程 | 点我访问 |
4 | 网络编程 | 点我访问 |
5 | shell | 点我访问 |
6 | Makefile | 点我访问 |
7 | 串口通信 | 点我访问 |
8 | I2C通信 | 点我访问 |
一 Linux下代码控制串口
Linux下把串口当成控制台对待,想对串口操作,其实就是对控制台操作;
Linux下有专门的串口驱动节点,通过系统io操作,这些节点的名字都有如下规律:
/dev/ttyS*
/dev/ttyAMA*
/dev/ttyO*
/dev/ttySAC*
…
至于你的开发板的串口驱动节点是哪一种名字,不同的开发板是不同的,所以要根据供货商提供的操作手册进行操作;
操作串口的方法:
系统IO -> open,read,write,close…
能用标准IO操作串口吗 -> 不能,因为串口是字符设备,标准io是以块为单位来传输的,不是以字符为单位传输的,所以不行。
1.1 判断驱动节点是否是串口设备
if(tcgetattr(fd, &oldtio) != 0){ //判断你的驱动节点是不是一个串口设备,假如不反回0,就不是串口设备
return -1;
}
1.2 struct termios详解
struct termios
{
unsigned short c_iflag; // 输入模式标志
unsigned short c_oflag; // 输出模式标志
unsigned short c_cflag; // 控制模式标志
unsigned short c_lflag; //区域模式标志或本地模式标志或局部模式
unsigned char c_line; //行控制line discipline
unsigned char c_cc[NCC]; // 控制字符特性
};
c_oflag参数
键 值 | 说 明 |
---|---|
OPOST | 处理后输出 |
OLCUC | 将输出的小写字符转换成大写字符(非POSIX) |
ONLCR | 将输出的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL | 将输出的CR(回车)转换成NL(换行) |
ONOCR | 第一行不输出回车符 |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以延迟终端输出 |
OFDEL | 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NULL |
NLDLY | 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY | 回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY | 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY | 空格输出延迟,可以取BS0或BS1 |
VTDLY | 垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY | 换页延迟,可以取FF0或FF1 |
c_cflag参数 | |
键 值 | 说 明 |
– | – |
CBAUD | 波特率(4+1位)(非POSIX) |
CBAUDEX | 附加波特率(1位)(非POSIX) |
CSIZE | 字符长度,取值范围为CS5、CS6、CS7或CS8 |
CSTOPB | 设置两个停止位 |
CREAD | 使用接收器 |
PARENB | 使用奇偶校验 |
PARODD | 对输入使用奇偶校验,对输出使用偶校验 |
HUPCL | 关闭设备时挂起 |
CLOCAL | 忽略调制解调器线路状态 |
CRTSCTS | 使用RTS/CTS流控制 |
c_lflag参数
键 值 | 说 明 |
---|---|
ISIG | 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号 |
ICANON | 使用标准输入模式 |
XCASE | 在ICANON和XCASE同时设置的情况下,终端只使用大写。 |
ECHO | 显示输入字符 |
ECHOE | 如果ICANON同时设置,ERASE将删除输入的字符 |
ECHOK | 如果ICANON同时设置,KILL将删除当前行 |
ECHONL | 如果ICANON同时设置,即使ECHO没有设置依然显示换行符 |
ECHOPRT | 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX) |
TOSTOP | 向后台输出发送SIGTTOU信号 |
c_cc支持的控制字符
宏 | 说 明 | 宏 | 说 明 |
---|---|---|---|
VINTR | Interrupt字符 | VEOL | 附加的End-of-file字符 |
VQUIT | Quit字符 | VTIME | 非规范模式读取时的超时时间 |
VERASE | Erase字符 | VSTOP | Stop字符 |
VKILL | Kill字符 | VSTART | Start字符 |
VEOF | End-of-file字符 | VSUSP | Suspend字符 |
VMIN | 非规范模式读取时的最小字符数 |
1.3 设置波特率
static speed_t getBaudrate(int baudrate)
{switch(baudrate) {case 0: return B0;case 50: return B50;case 75: return B75;case 110: return B110;case 134: return B134;case 150: return B150;case 200: return B200;case 300: return B300;case 600: return B600;case 1200: return B1200;case 1800: return B1800;case 2400: return B2400;case 4800: return B4800;case 9600: return B9600;case 19200: return B19200;case 38400: return B38400;case 57600: return B57600;case 115200: return B115200;case 230400: return B230400;case 460800: return B460800;case 500000: return B500000;case 576000: return B576000;case 921600: return B921600;case 1000000: return B1000000;case 1152000: return B1152000;case 1500000: return B1500000;case 2000000: return B2000000;case 2500000: return B2500000;case 3000000: return B3000000;case 3500000: return B3500000;case 4000000: return B4000000;default: return -1;}
}struct termios newtio,oldtio;/*struct termios{unsigned short c_iflag; // 输入模式标志unsigned short c_oflag; // 输出模式标志unsigned short c_cflag; // 控制模式标志unsigned short c_lflag; //区域模式标志或本地模式标志或局部模式unsigned char c_line; //行控制line discipline unsigned char c_cc[NCC]; // 控制字符特性}*/ speed_t bSpeed = B115200;bSpeed = getBaudrate(nSpeed);/* 设置输入波特率 */cfsetispeed(&newtio, bSpeed);/* 设置输出波特率 */cfsetospeed(&newtio, bSpeed);
1.4 数据位
/* 设置数据位 */
switch(nBits){case 5:newtio.c_cflag |= CS5;break;case 6:newtio.c_cflag |= CS6;break;case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;default:newtio.c_cflag |= CS8;break;
}
1.5 设置校验位
/* 设置校验位 */
switch(nEvent){case 'O': newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E': newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N': newtio.c_cflag &= ~PARENB;break;case 'S': newtio.c_cflag &= ~PARENB;break;default:newtio.c_cflag &= ~PARENB;break;
}
1.6 设置停止位
/* 设置停止位 */
switch (nStop){case 1 :newtio.c_cflag &= ~CSTOPB;break;case 2 :newtio.c_cflag |= CSTOPB;break;default:newtio.c_cflag &= ~CSTOPB;break;
}
1.7 设置超时
下面两句代码就是核心部分:
先来了解一种情况,比如a发送给b 64个字节的数据,正常情况下我们都希望b一次性收完,但是实际情况
不可能是一次收完的,b都会断断续续的收到;
下面这两句代码就是为了防止上面的这种情况发生,希望b会"等一下",等待a发完,但是假如b等了,a又不发了
这样就很难受了,所以如下2个参数就是为了防止这种情况发生;
c_cc[VTIME] = 3;
c_cc[VMIN] = 64;
首先read是会阻塞的;
64的意思是最小要读取到64个字节才返回,否则阻塞在read函数上;
3的意思就是假如在3*100ms内收不到64个字节,就强制返回,解除read阻塞;
其实这两个参数还有其他含义,比如:
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 64;
代表的意思是:
首先read是会阻塞的;
死等,必须等到64个字节再返回,没有接受超时时间;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
代表的意思是:
首先read是不会阻塞的;
收不到就算了,read是不阻塞的;
newtio.c_cc[VTIME] = 4;
newtio.c_cc[VMIN] = 0;
newtio.c_cc[VTIME] = 3;
newtio.c_cc[VMIN] = 64;
1.8 完整代码
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/wait.h>
#include <errno.h>
#include <linux/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <pthread.h>int g_fd;
#define DEBUG(fmt, arg...) printf(fmt, ##arg)static speed_t getBaudrate(int baudrate)
{switch(baudrate) {case 0: return B0;case 50: return B50;case 75: return B75;case 110: return B110;case 134: return B134;case 150: return B150;case 200: return B200;case 300: return B300;case 600: return B600;case 1200: return B1200;case 1800: return B1800;case 2400: return B2400;case 4800: return B4800;case 9600: return B9600;case 19200: return B19200;case 38400: return B38400;case 57600: return B57600;case 115200: return B115200;case 230400: return B230400;case 460800: return B460800;case 500000: return B500000;case 576000: return B576000;case 921600: return B921600;case 1000000: return B1000000;case 1152000: return B1152000;case 1500000: return B1500000;case 2000000: return B2000000;case 2500000: return B2500000;case 3000000: return B3000000;case 3500000: return B3500000;case 4000000: return B4000000;default: return -1;}
}int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{struct termios newtio,oldtio; //创建speed_t bSpeed = B115200;if(tcgetattr(fd, &oldtio) != 0){ //判断你的驱动节点是不是一个串口设备,假如不反回0,就不是串口设备return -1;}bzero(&newtio, sizeof(newtio));// 忽略调制解调器线路状态 | 使用接受器newtio.c_cflag |= (CLOCAL | CREAD); /* 清空数据位的值 */newtio.c_cflag &= ~CSIZE;newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);/* 关闭处理后输出 */newtio.c_oflag &= ~OPOST;/* 设置数据位 */switch(nBits){case 5:newtio.c_cflag |= CS5;break;case 6:newtio.c_cflag |= CS6;break;case 7:newtio.c_cflag |= CS7;break;case 8:newtio.c_cflag |= CS8;break;default:newtio.c_cflag |= CS8;break;}/* 设置校验位 */switch(nEvent){case 'O': newtio.c_cflag |= PARENB;newtio.c_cflag |= PARODD;newtio.c_iflag |= (INPCK | ISTRIP);break;case 'E': newtio.c_iflag |= (INPCK | ISTRIP);newtio.c_cflag |= PARENB;newtio.c_cflag &= ~PARODD;break;case 'N': newtio.c_cflag &= ~PARENB;break;case 'S': newtio.c_cflag &= ~PARENB;break;default:newtio.c_cflag &= ~PARENB;break;}bSpeed = getBaudrate(nSpeed);cfsetispeed(&newtio, bSpeed);cfsetospeed(&newtio, bSpeed);/* 设置停止位 */switch (nStop){case 1 :newtio.c_cflag &= ~CSTOPB;break;case 2 :newtio.c_cflag |= CSTOPB;break;default:newtio.c_cflag &= ~CSTOPB;break;}/*下面两句代码就是核心部分:先来了解一种情况,比如a发送给b 64个字节的数据,正常情况下我们都希望b一次性收完,但是实际情况不可能是一次收完的,b都会断断续续的收到;下面这两句代码就是为了防止上面的这种情况发生,希望b会"等一下",等待a发完,但是假如b等了,a又不发了这样就很难受了,所以如下2个参数就是为了防止这种情况发生;c_cc[VTIME] = 3;c_cc[VMIN] = 64;首先read是会阻塞的;64的意思是最小要读取到64个字节才返回,否则阻塞在read函数上;3的意思就是假如在3*100ms内收不到64个字节,就强制返回,解除read阻塞;其实这两个参数还有其他含义,比如:newtio.c_cc[VTIME] = 0;newtio.c_cc[VMIN] = 64;代表的意思是: 首先read是会阻塞的;死等,必须等到64个字节再返回,没有接受超时时间;newtio.c_cc[VTIME] = 0;newtio.c_cc[VMIN] = 0;代表的意思是:首先read是不会阻塞的;收不到就算了,read是不阻塞的;newtio.c_cc[VTIME] = 4;newtio.c_cc[VMIN] = 0;*/newtio.c_cc[VTIME] = 3;newtio.c_cc[VMIN] = 64;tcflush(fd, TCIOFLUSH); //清空缓冲区if((tcsetattr(fd, TCSANOW, &newtio)) != 0){ //把newtio的属性设置到当前的控制台return -1;}return 0;
}int open_port(int fd, char *path)
{/*O_NOCTTY: 不要把它当成串口控制台,因为它是通信串口,不是调试串口 O_NDELAY: 不要阻塞*/fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0){perror("open_oprt failed");return(-1);}DEBUG("fcntl: %d\n", fcntl(fd, F_SETFL, 0)); //再重新设置成非阻塞,保险return fd;
}/*path_utf: 串口驱动节点,nuc972的驱动节点是/dev/ttyS*,不是所有的板子都叫ttySAC*,有的叫ttyO*,ttyS*,ttyAMA*等等baud: 波特率,比如9600,115200等databits:数据位,只有5,6,7,8parity_utf: 校验位,只有'O','E','N','S'stopbits: 停止位,只有1, 1.5, 2例如:char a = 'O';OpenSerialPort("/dev/ttySAC2", 115200, 8, &a, 1);*/
int OpenSerialPort(char *path_utf, int baud, int databits, char *parity_utf, int stopbits)
{int fd, result;fd = open_port(fd, path_utf);result = set_opt(fd, baud, databits, parity_utf[0], stopbits);if(result < 0){close(fd);return result;}return fd;
}/* 注意,不要在子线程里scanf或者fgets */
void *read_thread(void *args)
{char buf[64];while(1){memset(buf, 0 ,sizeof(buf));read(g_fd, buf, sizeof(buf));printf("recv=%s\n", buf);}
}int main(int argc, char **argv)
{char buf[64];char cmd[64]="hahahaha";pthread_t tid;g_fd = OpenSerialPort(argv[1], 9600, 8, "N", 1);pthread_create(&tid, NULL, read_thread, NULL);while(1){write(g_fd, cmd, strlen(cmd));sleep(1);}return 0;
}
Linux系统编程7-串口通信相关推荐
- Linux系统编程:串口编程
Linux系统编程:串口编程 Linux下的串口概述 Linux串口编程 代码解析 编译和测试 代码中的常量介绍 Linux下的串口概述 常见的数据通信的基本方式分为并行通信和串行通信. 1.并行通信 ...
- Linux——Linux系统编程之串口编程总结(串口的初始化、读写操作实践)
目录 0 引言 1 串口编程的流程 1.1 打开串口 1.2 初始化串口 1.2.1 termios结构体 1.2.2 关键函数 1.2.3 初始化串口代码 2 串口的读写测试例程 0 引言 串口大家 ...
- linux进程通信发送方式,Linux服务器编程——Linux系统编程之进程通信
进程通信又称IPC IPC方法 方法:管道(最简单) 信号(开销最小) 共享映射区/共享内存(无血缘关系) 本地套接字(最稳定) Linux文件类型: - 文件 d 目录 l 符号链接 s ...
- 【Linux系统编程】进程通信之管道
1.进程间通信介绍 1.1 进程通信的基本概念 在之前我们已经学习过进程地址空间.Linux 环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不 ...
- linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...
信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...
- linux原子过程,linux系统编程:IO读写过程的原子性操作实验
所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...
- linux有名管道数据异常,Linux系统编程—有名管道
▋****1. 管道的概念 管道,又名「无名管理」,或「匿名管道」,管道是一种非常基本,也是使用非常频繁的IPC方式. 1.1 管道本质 管道的本质也是一种文件,不过是伪文件,实际上是一块内核缓冲区, ...
- Linux系统编程(三)进程间的通信
Linux系统编程(三)进程间的通信 一.为什么需要进程之间的通信(IPC)? 二.管道 1.概念 2.特质 3.原理 4.局限性 5.代码 2.读入数据 三.共享存储映射 注意事项 父子进程通信 一 ...
- linux系统编程 小项目,linux系统编程小项目.doc
linux系统编程小项目.doc 一.项目概述简单智能远程监控功能服务器端1.服务器端利用随机数模拟向串口读取传感数据,需要模拟的传感数据要求有温度.湿度.光照.室内噪音度等等.2.服务器要求在数据保 ...
最新文章
- 解决android 编译失败 Unexpected scopes found in folder
- 用verilog实现检测1的个数_入门指南:用Python实现实时目标检测(内附代码)
- 各种PID算法的整理和总结
- 在新的标签页中代开编辑文件
- ADOQuery代替ClientDataSet做3-Tier系统
- java Swing中随机验证码的实现
- ddl dml dcl
- 从崩溃的选课系统,论为什么更安全的 HTTPS 协议没有被全面采用
- 实现多个输入框的dialog
- 微服务注册中心为什么要使用Consul替代Eureka?
- 《现代操作系统(中文第四版)》课后习题答案 第一章 引论
- M语言中的操作符说明:大括号{}-列List,方括号[]-记录Record
- PFC(Power Factor Correction)功率因数校正电路
- Ubuntu 16.04 安装QQ解决方案
- 两个通宵熬出来的互联网大厂最新面试题收集整理1000道(三-Memcached),欢迎点赞收藏!!!
- 【我是老中医】codeblocks无法编译的问题解决方法
- 游戏3D美术设计就业前景如何?现在饱和了吗
- Swift学习笔记 ——(一)
- 【C】C课程设计-驾校考试模拟系统
- duet连win10_duet display windows
热门文章
- nfs-ganesha 导出多个目录
- 2021年熔化焊接与热切割考试资料及熔化焊接与热切割考试技巧
- 跟着小马哥学系列之 Spring AOP(基于 XML 定义 Advice 源码解析)
- 万年历显示c语言设计,万年历C语言设计
- 统计英文文件中单词数和各单词出现的频率(次数)
- 【Python自然语言处理】隐马尔可夫模型中维特比(Viterbi)算法解决商务选择问题实战(附源码 超详细必看)
- 基于java+springboot+vue的医院固定资产管理系统
- java中replaceall_java replace和replaceAll
- 必应地图,你这样子怎么和谷歌拼?
- 0479-如何禁用HDP2.6.5的HDFS HA