array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Linux下串口通信编程 - 爱码网

串口设备无论是在工控领域,还是在嵌入式设备领域,应用都非常广泛。而串口编程也就显得必不可少。偶然的一次机会,需要使用串口,而且操作系统还要求是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 = 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;

        ptty->ntm.c_iflag &= ~INPCK;

        break;

    case 'o':

    case 'O':

        ptty->ntm.c_cflag |= (PARODD|PARENB);

        ptty->ntm.c_iflag |= INPCK;

        break;

    case 'e':

    case 'E':

        ptty->ntm.c_cflag |= PARENB;

        ptty->ntm.c_cflag &= ~PARODD;

        ptty->ntm.c_iflag |= INPCK;

        break;

    case 'S':

    case 's':

        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:X\n",idx++,cc[0]);

    }

    cleanTTY(ptty);

}

#endif

 

串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,具有连接线少,通讯简单,得到广泛的使用。

    Linux对所有设备的访问是通过设备文件来进行的,串口也是这样,为了访问串口,只需打开其设备文件即可操作串口设备。在linux系统下面,每一个串口设备都有设备文件与其关联,设备文件位于系统的/dev目录下面。如linux下的/ttyS0,/ttyS1分别表示的是串口1和串口2。

    在串口编程中,比较重要的是串口的设置,我们要设置的部分包括:波特率,数据位,停止位,奇偶校验位;要注意的是,每台机器的串口默认设置可能是不同的,如果你没设置这些,仅仅按照默认设置进行发送数据,很可能出现n多异想不到而又查不出来的情况。

1) 设置波特率

#include <termios.h>
#include <unistd.h>
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);

2) 设置属性:奇偶校验位、数据位、停止位。主要设置<termbits.h>中的termios结构体即可:

#define NCCS 19
struct termios {
        tcflag_t c_iflag; 
        tcflag_t c_oflag; 
        tcflag_t c_cflag; 
        tcflag_t c_lflag; 
        cc_t c_line; 
        cc_t c_cc[NCCS]; 
};

有相应的函数供获取和设置属性:

int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, struct termios *termios_p);

3) 打开、关闭和读写串口。串口作为设备文件,可以直接用文件描述符来进行操作。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
#include <unistd.h>
int close(int fd);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);

 

嵌入式Linux操作系统使用接口标准POSIX的termios接口来控制串口的行为。在Linux系统中,串口等设备被当作文件进行处理,其程序模块主体实现如下:

int fd=open("/dev/ttyS1",O_RDWRIO_NOCTTY);//打开串口

……

new_options.c_cflag &=~PARENB;//无奇偶校验

new_options.c_cflag &=~CSIZE;//不隐藏数据位

new_options.c_cflag &=~CSTOP8;//无停止位

new_options.c_cflag |=CS8;//8位数据位

cfsetispeed(&new_options,B4800);//设置波特率4800bit/s

cfsetospeed(&new_options,B4800);

tcflush(fd,TCIOFLUSH);

tcsetattr(fd,TCSANOW,&new_options);//设置新的设备方式

    完成串口设置后,就可以使用read( )、write( )函数对串口进行操作。需注意的是,串口默认是阻塞型的,当没有数据到达时,将会阻塞挂起,这时可以通过多线程编程、串口超时设定或使用select轮询 等方式进行调整控制。本系统主要采用多线程编程实现对串口阻塞的调控,使用的是QT的Qthread类,也可以直接使用Linux自身的多线程函数进行操作。

相关文章: