序号 内容 链接
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-串口通信相关推荐

  1. Linux系统编程:串口编程

    Linux系统编程:串口编程 Linux下的串口概述 Linux串口编程 代码解析 编译和测试 代码中的常量介绍 Linux下的串口概述 常见的数据通信的基本方式分为并行通信和串行通信. 1.并行通信 ...

  2. Linux——Linux系统编程之串口编程总结(串口的初始化、读写操作实践)

    目录 0 引言 1 串口编程的流程 1.1 打开串口 1.2 初始化串口 1.2.1 termios结构体 1.2.2 关键函数 1.2.3 初始化串口代码 2 串口的读写测试例程 0 引言 串口大家 ...

  3. linux进程通信发送方式,Linux服务器编程——Linux系统编程之进程通信

    进程通信又称IPC IPC方法 方法:管道(最简单) 信号(开销最小) 共享映射区/共享内存(无血缘关系) 本地套接字(最稳定) Linux文件类型: -   文件 d  目录 l   符号链接 s  ...

  4. 【Linux系统编程】进程通信之管道

    1.进程间通信介绍 1.1 进程通信的基本概念 在之前我们已经学习过进程地址空间.Linux 环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不 ...

  5. linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...

    信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...

  6. linux原子过程,linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

  7. linux有名管道数据异常,Linux系统编程—有名管道

    ▋****1. 管道的概念 管道,又名「无名管理」,或「匿名管道」,管道是一种非常基本,也是使用非常频繁的IPC方式. 1.1 管道本质 管道的本质也是一种文件,不过是伪文件,实际上是一块内核缓冲区, ...

  8. Linux系统编程(三)进程间的通信

    Linux系统编程(三)进程间的通信 一.为什么需要进程之间的通信(IPC)? 二.管道 1.概念 2.特质 3.原理 4.局限性 5.代码 2.读入数据 三.共享存储映射 注意事项 父子进程通信 一 ...

  9. linux系统编程 小项目,linux系统编程小项目.doc

    linux系统编程小项目.doc 一.项目概述简单智能远程监控功能服务器端1.服务器端利用随机数模拟向串口读取传感数据,需要模拟的传感数据要求有温度.湿度.光照.室内噪音度等等.2.服务器要求在数据保 ...

最新文章

  1. 解决android 编译失败 Unexpected scopes found in folder
  2. 用verilog实现检测1的个数_入门指南:用Python实现实时目标检测(内附代码)
  3. 各种PID算法的整理和总结
  4. 在新的标签页中代开编辑文件
  5. ADOQuery代替ClientDataSet做3-Tier系统
  6. java Swing中随机验证码的实现
  7. ddl dml dcl
  8. 从崩溃的选课系统,论为什么更安全的 HTTPS 协议没有被全面采用
  9. 实现多个输入框的dialog
  10. 微服务注册中心为什么要使用Consul替代Eureka?
  11. 《现代操作系统(中文第四版)》课后习题答案 第一章 引论
  12. M语言中的操作符说明:大括号{}-列List,方括号[]-记录Record
  13. PFC(Power Factor Correction)功率因数校正电路
  14. Ubuntu 16.04 安装QQ解决方案
  15. 两个通宵熬出来的互联网大厂最新面试题收集整理1000道(三-Memcached),欢迎点赞收藏!!!
  16. 【我是老中医】codeblocks无法编译的问题解决方法
  17. 游戏3D美术设计就业前景如何?现在饱和了吗
  18. Swift学习笔记 ——(一)
  19. 【C】C课程设计-驾校考试模拟系统
  20. duet连win10_duet display windows

热门文章

  1. nfs-ganesha 导出多个目录
  2. 2021年熔化焊接与热切割考试资料及熔化焊接与热切割考试技巧
  3. 跟着小马哥学系列之 Spring AOP(基于 XML 定义 Advice 源码解析)
  4. 万年历显示c语言设计,万年历C语言设计
  5. 统计英文文件中单词数和各单词出现的频率(次数)
  6. 【Python自然语言处理】隐马尔可夫模型中维特比(Viterbi)算法解决商务选择问题实战(附源码 超详细必看)
  7. 基于java+springboot+vue的医院固定资产管理系统
  8. java中replaceall_java replace和replaceAll
  9. 必应地图,你这样子怎么和谷歌拼?
  10. 0479-如何禁用HDP2.6.5的HDFS HA