ANO匿名上位机V7协议&STM32

ANO匿名上位机V7协议&STM32

说明:以下程序为自己编写,若有误欢迎各位指出。

基于ANO匿名V7上位机的通信协议编写的代码


前言

提示:以下内容需用到C语言中的指针、结构体、枚举、及大小端储存方式,需提前知晓其中的基本用法&高级用法。


一、Ano V7上位机功能介绍

1,基本数据接收

注:使用基本收发时需开启显示功能
在这里插入图片描述

2,协议通信

界面如下所示,会显示出协议数据中的所有数据
在这里插入图片描述

a,格式设置

点击右边的边框,展开格式设置界面,用于配置用户数据,可用于波形显示
在这里插入图片描述
其中用户帧是从F1~FA共10帧
在这里插入图片描述
而每一帧里面可以携带10个数据,数据类型可为"uint8_t"、“uint16_t”、“int16_t”、“uint32_t"和"int32_t”
在这里插入图片描述
要使用那个帧就勾上使能该帧,否则该帧的数据无效
若要传输小数则需改变后面的传输缩放"1.0E+0"表示没缩放,"1.0E+1"表示缩放10倍

数据容器配置界面中,一共有20个用户的数据容器,可用于显示波形,只需要设置容器对应的数据位就行,后面会显示当前数据位的数据值
在这里插入图片描述

b,协议通信

协议通信界面可以进行发送命令、发送读取命令、发送写入命令
在这里插入图片描述

3,数据波形显示

在这里插入图片描述
在波形显示界面要配置需要显示的数据,右击波形名称,选择用户数据,找到对应的用户数据,并且在需要显示的数据前打勾在这里插入图片描述
其他详细操作请自行体会。

4,飞控参数

在这里插入图片描述
其中我们需要用到PID参数,Ano给我们留了18组PID
在这里插入图片描述

a,读取设备信息

在ANO V7上位机中,飞控参数界面需要指定的设备才能使用
在这里插入图片描述
所以,我们在连接串口后点击“读取信息”,会读取设备信息,此处我们只是借用“拓空者飞控”的ID
在这里插入图片描述
在这里插入图片描述

b,读取参数

点击读取参数,会显示出PID参数,直接点击参数值可进行更改,然后点击写入参数即可
在这里插入图片描述

二、程序

1,协议解析程序

MyAno.c

#include "MyAno.h"
#include "string.h"
#include "usart.h"
#include "math.h"

_ano MyAno = {0};			//发送的数据
_Para MPara = {0};			//参数

/** 	
 *	函数名:	参数初始化
 *	形	参:	无
 *	返回值:	无
**/
void Ano_Init(void) 
{
	MyAno.Head = 0xAA;
	MyAno.Addr = 0xFF;
	MyAno.Lenth = 0;
}

/**	
 * 函数名:	发送ID对应的参数
 * 形	参:	Id - 参数ID
 * 			para - 参数数据
 *	返回值:	无
**/
static void SendParaData(uint8_t Id,int32_t para)
{
	Ano_Set_Mdata(0xE2,(uint16_t *)&Id,2,1);	//加载ID
	Ano_Set_Mdata(0xE2,(int32_t *)&para,4,2);	//加载参数数据
	Ano_SendMdata();	//发送数据
}

/**
 * 函数名:	发送数据校验帧0x00
 * 形	参:	id - 帧的ID	
 * 			sumcheck 和校验数据
 * 			addcheck 附加校验数据
 * 返回值:无
**/
static void SendCheckAnalysis(uint8_t id,uint8_t *sumcheck,uint8_t *addcheck)
{
	Ano_Set_Mdata(0x00,(uint8_t *)&id,1,1);
	Ano_Set_Mdata(0x00,(uint8_t *)sumcheck,1,2);
	Ano_Set_Mdata(0x00,(uint8_t *)addcheck,1,3);
	Ano_SendMdata();
}

/**	
 *	函数名:	接收数据校验检测
 *	形	参:	_da数据
 *	返回值:	1校验成功	0校验失败 
**/
static uint8_t Receive_CheckData(uint8_t *_da)
{
	uint8_t i = 0;
	uint8_t sumcheck = 0,addcheck = 0;
	for(i = 0;i < _da[3] + 4;i++)
	{
		sumcheck += _da[i];
		addcheck += sumcheck;
	}	
	if((sumcheck == _da[_da[3] + 4]) && (addcheck == _da[_da[3] + 5]))		//校验通过
		return 1;
	else
		return 0;
}

/**	
 *	函数名:	发送数据和校验&附加校验计算
 *	形	参:	ano结构体
 *	返回值:	1->校验成功 0->校验失败
**/
static uint8_t Send_CheckData(_ano *ano)	
{
	uint8_t i = 0;
	uint8_t sumcheck = 0,addcheck = 0;
	for(i = 0;i < ano->Lenth + 4;i++)
	{
		sumcheck += ano->SendBuff[i];
		addcheck += sumcheck;
	}	
	memcpy(ano->SendBuff + 4 + ano->Lenth,(uint8_t*)&sumcheck,sizeof(sumcheck));
	memcpy(ano->SendBuff + 5 + ano->Lenth,(uint8_t*)&addcheck,sizeof(addcheck));
	/* 其中 ano->SendBuff[3] 表示数据长度 */
	if(sumcheck == ano->SendBuff[ano->SendBuff[3] + 4] && addcheck == ano->SendBuff[ano->SendBuff[3] + 5])
		return 1;
	else
		return 0;
}

/**
 * 函数名:	控制指令
 * 形 参:	_Ord命令
 * 返回值:	无
**/
__weak void ControlOrder(uint8_t _Ord)
{
	switch (_Ord)	//命令
	{
		case Stop:			//程序停止
			Ano_SendString("程序停止运行!\r\n",Color_Red);
		break;

		case Operation:		//运行程序
			Ano_SendString("程序开始运行!\r\n",Color_Green);
		break;

		case Store:			//储存参数
			Ano_SendString("参数储存成功!\r\n",Color_Green);
		break;
	}
}

/**
 * 函数名:	参数回传设置
 * 形 参:	_id参数的ID
 * 返回值:	无
**/
__weak void ParaOfReturn_Set(uint16_t _id)
{
	memset(MyAno.SendBuff,0x00,sizeof(MyAno.SendBuff));
	MyAno.Lenth = 0;
	switch (_id)
	{
		case 1:		SendParaData((uint8_t )_id,(int32_t)HWTYPE);	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_1_P:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par1_P);	break;	
		case PID_1_I:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par1_I);	break;	
		case PID_1_D:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par1_D);	break;	//参数回传(第一组PID)

		case PID_2_P:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par2_P);	break;	
		case PID_2_I:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par2_I);	break;	
		case PID_2_D:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par2_D);	break;	//参数回传(第二组PID)

		case PID_3_P:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par3_P);	break;	
		case PID_3_I:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par3_I);	break;	
		case PID_3_D:	SendParaData((uint8_t )_id,(int32_t)MPara.PID_Par3_D);	break;	//参数回传(第三组PID)

		default: SendParaData((uint8_t )_id,(int32_t)0x00);	break;	//若为其他参数则返回0	
	}
}

/**
 * 函数名:	参数数据读取设置
 * 形 参:	_id参数的ID
 * 			_val参数数据
 * 返回值:	无
**/
__weak void ParaRead_Set(uint16_t _id,int32_t _val)
{
	switch (_id)
	{
		case PID_1_P:	MPara.PID_Par1_P = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_1_I:	MPara.PID_Par1_I = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_1_D:	MPara.PID_Par1_D = _val;	break;	//参数回传硬件版本(此处借用拓空者)

		case PID_2_P:	MPara.PID_Par2_P = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_2_I:	MPara.PID_Par2_I = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_2_D:	MPara.PID_Par2_D = _val;	break;	//参数回传硬件版本(此处借用拓空者)

		case PID_3_P:	MPara.PID_Par3_P = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_3_I:	MPara.PID_Par3_I = _val;	break;	//参数回传硬件版本(此处借用拓空者)
		case PID_3_D:	MPara.PID_Par3_D = _val;	break;	//参数回传硬件版本(此处借用拓空者)
	}
}

/**	
 *	函数名:	接收数据解析
 *	形	参:	_da数据(需为完整的一帧数据)
 *	返回值:	无
**/
void Ano_DataAnalysis(uint8_t *_da)
{
	uint16_t HeadID = 0;
	int32_t DataVal = 0;
	MPara.OrderState = 1;	//在接收命令
	
	if(_da[0] == 0xAA && (_da[1] == 0xFF || _da[1] == 0x05))	//判断帧头、和目标地址
	{
		if(_da[2] == 0xE0)	//判断功能码(命令帧)
		{
			if(_da[4] == 0x10 && _da[5] == 0x00)		//判断CID和CMD0
				ControlOrder(_da[6]);	//控制指令(通过CMD1来控制程序)
			SendCheckAnalysis((uint8_t )0xE0,(uint8_t *)&(_da[_da[3] + 4]),(uint8_t *)&(_da[_da[3] + 5]));	//回传数据校验帧
			MPara.OrderState = 0;	//命令回传结束
		}
		else if(_da[2] == 0xE1)	//判断功能码(读参数)
		{
			if(Receive_CheckData(_da))
			{
				HeadID = *(uint16_t*)(&_da[4]);//(uint8_t)(_da[4] + (_da[5] << 8));
				ParaOfReturn_Set(HeadID);	//参数回传
				MPara.OrderState = 0;	//命令回传结束
			}
		}
		else if(_da[2] == 0xE2)	//判断功能码(写参数)
		{
			if(Receive_CheckData(_da))
			{
				HeadID =  *(uint16_t*)(&_da[4]);// (uint8_t)(_da[4] + (_da[5] << 8));		//(*(uint16_t*)(&_da[4]))
				DataVal = *(int32_t*)(&_da[6]);
				ParaRead_Set(HeadID,DataVal);	//参数数据读取设置
				SendCheckAnalysis((uint8_t )0xE2,(uint8_t *)&(_da[_da[3] + 4]),(uint8_t *)&(_da[_da[3] + 5]));	//回传数据校验帧
				MPara.OrderState = 0;	//命令回传结束
			}
		}
	}	
}

/**	
 *	函数名:	多数据配置函数(结合Ano_SendMdata多数据发送函数使用)
 *	形	参:	id->功能码(0xF1-0xFA) *data->发送的数据 len->数据长度(sizeof) num->用于指示第几个数据(可去除)
 *	返回值:	无
**/
void Ano_Set_Mdata(uint8_t id,void *data,uint8_t len,uint8_t num)
{
	MyAno.ID = id;
	memcpy(MyAno.SendBuff,(uint8_t*)&MyAno,3);
	memcpy(MyAno.SendBuff + 4 + MyAno.Lenth,(uint8_t*)data,len);
	MyAno.Lenth += len;
	memcpy(MyAno.SendBuff + 3,(uint8_t*)&MyAno.Lenth,1);
}

/**	
 *	函数名:	多数据发送(结合Ano_Set_Mdata多数据配置函数使用)
 *	形	参:	id->功能码(0xF1-0xFA) *data->发送的数据 len->数据长度(sizeof) num->用于指示第几个数据(可去除)
 *	返回值:	无
**/
void Ano_SendMdata(void)
{
	uint8_t check;
	check = Send_CheckData(&MyAno);
	if(check)
	{
		HAL_UART_Transmit(&huart2,MyAno.SendBuff,MyAno.Lenth + 6,100);
		memset(MyAno.SendBuff,0,sizeof(MyAno.SendBuff));
		MyAno.Lenth = 0;
	}
	else
	{
		memset(MyAno.SendBuff,0,sizeof(MyAno.SendBuff));
		MyAno.Lenth = 0;
	}
}

/**	
 *	函数名:	发送数据函数
 * 	形	参:	id->功能码(0xF1-0xFA) *Data->发送的数据 lenth->数据长度(sizeof)
 *	返回值:	无
**/
void Ano_Send_Data(uint8_t id, void *Data, uint8_t lenth)	//发送函数
{
	static uint8_t check;
	MyAno.ID = id;
	MyAno.Lenth = lenth;
	
	memcpy(MyAno.SendBuff,(uint8_t*)&MyAno,4);
	memcpy(MyAno.SendBuff + 4,(uint8_t*)Data,lenth);
	
	check = Send_CheckData(&MyAno);
	if(check)	//如果校验成功则发送数据,校验失败就丢弃此包
	{
		HAL_UART_Transmit(&huart2,MyAno.SendBuff,MyAno.Lenth + 6,100);
		memset(MyAno.SendBuff,0x00,sizeof(MyAno.SendBuff));
		MyAno.Lenth = 0;
	}	
}

/**
 * 函数名:	发送LOGO信息
 * 形 参:	字符串\数字
 * 返回值:	无
**/
void Ano_SendString(const char *str,uint8_t color)
{
	uint8_t i = 0;
	Ano_Set_Mdata(0xA0,(uint8_t*)&color,1,1);	//加载颜色数据
	while (*(str + i) != '\0')		//判断结束位
	{
		Ano_Set_Mdata(0xA0,(uint8_t*)(str+i++),1,2);	//加载字符串数据
	}
	Ano_SendMdata();	//发送数据
}

/**
 * 函数名:	发送LOGO信息+数据信息
 * 形 参:	字符串\数字
 * 返回值:	无
**/
void Ano_SendStringVal(const char *str,int32_t Val)
{
	uint8_t i = 0;
	Ano_Set_Mdata(0xA0,(int32_t*)&Val,4,1);	//加载数据信息
	while (*(str + i) != '\0')		//判断结束位
	{
		Ano_Set_Mdata(0xA1,(uint8_t*)(str+i++),1,2);	//加载字符串数据
	}
	Ano_SendMdata();	//发送数据
}

int16_t Ano_Show_Sin(float x)	//SIN函数
{
	return 100*sin(2*x);
}

float Ano_Show_Cos(float x)	//COS函数
{
	return 100*cos(2*x);
}

float Ano_Show_SQU(float x)	//方波
{
	if(x > 0 && x < 3.14f)
		return 10;
	else
		return -10;
}
	
void Show_Test(void)	//测试显示
{	
	int16_t y1,y2,y3;
	uint8_t nul = 0;
	static float x = 0;
	x += 0.01f;
	if(x > 3.14f)
		x = -3.14f;
	
	y1 = Ano_Show_Sin(x);		//数据*10发送
	y2 = Ano_Show_Cos(x);		//数据*10发送
	y3 = Ano_Show_SQU(x);
	
	Ano_Set_Mdata(0xF1,(int16_t*)&y1,sizeof(y1),1);
	Ano_Set_Mdata(0xF1,(int16_t*)&y2,sizeof(y2),2);
	Ano_Set_Mdata(0xF1,(int16_t*)&y3,sizeof(y3),3);
	Ano_Set_Mdata(0xF1,(uint8_t*)&nul,sizeof(nul),4);		//加载数据到对应的数据位
	Ano_SendMdata();	//发送数据
}

MyAno.h

#ifndef __MYANO_H
#define __MYANO_H

#include "stm32f3xx_hal.h"

#define Color_Black  	0
#define Color_Red  		1
#define Color_Green  	2

enum AnoID
{
	Stop = 0x00,		//停止运行
	Operation,			//开始运行
	Store,				//储存

	HWTYPE = 0x05,	//硬件种类

	PID_1_P = 11,
	PID_1_I,
	PID_1_D,		//第一组PID

	PID_2_P,
	PID_2_I,
	PID_2_D,		//第二组PID

	PID_3_P,
	PID_3_I,
	PID_3_D			//第三组PID
};

typedef struct
{
	uint8_t Head;
	uint8_t Addr;
	uint8_t ID;
	uint8_t Lenth;
	uint8_t SendBuff[1024];	//发送缓存数组
	uint8_t ReceiveBuf[10];	//接收缓存数组
}_ano;

typedef struct 
{
	uint8_t OrderState;		//命令接收状态

	int16_t PID_Par1_P;
	int16_t PID_Par1_I;
	int16_t PID_Par1_D;		//第一组PID

	uint16_t PID_Par2_P;
	uint16_t PID_Par2_I;
	uint16_t PID_Par2_D;	//第二组PID

	uint16_t PID_Par3_P;
	uint16_t PID_Par3_I;
	uint16_t PID_Par3_D;	//第三组PID
	/* data */
}_Para;

extern _ano MyAno;		//声明外部变量
extern _Para MPara;		//参数

/****** 用户不可调用函数 ******/
static void SendParaData(uint8_t Id,int32_t para);		//发送ID对应的参数
static void SendCheckAnalysis(uint8_t id,uint8_t *sumcheck,uint8_t *addcheck);		//发送数据校验帧0x00
static uint8_t Receive_CheckData(uint8_t *_da);		//接收数据校验检测
static uint8_t Send_CheckData(_ano *ano);			//发送数据和校验&附加校验计算

/****** 用户可调用函数 ******/
void Ano_Init(void); 												//参数初始化
void Ano_Send_Data(uint8_t id, void *Data, uint8_t lenth);			//发送数据函数
void Ano_Set_Mdata(uint8_t id,void *data,uint8_t len,uint8_t num);	//多数据配置
void Ano_SendMdata(void);											//多数据发送
void Ano_DataAnalysis(uint8_t *_da);								//接收数据解析(放串口回调函数里面)
void Ano_SendString(const char *str,uint8_t color);					//发送字符串
void Ano_SendStringVal(const char *str,int32_t Val);				//发送字符串+数据值

void Show_Test(void);	//测试显示函数

/****** 用户可调用&重写函数 ******/
__weak void ControlOrder(uint8_t _Ord);					//控制指令
__weak void ParaOfReturn_Set(uint16_t _id);				//参数回传设置
__weak void ParaRead_Set(uint16_t _id,int32_t _val);	//参数数据读取设置

#endif 

2,调用说明

注:需先调用"Ano_Init"参数初始化函数,在确定回传参数与实际参数的关系,然后把"Ano_DataAnalysis"扔进串口回调函数中,传入一个完整的一帧数据进行解析。

串口接收回调函数的示例:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == huart2.Instance)  //判断是否是USART1
  {
    MPara.OrderState = 1;	        //在接收命令数据
    if(UsartBuffer[0] == 0xAA)    //判断帧头
    {
      UsartBuffer[Usart1_RxCnt++] = RxBuffer;
      if((Usart1_RxCnt == 8 && UsartBuffer[2] == 0xE1) || (Usart1_RxCnt == 12 && UsartBuffer[2] == 0xE2) || (Usart1_RxCnt == 17 && UsartBuffer[2] == 0xE0))
      {
        Ano_DataAnalysis((uint8_t *)&UsartBuffer);
        Usart1_RxCnt = 0;			
			  memset(UsartBuffer,0x00,sizeof(UsartBuffer)); //清空数组
      }
    }
    else
    { 
      Usart1_RxCnt = 0;			
			memset(UsartBuffer,0x00,sizeof(UsartBuffer)); //清空数组
      UsartBuffer[Usart1_RxCnt++] = RxBuffer;
    }
    HAL_UART_Receive_IT(&huart2, (uint8_t *)&RxBuffer, 1);   //再开启接收中断	
  }
}

注:在发送数据的时候写一个判断语句,进行判断接收命令的状态

if(MPara.OrderState == 0) //命令接收结束
{
	//发送数据语句
}
  • 10
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值