一、匿名上位机介绍
匿名团队新推出了匿名助手,界面更加美观(猛男粉警告!),功能相对于匿名上位机V7主要是一些细节的优化和丰富,并无太大的改动。下面是匿名科技的官网welcome [匿名科创] (anotc.com)http://anotc.com/
下面是匿名助手的Gitee开源地址
笔者在打智能车竞赛,需要使用一个上位机调试PID参数,但是野火上位机虽然简单但自由度太低,不能忍受的是它存在一些小bug非常影响使用体验(一直没等来它的更新)。VOFA+相较于野火上位机有更高的自由度,可以自己定义组件、命令等。但是VOFA+的协议很简单,不够完备。尤其是对于无人机、智能车,错误的发生可能会造成硬件的损坏。此时的匿名助手恰巧刚刚推出,虽然目前看似乎不如VOFA+的自由度高,但功能都不缺,同时有一些相关产品的使用案例,有更安全更完备的通信协议,还有官方的猛男粉皮肤。
匿名助手的官方介绍视频
二、匿名助手协议介绍
匿名的通信协议通信高效、源码简单、可移植性高。
匿名协议采用小端模式,低字节在前,高字节在后
和校验 SUM CHECK 计算方法:从帧头 0xAB 字节开始,一直到 DATA 区结束,对每一字节进行累加操作,只取低 8 位
附加校验 ADD CHECK 计算方法:计算和校验时,每进行一字节的加法运算,同时进行一次 SUM CHECK 的累加操作,只取低 8 位。
校验计算示例:
假设数据帧缓存为 data_buf 数组, 0xAB 存放于数组起始位置,那么 data_buf[4]、 data_buf[5]存放的是数据长度,校验程序如下:
uint8_t sumcheck = 0;
uint8_t addcheck = 0;
uint16_t flen = data_buf[4] + data_buf[5] * 256;
For(uint16_t i=0; i < (flen+6); i++)
{
sumcheck += data_buf[i]; //从帧头开始,对每一字节进行求和,直到 DATA 区结束
addcheck += sumcheck; //每一字节的求和操作,进行一次 sumcheck 的累加
}
//如果计算出的 sumcheck 和 addcheck 和接收到的 check 数据相等,代表校验通过,反之数据有误
if(sumcheck == data_buf[flen+6] && addcheck == data_buf[flen+7])
return true; //校验通过
else
return false; //校验失败
匿名还有安全通信协议、灵活格式帧。简单来说,安全通信协议需要下位机返回一个确认帧,以确保通信及数据的正确性、安全性。灵活格式帧可以灵活地配置数据内容的数据个数、字节长度、数据格式。具体可自行参考《匿名通信协议.pdf》,在软件里可以获取到。
三、下位机代码移植
匿名科技在Gitee中提供了STM32F4的示例代码,本文以移植到NXP的i.MX RT1064为例,其他主控原理类似。
以下为匿名科技在Gitee中的说明
实现教程
1. 了解各个文件的作用
- AnoPTv8.h
包含协议相关函数的声明、硬件ID、版本号等的声明,在外部需要调用匿名V8协议的地方引用AnoPTv8.h即可。
- hardwareInterface.c/h
外部需要实现的相关函数,包括串口收发、周期调用任务函数。
- AnoPTv8DataDef.c/h
匿名V8协议中的数据定义、参数定义、命令定义。
- AnoPTv8Run.c/h
匿名V8协议主要运行相关函数,包含数据解析判断等。
- AnoPTv8FrameFactory.c/h
各个数据帧的打包发送函数。2. 实现串口收发及周期调用函数
匿名协议的正常运行,仅需要实现3个外部接口函数:
- void AnoPTv8HwSendBytes(uint8_t *buf, uint16_t len)数据发送函数,一般为设备的串口发送函数,注意,这里是数组多字节发送,所以设备的串口驱动必须为中断+环形缓冲或者DMA+缓冲的发送模式,单字节或阻塞式发送函数,将大量占用MCU时间。
- void AnoPTv8HwRecvByte(uint8_t dat)此函数传入的是字节数据,如果接收事件接收到的数据大于1字节,多次调用此函数即可。
- void AnoPTv8HwTrigger1ms(void)用户需要在1ms定时中断或者系统滴答或者自己设计的调度器内,以1ms的时间间隔调用此函数。
3. 测试
此时打开匿名助手,打开对应的串口连接,点击读取设备信息或读取参数,即可完成基于匿名V8协议的通信功能。
使用示例程序里的AnoPTv8Test.c/h内AnoPTv8TxFrameF1、AnoPTv8TxFrameF2函数,可实现灵活数据的上传功能,注意上位机要根据F1和F2帧的内容正确配置数据。
以下仅给出需要修改的代码
不难看出我们只需要,将hardwareInterface.c/h中的三个函数进行实现或者调用即可。
1. 在void AnoPTv8HwSendBytes(uint8_t *buf, uint16_t len)调用主控芯片上的多字节串口发送函数
这里以用中断+环形缓冲区实现,避免占用CPU太多时间,当然DMA会更好。
//AptHwSendBytes此函数需要根据用户自己的设备,具体实现,比如使用串口连接上位机,这里就对应该串口的发送函数
//注意:串口驱动务必使用中断+缓冲区或者DMA+缓冲区的方式,阻塞式发送将大大影响系统性能
//注意:串口缓冲区不应过小,推荐256字节或以上
//这里需引用对应的串口h文件
//#include "Drv_Uart.h"
#include "zf_driver_uart.h"
void AnoPTv8HwSendBytes(uint8_t *buf, uint16_t len)
{
Ano_uart1_SendBuf(buf, len);//中断式发送+缓存区
// uart_write_buffer(UART_1, buf, len);//阻塞式非常影响性能
// wireless_uart_send_buff(buf, len);//无线串口
// DrvUart1SendBuf(buf, len);
}
其中void Ano_uart1_SendBuf(unsigned char *DataToSend, uint8 data_num)实现如下:
LPUART_Type *uart_index[] = LPUART_BASE_PTRS;
uint8 Tx1Buffer[1024];
vuint64 Tx1Counter = 0;
vuint64 count1 = 0;
void Ano_uart1_SendBuf(unsigned char *DataToSend, uint8 data_num)
{
uint8 i;
for (i = 0; i < data_num; i++)
{
Tx1Buffer[(count1++)%1024] = *(DataToSend + i);
}
if (!(LPUART_GetEnabledInterrupts(LPUART1) & kLPUART_TxDataRegEmptyInterruptEnable))
{
uart_tx_interrupt(UART_1, 1); //打开发送中断
}
}
这里Tx1Buffer[(count1++)%1024]。取模之后相当于环形队列。
发送空闲中断函数如下:
void uart_tx_interrupt_handler (void)
{
LPUART_WriteByte(uart_index[1], Tx1Buffer[(Tx1Counter++)%1024]); // 写入发送数据 清除中断标志
if (Tx1Counter == count1)
{
uart_tx_interrupt(UART_1, 0); //关闭TXE(发送中断)中断
}
}
2. void AnoPTv8HwRecvByte(uint8_t dat)可以直接在串口接收中断里面调用,注意此函数每次只处理一个字节的数据。但建议在中断中先调用void drvU1GetByte(uint8 data)将数据存BUFF里,以免占用串口接受中断太长时间,然后在定时器里循环调用void drvU1DataCheck(void)
uint8 U1RxDataTmp[U1RXBUFF_SIZE];
uint8 U1RxInCnt = 0;
uint8 U1RxoutCnt = 0;
void drvU1GetByte(uint8 data)
{
U1RxDataTmp[U1RxInCnt++] = data;
if(U1RxInCnt >= U1RXBUFF_SIZE)
U1RxInCnt = 0;
}
void drvU1DataCheck(void)
{
while(U1RxInCnt!=U1RxoutCnt)
{
AnoPTv8HwRecvByte(U1RxDataTmp[U1RxoutCnt++]);
if(U1RxoutCnt >= U1RXBUFF_SIZE)
U1RxoutCnt = 0;
}
}
3. 最后在定时器中断里以1ms的周期循环调用void AnoPTv8HwTrigger1ms(void)
四、自定义参数及命令
直接在AnoPTv8DataDef.c/.h修改即可
五、效果演示
待完善...