天天看點

封裝uart序列槽類及測試

封裝uart序列槽類及測試

文章目錄

    • 封裝uart序列槽類及測試
    • 1.序列槽類
    • 2.序列槽類測試程式
    • 3.測試結果
      • (1)阻塞接收測試:
      • (2)非阻塞接收測試:
      • (3)發送測試

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: [email protected]
# 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,代表阻塞讀取序列槽,之後程式會一直阻塞在那等待讀取序列槽資料):

[email protected]:/# ./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!
*******************************************************************************************************************
[email protected]:/# ./test_serial brs
構造完成
open serial ok!
set serial ok!

           

然後可通過終端指令發送資料:

echo hello,serial>/dev/ttyS1
           

然後就會看到程式接收到資料後退出了(如果沒有接收到資料則會一直阻塞):

[email protected]:/# ./test_serial brs
構造完成
open serial ok!
set serial ok!
阻塞接收結果:13:hello,serial

析構完成
           

(2)非阻塞接收測試:

非阻塞接收即等待接收序列槽資料,如果一段時間後未接收到則該程式就結束了,等待的時間可以傳入(測試程式中預設設定了三秒,三秒後未接收則程式退出,這期間接收到則直接列印,當然也可以外部傳參的方式設定新的非阻塞讀取等待時間):

[email protected]:/# ./test_serial ubrs
構造完成
open serial ok!
set serial ok!
非阻塞接收結果:0:
析構完成
           
[email protected]:/# ./test_serial ubrs 5
構造完成
open serial ok!
set serial ok!
非阻塞接收結果:13:hello,serial

析構完成
           
[email protected]:/# ./test_serial ubrs 5
構造完成
open serial ok!
set serial ok!
非阻塞接收結果:0:
析構完成
           

(3)發送測試

發送測試可以結合minicom來測試。預設發送“hello serial”,也可以傳參發送想要發送的資料。

[email protected]:/# ./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!
           
[email protected]:/# ./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