linux串口通信编程

在linux下, 串口也被当做一个文件来使用, 所以串口传输数据实际上也就是对文件进行read/write操作.

UART串口基本知识:

一般, 串口至少有三根线: 

地线GND, 接收线RX和发送线TX. 有的开发板还可能把控制线CTS/RTS也引出来.


串口查看方式: 

在PC端, 如果用USB口连接, 一般显示为/dev/ttyUSBx, 其中x为0, 1, 2...

在开发板上,一般显示为/dev/ttySx, 其中x为0, 1, 2...


在电脑上可以使用下面的方法查看或者设置串口属性:

查看串口所有属性:  sudo stty -F /dev/ttyUSB1 -a
设置串口波特率: sudo stty -F /dev/ttyUSB1 ispeed 1152000 ospeed 1152000

串口常用参数:

波特率: 每秒钟传送的bit数. --> 通信速度.
数据位: 每字节中实际所占的bit数, 取决于通信协议的选取. 比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。
停止位: 单个数据包的最后一位. 典型值为1, 1.5和2位..
奇偶校验位: 有四种检错方式, 偶、奇、高和低。可以没有校验位.
比特率: 数字信号的传输速率,单位时间内传输的二进制代码的有效位(bit)数,其单位为bit/s(bps)、Kbps或Mbps (此处K和M分别为1000和1000000)。

串口编程流程:
打开串口 --> 设置串口参数 --> 读写数据 --> 关闭串口
其中设置串口属性是比较重要的环节.
下面代码的功能: 给串口发送一个启动命令, 然后进入循环---从串口接收数据(数据头和数据分开传输), 并发送命令消息, 直到遇到终端信号或程序出错.
第一个是C语言实现,第二个是python实现。
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>

#define UART_NAME "/dev/ttyS0"

static int g_quit;
static int g_speed;

static void handle_signal(int sig)
{
	if (SIGINT == sig || SIGTERM == sig)
		g_quit = 1;
}

static struct sigaction sigact = {
	.sa_handler = handle_signal,
};

static struct option longopts[] = {
	{"help", no_argument, NULL, 'h'},
	{"baudrate", required_argument, NULL, 'r'},
	{0, 0, 0, 0},
};


static void print_usage(void)
{
	fprintf(stdout, "usage:\n");
	fprintf(stdout, "\t-r | --baudrate  the serial baud rate\n");
	fprintf(stdout, "\t-h | --help      show this help and exit\n");
}


static int parse_arg(int argc, char **argv)
{
	int ret = 0;
	char c = '0';

	while (
		(c = getopt_long(argc, argv, "-:r:h", longopts, NULL))
		!= -1) {
		switch (c) {
		case 'r':
			g_speed = atoi(optarg);
			break;
		case 'h':
			ret = -1;
			break;
		case ':':
			fprintf(stdout, "%c require argument\n", optopt);
			ret = -1;
			break;
		case '?':
			fprintf(stdout, "%c invalid argument\n", optopt);
			ret = -1;
			break;
		default:
			break;
		}
	}

	return ret;
}

static int uart_open(char *filename)
{
	int fd;

	//fd = open(filename, O_RDWR);
	fd = open(filename, O_RDWR|O_NOCTTY|O_NDELAY);
	if (fd < 0) {
		printf("%s %d: failed to open output file\n",
				__FILE__, __LINE__);
		return -1;
	}

	//nonblock
	if (fcntl(fd, F_SETFL, 0) < 0) {
		printf("fcntl failed\n");
		close(fd);
		return -1;
	}
	return fd;
}

static int uart_set(int fd, int speed, int flow_ctrl,
		int databits, int parity, int stopbits)
{
	struct termios options;
	int ret = 0;

	if (tcgetattr(fd, &options) != 0) {
		printf("Setup serial fialed!\n");
		return -1;
	}

	int i;
	int speed_arr[] = {B1500000, B1152000, B1000000, B921600,
		B576000, B500000, B460800, B230400, B115200, B57600,
		B38400, B19200, B9600, B4800, B2400, B1800, B1200,
		B600, B300, B200, B150, B134, B110, B75, B50, B0};
	int name_arr[] = {1500000, 1152000, 1000000, 921600,
		576000, 500000, 460800, 230400, 115200, 57600,
		38400, 19200, 9600, 4800, 2400, 1800, 1200,
		600, 300, 200, 150, 134, 110, 75, 50, 0};

	if (tcgetattr(fd, &options) != 0) {
		printf("Setup serial fialed!\n");
		return -1;
	}

	printf("speed = %d\n", speed);
	for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
		if (speed == name_arr[i]) {
			ret = cfsetispeed(&options, speed_arr[i]);
			if (ret) {
				perror("cfsetispeed");
				printf("set in speed failed\n");
			}

			ret = cfsetospeed(&options, speed_arr[i]);
			if (ret) {
				perror("cfsetispeed");
				printf("set out speed failed\n");
			}
			break;
		}
	}

	options.c_cflag |= CLOCAL;
	options.c_cflag |= CREAD;

	switch (flow_ctrl) {
	case 0: // no flow control
		options.c_cflag &= ~CRTSCTS;
		break;
	case 1: // hardware flow control
		options.c_cflag |= CRTSCTS;
		break;
	case 2: //software flow control
		options.c_cflag |= IXON|IXOFF|IXANY;
		break;
	default:
		printf("Unsupported flow control\n");
		return -1;
	}

	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 databits!\n");
		return -1;
	}

	switch (parity) {
	case 'n': //no parity
	case 'N':
		options.c_cflag &= ~PARENB;
		options.c_iflag &= ~INPCK;
		break;
	case 'o': //odd parity
	case 'O':
		options.c_cflag |= (PARODD | PARENB);
		options.c_iflag &= INPCK;
		break;
	case 'e': //even parity
	case 'E':
		options.c_cflag |= PARENB;
		options.c_cflag &= ~PARODD;
		options.c_iflag |= INPCK;
		break;
	case 's': //blank
	case 'S':
		options.c_cflag &= ~PARENB;
		options.c_iflag &= ~CSTOPB;
		break;
	default:
		printf("Unsupported parity\n");
		return -1;
	}

	switch (stopbits) {
	case 1:
		options.c_cflag &= ~CSTOPB;
		break;
	case 2:
		options.c_cflag |= CSTOPB;
		break;
	default:
		printf("Unsupported stop bits\n");
		return -1;
	}

	//mode
	options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
	options.c_oflag  &= ~OPOST;   /*Output*/

	//wait_time: 0.1s; min char to read:1
	options.c_cc[VTIME] = 1;
	options.c_cc[VMIN] = 1;

	//if data overflow, receive data, but not read
	tcflush(fd, TCIFLUSH);

	//save configuration
	if (tcsetattr(fd, TCSANOW, &options) != 0) {
		printf("set serial error!\n");
		return -1;
	}
	return 0;
}

static int recv_data(int fd, int fdtest)
{
	int inflg = 0;
	int ret, size;
	int *data = NULL;

	do {
		if (g_quit)
			break;

		inflg = 0;
		ret = 0;
		size = 0;
		int hsize = read(fd, &size, 4);

		if (hsize < 0) {
			printf("read header size error\n");
			ret = -1;
			break;
		}

		if (size <= 0)
			continue;

		printf("size = %d\n", size);
		if (data) {
			free(data);
			data = NULL;
		}
		data = calloc(1, size);
		if (!data) {
			printf("calloc failed\n");
			ret = -1;
			break;
		}

		int len = 0;
		int left_size = size - len;

		do {
			len = read(fd, data, left_size);
			if (len < 0) {
				printf("read fd error\n");
				ret = -1;
				break;
			}
			//printf("real read size = %d\n", len);
			int n = write(fdtest, data, len);

			if (n < 0) {
				printf("write error!\n");
				ret = -1;
				break;
			}
			left_size = left_size - len;
		} while (left_size > 0);

		inflg = 1;
	} while (!inflg);

	if (data) {
		free(data);
		data = NULL;
	}
	return ret;
}

int main(int argc, char **argv)
{
	int ret = 0;
	int fd, fdtest;
	char send[1] = {'a'};

	ret = sigaction(SIGINT, &sigact, NULL);
	ret |= sigaction(SIGTERM, &sigact, NULL);
	if (ret) {
		printf("%s line%d: %s\n",
			__FILE__, __LINE__, strerror(errno));
		return -1;
	}

	g_speed = 9600;

	ret = parse_arg(argc, argv);
	if (ret) {
		print_usage();
		return -1;
	}

	fd = uart_open("/dev/ttyS0");
	if (fd < 0)
		return -1;

	int speed = g_speed;
	int flow_ctrl = 0;
	int databits = 8;
	int stopbits = 1;
	int parity = 'O';

	ret = uart_set(fd, speed, flow_ctrl, databits, parity, stopbits);
	if (ret) {
		printf("uart_set failed\n");
		goto out;
	}

	fdtest = open("/mnt/recv.h264", O_RDWR|O_SYNC);
	if (fdtest < 0) {
		printf("open fdtest failed\n");
		goto out;
	}

	int n = write(fd, send, 1);

	if (n <= 0) {
		printf("send a failed\n");
		close(fdtest);
		goto out;
	}

	do {
		if (g_quit)
			break;

		ret = recv_data(fd, fdtest);

		usleep(1000*10);
		send[0] = 'b';

		int res = write(fd, send, 1);

		if (res < 0)
			break;
		/* printf("%s: %s: %d\n", __FILE__, __func__, __LINE__); */
	} while (!ret);
	close(fdtest);
out:
	close(fd);
	return ret;
}


#!/usr/bin/python
import serial
import time
import struct

def w():
	baud = 115200
	fd = open("/home/sarah/2newh264/test15.h264", "w+")
	ser = serial.Serial('/dev/ttyUSB1', baud, timeout=8)
	print "baud: ", baud
	cmd0 = 'a'
	cmd1 = 'b'
	ser.write(cmd0)
	print "send \""+cmd0+"\" to remotes"
	while (1):
		print "send \""+cmd1+"\" to remotes"
		h = ser.read(4)
		if not h:
			continue
		size = struct.unpack("i", h)
		print "size: ", size[0]
		input = ser.read(size[0])
		fd.write(input)
		print "\n"
		ser.write(cmd1)
	ser.close()
	close(fd)

w()

参考文档:
串口通信程序比较完整的一个!     http://blog.csdn.net/w282529350/article/details/7378388
LInux下串口设置详解    http://www.linuxidc.com/Linux/2011-04/33976.htm

cfsetispeed 中文man页面  http://os.51cto.com/art/201108/286606.htm

python serial 介绍 http://blog.csdn.net/u011577439/article/details/51762041

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页