【PID上下位机通信】

代码

初始化代码

/**
 * @brief       初始化调试
 * @param       无
 * @retval      无
 */
void debug_init(void)
{
    debug_obj_init(&g_debug);            /* 初始化所需内存 */
}

被上面代码调用

/**
 * @brief       内存初始化
 * @param       *data:内存起始地址
 * @retval      无
 */
void debug_obj_init(debug_data *data)
{
    size_t obj_size = sizeof(debug_data);
    memset(data, 0, (size_t)obj_size);             /* 把指定范围内存清零 */
}

设置目标范围函数


/**
 * @brief       设置目标速度范围
 * @param       max_limit:最大值
 * @param       min_limit:最小值(反转时最大速度)
 * @param       step_max : 最大突变值
 * @retval      无
 */
void debug_set_point_range(float max_limit, float min_limit, float step_max)
{
    static float step_temp = 0.0;

    if (abs((int)(*debug_rev.speed - step_temp)) > step_max)     /* 判断速度突变是否超过允许范围 */
    {
        *debug_rev.speed = step_temp;                            /* 超过最大突变值,保持原来速度 */
    }

    step_temp = *debug_rev.speed;                                /* 保存本次速度 */

    if (*debug_rev.speed >= max_limit)                           /* 超过限速 */
    {
        *debug_rev.speed = max_limit;                            /* 配置为最大允许速度 */
    }

    if (*debug_rev.speed <= min_limit)                           /* 超过限速 */
    {
        *debug_rev.speed = min_limit;                            /* 配置为最大允许速度 */
    }
}

PID目标值上下位机同步

/**
 * @brief       PID数据上传
 * @param       PIDx      :PID组(1~10)
 * @param       *SetPoint :目标速度地址
 * @param       P、I、D   :PID参数
 * @retval      无
 */
void debug_send_initdata(upload_type PIDx, float *SetPoint, float P, float I, float D)
{
    debug_rev.speed = (float *)(SetPoint);          /* 开发板和上位机共用一个PID目标值的内存地址,数据同步更方便 */

    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[0] = P;  /* 传入P值 */
    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[1] = I;  /* 传入I值 */
    g_debug.pid[PIDx - TYPE_PID1].pid.pidf[2] = D;  /* 传入D值 */
    debug_upload_data(&g_debug, PIDx);              /* 发送PID参数 */
}

底层函数

实现数据接收和发送

数据接收

在这里插入图片描述
这里我们以没有数据位的介绍
在这里插入图片描述
先通过判断接受到帧尾

uint8_t  rev_data[ 17 ] ;					/* 存放接收数据的数组 */
uint8_t  rev_p = 0 ;                       	/* 地址偏移量 */ 

void debug_handle( uint8_t  *data )
{            
	if ( rev_p >= 17 )						/* 超过缓冲区最大长度 */
	{
		rev_p = 0 ;							/* 地址偏移量清零,将该字节放到第一个字节内存位置 */
	}

	rev_data[ rev_p ] = *(data);         	/* 从地址里取出数据,存进数组 */
	if (*data == DEBUG_DATA_END)            /* 判断是否收到帧尾 */
    {		
    	/* 数据包长度为5个字节,判断第一个字节是否为帧头,是进入接收到帧头后,向前偏移4个地址接收到帧头 */
        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD) 
        {
            for (i = 0; i < 2; i++)
            {
            	/* 取出帧头、数据类别,5个字节的数据包没有数据域 */
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + i) % DEBUG_REV_MAX_LEN];                         
            }
         /*下面就是CRC校验代码*/
#if EN_CRC                                         /* 进行CRC校验,这里的+2 +3就是地址偏移到crc校验字节的地址了 */
            if (crc16_calc(temp, 2) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 3) % DEBUG_REV_MAX_LEN]))
#endif			/*如果校验与帧头和数据类别对了*/
            {
                if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 1) % DEBUG_REV_MAX_LEN] == CMD_GET_ALL_DATA)/* 判断数据类别是否为:获取全部参数 */
                {
                    debug_upload_data(&g_debug, TYPE_STATUS);         /* 发送电机状态 */
                    debug_upload_data(&g_debug, TYPE_SPEED);          /* 发送速度值 */
                    debug_upload_data(&g_debug, TYPE_HAL_ENC);        /* 发送霍尔、编码器位置 */
                    debug_upload_data(&g_debug, TYPE_VBUS);           /* 发送电压 */
                    debug_upload_data(&g_debug, TYPE_AMP);            /* 发送电流 */
                    debug_upload_data(&g_debug, TYPE_TEMP);           /* 发送温度 */
                    debug_upload_data(&g_debug, TYPE_SUM_LEN);        /* 发送总里程 */
                    debug_upload_data(&g_debug, TYPE_BEM);            /* 发送反电动势 */
                    debug_upload_data(&g_debug, TYPE_MOTOR_CODE);     /* 发送电机类型 */
                    for (i = TYPE_PID1; i < TYPE_PID10; i++)
                    {
                        debug_upload_data(&g_debug, i);               /* 发送PID参数 */
                    }
                }
            }
        }
        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为6个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 3; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                         /* 进行CRC校验 */
            if (crc16_calc(temp, 3) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 3) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 4) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */
                {
                    case CMD_SET_CTR_CODE:                                                                                       /* 下发控制指令 */
                        debug_rev.Ctrl_code = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];
                        break;

                    case CMD_SET_CTR_MODE:                                                                                       /* 下发控制模式 */
                        debug_rev.Ctrl_mode = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];
                        break;
                }
            }
        }

        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为7个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 4; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                         /* 进行CRC校验 */
            if (crc16_calc(temp, 4) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 4) % DEBUG_REV_MAX_LEN] << 8) | \
                                         debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 5) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */
                {
                    case CMD_SET_SPEED:                                                                                          /* 设定电机速度 */
                        *(debug_rev.speed) = (int16_t)((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                                        debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN]);
                        break;

                    case CMD_SET_TORQUE:                                                                                         /* 设定转矩 */
                        *(debug_rev.torque) = (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \
                                               debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN];
                        break;
                }
            }
        }

        if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                       /* 数据包长度为17个字节,判断第一个字节是否为帧头 */
        {
            for (i = 0; i < 14; i++)
            {
                temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + i) % DEBUG_REV_MAX_LEN];                        /* 取出帧头、数据类别、数据域 */
            }
#if EN_CRC                                          /* 进行CRC校验 */
            if (crc16_calc(temp, 14) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 14) % DEBUG_REV_MAX_LEN] << 8) | \
                                          debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 15) % DEBUG_REV_MAX_LEN]))
#endif
            {
                switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN])                          /* 判断数据类别 */
                {
                    case CMD_SET_PID1:
                    case CMD_SET_PID2:
                    case CMD_SET_PID3:
                    case CMD_SET_PID4:
                    case CMD_SET_PID5:
                    case CMD_SET_PID6:
                    case CMD_SET_PID7:
                    case CMD_SET_PID8:
                    case CMD_SET_PID9:
                    case CMD_SET_PID10:
                    for (i = 0; i < 12; i++)                                                                                     /* 接收设定的PID参数 */
                    {
                        g_debug.pid[debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN] - CMD_SET_PID1].pid.pidi8[i] = \
                                    debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 2 + i) % DEBUG_REV_MAX_LEN];
                    }
                    break;
                }
            }
        }
    }

    debug_rev_p ++;			/* 地址偏移,当存完上一个,地址向后偏移一位再向后偏移 */
}

例程中采用环形环形缓冲区
存放接收数据为17个字节

数据发送

在这里插入图片描述

/**
 * @brief       数据上传
 * @param       *data:上传的数据(地址)
 * @param       upload_type:上传的数据类别
 * @retval      无
 */
void debug_upload_data(debug_data *data, uint8_t upload_type)
{
    uint8_t cur_data, i;
    uint8_t upload_data[37];                                            /* 数据上传数组 */
    upload_data[0] = DEBUG_DATA_HEAD;                                   /* 数据包第1个字节(数组第0个元素),固定为帧头 */
    cur_data = 2;                                                       /* 数据域从第3个字节(数组第2个元素)开始 */

    switch (upload_type)                                                /* 判断数据类别 */
    {
        case TYPE_STATUS:                                               /* 设备状态 */
            upload_data[1] = upload_type;                               /* 数据包第2个字节(数组第1个元素),固定为数据类别 */
            upload_data[cur_data++] = data->status;                     /* 存入要发送的数据域 */
            break;

        case TYPE_SPEED:                                                /* 电机速度 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->speed >> 8) & 0xFF;        /* 先存入速度值高8位(小端模式中u16赋给u8类型,只取低8位) */
            upload_data[cur_data++] = data->speed & 0xFF;               /* 再存入速度值低8位 */
            break;

        case TYPE_HAL_ENC:                                              /* 霍尔、编码器位置值 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->hall_p) & 0x08;            /* 存入霍尔位置值 */
            upload_data[cur_data++] = (data->encode_p >> 8) & 0xFF;     /* 存入编码器位置值高8位 */
            upload_data[cur_data++] = (data->encode_p) & 0xFF;          /* 存入编码器位置值低8位 */
            break;

        case TYPE_VBUS:                                                                 /* 电压,范围 0~100.99 V */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = ((uint8_t)data->bus_vol) % 101;                   /* 存入电压值整数部分,整数部分不允许超过100 */
            upload_data[cur_data++] = ((uint16_t)(data->bus_vol * 100)) % 100;          /* 存入电压值小数部分,小数部分不允许超过99 */
            break;

        case TYPE_AMP:                                                                  /* 电流 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->amp[0] * 1000)) >> 8) & 0xFF;   /* 存入U相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[0] * 1000)) & 0xFF;          /* 存入U相电流低8位 */
            upload_data[cur_data++] = (((int16_t)(data->amp[1] * 1000)) >> 8) & 0xFF;   /* 存入V相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[1] * 1000)) & 0xFF;          /* 存入V相电流低8位 */
            upload_data[cur_data++] = (((int16_t)(data->amp[2] * 1000)) >> 8) & 0xFF;   /* 存入W相电流高8位 */
            upload_data[cur_data++] = ((int16_t)(data->amp[2] * 1000)) & 0xFF;          /* 存入W相电流低8位 */
            break;

        case TYPE_TEMP:                                                                 /* 温度 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (uint8_t)(data->temp[0] + 50);                    /* 存入驱动板温度 */
            upload_data[cur_data++] = (uint8_t)(data->temp[1] + 50);                    /* 存入电机温度 */
            break;

        case TYPE_SUM_LEN:                                                              /* 总里程 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (data->sum_len >> 56) & 0xFF;                     /* 存入总里程 56~63 位 */
            upload_data[cur_data++] = (data->sum_len >> 48) & 0xFF;                     /* 存入总里程 48~55 位 */
            upload_data[cur_data++] = (data->sum_len >> 40) & 0xFF;                     /* 存入总里程 40~47 位 */
            upload_data[cur_data++] = (data->sum_len >> 32) & 0xFF;                     /* 存入总里程 32~39 位 */
            upload_data[cur_data++] = (data->sum_len >> 24) & 0xFF;                     /* 存入总里程 24~31 位 */
            upload_data[cur_data++] = (data->sum_len >> 16) & 0xFF;                     /* 存入总里程 16~23 位 */
            upload_data[cur_data++] = (data->sum_len >> 8) & 0xFF;                      /* 存入总里程 8~15 位 */
            upload_data[cur_data++] = (data->sum_len >> 0) & 0xFF;                      /* 存入总里程 0~7 位 */
            break;

        case TYPE_BEM:                                                                  /* 反电动势 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (int8_t)data->bem[0];                             /* 存入U相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[0] * 100)) % 100;            /* 存入U相反电动势电压小数部分 */
            upload_data[cur_data++] = (int8_t)data->bem[1];                             /* 存入V相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[1] * 100)) % 100;            /* 存入V相反电动势电压小数部分 */
            upload_data[cur_data++] = (int8_t)data->bem[2];                             /* 存入W相反电动势电压整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->bem[2] * 100)) % 100;            /* 存入W相反电动势电压小数部分 */
            break;

        case TYPE_MOTOR_CODE:                                                           /* 电机类型 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = data->motor_code;                                 /* 存入电机类型 */
            break;

        case TYPE_TORQUE:                                                               /* 扭矩 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->torque * 1000)) >> 8) & 0xFF;   /* 存入扭矩值整数部分 */
            upload_data[cur_data++] = ((int16_t)(data->torque * 1000)) & 0xFF;          /* 存入扭矩值小数部分 */
            break;

        case TYPE_POWER:                                                                /* 功率 */
            upload_data[1] = upload_type;
            upload_data[cur_data++] = (((int16_t)(data->power * 100)) >> 8) & 0xFF;     /* 存入功率值高8位 */
            upload_data[cur_data++] = ((int16_t)(data->power * 100)) & 0xFF;            /* 存入功率值低8位 */
            break;

        case TYPE_PID1:                                                                 /* PID参数组别 */
        case TYPE_PID2:
        case TYPE_PID3:
        case TYPE_PID4:
        case TYPE_PID5:
        case TYPE_PID6:
        case TYPE_PID7:
        case TYPE_PID8:
        case TYPE_PID9:
        case TYPE_PID10:
            upload_data[1] = upload_type;

            for (i = 0; i < 3; i++)                                            /* 循环存入P、I、D系数值,每个系数占4个字节 */
            {
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 0];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 1];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 2];
                upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 3];
            }
            break;

        case TYPE_USER_DATA:                                                   /* 波形数据 */
            upload_data[1] = upload_type;

            for (i = 0; i < 16; i++)                                           /* 循环存入1~16个通道波形数据 */
            {
                upload_data[cur_data++] = (data->user_data[i] >> 8) & 0xFF;    /* 存入波形数据高8位 */
                upload_data[cur_data++] =  data->user_data[i] & 0xFF;          /* 存入波形数据低8位 */
            }
            break;

        default :
            upload_data[1] = 0xFE;                                             /* 数据类别错误,存入错误码0xFE */
            break;
    }

    if (upload_data[1] == 0xFE)                                                /* 数据类别错误,直接跳出 */
    {
        return;
    }
    else                                                                       /* 数据类别正确 */
    {
        uint16_t crc_res = crc16_calc(&(upload_data[0]), cur_data);            /* 进行CRC校验 */
        upload_data[cur_data++] = (crc_res >> 8) & 0xFF;                       /* 存入校验结果高8位 */
        upload_data[cur_data++] = (crc_res) & 0xFF;                            /* 存入校验结果低8位 */
        upload_data[cur_data++] = DEBUG_DATA_END;                              /* 存入帧尾 */
        HAL_UART_Transmit(&g_uart1_handle, upload_data, cur_data, 0xFFFF);     /* 发送数据到上位机 */
    }
}
Vivado2023是一款集成开发环境软件,用于设计和验证FPGA(现场可编程门阵列)和可编程逻辑器件。对于使用Vivado2023的用户来说,license是必不可少的。 Vivado2023的license是一种许可证,用于授权用户合法使用该软件。许可证分为多种类型,包括评估许可证、开发许可证和节点许可证等。每种许可证都有不同的使用条件和功能。 评估许可证是免费提供的,让用户可以在一段时间内试用Vivado2023的全部功能。用户可以使用这个许可证来了解软件的性能和特点,对于初学者和小规模项目来说是一个很好的选择。但是,使用评估许可证的用户在使用期限过后需要购买正式的许可证才能继续使用软件。 开发许可证是付费的,可以永久使用Vivado2023的全部功能。这种许可证适用于需要长期使用Vivado2023进行开发的用户,通常是专业的FPGA设计师或工程师。购买开发许可证可以享受Vivado2023的技术支持和更新服务,确保软件始终保持最新的版本和功能。 节点许可证是用于多设备或分布式设计的许可证,可以在多个计算机上安装Vivado2023,并共享使用。节点许可证适用于大规模项目或需要多个处理节点进行设计的用户,可以提高工作效率和资源利用率。 总之,Vivado2023 license是用户在使用Vivado2023时必须考虑的问题。用户可以根据自己的需求选择合适的许可证类型,以便获取最佳的软件使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值