写在前面
仅提供思路与参考!!搭建整个工程需要稍作优化
需求分析
本项目需求设计一个低速星型无线通信系统,由于网络拓扑结构的限制,所以两个子节点之间的通信只能依靠主节点的转发,所以组帧是非常必要的,同时需要设计一个帧结构解析函数对帧结构进行解析,通过对帧的源地址和目的地址进行解析,以避免不同子节点之间发包时会提前抓包进行处理。同时主节点需要抓包复制到自己的缓存区buffer里面上传至与PC之间的接口,以供PC端通过VISA进行处理,在LABVIEW平台进行可视化的显示。同时为了避免冲突,通信阶段分为不同的时隙,在每个由主节点依次询问控制子节点向自己发送数据包
调试过程
概述
根据项目要求设计一个星型通信网络,所以由于网络拓扑结构的限制,子节点之间通信的数据包需要由主节点来进行转发,同时进行抓包,上传至PC通过VISA在LABVIEW平台进行可视化显示。首先,要设计一个帧结构,拟使用一个byte类型的结构体来作为帧结构,在此平台中byte类型的数据与unsigned char一样占用一个字节的空间,为了使得程序可读性更好选择这种面向字节的编码方式,后面需要打印的时候可以根据需要进行强制类型转换进行串口打印。帧结构设计示意性代码为FRAME{头部,目的地址,源地址,数据段,帧尾},其中数据段的长度固定为50个字节,使用byte类型的数组来表示。同时发送一个帧只需要按照此顺序逐步发,收端只需要对帧进行解析,解析函数会返回帧中的某一个字节然后进行比对源地址和目的地址来确保子节点之间不会提前收到包。
然后此模块还设计当主节点或者子节点收到包后,会将收到的数据写入自己的receivedbuffer然后根据不同的节点进行不同的处理,子节点收到数据后需要给主节点返回ACK进行转发给发端,若为主节点收到数据包后需要将此数据包更改目的地址后进行转发,收到ACK的回应后,则需要将此数据包通过串口打印给PC。
帧结构收发设计
帧结构
typedef struct
{
byte header;
byte targetAddress;
byte selfAddress;
byte Message[50];
byte end;
}FRAME;
发送帧模块
void sendframe(FRAME frame)
{
LoRa.beginPacket();
LoRa.write(frame.header);
LoRa.write(frame.targetAddress);
LoRa.write(frame.selfAddress);
for(int i = 0; i < 50; i++)
{
LoRa.write(frame.Message[i]);
}
LoRa.write(frame.end);
LoRa.endPacket();
}
帧解析模块
byte procframe(int8_t x)
{
int8_t packetSize = LoRa.parsePacket();
if (packetSize)
{
int8_t i = 0;
while(LoRa.peek() != 0xFF)
{
receivedbuffer[i] = LoRa.read();
i++;
}
receivedbuffer[i] = 0xFF;
return receivedbuffer[x];
}
else
{
return 0x00;
}
}
系统核心--任务调度
系统设计概述
由于此次的Arduino Uno平台不支持JLINK,所以在硬件平台上调试通信程序存在困难,但是我设计了良好的软件接口和添加调试输出,可以通过串口监视器查看程序运行状态,方便调试,同时我们小组经过前期良好的方案设计,加上嵌入式操作系统的前序知识储备,我们决定在主循环里面采用任务调度的方式来进行整个系统的运行,这样使得模块之间的耦合比较小,实现了低耦合的总体设计。
任务注册表
typedef struct
{
uint8_t cur;
void (*CurrentOperation)(void);
}TASK_TABLE;
这样,在loop里面就只用放下任务调度了!!!是不是非常简洁,而且最重要的是:
模块之间的耦合会非常的小!!
void loop()
{
t_table[current].CurrentOperation();
}
发送节点模式
void Sendermode(void)
{
inittime = millis();
sendframe(send);
curtime = millis();
while (1)
{
//Serial.println("now its sendermode");
received = procframe(3);
if (millis() - inittime > 500)
{
current = 0;
break;
}
else if (received != ACK)
{
if (millis() - curtime > 100)
{
sendframe(send);
curtime = millis();
}
}
else if (received == ACK)
{
if (millis() - curtime < 500)
{
Serial.print("received ack form B!");
current = 0;
break;
}
}
}
}
接收节点模式
void Receivermode(void)
{
bool flag = false;
inittime1 = millis();
while(!flag)
{
if (millis() - inittime1 < 20000)
{
//Serial.println("now its receivermode");
received = procframe(3);
if (received != 0x00 && receivedbuffer[2] == 0x10 && receivedbuffer[1] == 0xAA)
{
switch(received)
{
case DIANMING:
{
Serial.print("DIANMING..........................");
Serial.println("");
current = 1;// go to sendermode
flag = true;
break;
}
case REQUIREMENT:
{
Serial.print("REQ...............................");
LoRa.beginPacket();
LoRa.write(RESPOND);
LoRa.endPacket();
}
//case CHANGE:
default:
{
Serial.print("DEFAULT..................................................");
procframe(0);
sendframe(ack);
flag = true;
//break;
}
}
}
}
else
{
current = 2;// go to ruwang
flag = true;
//break;`
}
}
}
注意,运行不要忘记帧结构和任务表的实例化与任务注册!