一、主要功能
1.实现了非标波特率设置功能。
2.实现简单测试服务端和客户端。
3.实现一个简单通讯协议。
二、串口源码
cpp
#include <stdio.h> //标准输入输出.printf().perror()
#include <fcntl.h> //设备文件操作 open()
#include <sys/types.h> //设备文件操作 open()
#include <sys/stat.h> //设备文件操作 open()
#include <errno.h>
#include <string.h> //内存空间操作 memset()
#include <unistd.h> //读写函数 read()
#include <sys/select.h> //阻塞函数 select() 好像不要也行?
#include <sys/ioctl.h>
#include <asm/termbits.h>
//#include <linux/termios.h>
//int ioctl(int d, int request, ...);
#define COM2 "/dev/ttyS2"
#define BUFF_MAX_SIZE 1*1024*1024
#define LOG_ERROR(fmt, ...) fprintf(stderr, "[%s:%d]:" fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);
//#define _DEBUG
#ifdef _DEBUG
#define LOG_DEBUG(fmt, ...) fprintf(stdout, "[%s:%d]:" fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define LOG_DEBUG(fmt, ...)
#endif
int tcgetattr2(int fd, struct termios2 *termios_p)
{
struct termios2 termios;
if((ioctl(fd, TCGETS2, &termios))<0){
LOG_ERROR("faild to get the termios2:%s",strerror(errno));
return -1;
}
memcpy(termios_p, &termios, sizeof(termios));
return 0;
}
int tcsetattr2(int fd, int optional_actions, const struct termios2 *termios_p)
{
struct termios2 termios;
memcpy( &termios, termios_p, sizeof(termios));
if((ioctl(fd, TCSETS2, &termios))<0){
LOG_ERROR("faild to set the termios2:%s",strerror(errno));
return -1;
}
return 0;
}
int tcflush2(int fd, int queue_selector)
{
return ioctl (fd, TCFLSH, queue_selector);
}
int set_serial(int fd, int baudrate, int data, int parity, int stop)
{
struct termios2 struct_serial; //创建串口属性结构体
tcgetattr2(fd, &struct_serial); //获取当前串口属性
struct_serial.c_cflag &= ~CBAUD;
struct_serial.c_cflag |= BOTHER; //0x00001000;
struct_serial.c_ispeed = baudrate;
struct_serial.c_ospeed = baudrate;
struct_serial.c_cflag &= ~CSIZE; //初始化数据位为默认值
switch(data) {//设置数据位
case 7:
struct_serial.c_cflag |= CS7; //7位
break;
case 8:
struct_serial.c_cflag |= CS8; //8位
break;
default:
LOG_ERROR("set data %d error.", data);
return -1;
}
switch(parity) {//设置校验方式
case 0: //无校验
struct_serial.c_cflag &= ~PARENB; //关闭校验
struct_serial.c_iflag &= ~INPCK; //关闭输入校验
break;
case 1: //奇校验
struct_serial.c_cflag |= PARENB; //打开校验
struct_serial.c_iflag |= INPCK; //打开输入校验
struct_serial.c_cflag |= PARODD; //设为奇校验
break;
case 2: //偶校验
struct_serial.c_cflag |= PARENB; //打开校验
struct_serial.c_iflag |= INPCK; //打开输入校验
struct_serial.c_cflag &= ~PARODD; //设为奇校验
break;
default:
LOG_ERROR("set parity %d error.", parity);
return -1;
}
switch(stop) {//设置停止位
case 1: //1位
struct_serial.c_cflag &= ~CSTOPB;
break;
case 2: //2位
struct_serial.c_cflag |= CSTOPB;
break;
default:
LOG_ERROR("set stop %d error.", stop);
return -1;
}
//下列为一些配置,具体作用尚不明确
struct_serial.c_cc[VTIME] = 0; //超时时间.阻塞读操作中不再有数据到来的等待时间.单位取100ms.0无限
struct_serial.c_cc[VMIN] = 0; //单次接收最小字节数.阻塞读操作中若读取到的数据小于该数值则会等待
struct_serial.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); //非规范模式
struct_serial.c_oflag &= ~OPOST;//原始数据输出,二进制通讯一定要设置,否则协议会被驱动插入其他数据
struct_serial.c_cflag &= ~CRTSCTS;//关闭硬件流控制
struct_serial.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //这个是串口回显.若不配置则导致向Linux发什么回什么
tcflush2(fd, TCIOFLUSH); //配置前立刻清空一次缓冲区
if(-1 == tcsetattr2(fd, TCSANOW, &struct_serial)){
LOG_ERROR("set_data_stop_parity tcsetattr error"); //若出错附带打印错误信息
return -1;
}
return 0;
}
int open_serialport()
{
int fd = open(COM2, O_RDWR | O_NOCTTY ); //| O_NDELAY
if (fd < 0) {
LOG_ERROR("failed to open serial port. error:%d, msg:%s", errno, strerror(errno));
return -1;
}
if (set_serial(fd, 5000000, 8, 0, 1)) {
LOG_ERROR("failed to set serial port attr.");
return -1;
}
return fd;
}
int reset_serialport(int* fd)
{
if (*fd >=0)
close(*fd);
*fd = open_serialport();
if (*fd < 0) {
LOG_ERROR("failed to open serialport. error:%d, msg:%s", errno, strerror(errno));
return -1;
}
return 0;
}
int recv_select(int fd, int timeout) //设置阻塞
{
int res = -1;
struct timeval select_time; //超时时间结构体
select_time.tv_sec = timeout; //以s为单位
select_time.tv_usec = 0; //微妙部分设为0
fd_set set; //句柄映射数组
FD_ZERO(&set); //初始化
FD_SET(fd, &set); //建立句柄和数组的联系
res = select(fd+1, &set, NULL, NULL, &select_time); //阻塞等待fd文件的可读变化
return res;
}
int recv_data(int fd, unsigned char* buff, int len) //接收数据
{
int index=0, nread = -1;
int size = len;
do {
nread = read(fd, buff+index, size); //读取数据.这里200仅供参考.基本达不到
if(nread < 0) {
if (errno == EAGAIN) {
sleep(1);
LOG_DEBUG("read eagain.");
continue;
}
LOG_ERROR("failed to write data. error:%d, msg:%s", errno, strerror(errno));
return -1;
}
size -= nread;
index += nread;
}while(size > 0 && index < len);
return len;
}
int send_data(int fd, unsigned char* buff, int len)
{
int index=0, nwrite= -1;
int size = len;
do {
nwrite = write(fd, buff+index, size);
if (nwrite < 0) {
if (errno == EAGAIN) {
LOG_DEBUG("wirte eagain.");
sleep(1);
continue;
}
LOG_ERROR("failed to write data. error:%d, msg:%s", errno, strerror(errno));
return -1;
}
size -= nwrite;
index += nwrite;
}while(size > 0 && index < len);
return len;
}
void handle_any(int fd, int cmd)
{
unsigned char response_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00};
response_data[4] = cmd >> 8 & 0xff;
response_data[5] = cmd & 0xff;
if (send_data(fd, response_data, sizeof(response_data)) != sizeof(response_data)){
LOG_ERROR("reponse cmd0 error");
}
}
void handle_cmd1(int fd, unsigned char *buff, unsigned short cmd_len)
{
unsigned char response_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};
for (unsigned short i=0; i < cmd_len; ++i) {
if (buff[4+i] != 0xAA) {
LOG_ERROR("recived error cmd1 data");
response_data[7] = 1;
break;
}
}
LOG_DEBUG("server handle cmd1. cmd_len:%u, buff:[%X %X %X %X]",
cmd_len, buff[0], buff[1], buff[2], buff[3]);
if (send_data(fd, response_data, sizeof(response_data)) != sizeof(response_data)){
LOG_ERROR("reponse cmd1 error");
}
}
void handle_cmd2(int fd, unsigned char *buff)
{
unsigned int len = 0;
unsigned char response_data[BUFF_MAX_SIZE] = {0};
unsigned short count = buff[0] * 256 + buff[1];
char * msg = response_data + 6;
LOG_DEBUG("server handle cmd2: count:%u buff:[%X %X %X %X]",
count, buff[0], buff[1], buff[2], buff[3]);
sprintf(msg, "reponse serail port test mesage number: %u", count);
len = strlen(msg) + 5;
response_data[0] = 0xA5;
response_data[1] = 0x5A;
response_data[2] = ((len >> 8) & 0xff);
response_data[3] = (len & 0xff);
response_data[4] = 0x00;
response_data[5] = 0x02;
msg[len - 1] = 0;
msg[len - 2] = 0;
msg[len - 3] = 0;
len += 4;
if (send_data(fd, response_data, len) != len){
LOG_ERROR("reponse cmd2 error");
}
}
int run_server()
{
int fd = -1;
unsigned int len, cmd;
unsigned char *cmd_data;
unsigned char buff[BUFF_MAX_SIZE];
if (reset_serialport(&fd)) {
return -1;
}
while (1) {
memset(buff, 0, BUFF_MAX_SIZE);
if (recv_data(fd, buff, 4) != 4) {
LOG_ERROR("recv data header error.");
return -1;
}
if (buff[0] != 0xA5 || buff[1] != 0x5A) {
LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
tcflush2(fd, TCIOFLUSH);
sleep(1);
continue;
}
len = buff[2] * 256 + buff[3];
if (recv_data(fd, buff+4, len) != len) {
LOG_ERROR("recv data content error.");
return -1;
}
cmd = buff[4] * 256 + buff[5];
LOG_DEBUG("server handle cmd: %u len:%u buff:[%X %X %X %X %X %X %X %X %X %X]",
cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5],buff[6],buff[7],buff[8],buff[9]);
cmd_data = buff + 6;
switch (cmd) {
case 0x01:
handle_cmd1(fd, cmd_data, len - 8);
break;
case 0x02:
handle_cmd2(fd, cmd_data);
break;
default:
handle_any(fd, cmd);
break;
}
}
close(fd);
return 0;
}
int run_client()
{
int fd = -1;
unsigned int len, cmd;
unsigned short count = 0;
unsigned char cmd2_data[] = {0xA5, 0x5A, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
unsigned char buff[BUFF_MAX_SIZE];
if (reset_serialport(&fd)) {
return -1;
}
while (1) {
cmd2_data[6] = ((count >> 8) & 0xff);
cmd2_data[7] = (count & 0xff);
if (send_data(fd, cmd2_data, sizeof(cmd2_data)) != sizeof(cmd2_data)) {
LOG_ERROR("send cmd2 data error.");
return -1;
}
len = recv_select(fd, 3);
if (len < 0) {
LOG_ERROR("send cmd2 data error.");
return -1;
}
if (len == 0) {
LOG_DEBUG("send cmd2 data timeout. count:%u, data:[%X %X %X %X %X %X %X %X %X %X]",
count, cmd2_data[0], cmd2_data[1], cmd2_data[2], cmd2_data[3], cmd2_data[4],
cmd2_data[5], cmd2_data[6], cmd2_data[7], cmd2_data[8], cmd2_data[9]);
tcflush2(fd, TCIOFLUSH);
sleep(1);
continue;
}
memset(buff, 0, BUFF_MAX_SIZE);
if (recv_data(fd, buff, 4) != 4) {
LOG_ERROR("recv data header error.");
return -1;
}
if (buff[0] != 0xA5 || buff[1] != 0x5A) {
LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
tcflush2(fd, TCIOFLUSH);
sleep(1);
continue;
}
len = buff[2] * 256 + buff[3];
if (recv_data(fd, buff+4, len) != len) {
LOG_ERROR("recv data content error.");
return -1;
}
cmd = buff[4] * 256 + buff[5];
LOG_DEBUG("client handle cmd: %u len:%u buff:[%X %X %X %X %X %X]",
cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5]);
if (cmd != 0x02) {
LOG_ERROR("recv error cmd:%u.", cmd);
continue;
}
count++;
//LOG_DEBUG("SERIALPORT READ TEST COUNT:%d", count);
printf("recv message:%s\n", buff + 6);
}
close(fd);
return 0;
}
int run_client1()
{
int fd = -1;
unsigned int len, cmd, reponse;
unsigned int cmd1_len = 50*1024 + 2 + 2;
unsigned char cmd1_data[BUFF_MAX_SIZE];
unsigned char buff[BUFF_MAX_SIZE];
if (reset_serialport(&fd)) {
return -1;
}
memset(cmd1_data, 0xAA, BUFF_MAX_SIZE);
cmd1_data[0] = 0xA5;
cmd1_data[1] = 0x5A;
cmd1_data[2] = (cmd1_len >> 8) & 0xFF;
cmd1_data[3] = cmd1_len & 0xFF;;
cmd1_data[4] = 0;
cmd1_data[5] = 0x1;
cmd1_len += 4;
cmd1_data[cmd1_len - 1] = 0;
cmd1_data[cmd1_len - 2] = 0;
while (1) {
if (send_data(fd, cmd1_data, cmd1_len) != cmd1_len) {
LOG_ERROR("send cmd1 data error.");
return -1;
}
len = recv_select(fd, 3);
if (len < 0) {
LOG_ERROR("send cmd1 data error.");
return -1;
}
if (len == 0) {
LOG_DEBUG("send cmd1 data timeout. data:[%X %X %X %X %X %X %X %X %X %X]",
cmd1_data[0], cmd1_data[1], cmd1_data[2], cmd1_data[3], cmd1_data[4],
cmd1_data[5], cmd1_data[6], cmd1_data[7], cmd1_data[8], cmd1_data[9]);
tcflush2(fd, TCIOFLUSH);
sleep(1);
continue;
}
memset(buff, 0, BUFF_MAX_SIZE);
if (recv_data(fd, buff, 4) != 4) {
LOG_ERROR("recv data header error.");
return -1;
}
if (buff[0] != 0xA5 || buff[1] != 0x5A) {
LOG_ERROR("recv data header error magic:%X %X", buff[0], buff[1]);
tcflush2(fd, TCIOFLUSH);
sleep(1);
continue;
}
len = buff[2] * 256 + buff[3];
if (recv_data(fd, buff+4, len) != len) {
LOG_ERROR("recv data content error.");
return -1;
}
cmd = buff[4] * 256 + buff[5];
LOG_DEBUG("client handle cmd: %u len:%u buff:[%X %X %X %X %X %X]",
cmd, len, buff[0], buff[1],buff[2],buff[3],buff[4],buff[5]);
if (cmd != 0x01) {
LOG_ERROR("recv error cmd:%u.", cmd);
continue;
}
reponse = buff[6] * 256 + buff[7];
if (reponse) {
printf("cmd1 response message fail. error code:%d\n", reponse);
} else {
printf("cmd1 response message ok.\n");
}
}
close(fd);
return 0;
}
int main(int argc, const char* argv[])
{
if( argc < 2) {
printf("mode error.\n\t usage: ./%s mode(0-server, 1-client)\n", argv[0]);
return -1;
}
if (!strncasecmp(argv[1], "0", 1)) {
printf("run serial port server.\n");
run_server();
} else if (!strncasecmp(argv[1], "1", 1)) {
printf("run serial port client.\n");
run_client();
} else {
printf("run serial port client2.\n");
run_client1();
}
return 0;
}