基于匿名上位机V7的数据通信代码

发布时间:2024年01月04日

代码借鉴

匿名上位机V7波形显示教程-简单能用_匿名上位机配置-CSDN博客

并在此代码基础上进行修改。

截止文章发布时匿名上位机版本为V7.2.5.26

代码

ANDmessage.h

/*
 * ANDmessage.h
 *
 *  Created on: Jan 3, 2024
 *      Author:
 */

#ifndef INC_ANDMESSAGE_H_
#define INC_ANDMESSAGE_H_

#include <string.h>

#include "main.h"

// 目标地址宏定义
#define FRAME_HEADER 0XAA         //<匿名协议固定帧头
#define GENERAL_OUTPUT_ADDR 0XFF  //<广播型输出
#define HOST_ADDR 0XAF            //<向上位机输出
#define PRO_ADDR 0X05             //<拓空者PRO飞控
#define SHUCHUAN_ADDR 0X10        //<匿名数传
#define GUANGLIU_ADDR 0X22        //<匿名光流
#define UWB_ADDR 0X30             //<匿名UWB
#define IMU_ADDR 0X60             //<匿名凌霄IMU
#define LINGXIAO_ADDR 0X61        //<匿名凌霄飞控

#define HWTYPE 0X01     //<用于存储下位机的类型信息
#define ID_INFO5 0X05   //<预留位
#define ID_INFO6 0X06   //<预留位
#define ID_INFO7 0X07   //<预留位
#define ID_INFO8 0X08   //<预留位
#define ID_INFO9 0X09   //<预留位
#define ID_INFO10 0X0A  //<预留位

#define ANO_BLACK 0X00  //<字符黑色打印
#define ANO_RED 0X01    //<字符红色打印
#define ANO_GREEN 0X02  //<字符绿色打印

// 32位数据进行四个字节剥离拆分,从低位到高位
#define BYTE0(temp) (*(char*)(&temp))
#define BYTE1(temp) (*((char*)(&temp) + 1))
#define BYTE2(temp) (*((char*)(&temp) + 2))
#define BYTE3(temp) (*((char*)(&temp) + 3))

typedef struct {
    uint16_t par_id;  //<参数id
    int32_t par_val;  //<参数值
} par_struct;

/****通信帧对象结构体****/
typedef struct {
    uint8_t head;         //<帧头
    uint8_t target_addr;  //<目标地址
    uint8_t function_id;  //<该帧要实现某功能的功能码id
    uint8_t data_len;     //<数据长度
    uint8_t data[256];     //<数据内容,协议最高只支持40字节数据
    uint8_t sum_check;    //<和校验
    uint8_t add_check;    //<附加校验

    par_struct* parameter;  //<参数
} ano_frameStruct;

/*共同点:形参都含ano_frameStruct*类型指针*/
extern void ano_frame_reset(ano_frameStruct* frame);
// extern void ano_check_calculate(ano_frameStruct* frame);
// extern uint8_t ano_check(ano_frameStruct* frame);
// extern void frame_turn_to_array(ano_frameStruct* frame,uint8_t*str);
//.....

extern void ano_send_string(uint8_t color, char* str);
extern void ano_send_message(char* str, int32_t value);
extern void ano_send_flexible_frame(uint8_t id, int32_t x_coordinate, int32_t y_coordinate);
extern uint8_t ano_send_userdata_frame(uint8_t id, uint8_t data_num, uint8_t* ele_len_arr, uint32_t* ele_arr);
extern void ano_frame_init(void);

#endif /* INC_ANDMESSAGE_H_ */

ANDmessage.c

#include "ANDmessage.h"

#include "main.h"
#include "usart.h"

#if defined(STM32F1)
#include "stm32f1xx_hal.h"
#endif
#if defined(STM32F0)
#include "stm32f0xx_hal.h"
#endif
#if defined(STM32F4)
#include "stm32f4xx_hal.h"
#endif
#if defined(STM32F7)
#include "stm32f7xx_hal.h"
#endif
#if defined(STM32L4)
#include "stm32l4xx_hal.h"
#endif
#if defined(STM32H7)
#include "stm32h7xx_hal.h"
#endif

static par_struct send_parameter;          //<发送帧中的参数;
static par_struct rec_parameter;           //<接收帧的参数;
static ano_frameStruct send_frame_struct;  //<(发送)通信帧结构体
__IO ano_frameStruct rec_frame_struct;  //<(接收)通信帧结构体,因不止在本.c文件使用,故不用static修饰

/**
 * @brief   初始化通信帧结构体,使用前必须调用
 * @param   无输入参数
 * @retval  无返回
 **/
void ano_frame_init(void)
{
    /*参数结构体初始化*/
    send_frame_struct.parameter = &send_parameter;
    rec_frame_struct.parameter = &rec_parameter;
    send_frame_struct.parameter->par_id = 0;
    send_frame_struct.parameter->par_val = 0;
    rec_frame_struct.parameter->par_id = 0;
    rec_frame_struct.parameter->par_val = 0;

    send_frame_struct.head = rec_frame_struct.head = FRAME_HEADER;  // 帧头固定是0XAA
    send_frame_struct.target_addr = rec_frame_struct.target_addr = HOST_ADDR;
    send_frame_struct.function_id = 0XFF;  //<协议中没有定义的功能ID,这样初始化目的是为了启动瞬间不做任何动作

    memset(send_frame_struct.data, 0, 40);  //<缓存默认全部置0
    memset(rec_frame_struct.data, 0, 40);
}

/**
 * @brief   复位通信帧结构体,ano_frame_init()必须要运行过一次
 * @param   通信帧结构体对象
 * @retval  无返回
 **/
void ano_frame_reset(ano_frameStruct* frame)
{
    frame->function_id = 0XFF;
    frame->data_len = 0;
    memset(frame->data, 0, 40);
    frame->add_check = 0;
    frame->sum_check = 0;
}

/**
 * @brief   通信帧中参数结构体内成员的配置
 * @param   通信帧结构体对象,参数ID与参数值
 * @retval
 **/
void ano_par_struct_config(ano_frameStruct* frame, uint16_t id, int32_t val)
{
    frame->parameter->par_id = id;
    frame->parameter->par_val = val;
}

/**
 * @brief   通信帧校验计算
 * @param   通信帧结构体对象
 * @retval  无返回值
 **/
static void ano_check_calculate(ano_frameStruct* frame)
{
    frame->sum_check = 0;
    frame->add_check = 0;

    // 除去和校验,附加校验及数据部分,有4个部分4个字节,长度固定
    for (uint32_t i = 0; i < 4; i++) {
        frame->sum_check += *(uint8_t*)(&frame->head + i);
        frame->add_check += frame->sum_check;
    }
    // 获取数据长度部位,把数据部分全加上
    for (uint32_t i = 0; i < frame->data_len; i++) {
        frame->sum_check += *((uint8_t*)(frame->data) + i);
        frame->add_check += frame->sum_check;
    }
}

/**
 * @brief   通信帧校验检查(接收上位机通信帧时用)
 * @param   通信帧结构体对象
 * @retval   1:校验成功 0:校验失败
 **/
static uint8_t ano_check(ano_frameStruct* frame)
{
    uint8_t sum_check = 0;
    uint8_t add_check = 0;

    for (uint32_t i = 0; i < 4; i++) {
        sum_check += *(uint8_t*)(&frame->head + i);
        add_check += sum_check;
    }
    for (uint32_t i = 0; i < frame->data_len; i++) {
        sum_check += *((uint8_t*)(frame->data) + i);
        add_check += sum_check;
    }
    // 如果计算与获取的相等,校验成功
    if ((sum_check == frame->sum_check) && (add_check == frame->add_check))
        return 1;
    else
        return 0;
}

/**
 * @brief  匿名串口发送
 * @param  字符串,数据字节个数
 * @retval
 */
static void ano_usart_send(uint8_t* str, uint16_t num)
{
    uint16_t cnt = 0;
    do {
        HAL_UART_Transmit(&huart1, ((uint8_t*)(str)) + cnt, 1, 1000);
        cnt++;
    } while (cnt < num);
}

/**
 * @brief   通信帧结构体转化为线性数组
 * @param   要转换的通信帧,缓存数组
 * @retval
 **/
static void frame_turn_to_array(ano_frameStruct* frame, uint8_t* str)
{
    memcpy(str, (uint8_t*)frame, 4);
    memcpy(str + 4, (uint8_t*)frame->data, frame->data_len);
    memcpy(str + 4 + frame->data_len, (uint8_t*)(&frame->sum_check), 2);
}

/**
 * @brief  向上位机发送ASCII字符串
 * @param  color:希望上位机显示的字符串颜色,str:要发送的字符串
 * @retval 无返回值
 */
void ano_send_string(uint8_t color, char* str)
{
    uint8_t i = 0, cnt = 0;
    uint8_t buff[262];
    memset(send_frame_struct.data, 0, 256);
    send_frame_struct.function_id = 0XA0;   //<信息输出--字符串(功能码0XA0)
    send_frame_struct.data[cnt++] = color;  //<选择上位机打印的颜色
    /*字符串数据直接存入数据部分*/
    while (*(str + i) != '\0') {
        send_frame_struct.data[cnt++] = *(str + i++);
        if (cnt == 255)  //<若字符串长度超过255,强制结束
            break;
    }
    send_frame_struct.data_len = cnt;  //<记录下数据部分长度

    ano_check_calculate(&send_frame_struct);        //<计算校验和
    frame_turn_to_array(&send_frame_struct, buff);  //<通信帧转线性数组
    ano_usart_send(buff, 6 + send_frame_struct.data_len);
}

/**
 * @brief  向上位机发送ASCII字符串+数字组合
 * @param  value:32位的数值,str:要发送的字符串
 * @retval
 */
void ano_send_message(char* str, int32_t value)
{
    uint8_t i = 0, cnt = 0;
    uint8_t buff[262];
    memset(send_frame_struct.data, 0, 256);
    send_frame_struct.function_id = 0XA1;  // 信息输出--字符串+数字

    /*协议规定VAL在前,先对要求的32位数据进行截断*/
    send_frame_struct.data[cnt++] = BYTE0(value);
    send_frame_struct.data[cnt++] = BYTE1(value);
    send_frame_struct.data[cnt++] = BYTE2(value);
    send_frame_struct.data[cnt++] = BYTE3(value);
    /*再轮到字符串数据*/
    while (*(str + i) != '\0') {
        send_frame_struct.data[cnt++] = *(str + i++);
        if (cnt == 255) break;
    }

    send_frame_struct.data_len = cnt;  //<记录下数据部分长度

    ano_check_calculate(&send_frame_struct);        //<计算校验和
    frame_turn_to_array(&send_frame_struct, buff);  //<通信帧转线性数组
    ano_usart_send(buff, 6 + send_frame_struct.data_len);
}

/**
 * @brief  发送灵活格式帧
 * @param  id:0xF1~0XFA,x_coordinate:x轴坐标值 ,y_coordinate:y轴坐标值
 *         !!!要传多少个参数完全可以自己进行计算,最高只支持40字节的数据,低位先输出
 *         一般10个以内够用,40个字节限制,一个32位数据占4个字节,可以发送10个
 * @retval
 */
void ano_send_flexible_frame(uint8_t id, int32_t x_coordinate, int32_t y_coordinate)
{
    uint8_t buff[262];

    memset(send_frame_struct.data, 0, 256);
    send_frame_struct.function_id = id;
    send_frame_struct.data_len = 8;  //<根据自己的参数数填写

    /*第一个x_coordinate数据从低位到高位截断*/
    send_frame_struct.data[0] = BYTE0(x_coordinate);
    send_frame_struct.data[1] = BYTE1(x_coordinate);
    send_frame_struct.data[2] = BYTE2(x_coordinate);
    send_frame_struct.data[3] = BYTE3(x_coordinate);
    /*第二个数据y_coordinate从低位到高位截断*/
    send_frame_struct.data[4] = BYTE0(y_coordinate);
    send_frame_struct.data[5] = BYTE1(y_coordinate);
    send_frame_struct.data[6] = BYTE2(y_coordinate);
    send_frame_struct.data[7] = BYTE3(y_coordinate);
    /*第N个数据xxx从低位到高位截断*/
    //......用户自行添加

    ano_check_calculate(&send_frame_struct);
    frame_turn_to_array(&send_frame_struct, buff);

    ano_usart_send(buff, 6 + send_frame_struct.data_len);
}

/**
 * @brief  发送用户自定义格式帧
 * @param  id:0xF1~0XFA
 * 		   data_num:数据个数,1-10个,最多
 * 		   ele_len_arr:数据长度数组,为10数组,每个元素标志着每个数据的字节数,数组中只能有1 2 4,可以用0填充未使用的数据
 *         ele_arr:数据数组,为10数组
 * @retval 0:发送成功 1:数据总长度错误 2:数据长度数组错误
 */
uint8_t ano_send_userdata_frame(uint8_t id, uint8_t data_num, uint8_t* ele_len_arr, uint32_t* ele_arr)
{
    uint8_t buff[262];
    uint8_t len = 0;  // 传输数据字节数,最大为40
    for (uint8_t i = 0; i < data_num; i++) {
        if (ele_len_arr[i] != 1 && ele_len_arr[i] != 2 && ele_len_arr[i] != 4) {
            return 2;
        }
    }
    // 计算数据总长度
    for (uint8_t i = 0; i < data_num; i++) {
        len += ele_len_arr[i];
    }
    if (len > 40) {
        return 1;
    }

    memset(send_frame_struct.data, 0, 256);
    send_frame_struct.function_id = id;
    send_frame_struct.data_len = len;

    // 逐个填写数据
    uint8_t data_index = 0;
    for (uint8_t i = 0; i < data_num; i++) {
        for (uint8_t j = 0; j < ele_len_arr[i]; j++) {
            send_frame_struct.data[data_index] = (ele_arr[i] >> (j * 8)) & 0xFF;
            data_index++;
        }
    }

    ano_check_calculate(&send_frame_struct);
    frame_turn_to_array(&send_frame_struct, buff);

    ano_usart_send(buff, 6 + send_frame_struct.data_len);
    return 0;
}

需要的工作

如果你使用的是STM32CUBEIDE生成的STM32工程,那么这个代码可以直接加入到工程中。

需要做的是你在生成代码是需要设置单独生成C和H文件

否则的话你就需要修改ANDmessage.c第5行#include "usart.h",需要修改为能找到相关的串口端口定义huart1。

可能需要修改的地方

代码使用的是串口1,端口定义为huart1,如果你使用了其他的串口你需要在代码中修改为你使用的端口,需要修改ANDmessage.c第134行HAL_UART_Transmit(&huart1, ((uint8_t*)(str)) + cnt, 1, 1000);

函数解析

向上位机发送ASCII字符串

/**
 * @brief  向上位机发送ASCII字符串
 * @param  color:希望上位机显示的字符串颜色,str:要发送的字符串
 * @retval 无返回值
 */
void ano_send_string(uint8_t color, char* str)

color参数设置颜色,只能填写0,1,2,填写其他颜色匿名软件不会显示,还需要注意传输的字符串长度最大为255,因为LEN为一字节,最大只能255.

调用后会在这里进行打印。

向上位机发送ASCII字符串+数字组合

/**
 * @brief  向上位机发送ASCII字符串+数字组合
 * @param  value:32位的数值,str:要发送的字符串
 * @retval
 */
void ano_send_message(char* str, int32_t value)

第一个参数为字符串,最大长度只能为251个字符,因为还有4个字节的数字。

调用后会显示在这里。

发送灵活格式帧

首先介绍一下灵活帧格式

/**
 * @brief  发送用户自定义格式帧
 * @param  id:0xF1~0XFA
 * 		   data_num:数据个数,1-10个,最多
 * 		   ele_len_arr:数据长度数组,为10数组,每个元素标志着每个数据的字节数,数组中只能有1 2 4,可以用0填充未使用的数据
 *         ele_arr:数据数组,为10数组
 * @retval 0:发送成功 1:数据总长度错误 2:数据长度数组错误
 */
uint8_t ano_send_userdata_frame(uint8_t id, uint8_t data_num, uint8_t* ele_len_arr, int32_t* ele_arr)

第一个参数是功能码ID,需要0xF1-0xFA,第二个参数是你将要传输的数据个数,因为是灵活帧,所以数据可以是1-10个数据,第三个参数是一个数组,数组元素个数建议是10个,里面存的应该是你要传输的数据的字节数,比如int8就是一个字节,int32就是4个字节,这个数组里的数据只能是1 2 4,让你没用到的数据可以写0,否则函数会返回2。第四个参数是要传输的数据数组。

在软件中设置,需要根据你传输的数据个数和数据长度设置才能正确接收。这里我传输了两个32位的数据,所以如上如设置。

可以在这里选择数据波形。

文章来源:https://blog.csdn.net/andylauren/article/details/135384623
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。