开发日志 170329

遥控粗粗的可以跑通了,今天就开始做自动导航相关的东西了。想了一下,不如先处理GPS。我的GPS是串口的GPS,因此我需要一个函数,大概getline的效果差不多。

如果是STM32的话,比较简单,直接调HAL的函数来读读写写就好了;另外听说树莓派上用Python也挺方便的。可惜这次是打算用Raspberry Pi,还不能用Python,只能用C/C++。

先放出来我的参考文章,基本就按照这个配就行,另外那个cfmakeraw函数最好别用……

其实本来也没什么,不过以前C语言学的不牢,对stdio的缓冲区理解不到位,printf的时候没有\n,导致缓冲区没刷新。以前知道iostream的这个问题,但printf就没往这茬想,吃了个大亏,真是脚都要抠烂了……

既然是C++,那就封装成一个类吧(然而并没有什么卵用。

其实参考的那篇文章讲的挺好,我只是稍加改动:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* baudrate settings are defined in , which is included by */
// 波特率的设置定义在 . 包含在 里
#define BAUDRATE B115200 

/* change this definition for the correct port */
// 定义您所需要的串口号
#define MODEMDEVICE "/dev/ttyS1"

#define _POSIX_SOURCE 1 /*POSIX compliant source POSIX系统兼容*/

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE; 

main() {
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];

/* Open modem device for reading and writing and not as controlling 
tty because we don't want to get killed if linenoise sends CTRL-C.
开启设备用于读写,但是不要以控制 tty 的模式,因为我们并不希望在发送 Ctrl-C
后结束此进程
*/

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* save current serial port settings */
             // 储存当前的串口设置
bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */
                                     // 清空新的串口设置结构体
/* 
 BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
 CRTSCTS : output hardware flow control (only used if the cable has all 
            ecessary lines. See sect. 7 of Serial-HOWTO)
  CS8 : 8n1 (8bit,no parity,1 stopbit)
  CLOCAL : local connection, no modem contol
  CREAD : enable receiving characters
  BAUDRATE: 设置串口的传输速率bps, 也可以使用 cfsetispeed 和 cfsetospeed 来设置
  CRTSCTS : 输出硬件流控(只能在具完整线路的缆线下工作,参考 Serial-HOWTO 第七节)
  CS8 : 8n1 (每一帧8比特数据,无奇偶校验位,1 比特停止位)
  CLOCAL : 本地连接,无调制解调器控制
  CREAD : 允许接收数据
*/
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

   /*
IGNPAR : ignore bytes with parity errors
ICRNL : map CR to NL (otherwise a CR input on the other computer will not 
       terminate input) otherwise make device raw (no other input processing)
IGNPAR : 忽略奇偶校验出错的字节
ICRNL : 把 CR 映像成 NL (否则从其它机器传来的 CR 无法终止输入)或者就把设备设
    为 raw 状态(没有额外的输入处理)
*/
newtio.c_iflag = 0;

/*
Raw output. Raw 模式输出
*/
newtio.c_oflag = 0;

/*
ICANON : enable canonical input
disable all echo functionality, and don't send signals to calling program
ICANON : 启动 标准输出, 关闭所有回显echo 功能,不向程序发送信号
*/
newtio.c_lflag = ICANON;

/* 
initialize all control characters 
default values can be found in /usr/include/termios.h, and 
are given in the comments, but we don't need them here
初始化所有的控制字符, 默认值可以在 /usr/include/termios.h 找到,
并且做了注解,不过这里我们并不需要考虑这些
*/
newtio.c_cc[VINTR] = 0; /* Ctrl-c */ 
newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtio.c_cc[VERASE] = 0; /* del */
newtio.c_cc[VKILL] = 0; /* @ */
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
                               /* 不使用字符间的计时器 */
newtio.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
                                  /* 阻塞,直到读取到一个字符 */
newtio.c_cc[VSWTC] = 0; /* '\0' */
newtio.c_cc[VSTART] = 0; /* Ctrl-q */ 
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
newtio.c_cc[VEOL] = 0; /* '\0' */
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtio.c_cc[VEOL2] = 0; /* '\0' */

/* 
now clean the modem line and activate the settings for the port
清空数据线,启动新的串口设置
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

/*
terminal settings done, now handle input
In this example, inputting a 'z' at the beginning of a line will 
exit the program.
终端设置完成,现在就可以处理数据了
在本程序中,在一行的开始输入一个 'z' 会终止该程序 
*/
while (STOP==FALSE) { /* loop until we have a terminating condition */
        // 循环直到满足终止条件
/* read blocks program execution until a line terminating character is 
input, even if more than 255 chars are input. If the number
of characters read is smaller than the number of chars available,
subsequent reads will return the remaining chars. res will be set
to the actual number of characters actually read 
即使输入超过 255 个字节,读取的程序段还是会一直等到行结束符出现才会停止。
   如果读到的字符少于应刚获得的字符数,则剩下的字符串会在下一次读取时读到。
res 用来获得每次真正读到的字节数 
*/
res = read(fd,buf,255); 
buf[res]=0; /* set end of string, so we can printf */
                          // 设置字符串结束符,从而可以顺利使用 printf
printf(":%s:%d\n", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
/* restore the old port settings 恢复旧的串口设置 */
tcsetattr(fd,TCSANOW,&oldtio);
}

最开始我直接用了cfmakeraw函数,然后设置了一下c_cflag为115200 8N1。通讯啥的全都正常,就是每次read回来,\r\n在数组的最前面,而不是最后……而且每次只能读32Bytes进来。

后来对比了一下代码,发现termios的c_lflag上没给ICANON,工作在了原始模式,把这个标志加上后,以标准模式工作后,一次就能读N多字节,\r\n也在数组的最后面了。

这才是我想要的嘛,一个类似getline的函数xD,就这玩意折腾了一天啊(摔!)

发表评论