封裝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: [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,代表阻塞讀取串口,之後程序會一直阻塞在那等待讀取串口數據):
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