文章目录[隐藏]
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 *)¶,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) //命令接收结束
{
//发送数据语句
}
版权声明:本文为CSDN博主「Merca QV6」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43603289/article/details/118575359
暂无评论