linux c/c++串口通信_linux c++ 一条线程监听串口-程序员宅基地

技术标签: C++  

/*串口设备无论是在工控领域,还是在嵌入式设备领域,应用都非常广泛。而串口编程也就显得必不可少。
偶然的一次机会,需要使用串口,而且操作系统还要求是Linux,因此,趁着这次机会,综合别人的代码,
进行了一次整理和封装。具体的封装格式为C代码,这样做是为了很好的移植性,使它可以在C和C++环境下,
都可以编译和使用。代码的头文件如下: */

/// 
//filename:stty.h 
#ifndef __STTY_H__ 
#define __STTY_H__ 

//包含头文件
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <termios.h> 
#include <errno.h> 
#include <pthread.h> 
// 
// 串口设备信息结构 
typedef struct tty_info_t 

    int fd; // 串口设备ID 
    pthread_mutex_t mt; // 线程同步互斥对象 
    char name[24]; // 串口设备名称,例:"/dev/ttyS0" 
    struct termios ntm; // 新的串口设备选项 
    struct termios otm; // 旧的串口设备选项 
} TTY_INFO; 
// 
// 串口操作函数 
TTY_INFO *readyTTY(int id); 
int setTTYSpeed(TTY_INFO *ptty, int speed); 
int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits); 
int cleanTTY(TTY_INFO *ptty); 
int sendnTTY(TTY_INFO *ptty,char *pbuf,int size); 
int recvnTTY(TTY_INFO *ptty,char *pbuf,int size); 
int lockTTY(TTY_INFO *ptty); 
int unlockTTY(TTY_INFO *ptty); 

#endif 


/*从头文件中的函数定义不难看出,函数的功能,使用过程如下: 
(1) 打开串口设备,调用函数setTTYSpeed(); 
(2) 设置串口读写的波特率,调用函数setTTYSpeed(); 
(3) 设置串口的属性,包括停止位、校验位、数据位等,调用函数setTTYParity(); 
(4) 向串口写入数据,调用函数sendnTTY(); 
(5) 从串口读出数据,调用函数recvnTTY(); 
(6) 操作完成后,需要调用函数cleanTTY()来释放申请的串口信息接口; 
其中,lockTTY()和unlockTTY()是为了能够在多线程中使用。在读写操作的前后,需要锁定和释放串口资源。 
具体的使用方法,在代码实现的原文件中,main()函数中进行了演示。下面就是源代码文件: */

 
//stty.c 

#include <stdio.h> 
#include <sys/ioctl.h> 
#include "stty.h" 

/// 
// 初始化串口设备并进行原有设置的保存 
TTY_INFO *readyTTY(int id) 

    TTY_INFO *ptty; 
    
    ptty = (TTY_INFO *)malloc(sizeof(TTY_INFO)); 
    if(ptty == NULL) 
        return NULL; 
    memset(ptty,0,sizeof(TTY_INFO)); 
    pthread_mutex_init(&ptty->mt,NULL); 
    sprintf(ptty->name,"/dev/ttyS%d",id); 
    // 
    // 打开并且设置串口 
    ptty->fd = open(ptty->name, O_RDWR | O_NOCTTY |O_NDELAY); 
    if (ptty->fd <0) 
    { 
        free(ptty); 
        return NULL; 
    } 
    // 
    // 取得并且保存原来的设置 
    tcgetattr(ptty->fd,&ptty->otm); 
    return ptty; 

/// 
// 清理串口设备资源 
int cleanTTY(TTY_INFO *ptty) 

    // 
    // 关闭打开的串口设备 
    if(ptty->fd>0) 
    { 
        tcsetattr(ptty->fd,TCSANOW,&ptty->otm); 
        close(ptty->fd); 
        ptty->fd = -1; 
        free(ptty); 
        ptty = NULL; 
    } 
    
    return 0; 


/// 
// 设置串口通信速率 
// ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针 
// speed 参数类型(int),用来设置串口的波特率 
// return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值 
/// 
int setTTYSpeed(TTY_INFO *ptty, int speed) 

    int i; 
    // 
    // 进行新的串口设置,数据位为8位 
    bzero(&ptty->ntm, sizeof(ptty->ntm)); 
    tcgetattr(ptty->fd,&ptty->ntm); 
    ptty->ntm.c_cflag = /*CS8 |*/ CLOCAL | CREAD; 
    
    switch(speed) 
    { 
    case 300: 
        ptty->ntm.c_cflag |= B300; 
        break; 
    case 1200: 
        ptty->ntm.c_cflag |= B1200; 
        break; 
    case 2400: 
        ptty->ntm.c_cflag |= B2400; 
        break; 
    case 4800: 
        ptty->ntm.c_cflag |= B4800; 
        break; 
    case 9600: 
        ptty->ntm.c_cflag |= B9600; 
        break; 
    case 19200: 
        ptty->ntm.c_cflag |= B19200; 
        break; 
    case 38400: 
        ptty->ntm.c_cflag |= B38400; 
        break; 
    case 115200: 
        ptty->ntm.c_cflag |= B115200; 
        break; 
    } 
    ptty->ntm.c_iflag = IGNPAR; 
    ptty->ntm.c_oflag = 0; 
    // 
    // 
    tcflush(ptty->fd, TCIFLUSH); 
    tcsetattr(ptty->fd,TCSANOW,&ptty->ntm); 
    // 
    // 
    return 0; 

// 设置串口数据位,停止位和效验位 
// ptty 参数类型(TTY_INFO *),已经初始化的串口设备信息结构指针 
// databits 参数类型(int), 数据位,取值为7或者8 
// stopbits 参数类型(int),停止位,取值为1或者2 
// parity 参数类型(int),效验类型 取值为N,E,O,,S 
// return 返回值类型(int),函数执行成功返回零值,否则返回大于零的值 
/// 
int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits) 

    // 
    // 取得串口设置 
    if( tcgetattr(ptty->fd,&ptty->ntm) != 0) 
    { 
        printf("SetupSerial [%s]\n",ptty->name); 
        return 1; 
    } 
    
    bzero(&ptty->ntm, sizeof(ptty->ntm)); 
    ptty->ntm.c_cflag = CS8 | CLOCAL | CREAD; 
    ptty->ntm.c_iflag = IGNPAR; 
    ptty->ntm.c_oflag = 0; 
    // 
    // 设置串口的各种参数 
    ptty->ntm.c_cflag &= ~CSIZE; 
    switch (databits) 
    { //设置数据位数 
    case 7: 
        ptty->ntm.c_cflag |= CS7; 
        break; 
    case 8: 
        ptty->ntm.c_cflag |= CS8; 
        break; 
    default: 
        printf("Unsupported data size\n"); 
        return 5; 
    } 
    // 
    // 
    switch (parity) 
    { // 设置奇偶校验位数 
    case n: 
    case N: 
        ptty->ntm.c_cflag &= ~PARENB; /* Clear parity enable */ 
        ptty->ntm.c_iflag &= ~INPCK; /* Enable parity checking */ 
        break; 
    case o: 
    case O: 
        ptty->ntm.c_cflag |= (PARODD|PARENB); /* 设置为奇效验*/ 
        ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */ 
        break; 
    case e: 
    case E: 
        ptty->ntm.c_cflag |= PARENB; /* Enable parity */ 
        ptty->ntm.c_cflag &= ~PARODD; /* 转换为偶效验*/ 
        ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */ 
        break; 
    case S: 
    case s: /*as no parity*/ 
        ptty->ntm.c_cflag &= ~PARENB; 
        ptty->ntm.c_cflag &= ~CSTOPB; 
        break; 
    default: 
        printf("Unsupported parity\n"); 
        return 2; 
    } 
    // 
    // 设置停止位 
    switch (stopbits) 
    { 
    case 1: 
        ptty->ntm.c_cflag &= ~CSTOPB; 
        break; 
    case 2: 
        ptty->ntm.c_cflag |= CSTOPB; 
        break; 
    default: 
        printf("Unsupported stop bits\n"); 
        return 3; 
    } 
    // 
    // 
    ptty->ntm.c_lflag = 0; 
    ptty->ntm.c_cc[VTIME] = 0; // inter-character timer unused 
    ptty->ntm.c_cc[VMIN] = 1; // blocking read until 1 chars received 
    tcflush(ptty->fd, TCIFLUSH); 
    if (tcsetattr(ptty->fd,TCSANOW,&ptty->ntm) != 0) 
    { 
        printf("SetupSerial \n"); 
        return 4; 
    } 
    
    return 0; 

int recvnTTY(TTY_INFO *ptty,char *pbuf,int size) 

    int ret,left,bytes; 
    
    left = size; 
    
    while(left>0) 
    { 
        ret = 0; 
        bytes = 0; 
        
        pthread_mutex_lock(&ptty->mt); 
        ioctl(ptty->fd, FIONREAD, &bytes); 
        if(bytes>0) 
        { 
            ret = read(ptty->fd,pbuf,left); 
        } 
        pthread_mutex_unlock(&ptty->mt); 
        if(ret >0) 
        { 
            left -= ret; 
            pbuf += ret; 
        } 
        usleep(100); 
    } 
    
    return size - left; 

int sendnTTY(TTY_INFO *ptty,char *pbuf,int size) 

    int ret,nleft; 
    char *ptmp; 
    
    ret = 0; 
    nleft = size; 
    ptmp = pbuf; 
    
    while(nleft>0) 
    { 
        pthread_mutex_lock(&ptty->mt); 
        ret = write(ptty->fd,ptmp,nleft); 
        pthread_mutex_unlock(&ptty->mt); 
        
        if(ret >0) 
        { 
            nleft -= ret; 
            ptmp += ret; 
        } 
        //usleep(100); 
    } 
    
    return size - nleft; 

int lockTTY(TTY_INFO *ptty) 

    if(ptty->fd < 0) 
    { 
        return 1; 
    } 
    
    return flock(ptty->fd,LOCK_EX); 

int unlockTTY(TTY_INFO *ptty) 

    if(ptty->fd < 0) 
    { 
        return 1; 
    } 
    
    return flock(ptty->fd,LOCK_UN); 


#ifdef LEAF_TTY_TEST 
/// 
// 接口测试 
int main(int argc,char **argv) 

    TTY_INFO *ptty; 
    int nbyte,idx; 
    unsigned char cc[16]; 
    
    ptty = readyTTY(0); 
    if(ptty == NULL) 
    { 
        printf("readyTTY(0) error\n"); 
        return 1; 
    } 
    // 
    // 
    lockTTY(ptty); 
    if(setTTYSpeed(ptty,9600)>0) 
    { 
        printf("setTTYSpeed() error\n"); 
        return -1; 
    } 
    if(setTTYParity(ptty,8,N,1)>0) 
    { 
        printf("setTTYParity() error\n"); 
        return -1; 
    } 
    // 
    idx = 0; 
    while(1) 
    { 
        cc[0] = 0xFA; 
        sendnTTY(ptty,&cc[0],1); 
        nbyte = recvnTTY(ptty,cc,1); 
        printf("%d:%02X\n",idx++,cc[0]); 
    } 
    
    cleanTTY(ptty); 
    

#endif 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/developerof/article/details/82317540

智能推荐

flask-login-程序员宅基地

文章浏览阅读170次。创建扩展对象实例from flask_login import LoginManagerlogin_manager = LoginManager()login_manager.login_view = 'auth.login'# 上面这一句是设置登录视图的名称,如果一个未登录用户请求一个只有登录用户才能访问的视图,# 则闪现一条错误消息,并重定向到这里设置的登录视图。# 如果未设置..._python flask please log in to access this page

html怎么控制top值为0,关于vue滚动scrollTop 赋值一直为0问题-程序员宅基地

文章浏览阅读428次。Vue中document.body.scrollTop的值总为零的解决办法最近在做vue的时候监听页面滚动发现document.body.scrollTop一直为0但是发现document.body.scrollTop一直是0。查资料发现是DTD的问题。页面指定了DTD,即指定了DOCTYPE时,使用document.documentElement。页面没有DTD,即没指定DOCTYPE时,使用d..._滚动给scrolltop赋值

kingbase数据库安装教程(初步使用)(人大金仓)-程序员宅基地

文章浏览阅读2.1k次,点赞25次,收藏21次。人大金仓数据库管理系统KingbaseES(简称:金仓数据库或KingbaseES)是北京人大金仓信息技术股份有限公司自主研制开发的具有自主知识产权的通用关系型数据库管理系统。_kingbase

vue基础笔试题_vue笔试题-程序员宅基地

文章浏览阅读1.2w次,点赞20次,收藏156次。ctions 选项用来定义事件处理方法,用于处理 state 数据。actions 类似于 mutations,不同之处在于 actions 是异步执行的,事件处理函数可以接收 {commit} 对象,完成 mutation 提交,从而方便 devtools 调试工具跟踪状态的 state 变化。..............._vue笔试题

isis协议配置和详解-程序员宅基地

文章浏览阅读1.1w次,点赞2次,收藏23次。isis是一种与ospf很相似的网络协议(属于动态路由协议),它被应用在巨大规模网络,如运营商以及银行等。同样的它也是基于链路状态算法,支持clnp网络,ip网络。与ospf不同的是,它是基于数据链路层报文传输,而ospf则是在ip层进行计算。它可以自动的发现远程网络,只要网络拓扑结构发生了变化,路由器就会相互交换路由信息,不仅能够自动获知新增加的网络,还可以在当前网络连接失败时找出备用路径。无类..._isis协议配置

Proxychains 手册_proxychains是什么-程序员宅基地

文章浏览阅读1.9k次。名称:Proxychains – 通过代理服务器进行连接语法:proxychains 描述:这个程序会强制所有使用特定tcp连接的客户端所引起的TCP连接走代理通道。它是一种跳板程序。这个软件和sockscap、premo、eborder异曲同工。2.0版支持SOCKS4、SOCKS5、HTTP类的代理。认证方法:socks-“user/pass”,http-“basic_proxychains是什么

随便推点

Oracle触发器原理、创建、修改、删除_用oracle创建一个instead of触发器,当在course表中删除数据,不允许在course-程序员宅基地

文章浏览阅读3.7k次,点赞2次,收藏7次。本篇主要内容如下:8.1 触发器类型8.1.1 DML触发器8.1.2 替代触发器8.1.3 系统触发器8.2创建触发器8.2.1 触发器触发次序8.2.2 创建DML触发器8.2.3 创建替代(INSTEAD OF)触发器8.2.3 创建系统事件触发器8.2.4 系统触发器事件属性8.2.5 使用触发器谓词8.2.6 重新编译触发器8.3删除和使用触发器8.4触发器和数据字典8.5数据库触发器的应用举例8.6 触发器的查看8...._用oracle创建一个instead of触发器,当在course表中删除数据,不允许在course表

计算机科学与技术网上书店,计算机科学与技术毕业论文:基于web的网上书店.doc...-程序员宅基地

文章浏览阅读188次。本科毕业论文(设计)题  目  基于web的网上书店学生姓名专业名称  计算机科学与技术指导教师目录1、引言52、系统概述62.1概述62.2 开发平台73.需求分析73.1总体需求描述73.2系统总体功能图73.3系统需要实现的功能83.4业务流程图94.详细设计114.1数据库详细设计114.2建立数据库124.3页面详细设计:185用户手册225.1普通用户:225.2管理员:24参考文献3..._计算机科学与技术毕业设计网上书店

素数求和_输入一个正整数n和n个正整数,统计其中素数的和。-程序员宅基地

文章浏览阅读1.6k次。Description输入一个正整数N和N个正整数,统计其中素数的和。Input输入一个正整数N(1≤N≤100)和N个正整数(≥3),用空格分隔。Output输出所有素数,用空格隔开;再输出这些素数和。Sample Input10 4 5 8 12 13 24 34 37 20 885 1 5 8 12 13Sample Output5 13 37 s=555 13 s=..._输入一个正整数n和n个正整数,统计其中素数的和。

Oracle DB 使用RMAN创建备份2_rman 备份 生成两个文件-程序员宅基地

文章浏览阅读2.5k次。归档备份:概念归档备份:概念 如果需要在指定时间内保留联机备份,RMAN 通常会假定用户可能需要在自执行该备份以来到现在之间的任意时间执行时间点恢复。为了满足这一要求,RMAN 会在此时段内保留归档日志。但是,可能仅需要在指定的时间(如两年)内保留特定备份(并使其保持一致和可恢复)。用户不打算恢复到自执行该备份以后的某一时间点,只是希望能够正好恢复到执行该备_rman 备份 生成两个文件

JS实用技巧之断点调试详解_js断点调试-程序员宅基地

文章浏览阅读9.3k次,点赞9次,收藏58次。引言调试能力是一个程序员的生存根本,可是很多初学者却忽视调试。今天我们就来讨究一下JS的调试技巧。本文章将会详细列举JS相关的各种实用调试技巧。如果您是JS的初学者,那么这篇文章将对您有很大的帮助。为什么要调试?程序就是函数堆砌起来的,程序的运行就是函数的执行过程。而通过JS调试,我们可以更为直观的追踪到在程序运行中,函数的执行顺序,以及各个参数的变化。这样我们就可以快速的定位到问题所在。1. 什么是JS调试?在程序运行中,我们总会遇到各种bug,而通过代码的追踪代码的运行顺序从而定位到问题的过_js断点调试

记录一次kafka内存溢出,消费慢_kafka消费导致内存泄露-程序员宅基地

文章浏览阅读1k次。记录一次kafka内存溢出,消费慢_kafka消费导致内存泄露

推荐文章

热门文章

相关标签