#include "swapi.h" #include "swmem.h" #include "swthrd.h" #include "swtcp.h" #include "swstring.h" #include "serial.h" struct serial_t { /* fd */ int fd; /* 名称 */ char name[128]; /* 波特率 */ int baudrate; /* 校检位: "odd"-奇校检; "even"-偶校检; "no"-无校检 */ char parityCheck[5]; /* 原有配置 */ struct termios oldtio; bool bGetOldtioOK; /* 数据接收线程句柄 */ void *hRecvThrd; /* 数据接收缓存 */ unsigned char recvBuf[65535]; /* 已接收的字节数 */ int nRecvBytes; /* 处理已接收的数据(用户回调) */ PSerialRecvHandler pRecvHandler; /* 无数据空闲时执行(用户回调) */ PSerialIdleHandler pIdleHandler; /* 日志打印前缀 */ char log_prefix[MAX_LINE_CHARS]; /* 日志打印缓存 */ char *pLogRecvBuf; // 堆内存 }; /* 数据接收处理(线程回调) */ static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam); /* 打开串口(8个数据位1个停止位), NULL失败, NOT NULL成功 */ void *serial_open(const char *serialName, int baudrate, const char *parityCheck /* "odd"-奇校检; "even"-偶校检; NULL或其它无效值-无校检 */, \ PSerialRecvHandler pRecvHandler, PSerialIdleHandler pIdleHandler, void *pUserData) { struct serial_t *pSerial; struct termios newtio; int nSpeed; /* 1, 创建串口对象 */ if(!serialName) return NULL; pSerial = (struct serial_t *)sw_heap_malloc(sizeof(struct serial_t)); if(!pSerial) return NULL; else { memset(pSerial, 0, sizeof(struct serial_t)); strncpy(pSerial->name, serialName, sizeof(pSerial->name)-1); } /* 2, 打开串口 */ pSerial->fd = open(pSerial->name, O_RDWR | O_NOCTTY | O_NDELAY | __O_CLOEXEC); if(pSerial->fd == -1) goto ErrorP; if(flock(pSerial->fd, LOCK_EX | LOCK_NB) == -1) goto ErrorP; /* 3, 保存当前串口配置 */ if(tcgetattr(pSerial->fd, &pSerial->oldtio) == -1) goto ErrorP; else pSerial->bGetOldtioOK = true; /* 4, 设置阻塞 */ if(fcntl(pSerial->fd, F_SETFL, 0) == -1) goto ErrorP; /* 5, 设置参数(8 databits, no parity, 1 stopbit, 1 second read timeout) */ pSerial->baudrate = baudrate; switch(pSerial->baudrate) { case 0 : nSpeed = B0; break; case 50 : nSpeed = B50; break; case 75 : nSpeed = B75; break; case 110 : nSpeed = B110; break; case 134 : nSpeed = B134; break; case 150 : nSpeed = B150; break; case 200 : nSpeed = B200; break; case 300 : nSpeed = B300; break; case 600 : nSpeed = B600; break; case 1200 : nSpeed = B1200; break; case 1800 : nSpeed = B1800; break; case 2400 : nSpeed = B2400; break; case 4800 : nSpeed = B4800; break; case 9600 : nSpeed = B9600; break; case 19200 : nSpeed = B19200; break; case 38400 : nSpeed = B38400; break; case 57600 : nSpeed = B57600; break; case 115200 : nSpeed = B115200; break; case 230400 : nSpeed = B230400; break; default : goto ErrorP; } memset(&newtio, 0, sizeof(newtio)); newtio.c_cflag = nSpeed | CS8 | CLOCAL | CREAD; // 设置波特率、数据带宽、本地连接、接收使能 newtio.c_cflag &= ~CSTOPB; // 一个停止位 if(xstrcasecmp(parityCheck, "odd") == 0) { // 使能奇校检 newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "odd"); } else if(xstrcasecmp(parityCheck, "even") == 0) { // 使能偶校检 newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "even"); } else { // 无校检 newtio.c_cflag &= ~PARENB; newtio.c_iflag = IGNPAR; strcpy(pSerial->parityCheck, "no"); } newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VMIN] = 0; // read block untill n bytes are received, 阻塞模式有效 newtio.c_cc[VTIME] = 1; // read block untill a timer expires(单位: 百毫秒, 十分之一秒), 阻塞模式有效 if(tcflush(pSerial->fd, TCIOFLUSH) == -1) goto ErrorP; // 清空串口输入、输出缓存 if(tcsetattr(pSerial->fd, TCSANOW, &newtio) == -1) goto ErrorP; // 激活新配置 /* 6, 生成日志打印前缀 */ if(xstrcasecmp(pSerial->parityCheck, "odd") == 0) sprintf(pSerial->log_prefix, "[%s:%d(odd parity)]", pSerial->name, pSerial->baudrate); else if(xstrcasecmp(pSerial->parityCheck, "even") == 0) sprintf(pSerial->log_prefix, "[%s:%d(even parity)]", pSerial->name, pSerial->baudrate); else sprintf(pSerial->log_prefix, "[%s:%d(no parity)]", pSerial->name, pSerial->baudrate); /* 7, 申请日志打印缓存 */ pSerial->pLogRecvBuf = (char *)sw_heap_malloc(sizeof(pSerial->log_prefix)+MAX_LINE_CHARS+sizeof(pSerial->recvBuf)*2); if(!pSerial->pLogRecvBuf) goto ErrorP; /* 8, 打开串口数据接收线程 */ pSerial->pRecvHandler = pRecvHandler; pSerial->pIdleHandler = pIdleHandler; // 用户回调 pSerial->hRecvThrd = sw_thrd_create(pSerial->name, THREAD_DEFAULT_PRIORITY, 4*1024*1024, serial_data_recv_proc, (unsigned long)pSerial, (unsigned long)pUserData); if(!pSerial->hRecvThrd) goto ErrorP; else sw_thrd_resume(pSerial->hRecvThrd); // 激活数据接收线程 return (void *)pSerial; /* 9, 错误 */ ErrorP: serial_close(pSerial, 0); return NULL; } /* 关闭串口 */ void serial_close(void *hSerial, int timeout) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial && pSerial->hRecvThrd) sw_thrd_destroy(pSerial->hRecvThrd, timeout); // 销毁数据接收线程 if(pSerial->pLogRecvBuf) sw_heap_free((void *)pSerial->pLogRecvBuf); // 释放日志打印缓存 if(pSerial && pSerial->fd != -1) { if(pSerial->bGetOldtioOK) tcsetattr(pSerial->fd, TCSANOW, &pSerial->oldtio); // 恢复原有的串口配置 close(pSerial->fd); // 关闭串口 } if(pSerial) sw_heap_free((void *)pSerial); // 释放内存, 销毁串口对象 } /* 获取日志打印前缀 */ char *serial_get_log_prefix(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) return pSerial->log_prefix; else return NULL; } /* 发送数据, -1失败, >=0发送的字节数 */ int serial_send_data(const void *hSerial, const unsigned char *data, int len) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial && pSerial->fd != -1) return write(pSerial->fd, data, len); else return -1; } /* 取得接收缓存区, NULL失败, NOT NULL成功 */ const unsigned char *serial_get_recv_buffer(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) return pSerial->recvBuf; else return NULL; } /* 取得接收缓存区中当前有效数据的字节数, -1失败, 返回值>=0 */ int serial_get_recv_buffer_bytes(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) return pSerial->nRecvBytes; else return -1; } /* 清空接收缓存区 */ void serial_clear_recv_buffer(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) { memset(pSerial->recvBuf, 0, sizeof(pSerial->recvBuf)); pSerial->nRecvBytes = 0; } } /* 打印接收缓存区 */ void serial_printf_recv_buffer(const void *hSerial, int logLevel) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) { pSerial->pLogRecvBuf[0] = '\0'; // 复位日志缓存 sprintf(pSerial->pLogRecvBuf, "%s the received buffer has %d bytes (hex)", \ pSerial->log_prefix, pSerial->nRecvBytes); if(pSerial->nRecvBytes > 0) { strcat(pSerial->pLogRecvBuf, ":\n"); xstrfromhex(pSerial->recvBuf, pSerial->nRecvBytes, pSerial->pLogRecvBuf, NULL, NULL); } else strcat(pSerial->pLogRecvBuf, "."); sw_log_writeWithLevel(logLevel, __FILE__, -1, "%s", pSerial->pLogRecvBuf); } } /* 判断接收线程是否还在 */ bool serial_recvThrd_isAlive(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; return pSerial ? sw_thrd_isAlive(pSerial->hRecvThrd) : false; } /* 暂停接收线程接收数据 */ void serial_recvThrd_pause(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) sw_thrd_pause(pSerial->hRecvThrd); } /* 继续接收线程接收数据 */ void serial_recvThrd_resume(const void *hSerial) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial) sw_thrd_resume(pSerial->hRecvThrd); } #ifdef _DEBUG /* 向接收缓存中写入数据(一般用于仿真串口输入, 模拟测试时使用) */ void serial_write_recv_buffer(const void *hSerial, const unsigned char *hex, int hexCnt) { struct serial_t *pSerial = (struct serial_t *)hSerial; if(pSerial && hex && hexCnt > 0 && hexCnt < sizeof(pSerial->recvBuf)) { memcpy(pSerial->recvBuf+pSerial->nRecvBytes, hex, hexCnt); pSerial->nRecvBytes += hexCnt; } sw_thrd_delay(1000); // 延时1秒钟, 等待数据被回调处理, 否则会累积(模拟仿真和真实触发还是有区别) } #endif /* 检测串口状态, timeout单位为ms */ #define serial_select sw_tcp_select /* 数据接收处理(线程回调), "返回值 < 0"时表示处理失败, 将不再调用该接收回调函数, "返回值 >= 0"时表示 处理成功, 延时一段"返回值"时间(ms)后继续调用该接收回调函数 */ static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam) { struct serial_t *pSerial = (struct serial_t *)wParam; fd_set rSet; int nRecvBufSize = sizeof(pSerial->recvBuf), nInterval, nRet; RecvP: if(!sw_thrd_isAlive(pSerial->hRecvThrd)) return -1; nRet = serial_select(pSerial->fd, &rSet, NULL, NULL, 0); if(nRet == 0) { // 串口无数据 NoneP: if(pSerial->pIdleHandler) nInterval = pSerial->pIdleHandler(wParam, lParam); // 触发无数据时的用户回调 else nInterval = 1; goto EndP; } else if(nRet > 0 && FD_ISSET(pSerial->fd, &rSet)) { // 串口有数据 if(pSerial->nRecvBytes >= nRecvBufSize) { // 判断接收缓存是否溢出 sw_log_warn("\"%s\" serial port receiving buffer overflow, the earliest one byte(%02x) will discard!", \ pSerial->name, pSerial->recvBuf[0]); memmove(pSerial->recvBuf, pSerial->recvBuf+1, nRecvBufSize-1); // 丢弃最早的一个字节 pSerial->nRecvBytes = nRecvBufSize-1; // 新的已接收字节数 } nRet = read(pSerial->fd, pSerial->recvBuf+pSerial->nRecvBytes, 1); // 读一个字节 if(nRet == 1) pSerial->nRecvBytes += 1; // 已接收字节数累加1 else if(nRet == 0 && errno == ETIMEDOUT) goto NoneP; // 读数据超时 else { // 读一个字节错误 sw_log_fatal("read one byte error(ret=%d, errno=%d), \"%s\" serial port receiving thread abort!!!", \ nRet, errno, pSerial->name); return -1; } if(pSerial->pRecvHandler) { // 处理已接收的数据 nInterval = pSerial->pRecvHandler(wParam, lParam); // 触发有数据时的用户回调 if(nInterval < 0) goto EndP; } goto RecvP; // 继续接收后续数据 } else if(nRet < 0 && errno != EINTR) { // 有错误发生 sw_log_fatal("select error(%s), \"%s\" serial port receiving thread abort!!!", strerror(errno), pSerial->name); return -1; } else nInterval = 1; // 不满足上述组合判断条件时 EndP: return nInterval; }