众所周知, 计算机内部CPU与接口之间是按并行方式进行工作的,而许多外设和计算机之间是按串行方式进行通信,所谓的串行方式,就是在一根传输线上一位一位的传送信息,所用的传输线少,并且可以借助现成的电话网进行信息传送,因此,特别适合于远距离传输.对于那些与计算机相距不远的人-机交换设备和串行存储的外部设备如终端、打印机、逻辑分析仪、磁盘等,采用串行方式交换数据也很普遍. 所以不要看不起串行通信,它是一种经典的传统的通信方式。网上用C写的串口通信程序很多,这里我用C++封装了一个类,并加入了自己写的日志操作。
/*
加入了日志功能,记录串口的状态信息及接收信息,对发送信息新建日志;
加入自动锁的功能,使主线程与接收线程和发送线程自动同步
*/
#ifndef MY_COM_H
#define MY_COM_H
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include "my_log/my_log.h"
#include <string>
#include <string.h>
#include "my_locker/auto_lock.h"
using namespace std;
const int MAXREADLEN = 256;
const int MAXSENDLEN = 256;
const int MINSIZE = 256; //the minsize of g_buffer
const int MAXSIZE = 10240; // the maxsize of g_buffer
const int SLEEPTIME = 60;
const int SLEEPTINES = 5;
enum mutex
{
RDLK = 0,
WRLK = 1,
};
class My_com
{
public:
My_com();
My_com(char* log_path, char *log_name);
~My_com();
int open_com(const int nport);
int set_com(const int baud_rate, const int data_bits,
char parity, const int stop_bits);
int read_com(int rdlen = MAXREADLEN);
int write_com(int wrlen = MAXSENDLEN);
static void* read_thread_function(void*);
static void* send_thread_function(void*);
int create_read_thread();
int create_send_thread();
friend int parse(My_com *pcom, string &str); //find the str betweeb '$' and '\n'
friend int send(My_com* , int fd, int wrlen = MAXSENDLEN); //send to fd
char m_recv_buf[MAXREADLEN];
char m_send_buf[MAXSENDLEN];
pthread_t m_read_thrdID; //default two thread
pthread_t m_send_thrdID;
My_log *m_plog;
private:
int m_port;
};
#endif // MY_COM_H
以下实现:
#include "my_com.h"
string g_buffer; //接收BUFFER
Mutex_locker g_locker; //全局锁, 用于读写线程对全局BUFFER的加锁
//Mutex_locker g_log_locker;
My_com::My_com()
{
this->m_read_thrdID = 0;
this->m_send_thrdID = 0;
this->m_port = 0;
memset(m_recv_buf, 0, sizeof(m_recv_buf));
memset(m_send_buf, 0 , sizeof(m_send_buf));
m_plog = new My_log("./", "com_status.log");
}
My_com::My_com(char *log_path, char *log_name)
{
this->m_read_thrdID = 0;
this->m_send_thrdID = 0;
this->m_port = 0;
memset(m_recv_buf, 0, sizeof(m_recv_buf));
memset(m_send_buf, 0 , sizeof(m_send_buf));
m_plog = new My_log(log_path, log_name);
}
My_com::~My_com()
{
if(close(m_port))
{
char msg[100] = {0};
sprintf(msg, "close port failed :%s\n", strerror(errno));
m_plog->write_log(msg, ERR);
exit(-1);
}
m_plog->write_log("close port done\n", INFO);
delete m_plog;
}
int My_com::open_com(const int nport)
{
char path_name[20];
if((nport < 0) || (nport > 3))
{
char msg[100] = {0};
sprintf(msg, "the port id [%d] is out of rang\n", nport);
m_plog->write_log(msg, ERR);
return -1;
}
sprintf(path_name, "/dev/ttyS%d", nport);
if(m_port != 0)
{
m_plog->write_log("the port is busying", ERR);
return -1;
}
if((m_port = open(path_name, O_RDWR | O_NOCTTY)) <= 0)
{
char msg[100] = {0};
sprintf(msg, "can not open port [%d]: %s \n", nport, strerror(errno));
m_plog->write_log(msg, ERR);
exit(-1);
}
if(!isatty(m_port))
{
m_plog->write_log("is not a terminal\n", ERR);
}
char msg[100] = {0};
sprintf(msg, "open port done, the port id is %d\n", m_port);
m_plog->write_log(msg, INFO);
return 0;
}
int My_com::set_com(const int baud_rate, const int data_bits, char parity, const int stop_bits)
{
struct termios newtio;
memset(&newtio, 0, sizeof(newtio));
if(tcgetattr(m_port, &newtio))
{
char msg[100] = {0};
sprintf(msg, "tcgetattr error : %s\n", strerror(errno));
m_plog->write_log(msg, ERR);
return -1;
}
m_plog->write_log("tcgetattr done\n", INFO);
//set data bits
newtio.c_cflag &= ~CSIZE;
switch(data_bits)
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio .c_cflag |= CS8;
break;
default:
newtio.c_cflag |= CS8;
break;
}
//set baud rate
switch (baud_rate)
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
}
//set stop bits
switch (stop_bits)
{
case 1:
newtio.c_cflag |= ~CSTOPB;
break;
case 2:
newtio.c_cflag |= CSTOPB;
break;
default:
newtio.c_cflag |= ~CSTOPB;
break;
}
//set parity
switch (parity)
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~PARODD;
break;
}
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CRTSCTS;
//raw model
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
newtio.c_oflag &= ~OPOST;
newtio.c_iflag &= ~(INLCR|IGNCR|ICRNL);
newtio.c_iflag &= ~(ONLCR|OCRNL);
newtio.c_oflag &= ~(INLCR|IGNCR|ICRNL);
newtio.c_oflag &= ~(ONLCR|OCRNL);
//different from minicom set
newtio.c_cflag &= ~HUPCL;
newtio.c_iflag &= ~INPCK;
newtio.c_iflag |= IGNBRK;
newtio.c_iflag &= ~ICRNL;
newtio.c_iflag &= ~IXON;
newtio.c_lflag &= ~IEXTEN;
newtio.c_lflag &= ~ECHOK;
newtio.c_lflag &= ~ECHOCTL;
newtio.c_lflag &= ~ECHOKE;
newtio.c_oflag &= ~ONLCR;
//
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 0;
tcflush(m_port, TCIOFLUSH);
if(tcsetattr(m_port, TCSANOW, &newtio))
{
char msg[100] = {0};
sprintf(msg, "tcsetattr error : %s\n", strerror(errno));
m_plog->write_log(msg, ERR);
return -1;
}
char msg[100];
sprintf(msg, "tcsetattr done, baud_rate:%d, data_bits:%d, parity:%c, stop_bit:%d\n", baud_rate, data_bits, parity, stop_bits);
m_plog->write_log(msg, INFO);
}
//@param read_len: read number one time
int My_com::read_com(int read_len)
{
static long int rdcnt = 0;
int rdlen = 0;
int len = 0;
if((m_recv_buf == NULL) || (read_len > MAXREADLEN))
{
m_plog->write_log("the recv_buf is null or out of range", ERR);
return -1;
}
m_plog->write_log("\nstarting recving.........", INFO);
do
{
while (true)
{
pthread_testcancel(); //set cancel state
rdlen = read(m_port, m_recv_buf+ len, read_len);
//m_plog->write_log(".", INFO);
pthread_testcancel();
len += rdlen;
if(len >= MAXREADLEN)
{
rdlen = 0;
break;
}
}
rdcnt += len; //in all read
len = 0;
char msg[100] = {0};
sprintf(msg, "have read %ld : ", rdcnt);
m_plog->write_log(msg, INFO);
m_plog->write_log(m_recv_buf, INFO);
//m_plog->write_log("\n", INFO);
}while(false);
}
int My_com::write_com(int wrlen)
{
m_plog->write_log("\nstarting sending.........", INFO);
static long int sdcnt = 0;
int len = write(m_port, m_send_buf, wrlen);
sdcnt += len;
if(len <= 0)
{
char msg[100] = {0};
sprintf(msg, "send error: %s ", strerror(errno));
m_plog->write_log(msg, ERR);
return len;
}
char msg[100] = {0};
sprintf(msg, "have send %ld :", sdcnt);
m_plog->write_log(msg, INFO);
m_plog->write_log(m_send_buf, INFO);
//m_plog->write_log("\n", INFO);
return len;
}
void* My_com::read_thread_function(void *arg)
{
//My_log rd_thrd_log;
char msg[100];
sprintf(msg, "new read thead create, ID: %x", pthread_self());
//rd_thrd_log.write_log(msg, INFO)
My_com *pcom = (My_com*)arg;
int cnt_sleep = 0;
do
{
if(g_buffer.size() >= MAXSIZE)
{
sleep(SLEEPTIME);
++cnt_sleep;
if(cnt_sleep >= SLEEPTINES)
{
break;
}
}
pthread_testcancel(); //set cancel point
pcom->read_com(1);
Auto_lock lock(g_locker);
g_buffer.append(pcom->m_recv_buf);
}while (true);
pthread_exit(NULL);
}
void* My_com::send_thread_function(void *arg)
{
My_com *pcom = (My_com*)arg;
int cnt_sleep = 0;
do
{
pthread_testcancel(); //set cancel point
if(g_buffer.size() <= MINSIZE)
{
sleep(SLEEPTIME);
++cnt_sleep;
if(cnt_sleep >= SLEEPTINES)
{
break;
}
}
Auto_lock lock(g_locker);
cout<<g_buffer.size()<<endl;
parse(pcom, g_buffer);
cout<<g_buffer.size()<<endl;
pcom->write_com(MAXSENDLEN);
//pthread_testcancel();
}while(true);
pthread_exit(NULL);
}
int parse(My_com *pcom, string &str)
{
int start = 0;
int end = 0;
int len = 0;
start = str.find_first_of('$', 0);
end = str.find('\n', start);
len = end - start +1;
strncpy(pcom->m_send_buf, str.substr(start, end).c_str(), len);
str.erase(0, start + len);
//cout<<str.size()<<endl;
}
int My_com::create_read_thread()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
int res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if(pthread_create(&m_read_thrdID, &attr, read_thread_function, (void*)this) != 0)
{
m_plog->write_log("create read thread error", ERR);
return -1;
}
return 0;
}
int My_com::create_send_thread()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
int res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if(pthread_create(&m_read_thrdID, &attr, send_thread_function, (void*)this) != 0)
{
m_plog->write_log("create read thread error", ERR);
return -1;
}
return 0;
}
int send(My_com* pcom, int fd, int wrlen)
{
pcom->m_plog->write_log("\nstating sending.........", INFO);
static long int sdcnt = 0;
int len = write(fd, pcom->m_send_buf, wrlen);
sdcnt += len;
if(len <= 0)
{
char msg[100] = {0};
sprintf(msg, "send error: %s ", strerror(errno));
pcom->m_plog->write_log(msg, ERR);
return len;
}
char msg[100] = {0};
sprintf(msg, "have send %ld :", sdcnt);
pcom->m_plog->write_log(msg, INFO);
pcom->m_plog->write_log(pcom->m_send_buf, INFO);
//m_plog->write_log("\n", INFO);
return len;
}
demo:
#include <iostream>
#include "my_com.h"
#include "my_locker/auto_lock.h"
using namespace std;
extern string g_buffer;
extern Mutex_locker g_locker; //lock the g_buffer
extern Mutex_locker g_log_locker;
bool rdstop_flag = false;
bool wrstop_flag = false;
int main()
{
My_com com("./", "chen");
com.open_com(0);
com.set_com(9600, 8, 'N', 1);
// string buf;
// while(true)
// {
// com.read_com(1);
// buf.append(com.m_recv_buf);
// }
com.create_read_thread();
sleep(5);
com.create_send_thread();
sleep(10);
com.m_plog->write_log("will cancel read thread\n", INFO);
pthread_cancel(com.m_read_thrdID);
pthread_join(com.m_read_thrdID, NULL);
com.m_plog->write_log("cancel read thread done\n", INFO);
com.m_plog->write_log("will cancel send thread\n", INFO);
pthread_join(com.m_send_thrdID, NULL);
com.m_plog->write_log("cancel send thread done\n", INFO);
return 0;
}