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

智能推荐

个人联想Thinkpad X1 carbon 笔记本遇到的问题及解决办法_thinkpad x1carbon开机黑屏-程序员宅基地

文章浏览阅读214次。新买的一台联想Thinkpad X1 carbon 12代笔记本,不知道怎么搞的,突然有一天启动后会出现一段黑屏时间,然后等一段时间才会正常,非常地烦人。打电话给联想客服只提供了附近的维修地点,但那得花时间跑去啊。今天下午自己摸索终于解决问题了。没什么好办法,我开始卸载电脑上很多的软件,一是不想用了,二是想卸载的这些软件可能就是导致这个问题出现的原因,在卸载的时候看到电脑上的相关应用,但是在点开相机应用的时候竞然发现相机不能用了。怎么这么奇怪,我想到我以前确实是设置了一下相机的权限,难道是这个问题引起的。_thinkpad x1carbon开机黑屏

Krpano:打造全景漫游体验—基础(一)-程序员宅基地

文章浏览阅读577次,点赞29次,收藏8次。由于基础知识太多,篇幅太长,本文只讲解了krpano的运作机制、tour.js的讲解以及krpano各个元素的简单介绍,下一篇文章将详细讲解krpano中的动作也就是< action >元素的使用和用法。

vue 前端表格导入与导出_vue fixdata-程序员宅基地

文章浏览阅读557次。excel导入到表格表格导入与导出,文末带Blob.js和Export2Excel.js导入按钮<input type="file" @change="import(this)" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/>导入方法import(obj) { _vue fixdata

Error: protect(): protection stack overflow [防护堆叠上溢] 解决方案①_r studio中保护堆栈溢出-程序员宅基地

文章浏览阅读6.4k次。在执行R代码是遇到防护堆叠上溢的error,有效方案①1、命令行到Rgui.exe的地址(cmd)2、输入Rgui.exe --max-ppsize=5000003、在打开的R中输入options("expressions"=20000)memory.limit(size=8000000)https://www.researchgate.net/post/error_protect_protection_stack_overflow_in_R..._r studio中保护堆栈溢出

ORA-01092、ORA-00704、ORA-39700 错误的处理方法_ora-01092: oracle instance terminated. disconnecti-程序员宅基地

文章浏览阅读7.6k次。场景:通过rman做一个数据库的异机恢复,进行到最关键的一步,打开数据库的时候报了错误,详细如下SQL&gt; startup mount;Oracle 例程已经启动。Total System Global Area 6797832192 bytesFixed Size 2241264 bytesVariable Size 3523218704 bytesDatabase Buffers 3254..._ora-01092: oracle instance terminated. disconnection forced ora-00704: boots

java 判断ftp文件是否存在_java如何检验ftp服务器中指定文件是否存在-程序员宅基地

文章浏览阅读202次。项目工作中,需要检验ftp服务器中指定文件是否存在,在网上查阅了相关资料,可以通过ftpClient类进行实现。免费学习视频教程分享:java课程具体实现代码:import org.apache.commons.net.ftp.FTP;import org.apache.commons.net.ftp.FTPClient;import org.apache.commons.net.ftp.FTPR..._java判断ftp file是否存在

随便推点

ubuntu上opencv源码编译_libjasper-dev源码-程序员宅基地

文章浏览阅读301次。安装必备包sudo apt-get install build-essential cmake libjpeg-dev libtiff5-dev libjasper-dev安装gtkfor opencv3.2 and above:sudo apt-get install libgtk-3-devfor opencv2.4.x:sudo apt-get install libgtk2.0-dev安装v4l2sudo apt-get install v4l2ucp v4l-ut._libjasper-dev源码

安卓设备连接Unity Profiler进行性能分析_unity profile 手机-程序员宅基地

文章浏览阅读4.6k次,点赞2次,收藏7次。内容会持续更新,有错误的地方欢迎指正,谢谢!方式一:手机上运行游戏,以ADB方式连UnityProfiler分析游戏性能1.安卓环境:jdk、sdk:cmd(Win+R打开界面并输入cmd) 里直接输入 java 能正常输出即可 https://www.jianshu.com/p/21babde25dd5 adb:cmd 里直接输入 adb 能正常输出即可 https://www..._unity profile 手机

海思3559AV100实现8k文件编码_rk_mpi_cal_comm_getpicbuffersize-程序员宅基地

文章浏览阅读913次,点赞13次,收藏27次。有些小改动需要在common中改,都很简单就不写了,看一下就知道,另外我这边只测试了8k的一帧编码,如需连续编码视频需要再扩展一下,我这边由于公司项目安排就没再继续做了,测试出来8k文件编码的结果如下,颜色偏差是因为我的YUV和板子支持的YUV的存储顺序不一致,这里时间关系也就不调了,也算是顺利实现了芯片的8k文件编码。第二个函数是SAMPLE_VENC_CheckSensor,应该是检查镜头的分辨率支不支持要编的尺寸,这里我们先试的4k,所以也可以不管这个。_rk_mpi_cal_comm_getpicbuffersize

Android BLE 蓝牙通信库,2024年最新应届生面试销售岗位的面试问题技巧_android蓝牙通信-程序员宅基地

文章浏览阅读919次,点赞12次,收藏10次。然后根据自定义的协议,解析对应的 BeaconItem 中的 bytes,首先创建一个 BeaconParser,传入对应的 BeaconItem,然后根据协议不断读取数据, 如果协议中某个字段占 1 个字节,则调用 readByte,若占用两个字节则调用 readShort,如果要取某个字节的某个 bit 则调用 getBit。// 获取第 1 字节的第 1bit。可以在广播中携带设备的自定义数据,用于设备识别,数据广播,事件通知等,这样手机端无需连接设备就可以获取设备推送的数据。_android蓝牙通信

vue el-input表单验证禁止输入空格_element表单空格校验-程序员宅基地

文章浏览阅读1w次,点赞6次,收藏7次。element ui 中自带的表单必填项校验输入空格时,依然能逃过验证(required: true还是可以通过),需要再 在v-model 加上.trim来禁止输入空格字符。加上之后则不能输入空格。..._element表单空格校验

推荐文章

热门文章

相关标签