/*
********************************************************************************
*******************************************************************************
*/
#include<stdio.h> /*标准输入输出定义*/
#include<stdlib.h> /*标准函数库定义*/
#include<unistd.h> /*Unix 标准函数定义*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h> /*文件控制定义*/
#include<termios.h> /*PPSIX 终端控制定义*/
#include<errno.h> /*错误号定义*/
#include<string.h>
#include<pthread.h>
#define RET_ERR -1
#define RET_OK 0
#define LEN_BUF_RECV_UNPACK 16
#define LEN_BUF_RECV 64
static int g_fd;
static char s_buf_recv[LEN_BUF_RECV_UNPACK];
/*
******************************************************************
*名称: uart_open
*功能: 打开串口并返回串口设备文件描述
*入口参数: fd 文件描述符
dev 串口号(ttyS0,ttyS1,ttyS2)
*出口参数:正确返回为0,错误返回为-1
******************************************************************
*/
int uart_open(char*dev, int *fd)
{
int ret;
*fd = open(dev, O_RDWR|O_NOCTTY|O_NDELAY);
printf("fd->open=%d\n", *fd);
if (0 > *fd) {
printf("Can't Open Serial Port");
return RET_ERR;
}
//恢复串口为阻塞状态
ret = fcntl(*fd, F_SETFL, 0);
printf("fcntl=%d\n", ret);
if(0 > ret) {
printf("fcntl failed!\n");
return RET_ERR;
}
//测试是否为终端设备
if(0 == isatty(STDIN_FILENO)) {
printf("standard input is not a terminal device\n");
return RET_ERR;
}
printf("isatty success!\n");
return RET_OK;
}
/*
******************************************************************
*名称: uart_close
*功能: 关闭串口并返回串口设备文件描述
*入口参数: fd 文件描述符
port 串口号(ttyS0,ttyS1,ttyS2)
*出口参数:void
******************************************************************
*/
void uart_close(int fd)
{
close(fd);
}
/*
******************************************************************
*名称: uart_set
*功能: 设置串口数据位,停止位和效验位
*入口参数: fd 串口文件描述符
* speed 串口速度
* flow_ctrl 数据流控制
* databits 数据位 取值为 7 或者8
* stopbits 停止位 取值为 1 或者2
* parity 效验类型 取值为N,E,O,,S
*出口参数:正确返回为0,错误返回为-1
******************************************************************
*/
int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
int i, status;
int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};
int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};
struct termios options;
/* tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,
该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1. */
if(0 != tcgetattr(fd, &options)) {
printf("SetupSerial 1\n");
return RET_ERR;
}
//设置串口输入波特率和输出波特率
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
if (speed == name_arr[i]) {
cfsetispeed(&options, speed_arr[i]);
cfsetospeed(&options, speed_arr[i]);
}
}
//修改控制模式,保证程序不会占用串口
options.c_cflag |= CLOCAL;
//修改控制模式,使得能够从串口中读取输入数据
options.c_cflag |= CREAD;
//设置数据流控制
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;
}
//设置数据位
//屏蔽其他标志位
options.c_cflag &= ~CSIZE;
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:
printf("Unsupported data size\n");
return RET_ERR;
}
//设置校验位
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;
case 's':
case 'S': //设置为空格
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
printf("Unsupported parity\n");
return RET_ERR;
}
// 设置停止位
switch (stopbits) {
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
printf("Unsupported stop bits\n");
return RET_ERR;
}
//修改输出模式,原始数据输出
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//options.c_lflag &= ~(ISIG | ICANON);
options.c_iflag &= ~ICRNL; //禁止将输入的CR转换为NL
options.c_iflag &= ~(IXON); //清bit位 关闭流控字符
//设置等待时间和最小接收字符
options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */
options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
tcflush(fd,TCIFLUSH);
//激活配置 (将修改后的termios数据设置到串口中)
if (0 != tcsetattr(fd,TCSANOW,&options)) {
printf("com set error!\n");
return RET_ERR;
}
return RET_OK;
}
/*
******************************************************************
*名称: uart_init()
*功能: 串口初始化
*入口参数: fd 文件描述符
* speed 串口速度
* flow_ctrl 数据流控制
* databits 数据位 取值为 7 或者8
* stopbits 停止位 取值为 1 或者2
* parity 效验类型 取值为N,E,O,,S
*
*出口参数:正确返回为0,错误返回为-1
******************************************************************
*/
int uart_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
int ret;
//设置串口数据帧格式
//ret = uart_set(fd, 115200, 0, 8, 1, 'N');
ret = uart_set(fd, speed, flow_ctrl, databits, stopbits, parity);
return ret;
}
/*
*******************************************************************
* 名称: uart_send
* 功能: 发送数据
* 入口参数: fd 文件描述符
* buf 存放串口发送数据
* data_len 一帧数据的个数
* 出口参数: 正确返回为0,错误返回为-1
******************************************************************
*/
int uart_send(int fd, char *buf, int data_len)
{
int len = 0;
len = write(fd, buf, data_len);
printf("send data is %s\n", buf);
if (len != data_len) {
tcflush(fd, TCOFLUSH);
return RET_ERR;
}
return RET_OK;
}
/*
******************************************************************
* 名称: uart_unpack
* 功能: 串口数据解包
* 入口参数: c 串口数据
*
* 出口参数: 正确返回为0,错误返回为-1
******************************************************************
*/
static int uart_unpack(unsigned char c)
{
//printf("%s is running\r\n",__FUNCTION__);
static int flg, index;
/*接收到的数据开头为A*/
if('A' == c) {
flg = 1;
index = 0;
} else if('\n' == c && 0 < index && '\r' == s_buf_recv[index - 1]) { /*以回车换行结束如:A123456\r\n*/
//buf[index] = c;
index = 0;
flg = 0;
return RET_OK;
}
/*没找到继续找*/
if (1 == flg) {
s_buf_recv[index++] = c;
if(index >= sizeof(s_buf_recv))
index = 0;
}
return RET_ERR;
}
/*
******************************************************************
* 名称: uart_recv_data_frame
* 功能: 串口数据每帧数据解析
* 入口参数:
* 出口参数: 正确返回为0,错误返回为-1
******************************************************************
*/
static int uart_recv_data_frame()
{
int len;
unsigned char type;
printf("buf0-7 %x %x %x %x %x %x %x %x", s_buf_recv[0], s_buf_recv[1],s_buf_recv[2],s_buf_recv[3],s_buf_recv[4],s_buf_recv[5],s_buf_recv[6],s_buf_recv[7]);
len = s_buf_recv[1];
if ('A' != s_buf_recv[0] || 0 >= len || sizeof(s_buf_recv) <= len + 2 || 0x0d != s_buf_recv[len + 2]) {
printf("unknow uart_recv_data_frame:%d\n", len);
return RET_ERR;
}
type = (unsigned char)s_buf_recv[2];
switch(type){
case 0xAA:
printf("uart_recv_data_frame:0xaa\n");
break;
default:
printf("unknow uart_recv_data_frame:%x\n", type);
break;
}
return RET_OK;
}
/*
******************************************************************
* 名称: uart_recv_thread
* 功能: 接收串口数据线程
* 入口参数:
* 出口参数: 错误返回为-1
******************************************************************
*/
static void *uart_recv_thread(void *arg)
{
int ret, i, n;
fd_set readfds;
char buf[LEN_BUF_RECV];
while(1) {
FD_ZERO(&readfds);
FD_SET(g_fd, &readfds);
ret = select(g_fd + 1, &readfds, NULL, NULL, NULL);
//memset(buf, 0, sizeof(buf));
if(0 >= ret) {
printf("select failed %d\n", ret); //0超时 1失败
continue;
}
if(0 >= FD_ISSET(g_fd, &readfds)){
printf("FD_ISSET failed\n");
continue;
}
do {
n = read(g_fd, buf, LEN_BUF_RECV); //配置为非阻塞
if(0 >= n) {
printf("read failed %d\n", n);
//conitnue;
break;
}
for (i = 0; i < n; i++) {
ret = uart_unpack(buf[i]);
if (RET_OK == ret) {
uart_recv_data_frame();
}
}
}while(0 < n);
}
return NULL;
}
/*
********************************************************************************
*******************************************************************************
*/
int main(int argc, char **argv)
{
int ret, len, i;
char rcv_buf[256];
char send_buf[256];
pthread_t p_serial;
if(argc != 2) {
printf("Usage: %s /dev/ttySn 0 #(send data)\n",argv[0]);
printf("Usage: %s /dev/ttySn 1 #1(receive data)\n",argv[1]);
printf("open failure : %s\n", strerror(errno));
return RET_ERR;
}
ret = uart_open(argv[1], &g_fd); //打开串口,返回文件描述符
// fd=open("dev/ttyS1", O_RDWR);
//printf("fd= \n",fd);
ret = uart_init(g_fd, 9600, 0, 8, 1, 'N');
printf("Set Port Exactly!\n");
//sleep(1);
if(RET_ERR == ret) {
printf("ret init = %d\n",ret);
return RET_ERR;
}
pthread_create(&p_serial, NULL, uart_recv_thread, NULL);/*线程函数在下面*/
fgets(send_buf, 256, stdin); //输入内容,最大不超过40字节,fgets能吸收回车符,这样pc收到的数据就能自动换行
for(i = 0; i < 10; i++) {
len = uart_send(g_fd, send_buf, 40);
if(len > 0)
printf(" %d time send %d data successful\n",i,len);
else
printf("send data failed!\n");
sleep(1);
}
uart_close(g_fd);
}