第1个任务radiolinkTask 处理串口接收到的数据包,把命令提取出来。
一条指令,包含 功能ID 和数据。功能ID–就是接收方用来区别需要做什么事,数据 --指令中需要的数据。另外,为了区别指令的开始位置,要在开始加上 帧头,为了保证接收正确,还要在最后加上一个校验,如果接收方发现校验错了,就把该指令丢弃。
在atkp.h中:
1、宏定义,定义帧头,功能ID 等
2、结构体 数据定义
3、atkp.c中的函数声名,方便其它模块通过包含atkp.h头文件,就可以调用atkp.c中的函数
指令格式: 帧头(AA+AF)+功能ID+数据长度+数据+校验
xTaskCreate(radiolinkTask, "RADIOLINK", 150, NULL, 5, NULL);
任务优先级被定义为 5
//radiolink接收ATKPPacket任务
void radiolinkTask(void *param)
{
rxState = waitForStartByte1;
u8 c;
u8 dataIndex = 0;
u8 cksum = 0;
while(1)
{
if (uartslkGetDataWithTimout(&c))
{
switch(rxState)
{
case waitForStartByte1: #帧头第1字节AA
rxState = (c == DOWN_BYTE1) ? waitForStartByte2 : waitForStartByte1;
cksum = c;
break;
case waitForStartByte2: #帧头第2字节AF
rxState = (c == DOWN_BYTE2) ? waitForMsgID : waitForStartByte1;
cksum += c;
break;
case waitForMsgID:
rxPacket.msgID = c;
rxState = waitForDataLength;
cksum += c;
break;
case waitForDataLength:
if (c <= ATKP_MAX_DATA_SIZE)
{
rxPacket.dataLen = c;
dataIndex = 0;
rxState = (c > 0) ? waitForData : waitForChksum1; /*c=0,数据长度为0,校验1*/
cksum += c;
} else
{
rxState = waitForStartByte1;
}
break;
case waitForData:
rxPacket.data[dataIndex] = c;
dataIndex++;
cksum += c;
if (dataIndex == rxPacket.dataLen)
{
rxState = waitForChksum1;
}
break;
case waitForChksum1:
if (cksum == c) /*所有校验正确*/
{
atkpPacketDispatch(&rxPacket);
}
else /*校验错误*/
{
rxState = waitForStartByte1;
IF_DEBUG_ASSERT(1);
}
rxState = waitForStartByte1;
break;
default:
ASSERT(0);
break;
}
}
else /*超时处理*/
{
rxState = waitForStartByte1;
}
}
}
任务的流程如下:
1、调用uartslkGetDataWithTimout(&c)获取1个字节
2、识别出帧头AA+AF
3、把功能ID,数据长度+数据 存到变量rxPacket中
4、这里采用的校验码是把前面所有的数据相加,只取1个字节。如果校验码正确,把接收到的存在rxPacket中的指令通过函数atkpPacketDispatch(&rxPacket);发送出去,然后回到开头等待帧头AA+AF
5、如果检验错误,也重新等待帧头AA+AF
6、如果1超时都没有获得1个字节,继续等待帧头AA+AF
整个过程的逻辑还是比较简单,这就是系统的好处
上面的任务中,需要获取1个字节,这不是直接从串口获取,而是从队列uartslkDataDelivery获得。
解析出来的指令,调用atkpPacketDispatch(&rxPacket)把它发送到队列rxQueue中,发送的是指令的指针。然后看一下txQueue队列,有没有数据要回复给发送方,如果有,则取一条数据回复过去。
1、uartslkDataDelivery队列
2、rxQueue队列
3、txQueue队列
从上面分析我们看到,串口中断接收到的数据放进队列uartslkDataDelivery中,在任务中,再从该队列读取数据。那么队列就相当于全局变量了,它需要定义在什么地方呢?
1、uartslkDataDelivery队列----定义在uart_syslink.c开头----使用到该队列的函数:初始化函数,串口中断函数,从队列读数据函数,都定义在这个.c文件里---------任务函数通过包含#include "radiolink.h"头文件来调用这个.c文件的函数
2、同理,rxQueue队列的,定义,初始化,向队列发送消息,从队列获取消息的函数,都定义在atkp.c文件里
3、txQueue队列好像有点特殊,它在两个地方定义了usblink.c 和radiolink.c,这两个c文件里的队列只是同名而已,其实它是两个不同的队列,因为在定义的时候加了static 只限本文件使用,所以txQueue相当于每个文件的局部变量。
这里有一个疑问,把接收到的指令解包到变量rxPacket中,然后把变量rxPacket的指针发送到队列rxQueue中,如果指令没有处理,接收第二指令的时候不是会把第一个指令覆盖了?因为都是接收到同一个变量里的啊。
FreeRTOS的队列是值传递,发送函数的第二个参数就是指向 待发数据 的指针,在发送函数里会从该指令复制N个字节到队列里,N就是在队列初始化中定义的项目大小。