知道了上面的问题,我们设计结构体就要注意,不同的数据类型顺序可能会导致不同的结构体大小,而我们在单片机如Arduino设计结构体时也要考虑对齐问题,比如将上面例子的char定义为二维数组char c[2],或者把char放到最后,这样即使我们接收时弄错了结构体大小,接到的数据也能够准确的放到结构体中。
在Arduino中,print函数和write函数都无法直接发送结构体数据,因为结构体是用户自己的定义的,无法像其他数据那样有固定的格式或大小。所以我们必须将结构体转为字节数组,这样就可以通过write函数发送了,下面提供两种方法实现转换。
方法一:使用memcpy函数,将结构体中的数据拷贝到一个字节数组中
sdata s;
。。。。。。
byte b[sizeof(sdata)];
memcpy(b,&s,sizeof(sdata));
Serial.write(b,sizeof(sdata));
方法二:用一个字节类型的指针强制指向结构体首地址
sdata s;
。。。。。。
byte *tobyte=(byte*)&s;
Serial.write(tobyte,sizeof(sdata));
两种方法在Arduino都测试过,都能成功发送。
接收到这些数据时都是字节流,一般存储在缓冲字节数组中,如果想转换成原始的结构体,我们同样可以用memcpy,用法同上,只是源和目标换了位置。
最后再说一点,如果结构体很大,数据很多,那么结构体没有赋值前数据是不确定的,为了避免不确定数带来的问题,我们该怎么初始化这个结构体呢?一条一条赋值可以,但是很麻烦。如果要求不高,把结构体内的数据全部置零也算是一种初始化吧,用memset函数可以将目标地址内的数据全部置为指定的数据。不知道还有没有其他更好的方法,如果有知道的希望能交流交流。

五、Arduino-串口函数Serial:

串口是Arduino与其它设备进行通信的,我们需要很好的掌握它的使用。Arduino串口使用相关的函数共有10个(随着版本的升级,新版本则加入了更多。
Serial.begin(); //开启串行通信接口并设置通信波特率
Serial.end(); //关闭通信串口
Serial.avlable();//判断串口缓冲器是否有数据装入
Serial.read(); //读取串口数据
Serial.peek(); //返回下一字节(字符)输入数据,但不删除它
Serial.flush(); //清空串口缓存
Serial.print(); //写入字符串数据到串口
Serial.println(); //写入字符串数据+换行到串口
Serial.write(); //写入二进制数据到串口
Serial.SerialEvent();//read时触发的事件函数
Serial.readBytes(buffer,length);//读取固定长度的二进制流
Serial.println(incomingByte, DEC);//打印接到数据十进制表示的ascii码。 HEX 十六进制表示
peek():功能类似于read(),但是我们知道当调用一次read()后,缓冲区的数据流会被读取并删除read过的数据,也就是available值会减少,但peek()不会出现类似情况,其功能类似于检测缓冲区的头部数据,反复调用peek()返回的值是一样的。用得好的话可以精简很多代码。
write():写入二进制数据到串行端口。这个数据是作为一个字节或字节序列发送的。如果发送字符串则需要用到print方法。
最后注意一下:当烧程序入板卡的时候,在完成前即L、TX、RX灯闪烁前,断开数据线很可能损坏硬件。同时测试前养成按一次reset键复位的习惯,这里特别注意,在打开串口监视器后,需要reset一次,否则无法看到效果。