我有一个微控制器和一个嵌入式PC。这两个通过串行连接通信,两者都是相互接收和发送数据。两者之间的波特率是38400。微控制器和PC具有相同的配置,以确保通信(8个数据位,1个停止位,偶数奇偶校验)。
通信工作正常,直到微控制器开始每10毫秒发送一次消息。此时,微控制器的发送队列将满并溢出。然后他向PC发送一个错误消息(这就是我如何知道的,微控制器的发送队列被溢出,而不是PC的发送队列。
在PC程序的嵌入式Linux版本之前,微控制器用DOS版本的PC程序运行,而不会造成错误。在DOS中,单个字节直接从串行端口读取和写入(Linux中没有内核缓冲区),因为大多数C代码是可移植到Linux的,所以我试图复制Linux中串行端口读写的DOS行为,以保持其余的进程处理这些单个字节。
我打开并初始化PC中的串口,如下所示。
fd_mc = open("/dev/ttyS1", O_NOCTTY | O_RDWR /*| O_NONBLOCK*/ /*| O_SYNC*/);
if(fd_mc == -1)
{
perror("Could not open µc port.");
}
else
{
struct termios tty;
memset(&tty, 0, sizeof(tty));
if ( tcgetattr ( fd_mc, &tty ) != 0 )
{
perror("Error getting termios attributes");
}
cfsetospeed (&tty, B38400);
cfsetispeed (&tty, B38400);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //raw input
tty.c_oflag &= ~OPOST; //raw output
tty.c_cflag |= PARENB; //even parity
tty.c_cflag &= ~PARODD;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag |= (CLOCAL | CREAD);
tcsetattr(fd_mc, TCSANOW, &tty);
}
上面的代码是一个从初始化两个串行端口的函数的片段(其中一个是对微控制器的一个)。
编辑:
这是流量控制的设置,没有。
tty.c_cflag &= ~CRTSCTS; //No hardware based flow control
tty.i_cflag &= ~(IXON | IXOFF | IXANY); //no software based flow control
在环形缓冲区和pollin的帮助下,串行端口的读取在线程内部进行。此线程正在主循环内调用。代码如下:
void *thread_read()
{
struct sched_param param;
param.sched_priority = 97;
int ret_par = 0;
ret_par = sched_setscheduler(3, SCHED_FIFO, ¶m);
if (ret_par == -1) {
perror("sched_setscheduler");
return 0;
}
struct pollfd poll_fd[2];
int ret;
extern struct fifo mc_fifo, serial_fifo;
ssize_t t;
char c;
poll_fd[0].fd = fd_mc;
poll_fd[0].events = POLLIN;
poll_fd[1].fd = fd_serial;
poll_fd[1].events = POLLIN;
while(1) {
ret = poll(poll_fd, 2, 10000);
if(ret == -1) {
perror("poll");
}
if(poll_fd[0].revents & POLLIN) {
t = read(fd_mc, &c, 1);
if(t>0) {
fifo_in(&mc_fifo, c);
}
}
if(poll_fd[1].revents & POLLIN) {
t = read(fd_serial, &c, 1);
if(t>0) {
fifo_in(&serial_fifo, c);
}
}
}
pthread_exit(NULL);
}
在主循环内调用。
pthread_t read;
pthread_create(&read, NULL, thread_read, NULL);
在缓冲区内写入(fifo-in)的功能是
int fifo_in(struct fifo *f, char data)
{
if( (f->write + 1 == f->read) || (f->read == 0 && f->write + 1 == FIFO_SIZE) ) //checks if write one before read or at the end
{
printf("fifo in overflow\n");
return 0; //fifo full
}
else {
f->data[f->write] = data;
f->write++;
// printf("Byte in: Containing %4d\tData:\t%4d\n", BytesInReceiveBuffer(1), data); //Bytes contained in fifo of mc
if(f->write >= FIFO_SIZE) {
f->write = 0;
}
return 1; //success
}
}
这个函数的基本功能是检查读写位置和写位置
data
如果两个位置不重叠且大于
+1
彼此远离。
当另一个函数需要它调用的环缓冲区中的字节时
GetByte
从环形缓冲区读取字节。
获得字节
int GetByte(int port)
{
char c;
switch(port) {
case 0: //COM1
fifo_out(&serial_fifo,&c);
break;
case 1: //COM2
fifo_out(&mc_fifo,&c);
break;
}
return (int)c;
}
菲福克斯
int fifo_out(struct fifo *f, char *data) {
if(f->read == f->write) {
printf("fifo in overfwrite\n");
*data = 0;
return 0;
}
else {
*data = f->data[f->read];
f->read++;
// printf("Byte out: Containing %4d\tData:\t%4d\n", BytesInReceiveBuffer(1), *data);
if(f->read >= FIFO_SIZE) {
f->read = 0;
}
return 1;
}
}
在linux移植之前,dos版本中的所有操作都是顺序的。
吃的那一刻我最好的猜测是
read()
就是在某个时候减慢你的速度,并开始减慢从缓冲区的读取速度,这再次阻塞了线程的调用。也许我错了。目前,我有点不知道究竟是什么样的错误,甚至如何解决这个问题。
每一个好的建议都是值得赞赏的。