封装uart串口类及测试

封装uart串口类及测试

1.串口类

我们后续将使用c++来开发程序,因此有必要将串口模块功能移植为串口类,移植后的结果:

serial.cpp:

#include "serial.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <strings.h>

#define SPEED_NUM 8  //支持的可设置的波特率的数量

serial::serial()
{
    printf("构造完成\n");
}

serial::~serial()
{    
    printf("析构完成\n");
}

serial::serial(const serial &obj)
{
    
}

int serial::open_serial(int fd,char* port)
{
    int ret = 0;

    fd = open(port, O_RDWR|O_NDELAY|O_NOCTTY);
    if(-1 == fd)
    {
        return -1;
    }

    ret = fcntl(fd, F_SETFL, 0);
    if(ret < 0)
        ;

    if(!isatty(STDIN_FILENO))
        ;
    
    return fd;
}

void serial::close_serial(int fd)
{
    close(fd);
}

int serial::set_serial(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
    int i;
    int ret = 0;
    int status;
    struct termios options_old, options_new;

    ret = tcgetattr(fd, &options_old);
    if(ret)
    {
        printf("file:%s,line:%d.Tcgetattr failed,tcgetattr(fd,&options_old)->%d\n",__FILE__,__LINE__,tcgetattr(fd,&options_old));
        return -1;
    }

    bzero(&options_new, sizeof(options_new));

    set_char_uart0_char_size(&options_new);

    if(set_baud_rate(speed, &options_new) < 0)
        return -2;

    if(set_flow_control(flow_ctrl, &options_new) < 0)
        return -3;

    if(set_data_bits(databits, &options_new) < 0)
        return -4;

    if(set_parity_check_bits(parity, &options_new) < 0)
        return -5;

    if(set_stop_bits(stopbits, &options_new) < 0)
        return -6;

    options_new.c_cc[VTIME] = 1;
    options_new.c_cc[VMIN] = 0;

    tcflush(fd,TCIFLUSH);

    ret = tcsetattr(fd, TCSANOW, &options_new);
    if(ret)
    {
        printf("file:%s,line:%d.Tcsetattr failed\n",__FILE__,__LINE__);
        return -7;
    }

    return 0;
}

int serial::block_read_from_serial(int fd, char *rcv_buf, int recv_len)
{
    int len = 0;
    int ret = 0;
    int time_flag = 0;
    fd_set rd;    
    
    FD_ZERO(&rd);
    FD_SET(fd, &rd);

    if(select(fd +1, &rd, NULL, NULL, NULL) < 0)
        ;
    else if(FD_ISSET(fd, &rd) > 0)
    {
        while((ret = read(fd, rcv_buf + len,recv_len - 1 - len)) >= 0)
        {
            len+=ret;
            if(ret < 16)
            {
                time_flag = 1;
                break;
            }
        }
        if(!time_flag)
            return -2;
    }

    return len;
}

int serial::unblock_read_from_serial(int fd, int wait_time, char *rcv_buf, int recv_len)
{
    int len = 0;
    int ret = 0;
    int time_flag = 0;
    fd_set rd;
    struct timeval tv;

    tv.tv_sec = 0;
    tv.tv_usec = wait_time * 1000000;
    FD_ZERO(&rd);
    FD_SET(fd, &rd);

    if(select(fd + 1, &rd, NULL, NULL, &tv) < 0)
        ;
    else if(FD_ISSET(fd, &rd) > 0)
    {
        while ((ret = read(fd, rcv_buf + len,recv_len - 1 - len)) >= 0)
        {
            len+=ret;
            if(ret < 16)
            {
                time_flag = 1;
                break;
            }
        }
        if(!time_flag)
            return -2;
    }

    return len;
}

int serial::write_to_serial(int fd, char *send_buf, int data_len)
{
    int len = 0;

    len = write(fd, send_buf, data_len);
    if(len == data_len)
        return len;
    else
        return -1;
}

void serial::set_char_uart0_char_size(struct termios* options)
{
    options->c_cflag |= (CLOCAL|CREAD);
    options->c_cflag &= ~CSIZE;

    return;
}

int serial::set_baud_rate(int speed, struct termios* options)
{
    int speed_arr[SPEED_NUM] = {B115200,B38400,B19200,B9600,B4800,B2400,B1200,B300};
    int name_arr[SPEED_NUM]  = {115200,38400,19200,9600,4800,2400,1200,300};
    int flag = 0;
    int i;

    for(i = 0;i < SPEED_NUM;i++)
    {
        if(speed == name_arr[i])
        {
            cfsetispeed(options,speed_arr[i]);
            cfsetospeed(options,speed_arr[i]);
            flag = 1;
        }
    }
    
    if(!flag)
    {
        printf("file:%s,line:%d.unsupported baud rate.\n",__FILE__,__LINE__);
        return -1;
    }
    
    return 1;
}

int serial::set_flow_control(int flow_ctrl, struct termios* options)
{


    switch (flow_ctrl)
    {
        case 0:
            options->c_cflag &= ~CRTSCTS;
            break;
        case 1:
            options->c_cflag |= CRTSCTS;
            break;
        case 2:
            options->c_cflag |= IXON |IXOFF |IXANY;
            break;
        default:
            return -1;
    }

    return 1;
}

int serial::set_data_bits(int databits, struct termios* options)
{
    switch (databits)
    {
        case 5:
            options->c_cflag |= CS5;
            break;
        case 6:
            options->c_cflag |= CS6;
            break;
        case 7:
            options->c_cflag |= CS7;
            break;
        case 8:
            options->c_cflag |= CS8;
            break;
        default:
            return -1;
    }

    return 0;
}

int serial::set_parity_check_bits(int parity, struct termios* options)
{
    switch (parity)
    {
        case 'n':
        case 'N':
            options->c_cflag &= ~PARENB;
            options->c_iflag &= ~INPCK;
            break;
        case 'o':
        case 'O':
            options->c_cflag |= (PARODD | PARENB);
            options->c_iflag |= INPCK;
            break;
        case 'e':
        case 'E':
            options->c_cflag |= PARENB;
            options->c_cflag &= ~PARODD;
            options->c_iflag |= INPCK;
            break;
        default:
            return -1;
    }

    return 1;
}

int serial::set_stop_bits(int stopbits, struct termios* options)
{
    switch(stopbits)
    {
        case 1:
            options->c_cflag &= ~CSTOPB;
            break;
        case 2:
            options->c_cflag |= CSTOPB;
            break;
        default:
            return -1;
    }
    return 1;
}

serial.h:

/******************************************************************************

                  版权所有 (C), 2017-2019, ZY

 ******************************************************************************
  文 件 名   : serial.h
  版 本 号   : 初稿
  作    者   : ZY
  生成日期   : 2018年9月13日 星期四
  最近修改   :
  功能描述   : serial.cpp 的头文件
  函数列表   :
  修改历史   :
  1.日    期   : 2018年9月13日 星期四
    作    者   : ZY
    修改内容   : 创建文件

******************************************************************************/

/*----------------------------------------------*
 * 包含头文件                                   *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 外部变量说明                                 *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 外部函数原型说明                             *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 内部函数原型说明                             *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 全局变量                                     *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 模块级变量                                   *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 常量定义                                     *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 宏定义                                       *
 *----------------------------------------------*/

#ifndef __SERIAL_H__
#define __SERIAL_H__


#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

#include<termios.h>

class serial
{
    private:
        void set_char_uart0_char_size(struct termios* options);

        int set_baud_rate(int speed, struct termios* options);
        
        int set_flow_control(int flow_ctrl, struct termios* options);
        
        int set_data_bits(int databits, struct termios* options);
        
        int set_parity_check_bits(int parity, struct termios* options);
        
        int set_stop_bits(int stopbits, struct termios* options);
        
    public:
        serial();

        ~serial();

        serial(const serial &obj);

        int open_serial(int fd,char* port);

        void close_serial(int fd);

        int set_serial(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity);

        int block_read_from_serial(int fd, char *rcv_buf, int recv_len);

        int unblock_read_from_serial(int fd, int wait_time, char *rcv_buf, int recv_len);

        int write_to_serial(int fd, char *send_buf, int data_len);
};


#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */


#endif /* __SERIAL_H__ */

2.串口类测试程序

test.cpp:

/******************************************************************************

                  版权所有 (C), 2017-2019, ZY

 ******************************************************************************
  文 件 名   : test.cpp
  版 本 号   : 初稿
  作    者   : ZY
  生成日期   : 2018年9月13日 星期四
  最近修改   :
  功能描述   : 測試串口類是否可用
  函数列表   :
  修改历史   :
  1.日    期   : 2018年9月13日 星期四
    作    者   : ZY
    修改内容   : 创建文件

******************************************************************************/

/*----------------------------------------------*
 * 包含头文件                                   *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 外部变量说明                                 *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 外部函数原型说明                             *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 内部函数原型说明                             *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 全局变量                                     *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 模块级变量                                   *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 常量定义                                     *
 *----------------------------------------------*/

/*----------------------------------------------*
 * 宏定义                                       *
 *----------------------------------------------*/

#include <string.h>
#include "serial.h"
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
    int fd = -1;
    int set_serial_res = -1;

    if(2 > argc)
    {
        printf("*******************************************************************************************************************\n");
        printf("please run like:%s %s (explain:brs means block read serial,ubrs means unblock read serial,ws means write serial)\n",argv[0],"'brs' or 'ubrs' or 'ws'");
        printf("if you want to write to serial, you can run like this:%s ws %s\n",argv[0],"hello serial!");
        printf("*******************************************************************************************************************\n");
        return -1;
    }

    //创建串口对象
    serial serial_obj;

    //打开串口
    fd = serial_obj.open_serial(fd, "/dev/ttyS1");
    if(fd < 0)
    {
        printf("open com filed\n");
        return -1;
    }
    printf("open serial ok!\n");

    //设置串口
    set_serial_res = serial_obj.set_serial(fd,38400,0,8,1,'N');
    if(set_serial_res < 0)
    {
        printf("set serial failed!:%d\n",set_serial_res);
        return -1;
    }
    printf("set serial ok!\n");

    char rcv_buf[100] = "\0";
    int recv_res = 0;

    //发送数据到串口
    if(!strcmp(argv[1],"ws"))
    {
        char send_buf[100] = "hello serial!";
        int send_serial_res = 0;

        if(3 == argc && argv[2] != NULL)
        {    
            memset(send_buf,'\0',sizeof(send_buf));
            strcpy(send_buf,argv[2]);
        }

        send_serial_res = serial_obj.write_to_serial(fd, send_buf, sizeof(send_buf));
        printf("发送结果:%d:%s\n",send_serial_res,send_buf);
    }

    //阻塞接收串口数据
    if(!strcmp(argv[1],"brs"))
    {
        recv_res = serial_obj.block_read_from_serial(fd, rcv_buf, sizeof(rcv_buf));
        printf("阻塞接收结果:%d:%s\n",recv_res,rcv_buf);
    }
    
    //非阻塞接收串口数据(这里等待时间的单位为s)
    if(!strcmp(argv[1],"ubrs"))
    {
        unsigned int wait_time = 3;
        
        if(3 == argc && argv[2] != NULL)
            wait_time = atoi(argv[2]);

        recv_res = serial_obj.unblock_read_from_serial(fd, wait_time, rcv_buf, sizeof(rcv_buf));
        printf("非阻塞接收结果:%d:%s\n",recv_res,rcv_buf);
    }

    //关闭串口
    serial_obj.close_serial(fd);

    return 0;
}

编译脚本:

#########################################################################
# File Name: compile.sh
# Author: loon
# mail: 2453419889@qq.com
# Created Time: 2018年09月13日 星期四 15时59分02秒
#########################################################################
#!/bin/bash

CC=mipsel-openwrt-linux-g++

$CC -o test_serial test.cpp serial.cpp

3.测试结果

在开发板上测试时,我们需要将要测试的串口短接(RXD1和TXD1,我这里是ttyS1),然后运行程序,分别测试阻塞接收、非阻塞接收和发送。

(1)阻塞接收测试:

运行test_serial程序(这里使用了外部传参的方式运行程序,所以运行时需要传参brs,代表阻塞读取串口,之后程序会一直阻塞在那等待读取串口数据):

root@ZhuoTK:/# ./test_serial 
*******************************************************************************************************************
please run like:./test_serial 'brs' or 'ubrs' or 'ws' (explain:brs means block read serial,ubrs means unblock read serial,ws means write serial)
if you want to write to serial, you can run like this:./test_serial ws hello serial!
*******************************************************************************************************************
root@ZhuoTK:/# ./test_serial brs
构造完成
open serial ok!
set serial ok!

然后可通过终端命令发送数据:

echo hello,serial>/dev/ttyS1

然后就会看到程序接收到数据后退出了(如果没有接收到数据则会一直阻塞):

root@ZhuoTK:/# ./test_serial brs
构造完成
open serial ok!
set serial ok!
阻塞接收结果:13:hello,serial

析构完成

(2)非阻塞接收测试:

非阻塞接收即等待接收串口数据,如果一段时间后未接收到则该程序就结束了,等待的时间可以传入(测试程序中默认设置了三秒,三秒后未接收则程序退出,这期间接收到则直接打印,当然也可以外部传参的方式设置新的非阻塞读取等待时间):

root@ZhuoTK:/# ./test_serial ubrs
构造完成
open serial ok!
set serial ok!
非阻塞接收结果:0:
析构完成
root@ZhuoTK:/# ./test_serial ubrs 5
构造完成
open serial ok!
set serial ok!
非阻塞接收结果:13:hello,serial

析构完成
root@ZhuoTK:/# ./test_serial ubrs 5
构造完成
open serial ok!
set serial ok!
非阻塞接收结果:0:
析构完成

(3)发送测试

发送测试可以结合minicom来测试。默认发送“hello serial”,也可以传参发送想要发送的数据。

root@ZhuoTK:/# ./test_serial ws
构造完成
open serial ok!
set serial ok!
发送结果:100:hello serial!
析构完成
Welcome to minicom 2.7

OPTIONS: 
Compiled on Jan 27 2016, 19:22:46.
Port /dev/ttyS1, 19:13:32

Press CTRL-A Z for help on special keys

hello serial!
root@ZhuoTK:/# ./test_serial ws serial!hello
构造完成
open serial ok!
set serial ok!
发送结果:100:serial!hello
析构完成
Welcome to minicom 2.7

OPTIONS: 
Compiled on Jan 27 2016, 19:22:46.
Port /dev/ttyS1, 19:23:23

Press CTRL-A Z for help on special keys

hello serial!serial!hello
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值