MiniFly Firmware V1.1開源四軸程式碼分析二:atpk.c

weixin_45456099發表於2020-10-17

指令格式:幀頭(AA+AF)+功能ID+資料長度+資料+校驗
校驗幀頭:AA+AF
功能ID: 表示該資料的意義,msgID分為 上行指令ID :upmsgID_e ;下行指令ID :downmsgID_e,這是兩個列舉變數
資料長度:dataLen
資料陣列:data 最大為30
atkpTxTask: ATKP 資料包傳送任務。 該任務主要是獲取 stabilizerTask 中的感測器資料、姿態資料、電機 PWM 輸出資料等資料以定週期傳送給 radiolinkTask 和 usblinkTxTask, 由這兩個任務分別傳送飛遙控器和上位機。
atkpRxAnlTask: ATKP 資料包接收處理任務。 該任務主要是處理遙控器和上位機發下來的資料包, 解析到的控制指令則傳送到 stabilizerTask 中去。

#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "atkp.h"
#include "radiolink.h"
#include "usblink.h"
#include "usbd_usr.h"
#include "stabilizer.h"
#include "motors.h"
#include "commander.h"
#include "flip.h"
#include "pm.h"
#include "pid.h"
#include "attitude_pid.h"
#include "sensors.h"
#include "position_pid.h"
#include "config_param.h"
#include "power_control.h"
#include "remoter_ctrl.h"

/*FreeRTOS相關標頭檔案*/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"


//資料拆分巨集定義
#define  BYTE0(dwTemp)       ( *( (u8 *)(&dwTemp)	)  )
#define  BYTE1(dwTemp)       ( *( (u8 *)(&dwTemp) + 1) )
#define  BYTE2(dwTemp)       ( *( (u8 *)(&dwTemp) + 2) )
#define  BYTE3(dwTemp)       ( *( (u8 *)(&dwTemp) + 3) )

//資料返回週期時間(單位ms)
#define  PERIOD_STATUS		30
#define  PERIOD_SENSOR 		10
#define  PERIOD_RCDATA 		40
#define  PERIOD_POWER 		100
#define  PERIOD_MOTOR		40
#define  PERIOD_SENSOR2 	40
#define  PERIOD_SPEED   	50

#define ATKP_RX_QUEUE_SIZE 	10 /*ATKP包接收佇列訊息個數*/

typedef struct  
{
	u16 roll;
	u16 pitch;
	u16 yaw;
	u16 thrust;
}joystickFlyui16_t;

bool isConnect = false;
bool isInit = false;
bool flyable = false;
static joystickFlyui16_t rcdata;
static xQueueHandle rxQueue;

extern PidObject pidAngleRoll;
extern PidObject pidAnglePitch;
extern PidObject pidAngleYaw;
extern PidObject pidRateRoll;
extern PidObject pidRatePitch;
extern PidObject pidRateYaw;
//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
static void atkpSendPacket(atkp_t *p)
{
	radiolinkSendPacket(p);//傳送到radiolink的“傳送佇列txQueue”
	
	if(getusbConnectState())
	{
		usblinkSendPacket(p);//傳送到usblink的“傳送佇列txQueue”
	}	
}
/***************************傳送至匿名上位機指令******************************/
static void sendStatus(float roll, float pitch, float yaw, s32 alt, u8 fly_model, u8 armed)
{
	u8 _cnt=0;
	atkp_t p;
	vs16 _temp;
	vs32 _temp2 = alt;
	
	p.msgID = UP_STATUS;
	
	_temp = (int)(roll*100);
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = (int)(pitch*100);
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = (int)(yaw*100);
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	
	p.data[_cnt++]=BYTE3(_temp2);
	p.data[_cnt++]=BYTE2(_temp2);
	p.data[_cnt++]=BYTE1(_temp2);
	p.data[_cnt++]=BYTE0(_temp2);
	
	p.data[_cnt++] = fly_model;
	p.data[_cnt++] = armed;
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}
//傳送MPU九軸感測器資料
static void sendSenser(s16 a_x,s16 a_y,s16 a_z,s16 g_x,s16 g_y,s16 g_z,s16 m_x,s16 m_y,s16 m_z)
{
	u8 _cnt=0;
	atkp_t p;
	vs16 _temp;
	
	p.msgID = UP_SENSER;

	_temp = a_x;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = a_y;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = a_z;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	
	_temp = g_x;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = g_y;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = g_z;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	
	_temp = m_x;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = m_y;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = m_z;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = 0;	
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);	
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}
static void sendRCData(u16 thrust,u16 yaw,u16 roll,u16 pitch,u16 aux1,u16 aux2,u16 aux3,u16 aux4,u16 aux5,u16 aux6)
{
	u8 _cnt=0;
	atkp_t p;
	
	p.msgID = UP_RCDATA;
	p.data[_cnt++]=BYTE1(thrust);
	p.data[_cnt++]=BYTE0(thrust);
	p.data[_cnt++]=BYTE1(yaw);
	p.data[_cnt++]=BYTE0(yaw);
	p.data[_cnt++]=BYTE1(roll);
	p.data[_cnt++]=BYTE0(roll);
	p.data[_cnt++]=BYTE1(pitch);
	p.data[_cnt++]=BYTE0(pitch);
	p.data[_cnt++]=BYTE1(aux1);
	p.data[_cnt++]=BYTE0(aux1);
	p.data[_cnt++]=BYTE1(aux2);
	p.data[_cnt++]=BYTE0(aux2);
	p.data[_cnt++]=BYTE1(aux3);
	p.data[_cnt++]=BYTE0(aux3);
	p.data[_cnt++]=BYTE1(aux4);
	p.data[_cnt++]=BYTE0(aux4);
	p.data[_cnt++]=BYTE1(aux5);
	p.data[_cnt++]=BYTE0(aux5);
	p.data[_cnt++]=BYTE1(aux6);
	p.data[_cnt++]=BYTE0(aux6);

	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}

static void sendPower(u16 votage, u16 current)
{
	u8 _cnt=0;
	atkp_t p;
	
	p.msgID = UP_POWER;
	
	p.data[_cnt++]=BYTE1(votage);
	p.data[_cnt++]=BYTE0(votage);
	p.data[_cnt++]=BYTE1(current);
	p.data[_cnt++]=BYTE0(current);
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}

static void sendMotorPWM(u16 m_1,u16 m_2,u16 m_3,u16 m_4,u16 m_5,u16 m_6,u16 m_7,u16 m_8)
{
	u8 _cnt=0;
	atkp_t p;
	
	p.msgID = UP_MOTOR;
	
	p.data[_cnt++]=BYTE1(m_1);
	p.data[_cnt++]=BYTE0(m_1);
	p.data[_cnt++]=BYTE1(m_2);
	p.data[_cnt++]=BYTE0(m_2);
	p.data[_cnt++]=BYTE1(m_3);
	p.data[_cnt++]=BYTE0(m_3);
	p.data[_cnt++]=BYTE1(m_4);
	p.data[_cnt++]=BYTE0(m_4);
	p.data[_cnt++]=BYTE1(m_5);
	p.data[_cnt++]=BYTE0(m_5);
	p.data[_cnt++]=BYTE1(m_6);
	p.data[_cnt++]=BYTE0(m_6);
	p.data[_cnt++]=BYTE1(m_7);
	p.data[_cnt++]=BYTE0(m_7);
	p.data[_cnt++]=BYTE1(m_8);
	p.data[_cnt++]=BYTE0(m_8);
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}

static void sendSenser2(s32 bar_alt,u16 csb_alt)//傳送氣壓計資訊
{
	u8 _cnt=0;
	atkp_t p;
	
	p.msgID = UP_SENSER2;
	
	p.data[_cnt++]=BYTE3(bar_alt);
	p.data[_cnt++]=BYTE2(bar_alt);
	p.data[_cnt++]=BYTE1(bar_alt);
	p.data[_cnt++]=BYTE0(bar_alt);
	
	p.data[_cnt++]=BYTE1(csb_alt);
	p.data[_cnt++]=BYTE0(csb_alt);
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}

static void sendPid(u8 group,float p1_p,float p1_i,float p1_d,float p2_p,float p2_i,float p2_d,float p3_p,float p3_i,float p3_d)
{
	u8 _cnt=0;
	atkp_t p;
	vs16 _temp;
	
	p.msgID = 0x10+group-1;

	_temp = p1_p * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p1_i * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p1_d * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p2_p * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p2_i * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p2_d * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p3_p * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p3_i * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	_temp = p3_d * 10 ;
	p.data[_cnt++]=BYTE1(_temp);
	p.data[_cnt++]=BYTE0(_temp);
	
	p.dataLen = _cnt;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}

static void sendCheck(u8 head, u8 check_sum)
{
	atkp_t p;
	
	p.msgID = UP_CHECK;
	p.dataLen = 2;
	p.data[0] = head;
	p.data[1] = check_sum;
	atkpSendPacket(&p);//把ATKP包傳送到radiolink和usblink的“傳送佇列txQueue”
}
/****************************************************************************/

/*資料週期性傳送給上位機,每1ms呼叫一次*/
static void atkpSendPeriod(void)
{
	static u16 count_ms = 1;
	//if(!()) ,只有當裡面的為0時,!0就是真。
	if(!(count_ms % PERIOD_STATUS))//count_ms/30ms,每30ms傳送一次
	{
		attitude_t attitude;
		getAttitudeData(&attitude);
		int positionZ = getPositionZ() * 100.f;
		sendStatus(attitude.roll, attitude.pitch, attitude.yaw, positionZ, 0, flyable);	//傳送狀態			
	}
	if(!(count_ms % PERIOD_SENSOR))//count_ms/10ms,每10ms傳送一次MPU資料
	{
		Axis3i16 acc;
		Axis3i16 gyro;
		Axis3i16 mag;
		getSensorRawData(&acc, &gyro, &mag);
		sendSenser(acc.x, acc.y, acc.z, gyro.x, gyro.y, gyro.z, mag.x, mag.y, mag.z);//傳送MPU九軸感測器資料			
	}
	if(!(count_ms % PERIOD_RCDATA))//count_ms/40ms,每40ms傳送一次遙控資料(油門,偏航,翻滾,俯仰)
	{
		sendRCData(rcdata.thrust, rcdata.yaw, rcdata.roll,
					rcdata.pitch, 0, 0, 0, 0, 0, 0);//傳送遙控器的資料
	}
	if(!(count_ms % PERIOD_POWER))//count_ms/100ms,每100ms傳送一次電壓資料
	{
		float bat = pmGetBatteryVoltage();
		sendPower(bat*100,500);//傳送電量資訊
	}
	if(!(count_ms % PERIOD_MOTOR))//count_ms/40ms,每40ms傳送一次電機PWMxxx資料
	{
		u16 m1,m2,m3,m4;
		motorPWM_t motorPWM;
		getMotorPWM(&motorPWM);
		m1 = (float)motorPWM.m1/65535*1000;
		m2 = (float)motorPWM.m2/65535*1000;
		m3 = (float)motorPWM.m3/65535*1000;
		m4 = (float)motorPWM.m4/65535*1000;
		sendMotorPWM(m1,m2,m3,m4,0,0,0,0);
	}
	if(!(count_ms % PERIOD_SENSOR2))//count_ms/40ms,每40ms傳送一次氣壓計資料
	{
		int baro = getBaroData() * 100.f;
		sendSenser2(baro,0);//傳送氣壓計資訊
	}
	if(++count_ms>=65535) 
		count_ms = 1;	
}
//將幀頭,地址,幀資料部分的長度,每個資料求和作為校驗
static u8 atkpCheckSum(atkp_t *packet)//分析atkp_t結構體(包),得出校驗值
{
	u8 sum;
	sum = DOWN_BYTE1;/*下行幀頭*/
	sum += DOWN_BYTE2;
	sum += packet->msgID;//幀地址
	sum += packet->dataLen;//幀資料部分的長度
	for(int i=0; i<packet->dataLen; i++)
	{
		sum += packet->data[i];
	}
	return sum;
}
//解包,根據msgID==下行指令ID(列舉型別)和data[0]做出處理
static void atkpReceiveAnl(atkp_t *anlPacket)
{
	if(anlPacket->msgID	== DOWN_COMMAND)//DOWN_COMMAND	= 0x01,下行指令
	{
		switch(anlPacket->data[0])
		{
			case D_COMMAND_ACC_CALIB:
				break;
			
			case D_COMMAND_GYRO_CALIB:
				break;
			
			case D_COMMAND_MAG_CALIB:
				break;
			
			case D_COMMAND_BARO_CALIB:
				break;
			//上面的命令直接退出switch
			case D_COMMAND_FLIGHT_LOCK:
				flyable = false;//不能飛,上鎖
				break;
			
			case D_COMMAND_FLIGHT_ULOCK:
				flyable = true;//能飛,解鎖
		}
	}			
	else if(anlPacket->msgID == DOWN_ACK)//DOWN_ACK	= 0x02,
	{
		if(anlPacket->data[0] == D_ACK_READ_PID)/*讀取PID引數*/
		{
			sendPid(1,pidRateRoll.kp, pidRateRoll.ki, pidRateRoll.kd,
					  pidRatePitch.kp, pidRatePitch.ki, pidRatePitch.kd,
					  pidRateYaw.kp, pidRateYaw.ki, pidRateYaw.kd 
				   );
			sendPid(2,pidAngleRoll.kp, pidAngleRoll.ki, pidAngleRoll.kd,
					  pidAnglePitch.kp, pidAnglePitch.ki, pidAnglePitch.kd,
					  pidAngleYaw.kp, pidAngleYaw.ki, pidAngleYaw.kd 
				   );
			float kp, ki, kd;
			getPositionPIDZ(&kp, &ki, &kd);
			sendPid(3,kp,ki,kd,0,0,0,0,0,0);
		}
		if(anlPacket->data[0] == D_ACK_RESET_PARAM)/*恢復預設引數*/
		{
			resetConfigParamPID();
			positionControlInit();
			attitudeControlInit();
			
			sendPid(1,pidRateRoll.kp, pidRateRoll.ki, pidRateRoll.kd,
					  pidRatePitch.kp, pidRatePitch.ki, pidRatePitch.kd,
					  pidRateYaw.kp, pidRateYaw.ki, pidRateYaw.kd 
				   );
			sendPid(2,pidAngleRoll.kp, pidAngleRoll.ki, pidAngleRoll.kd,
					  pidAnglePitch.kp, pidAnglePitch.ki, pidAnglePitch.kd,
					  pidAngleYaw.kp, pidAngleYaw.ki, pidAngleYaw.kd 
				   );
			float kp, ki, kd;
			getPositionPIDZ(&kp, &ki, &kd);
			sendPid(3,kp,ki,kd,0,0,0,0,0,0);
		}
	}
	else if(anlPacket->msgID == DOWN_RCDATA)
	{
		rcdata = *((joystickFlyui16_t*)anlPacket->data);
	}
	else if(anlPacket->msgID == DOWN_POWER)/*nrf51822*/
	{
		pmSyslinkUpdate(anlPacket);
	}
	else if(anlPacket->msgID == DOWN_REMOTER)/*遙控器*/	
	{
		remoterCtrlProcess(anlPacket);
	}
	else if(anlPacket->msgID == DOWN_PID1)//線上PID除錯
	{
		pidRateRoll.kp  = 0.1*((s16)(*(anlPacket->data+0)<<8)|*(anlPacket->data+1));
		pidRateRoll.ki  = 0.1*((s16)(*(anlPacket->data+2)<<8)|*(anlPacket->data+3));
		pidRateRoll.kd  = 0.1*((s16)(*(anlPacket->data+4)<<8)|*(anlPacket->data+5));
		pidRatePitch.kp = 0.1*((s16)(*(anlPacket->data+6)<<8)|*(anlPacket->data+7));
		pidRatePitch.ki = 0.1*((s16)(*(anlPacket->data+8)<<8)|*(anlPacket->data+9));
		pidRatePitch.kd = 0.1*((s16)(*(anlPacket->data+10)<<8)|*(anlPacket->data+11));
		pidRateYaw.kp   = 0.1*((s16)(*(anlPacket->data+12)<<8)|*(anlPacket->data+13));
		pidRateYaw.ki   = 0.1*((s16)(*(anlPacket->data+14)<<8)|*(anlPacket->data+15));
		pidRateYaw.kd   = 0.1*((s16)(*(anlPacket->data+16)<<8)|*(anlPacket->data+17));
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}
	else if(anlPacket->msgID == DOWN_PID2)
	{
		pidAngleRoll.kp  = 0.1*((s16)(*(anlPacket->data+0)<<8)|*(anlPacket->data+1));
		pidAngleRoll.ki  = 0.1*((s16)(*(anlPacket->data+2)<<8)|*(anlPacket->data+3));
		pidAngleRoll.kd  = 0.1*((s16)(*(anlPacket->data+4)<<8)|*(anlPacket->data+5));
		pidAnglePitch.kp = 0.1*((s16)(*(anlPacket->data+6)<<8)|*(anlPacket->data+7));
		pidAnglePitch.ki = 0.1*((s16)(*(anlPacket->data+8)<<8)|*(anlPacket->data+9));
		pidAnglePitch.kd = 0.1*((s16)(*(anlPacket->data+10)<<8)|*(anlPacket->data+11));
		pidAngleYaw.kp   = 0.1*((s16)(*(anlPacket->data+12)<<8)|*(anlPacket->data+13));
		pidAngleYaw.ki   = 0.1*((s16)(*(anlPacket->data+14)<<8)|*(anlPacket->data+15));
		pidAngleYaw.kd   = 0.1*((s16)(*(anlPacket->data+16)<<8)|*(anlPacket->data+17));
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}		
	else if(anlPacket->msgID == DOWN_PID3)
	{
		float kp  = 0.1*((s16)(*(anlPacket->data+0)<<8)|*(anlPacket->data+1));
		float ki  = 0.1*((s16)(*(anlPacket->data+2)<<8)|*(anlPacket->data+3));
		float kd  = 0.1*((s16)(*(anlPacket->data+4)<<8)|*(anlPacket->data+5));
		setPositionPIDZ(kp, ki, kd);
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}
	else if(anlPacket->msgID == DOWN_PID4)
	{
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}
	else if(anlPacket->msgID == DOWN_PID5)
	{
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}
	else if(anlPacket->msgID == DOWN_PID6)
	{
//		s16 temp1  = ((s16)(*(anlPacket->data+0)<<8)|*(anlPacket->data+1));
//		s16 temp2  = ((s16)(*(anlPacket->data+2)<<8)|*(anlPacket->data+3));
//		s16 temp3  = ((s16)(*(anlPacket->data+4)<<8)|*(anlPacket->data+5));
		s16 enable = ((s16)(*(anlPacket->data+6)<<8)|*(anlPacket->data+7));
		s16 m1_set = ((s16)(*(anlPacket->data+8)<<8)|*(anlPacket->data+9));
		s16 m2_set = ((s16)(*(anlPacket->data+10)<<8)|*(anlPacket->data+11));
		s16 m3_set = ((s16)(*(anlPacket->data+12)<<8)|*(anlPacket->data+13));
		s16 m4_set = ((s16)(*(anlPacket->data+14)<<8)|*(anlPacket->data+15));
		setMotorPWM(enable,m1_set,m2_set,m3_set,m4_set);
		attitudePIDwriteToConfigParam();
		u8 cksum = atkpCheckSum(anlPacket);
		sendCheck(anlPacket->msgID,cksum);
	}
} 

void atkpTxTask(void *param)
{
	sendMsgACK();//返回四軸資訊
	while(1)
	{
		atkpSendPeriod();//資料週期性傳送給上位機,每1ms呼叫一次
		vTaskDelay(1);
	}
}
//ATKP 資料包接收處理任務,主要是處理遙控器和上位機發下來的資料包,\
解析到的控制指令則傳送到 stabilizerTask 中去
void atkpRxAnlTask(void *param)
{
	atkp_t p;
	while(1)
	{
		xQueueReceive(rxQueue, &p, portMAX_DELAY);//從“接收佇列rxQueue”提取一個包,存入緩衝區p
		atkpReceiveAnl(&p);//雙向值傳遞,分析處理包
	}
}

void atkpInit(void)//檢測是否建立了“接收佇列”,沒有則建立
{
	if(isInit) return;
	rxQueue = xQueueCreate(ATKP_RX_QUEUE_SIZE, sizeof(atkp_t));
	ASSERT(rxQueue);
	isInit = true;
}

bool atkpReceivePacketBlocking(atkp_t *p)//接收ATKP包,帶阻塞(死等),\
	                                   只被usblinkRxTask和atkpPacketDispatch呼叫
{
	ASSERT(p);
	ASSERT(p->dataLen <= ATKP_MAX_DATA_SIZE);
	return xQueueSend(rxQueue, p, portMAX_DELAY);	
}

流程圖:
在這裡插入圖片描述

相關文章