关于PID闭环控制中上位机与下位机通讯代码的解析分享(一)

news2024/11/25 1:24:27

下位机接收数据代码(以STM32单片机为例)与上位机发送数据C#代码分享

1、下位机代码:

/**
 * @brief   接收的数据处理
 * @param   void
 * @return  -1:没有找到一个正确的命令.
 */
int8_t receiving_process(void)
{
  uint8_t frame_data[128];         // 要能放下最长的帧
  uint16_t frame_len = 0;          // 帧长度
  uint8_t cmd_type = CMD_NONE;     // 命令类型
  
  while(1)
  {
    cmd_type = protocol_frame_parse(frame_data, &frame_len);
    switch (cmd_type)
    {
      case CMD_NONE:
      {
        return -1;
      }

      case SET_P_I_D_CMD:
      {
        uint32_t temp0 = COMPOUND_32BIT(&frame_data[13]);
        uint32_t temp1 = COMPOUND_32BIT(&frame_data[17]);
        uint32_t temp2 = COMPOUND_32BIT(&frame_data[21]);
        
        float p_temp, i_temp, d_temp;
        
        p_temp = *(float *)&temp0;
        i_temp = *(float *)&temp1;
        d_temp = *(float *)&temp2;
        
        set_p_i_d(p_temp, i_temp, d_temp);    // 设置 P I D
      }
      break;

      case SET_TARGET_CMD:
      {
        int actual_temp = COMPOUND_32BIT(&frame_data[13]);    // 得到数据
        
        set_pid_target(actual_temp);    // 设置目标值
      }
      break;
      
      case START_CMD:
      {
        set_motor_enable();              // 启动电机
      }
      break;
      
      case STOP_CMD:
      {
        set_motor_disable();              // 停止电机
      }
      break;
      
      case RESET_CMD:
      {
        HAL_NVIC_SystemReset();          // 复位系统
      }
      break;
      
      case SET_PERIOD_CMD:
      {
        uint32_t temp = COMPOUND_32BIT(&frame_data[13]);     // 周期数
        SET_BASIC_TIM_PERIOD(temp);                             // 设置定时器周期1~1000ms
      }
      break;

      default: 
        return -1;
    }
  }
}

这段代码是一个接收数据并进行处理的函数。函数名为receiving_process,返回类型为int8_t,参数为空。函数的作用是接收数据并根据接收到的命令类型进行相应的处理。如果没有找到正确的命令,函数返回-1。

在函数内部,定义了一些变量用于存储接收到的数据和命令类型。frame_data是一个长度为128的uint8_t数组,用于存储接收到的数据帧。frame_len是一个uint16_t类型的变量,用于记录数据帧的长度。cmd_type是一个uint8_t类型的变量,用于存储命令类型。

接下来是一个无限循环,用于不断接收并处理数据。

循环内部首先调用了一个名为protocol_frame_parse的函数,该函数用于解析数据帧并返回命令类型。解析过程将接收到的数据帧存储在frame_data中,并将数据帧的长度保存在frame_len中。解析结果保存在cmd_type中。

接下来通过switch语句对cmd_type进行判断,根据不同的命令类型进行相应的处理。

如果命令类型为CMD_NONE,表示没有找到正确的命令,返回-1。

如果命令类型为SET_P_I_D_CMD,表示接收到了设置PID参数的命令。接下来通过COMPOUND_32BIT宏将frame_data中的字节转换为32位整数,然后再通过强制类型转换将32位整数转换为float类型。将转换后的参数传递给set_p_i_d函数,用于设置PID参数。

如果命令类型为SET_TARGET_CMD,表示接收到了设置目标值的命令。通过COMPOUND_32BIT宏将frame_data中的字节转换为32位整数,然后将转换后的参数传递给set_pid_target函数,用于设置目标值。

如果命令类型为START_CMD,表示接收到了启动电机的命令。调用set_motor_enable函数,用于启动电机。

如果命令类型为STOP_CMD,表示接收到了停止电机的命令。调用set_motor_disable函数,用于停止电机。

如果命令类型为RESET_CMD,表示接收到了复位系统的命令。调用HAL_NVIC_SystemReset函数,用于复位系统。

如果命令类型为SET_PERIOD_CMD,表示接收到了设置定时器周期的命令。通过COMPOUND_32BIT宏将frame_data中的字节转换为32位整数,然后将转换后的参数传递给SET_BASIC_TIM_PERIOD函数,用于设置定时器周期。

最后,如果命令类型不在上述情况中,默认返回-1。

总结起来,这段代码是一个接收数据并进行处理的函数。函数通过解析数据帧获取命令类型,并根据命令类型进行相应的处理操作。

2、上位机代码:

using System;
using System.IO.Ports;
using System.Threading;

public class UpperComputer
{
    private SerialPort serialPort;

    public UpperComputer()
    {
        // 初始化串口
        serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
    }

    public void Connect()
    {
        try
        {
            serialPort.Open();  // 打开串口连接
            Console.WriteLine("成功连接下位机。");
        }
        catch (Exception ex)
        {
            Console.WriteLine("连接下位机失败:" + ex.Message);
        }
    }

    public void Disconnect()
    {
        serialPort.Close();  // 关闭串口连接
        Console.WriteLine("已断开下位机连接。");
    }

    public void SendCommand(byte[] command)
    {
        try
        {
            serialPort.Write(command, 0, command.Length);  // 发送命令
            Console.WriteLine("发送命令成功。");
        }
        catch (Exception ex)
        {
            Console.WriteLine("发送命令失败:" + ex.Message);
        }
    }

    public static void Main(string[] args)
    {
        UpperComputer upperComputer = new UpperComputer();
        upperComputer.Connect();

        // 示例:发送设置PID参数的命令
        byte[] setPIDCommand = new byte[25];
        setPIDCommand[0] = 1;  // 命令类型:设置PID参数
        Buffer.BlockCopy(BitConverter.GetBytes(1.23f), 0, setPIDCommand, 1, 4);  // 设置P参数
        Buffer.BlockCopy(BitConverter.GetBytes(4.56f), 0, setPIDCommand, 5, 4);  // 设置I参数
        Buffer.BlockCopy(BitConverter.GetBytes(7.89f), 0, setPIDCommand, 9, 4);  // 设置D参数
        
        upperComputer.SendCommand(setPIDCommand);

        // 示例:发送设置目标值的命令
        byte[] setTargetCommand = new byte[9];
        setTargetCommand[0] = 2;  // 命令类型:设置目标值
        Buffer.BlockCopy(BitConverter.GetBytes(100), 0, setTargetCommand, 1, 4);  // 设置目标值为100
        
        upperComputer.SendCommand(setTargetCommand);

        // 示例:发送启动电机的命令
        byte[] startCommand = new byte[1];
        startCommand[0] = 3;  // 命令类型:启动电机
        
        upperComputer.SendCommand(startCommand);

        // 示例:发送停止电机的命令
        byte[] stopCommand = new byte[1];
        stopCommand[0] = 4;  // 命令类型:停止电机
        
        upperComputer.SendCommand(stopCommand);

        // 示例:发送复位系统的命令
        byte[] resetCommand = new byte[1];
        resetCommand[0] = 5;  // 命令类型:复位系统
        
        upperComputer.SendCommand(resetCommand);

        // 示例:发送设置定时器周期的命令
        byte[] setPeriodCommand = new byte[5];
        setPeriodCommand[0] = 6;  // 命令类型:设置定时器周期
        Buffer.BlockCopy(BitConverter.GetBytes(500), 0, setPeriodCommand, 1, 4);  // 设置周期为500ms
        
        upperComputer.SendCommand(setPeriodCommand);

        upperComputer.Disconnect();
    }
}
  1. 导入命名空间:

    • using System;:引入System命名空间,包含了一些常用的系统类型和功能。

    • using System.IO.Ports;:引入System.IO.Ports命名空间,提供了与串口通信相关的类和方法。

    • using System.Threading;:引入System.Threading命名空间,提供了多线程编程相关的类和方法。

  2. 定义UpperComputer类:

    • private SerialPort serialPort;:定义一个私有变量serialPort,用于串口通信。

    • public UpperComputer():构造方法,用于初始化串口。

  3. Connect()方法:

    • serialPort.Open();:打开串口连接。

    • Console.WriteLine("成功连接下位机。");:在控制台输出连接成功的提示信息。

  4. Disconnect()方法:

    • serialPort.Close();:关闭串口连接。

    • Console.WriteLine("已断开下位机连接。");:在控制台输出断开连接的提示信息。

  5. SendCommand()方法:

    • serialPort.Write(command, 0, command.Length);:发送命令给下位机。

    • Console.WriteLine("发送命令成功。");:在控制台输出发送命令成功的提示信息。

  6. Main()方法:

    • UpperComputer upperComputer = new UpperComputer();:创建一个UpperComputer对象。

    • upperComputer.Connect();:调用Connect()方法连接下位机。

  7. 示例代码:

    • 发送设置PID参数的命令:

      • byte[] setPIDCommand = new byte[25];:定义一个长度为25的byte数组,用于存储设置PID参数的命令。

      • setPIDCommand[0] = 1;:设置命令类型为设置PID参数。

      • Buffer.BlockCopy(BitConverter.GetBytes(1.23f), 0, setPIDCommand, 1, 4);:将浮点数1.23转换为字节数组,并复制到setPIDCommand数组的指定位置,用于设置P参数。

      • Buffer.BlockCopy(BitConverter.GetBytes(4.56f), 0, setPIDCommand, 5, 4);:将浮点数4.56转换为字节数组,并复制到setPIDCommand数组的指定位置,用于设置I参数。

      • Buffer.BlockCopy(BitConverter.GetBytes(7.89f), 0, setPIDCommand, 9, 4);:将浮点数7.89转换为字节数组,并复制到setPIDCommand数组的指定位置,用于设置D参数。

      • upperComputer.SendCommand(setPIDCommand);:调用SendCommand()方法发送设置PID参数的命令。

    • 发送设置目标值的命令:

      • byte[] setTargetCommand = new byte[9];:定义一个长度为9的byte数组,用于存储设置目标值的命令。

      • setTargetCommand[0] = 2;:设置命令类型为设置目标值。

      • Buffer.BlockCopy(BitConverter.GetBytes(100), 0, setTargetCommand, 1, 4);:将整数100转换为字节数组,并复制到setTargetCommand数组的指定位置,用于设置目标值。

      • upperComputer.SendCommand(setTargetCommand);:调用SendCommand()方法发送设置目标值的命令。

    • 发送启动电机的命令:

      • byte[] startCommand = new byte[1];:定义一个长度为1的byte数组,用于存储启动电机的命令。

      • startCommand[0] = 3;:设置命令类型为启动电机。

      • upperComputer.SendCommand(startCommand);:调用SendCommand()方法发送启动电机的命令。

    • 发送停止电机的命令:

      • byte[] stopCommand = new byte[1];:定义一个长度为1的byte数组,用于存储停止电机的命令。

      • stopCommand[0] = 4;:设置命令类型为停止电机。

      • upperComputer.SendCommand(stopCommand);:调用SendCommand()方法发送停止电机的命令。

    • 发送复位系统的命令:

      • byte[] resetCommand = new byte[1];:定义一个长度为1的byte数组,用于存储复位系统的命令。

      • resetCommand[0] = 5;:设置命令类型为复位系统。

      • upperComputer.SendCommand(resetCommand);:调用SendCommand()方法发送复位系统的命令。

    • 发送设置定时器周期的命令:

      • byte[] setPeriodCommand = new byte[5];:定义一个长度为5的byte数组,用于存储设置定时器周期的命令。

      • setPeriodCommand[0] = 6;:设置命令类型为设置定时器周期。

      • Buffer.BlockCopy(BitConverter.GetBytes(500), 0, setPeriodCommand, 1, 4);:将整数500转换为字节数组,并复制到setPeriodCommand数组的指定位置,用于设置周期。

      • upperComputer.SendCommand(setPeriodCommand);:调用SendCommand()方法发送设置定时器周期的命令。

    • upperComputer.Disconnect();:调用Disconnect()方法断开下位机连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/721330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚幻引擎程序化资源生成框架PCG 之 常用撒点方法小结

PCG真好玩,门槛很低,天花板很高 文章目录 前言1. 基本撒点1.1 Landscape上撒点1.2 使用射线检测在地表面撒点1.3 使用曲线撒点1.3.1 沿曲线撒点1.3.2 在闭合曲线内部撒点 1.4 在StaticMesh表面撒点 2. 进阶撒点2.1 在闭合曲线内部放射状撒点2.2 在Mesh表…

MedCalc v22.009 医学ROC曲线统计分析软详细图文教程

简介 MedCalc是一款医学 ROC 曲线统计软件,用于ROC曲线分析的参考软件,医学工作者设计的医学计算器,功能齐全。它可以帮助医生快速作出普通的医学计算,从而对症下药。提供超过76种常用的规则和方法,包括:病…

《消失的她》豆瓣短评数据分析

《消失的她》豆瓣短评数据分析 文章目录 《消失的她》豆瓣短评数据分析一、前言二、数据加载和预处理三、探索性数据分析1、查看评论的评价分布2、查看评论点赞数的分布3、查看评论的地理分布 四、情感分析 一、前言 最近爆火的电影《消失的她》你们有没有去看过呢&#xff1f…

开源站点(jpress)部署

第三阶段基础 时 间:2023年7月5日 参加人:全班人员 内 容: 开源站点部署(jpress) 服务器设置;单台服务器,安装tomcat和mariadb 环境配置: 1、关闭防火墙 systemctl stop fir…

【大数据之Hive】二十、Hive之调优相关配置及Explain查看执行计划

1 Yarn资源配置 需要调整Yarn的参数与CPU、内存等资源有关 (1)yarn.nodemanager.resource.memory-mb   设置一个NodeManager节点分配给容器Container使用的内存,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量&#x…

搭建高性能数据库集群之二:MySQL读写分离(基于mycat2-1.22)

一、概述 读写分离是常见的一种数据库架构,一般是由 1 主多从构成,特殊场景下也会存在多主多从的架构。 无论哪一种架构,对于应用程序来说都是多个数据源,增加了代码的复杂性。如果配合 mycat,则可以实现屏蔽数据库复…

【C++】深入剖析vector

好久不见~让大家久等啦~ 本期让我们来揭开vector的面纱,看看它底层是怎么实现的~ 目录 一、STL定义vector的源码分析: 二、vector的模拟实现 2.1 vector框架的搭建(一些简单功能函数的实现) 2.2 迭代器失效问题 2.2.1 实现i…

【Spring】SpringCloud Ribbon中的7种负载均衡策略!

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员,2024届电子信息研究生 负载均衡通器常有两种实现手段,一种是服务端负载均衡器,另一种是客户端负载均衡器,而我们今天的主角 Ribbon 就属于后者——客户端负载均衡器。 服务…

个人总结:测试用例万能公式+常见例子(公式的运用)

前言 测试工程师面试的时候,有时候会当场考测试用例,毕竟这是测试工程师的基本功。 对于我来说,让我写测试用例会比让我直接说测试用例更好点。 直接嘴里说出来,容易逻辑混乱,给人一种想到啥说啥的感觉。 其实个人感…

MySQL学习基础篇(九)---子查询

MySQL学习基础篇(九)—子查询 子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。SQL 中子查询的使用大大增强了 SELECT查询的能力,因为很多时候查询需要从结果集中获取数据,或者需要从同一个表中先计算得出…

MySQL安装与部署

第一种方法:在线安装 配置一个安装yum源 Adding the MySQL Yum Repository 可以手动配置yum源,baseurl指向国内镜像源地址,比如清华、中科大。 Installing MySQL Starting the MySQL Server: 查询临时登录密码 修改数据库密码…

CTFHub XSS DOM反射 WriteUp

前言:本文需要注册一个xss平台,以接收xss反弹回来的数据,请自己在互联网上寻找合适的xss平台 1. 构造闭合语句 根据题目提示,判断网站存在DOM xss漏洞 查看页面源代码,发现关键位置,其中CTFHub is very n…

【揭秘Vue核心】深入解析Object.defineProperty和Proxy的区别,让你秒懂!

问题:Object.difineProperty 和 proxy 有什么区别? Object.defineProperty 和 Proxy 是用于实现响应式数据的两种不同方式。 Object.defineProperty Object.defineProperty 通过直接修改对象的属性描述符来实现数据的劫持。Vue 2.x 中就是通过 Objec…

vscode 之 工作区的应用(解决vue2插件vetur、vue3插件volar禁用启用问题)

目录 前言创建工作区添加文件夹到工作区为当前打开的工作区指定特定环境工作区删除文件夹如何切换工作区 前言 工作区???为什么要工作区??? 首先工作区简单理解就是vscode工作时的区域、范围; 延…

如何正确的安装MySQL

1. 使用rpm包在线安装 1.1 确认自己电脑版本(linux) [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)ps:本次安装采用的系统为Centos 7.9 所使用的mysql为5.7 1.2 下载rpm包 下载地址 https://dev.mysql.com/downloads/mysql/ ps: 自己…

初识树莓派:强大功能与创造力的结合

目录 树莓派4B简介: 树莓派系统镜像下载: 树莓派4B简介: 树莓派4B是一款功能强大且受欢迎的单板计算机,由树莓派基金会开发。作为树莓派系列的最新版本,4B提供了许多令人兴奋的特性和改进,使其成为教育、创…

SSM简单项目遇到的几个问题(最后一个问题,虽然能运行,但是我找不出问题出在哪里)

这几个问题,干扰了我很长时间。 主要因为书本的例子,是通过controller层返回到jsp层。但是,最后一个SSM项目,它用的是controller返回信息给Service层,再由Service层返回Jsp层。 实训:编写一个模糊查询姓名…

【Java基础教程】(三)程序概念篇 · 上:探索Java编程基础,注释、标识符、关键字、数据类型~

Java基础教程之程序概念 上 本节学习目标1️⃣ Java中的注释2️⃣ 标识符3️⃣ 关键字4️⃣ 数据类型4.1 整型🔍 什么是变量?什么是常量?🔍关于数据类型转换的规则? 4.2 浮点数🔍关于Java的计算的缺陷 4.3…

chatGPT写综述

文献是任何学术研究的基础,但搜集和整合众多的信息,写成一篇健全的文献综述却是一项艰巨的任务。随着人工智能技术的发展,大型预训练语言模型(LM),如OpenAI的ChatGPT变得越来越流行。本文将探讨如何使用Cha…

K8S的概念和基本应用

学习视频:Kubernetes基本概念和应用_哔哩哔哩_bilibili 零 . 架构概览 master节点:管理调度集群资源,一般为多节点构成,可以是物理机,也可以是虚拟机。worker节点:资源的提供者,一般为多节点构…